MCPcopy Create free account
hub / github.com/mudler/LocalAI / LoadModel

Method LoadModel

pkg/model/loader.go:304–405  ·  view source on GitHub ↗
(modelID, modelName string, loader func(string, string, string) (*Model, error))

Source from the content-addressed store, hash-verified

302}
303
304func (ml *ModelLoader) LoadModel(modelID, modelName string, loader func(string, string, string) (*Model, error)) (*Model, error) {
305 ml.mu.Lock()
306 distributed := ml.modelRouter != nil
307 ml.mu.Unlock()
308
309 if distributed {
310 // Distributed mode: SmartRouter must run per inference request so
311 // PickBestReplica (core/services/nodes/replicapicker.go) picks the
312 // least-loaded replica each time. The cached *Model returned from a
313 // previous call holds a client wrapper bound to one (nodeID,
314 // replicaIndex), so reusing it pins every subsequent request to the
315 // node that won the very first pick — defeating per-replica load
316 // balancing. Bypass the cache and the loading-coalesce map; the
317 // router does its own coalescing for first-time loads (advisory DB
318 // lock + singleflight on backend.install RPC), so concurrent first
319 // requests still produce a single worker-side install.
320 //
321 // TODO(distributed-cache): if profiling shows the per-request
322 // FindAndLockNodeWithModel SELECT FOR UPDATE becomes a hot path
323 // under burst load, replace this branch with a per-modelID cache
324 // that holds a *list* of replicas (refreshed every ~5s in
325 // background) and picks per call via PickBestReplica against
326 // locally-tracked in-flight counters. Same policy, no DB round-trip
327 // per inference. Trade-off: cross-frontend in-flight visibility
328 // becomes eventually consistent, acceptable for 1-3 frontend
329 // deployments.
330 modelFile := filepath.Join(ml.ModelPath, modelName)
331 model, err := loader(modelID, modelName, modelFile)
332 if err != nil {
333 return nil, fmt.Errorf("failed to route model with internal loader: %s", err)
334 }
335 if model == nil {
336 return nil, fmt.Errorf("loader didn't return a model")
337 }
338 // Record the latest mapping so DistributedModelStore.Range, shutdown,
339 // and listing endpoints see a representative entry. The DB is the
340 // source of truth for cluster-wide state; the local store is just a
341 // stub for in-process callers.
342 ml.mu.Lock()
343 ml.store.Set(modelID, model)
344 ml.mu.Unlock()
345 return model, nil
346 }
347
348 ml.mu.Lock()
349
350 // Check if we already have a loaded model
351 if model := ml.checkIsLoaded(modelID); model != nil {
352 ml.mu.Unlock()
353 return model, nil
354 }
355
356 // Check if another goroutine is already loading this model
357 if loadingChan, isLoading := ml.loading[modelID]; isLoading {
358 ml.mu.Unlock()
359 // Wait for the other goroutine to finish loading
360 xlog.Debug("Waiting for model to be loaded by another request", "modelID", modelID)
361 <-loadingChan

Callers 1

backendLoaderMethod · 0.95

Calls 5

checkIsLoadedMethod · 0.95
deleteFunction · 0.85
LockMethod · 0.65
UnlockMethod · 0.65
SetMethod · 0.65

Tested by

no test coverage detected