2023-05-11 14:34:16 +00:00
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
import (
|
2023-07-14 23:19:43 +00:00
|
|
|
"context"
|
2024-05-04 15:56:12 +00:00
|
|
|
"errors"
|
2023-05-11 14:34:16 +00:00
|
|
|
"fmt"
|
2023-07-14 23:19:43 +00:00
|
|
|
"os"
|
2023-05-11 14:34:16 +00:00
|
|
|
"strings"
|
2023-07-14 23:19:43 +00:00
|
|
|
"time"
|
2023-05-11 14:34:16 +00:00
|
|
|
|
2024-06-23 08:24:36 +00:00
|
|
|
grpc "github.com/mudler/LocalAI/pkg/grpc"
|
2023-07-14 23:19:43 +00:00
|
|
|
"github.com/phayes/freeport"
|
2023-05-11 14:34:16 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-18 11:24:12 +00:00
|
|
|
const (
|
|
|
|
|
LLamaCPP = "llama-cpp"
|
|
|
|
|
)
|
|
|
|
|
|
2023-12-16 17:22:45 +00:00
|
|
|
var Aliases map[string]string = map[string]string{
|
2025-01-18 17:30:30 +00:00
|
|
|
"go-llama": LLamaCPP,
|
|
|
|
|
"llama": LLamaCPP,
|
|
|
|
|
"embedded-store": LocalStoreBackend,
|
|
|
|
|
"huggingface-embeddings": TransformersBackend,
|
|
|
|
|
"langchain-huggingface": LCHuggingFaceBackend,
|
|
|
|
|
"transformers-musicgen": TransformersBackend,
|
|
|
|
|
"sentencetransformers": TransformersBackend,
|
2025-01-23 08:30:47 +00:00
|
|
|
"mamba": TransformersBackend,
|
2025-01-22 18:34:16 +00:00
|
|
|
"stablediffusion": StableDiffusionGGMLBackend,
|
2025-01-18 17:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var TypeAlias map[string]string = map[string]string{
|
|
|
|
|
"sentencetransformers": "SentenceTransformer",
|
|
|
|
|
"huggingface-embeddings": "SentenceTransformer",
|
2025-01-23 08:30:47 +00:00
|
|
|
"mamba": "Mamba",
|
2025-01-18 17:30:30 +00:00
|
|
|
"transformers-musicgen": "MusicgenForConditionalGeneration",
|
2023-12-16 17:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 14:34:16 +00:00
|
|
|
const (
|
2025-01-22 18:34:16 +00:00
|
|
|
WhisperBackend = "whisper"
|
|
|
|
|
StableDiffusionGGMLBackend = "stablediffusion-ggml"
|
|
|
|
|
LCHuggingFaceBackend = "huggingface"
|
2024-03-22 20:14:04 +00:00
|
|
|
|
2025-01-17 17:16:17 +00:00
|
|
|
TransformersBackend = "transformers"
|
|
|
|
|
LocalStoreBackend = "local-store"
|
2023-05-11 14:34:16 +00:00
|
|
|
)
|
|
|
|
|
|
2023-07-20 20:10:12 +00:00
|
|
|
// starts the grpcModelProcess for the backend, and returns a grpc client
|
|
|
|
|
// It also loads the model
|
2025-07-18 11:24:12 +00:00
|
|
|
func (ml *ModelLoader) grpcModel(backend string, o *Options) func(string, string, string) (*Model, error) {
|
2024-10-02 06:55:58 +00:00
|
|
|
return func(modelID, modelName, modelFile string) (*Model, error) {
|
2024-06-06 06:40:51 +00:00
|
|
|
|
2024-10-02 06:55:58 +00:00
|
|
|
log.Debug().Msgf("Loading Model %s with gRPC (file: %s) (backend: %s): %+v", modelID, modelFile, backend, *o)
|
2023-07-14 23:19:43 +00:00
|
|
|
|
2024-08-25 12:36:09 +00:00
|
|
|
var client *Model
|
2023-07-14 23:19:43 +00:00
|
|
|
|
2023-07-20 20:10:12 +00:00
|
|
|
getFreeAddress := func() (string, error) {
|
|
|
|
|
port, err := freeport.GetFreePort()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("failed allocating free ports: %s", err.Error())
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("127.0.0.1:%d", port), nil
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-07 13:37:45 +00:00
|
|
|
// If no specific model path is set for transformers/HF, set it to the model path
|
|
|
|
|
for _, env := range []string{"HF_HOME", "TRANSFORMERS_CACHE", "HUGGINGFACE_HUB_CACHE"} {
|
|
|
|
|
if os.Getenv(env) == "" {
|
2024-04-29 13:11:42 +00:00
|
|
|
err := os.Setenv(env, ml.ModelPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error().Err(err).Str("name", env).Str("modelPath", ml.ModelPath).Msg("unable to set environment variable to modelPath")
|
|
|
|
|
}
|
2024-03-07 13:37:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:10:12 +00:00
|
|
|
// Check if the backend is provided as external
|
2025-06-15 12:56:52 +00:00
|
|
|
if uri, ok := ml.GetAllExternalBackends(o)[backend]; ok {
|
2023-07-20 20:10:12 +00:00
|
|
|
log.Debug().Msgf("Loading external backend: %s", uri)
|
|
|
|
|
// check if uri is a file or a address
|
2024-08-23 22:27:14 +00:00
|
|
|
if fi, err := os.Stat(uri); err == nil {
|
|
|
|
|
log.Debug().Msgf("external backend is file: %+v", fi)
|
2023-07-20 20:10:12 +00:00
|
|
|
serverAddress, err := getFreeAddress()
|
|
|
|
|
if err != nil {
|
2024-08-25 12:36:09 +00:00
|
|
|
return nil, fmt.Errorf("failed allocating free ports: %s", err.Error())
|
2023-07-20 20:10:12 +00:00
|
|
|
}
|
|
|
|
|
// Make sure the process is executable
|
2024-10-02 06:55:58 +00:00
|
|
|
process, err := ml.startProcess(uri, modelID, serverAddress)
|
2024-09-26 10:44:55 +00:00
|
|
|
if err != nil {
|
2024-08-23 22:27:14 +00:00
|
|
|
log.Error().Err(err).Str("path", uri).Msg("failed to launch ")
|
2024-08-25 12:36:09 +00:00
|
|
|
return nil, err
|
2023-07-20 20:10:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Debug().Msgf("GRPC Service Started")
|
|
|
|
|
|
2024-10-02 06:55:58 +00:00
|
|
|
client = NewModel(modelID, serverAddress, process)
|
2023-07-20 20:10:12 +00:00
|
|
|
} else {
|
2024-10-04 16:32:29 +00:00
|
|
|
log.Debug().Msg("external backend is a uri")
|
2023-07-20 20:10:12 +00:00
|
|
|
// address
|
2024-10-02 06:55:58 +00:00
|
|
|
client = NewModel(modelID, uri, nil)
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
2023-07-20 20:10:12 +00:00
|
|
|
} else {
|
2025-08-28 17:11:02 +00:00
|
|
|
log.Error().Msgf("Backend not found: %s", backend)
|
2025-07-22 14:31:04 +00:00
|
|
|
return nil, fmt.Errorf("backend not found: %s", backend)
|
2023-07-20 20:10:12 +00:00
|
|
|
}
|
2023-07-14 23:19:43 +00:00
|
|
|
|
2024-09-17 14:51:40 +00:00
|
|
|
log.Debug().Msgf("Wait for the service to start up")
|
2025-01-18 17:30:30 +00:00
|
|
|
log.Debug().Msgf("Options: %+v", o.gRPCOptions)
|
2024-09-17 14:51:40 +00:00
|
|
|
|
2023-07-14 23:19:43 +00:00
|
|
|
// Wait for the service to start up
|
|
|
|
|
ready := false
|
2023-08-15 23:11:32 +00:00
|
|
|
for i := 0; i < o.grpcAttempts; i++ {
|
2024-01-07 23:37:02 +00:00
|
|
|
alive, err := client.GRPC(o.parallelRequests, ml.wd).HealthCheck(context.Background())
|
|
|
|
|
if alive {
|
2023-07-14 23:19:43 +00:00
|
|
|
log.Debug().Msgf("GRPC Service Ready")
|
|
|
|
|
ready = true
|
|
|
|
|
break
|
|
|
|
|
}
|
2024-01-07 23:37:02 +00:00
|
|
|
if err != nil && i == o.grpcAttempts-1 {
|
2024-04-04 07:24:22 +00:00
|
|
|
log.Error().Err(err).Msg("failed starting/connecting to the gRPC service")
|
2024-01-07 23:37:02 +00:00
|
|
|
}
|
2023-08-15 23:11:32 +00:00
|
|
|
time.Sleep(time.Duration(o.grpcAttemptsDelay) * time.Second)
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !ready {
|
|
|
|
|
log.Debug().Msgf("GRPC Service NOT ready")
|
2024-10-02 18:37:40 +00:00
|
|
|
if process := client.Process(); process != nil {
|
|
|
|
|
process.Stop()
|
|
|
|
|
}
|
2024-08-25 12:36:09 +00:00
|
|
|
return nil, fmt.Errorf("grpc service not ready")
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
options := *o.gRPCOptions
|
2023-08-07 20:39:10 +00:00
|
|
|
options.Model = modelName
|
|
|
|
|
options.ModelFile = modelFile
|
2024-10-31 11:12:22 +00:00
|
|
|
options.ModelPath = ml.ModelPath
|
2023-07-14 23:19:43 +00:00
|
|
|
|
|
|
|
|
log.Debug().Msgf("GRPC: Loading model with options: %+v", options)
|
|
|
|
|
|
2023-11-26 17:36:23 +00:00
|
|
|
res, err := client.GRPC(o.parallelRequests, ml.wd).LoadModel(o.context, &options)
|
2023-07-14 23:19:43 +00:00
|
|
|
if err != nil {
|
2024-10-02 18:37:40 +00:00
|
|
|
if process := client.Process(); process != nil {
|
|
|
|
|
process.Stop()
|
|
|
|
|
}
|
2024-08-25 12:36:09 +00:00
|
|
|
return nil, fmt.Errorf("could not load model: %w", err)
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
|
|
|
|
if !res.Success {
|
2024-10-02 18:37:40 +00:00
|
|
|
if process := client.Process(); process != nil {
|
|
|
|
|
process.Stop()
|
|
|
|
|
}
|
2024-08-25 12:36:09 +00:00
|
|
|
return nil, fmt.Errorf("could not load model (no success): %s", res.Message)
|
2023-07-14 23:19:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return client, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-08 20:54:25 +00:00
|
|
|
func (ml *ModelLoader) backendLoader(opts ...Option) (client grpc.Backend, err error) {
|
2023-07-14 23:19:43 +00:00
|
|
|
o := NewOptions(opts...)
|
|
|
|
|
|
2025-02-10 11:06:16 +00:00
|
|
|
log.Info().Str("modelID", o.modelID).Str("backend", o.backendString).Str("o.model", o.model).Msg("BackendLoader starting")
|
2023-07-14 23:19:43 +00:00
|
|
|
|
|
|
|
|
backend := strings.ToLower(o.backendString)
|
2023-12-16 17:22:45 +00:00
|
|
|
if realBackend, exists := Aliases[backend]; exists {
|
2025-01-18 17:30:30 +00:00
|
|
|
typeAlias, exists := TypeAlias[backend]
|
|
|
|
|
if exists {
|
|
|
|
|
log.Debug().Msgf("'%s' is a type alias of '%s' (%s)", backend, realBackend, typeAlias)
|
|
|
|
|
o.gRPCOptions.Type = typeAlias
|
|
|
|
|
} else {
|
|
|
|
|
log.Debug().Msgf("'%s' is an alias of '%s'", backend, realBackend)
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-16 17:22:45 +00:00
|
|
|
backend = realBackend
|
|
|
|
|
}
|
2023-07-20 20:10:12 +00:00
|
|
|
|
2025-07-19 06:31:33 +00:00
|
|
|
model, err := ml.LoadModel(o.modelID, o.model, ml.grpcModel(backend, o))
|
2023-11-16 07:20:05 +00:00
|
|
|
if err != nil {
|
2025-08-28 17:11:02 +00:00
|
|
|
log.Error().Str("modelID", o.modelID).Err(err).Msgf("Failed to load model %s with backend %s", o.modelID, o.backendString)
|
2025-07-18 11:24:12 +00:00
|
|
|
return nil, err
|
2023-05-11 14:34:16 +00:00
|
|
|
}
|
2023-11-16 07:20:05 +00:00
|
|
|
|
2024-08-25 12:36:09 +00:00
|
|
|
return model.GRPC(o.parallelRequests, ml.wd), nil
|
2023-05-11 14:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 14:55:57 +00:00
|
|
|
func (ml *ModelLoader) stopActiveBackends(modelID string, singleActiveBackend bool) {
|
2025-03-31 22:01:10 +00:00
|
|
|
if !singleActiveBackend {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-11 14:55:57 +00:00
|
|
|
// If we can have only one backend active, kill all the others (except external backends)
|
2025-03-31 22:01:10 +00:00
|
|
|
|
|
|
|
|
// Stop all backends except the one we are going to load
|
|
|
|
|
log.Debug().Msgf("Stopping all backends except '%s'", modelID)
|
|
|
|
|
err := ml.StopGRPC(allExcept(modelID))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error().Err(err).Str("keptModel", modelID).Msg("error while shutting down all backends except for the keptModel - greedyloader continuing")
|
2024-10-11 14:55:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-01 18:58:11 +00:00
|
|
|
func (ml *ModelLoader) Close() {
|
|
|
|
|
if !ml.singletonMode {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ml.singletonLock.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ml *ModelLoader) lockBackend() {
|
|
|
|
|
if !ml.singletonMode {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ml.singletonLock.Lock()
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-08 20:54:25 +00:00
|
|
|
func (ml *ModelLoader) Load(opts ...Option) (grpc.Backend, error) {
|
2025-04-01 18:58:11 +00:00
|
|
|
ml.lockBackend() // grab the singleton lock if needed
|
|
|
|
|
|
2023-07-14 23:19:43 +00:00
|
|
|
o := NewOptions(opts...)
|
|
|
|
|
|
2023-08-18 23:49:33 +00:00
|
|
|
// Return earlier if we have a model already loaded
|
|
|
|
|
// (avoid looping through all the backends)
|
2024-10-02 06:55:58 +00:00
|
|
|
if m := ml.CheckIsLoaded(o.modelID); m != nil {
|
|
|
|
|
log.Debug().Msgf("Model '%s' already loaded", o.modelID)
|
2023-11-16 07:20:05 +00:00
|
|
|
|
2024-08-25 12:36:09 +00:00
|
|
|
return m.GRPC(o.parallelRequests, ml.wd), nil
|
2023-05-11 14:34:16 +00:00
|
|
|
}
|
2024-08-25 12:36:09 +00:00
|
|
|
|
2025-04-01 18:58:11 +00:00
|
|
|
ml.stopActiveBackends(o.modelID, ml.singletonMode)
|
2023-08-18 23:49:33 +00:00
|
|
|
|
2025-03-31 22:01:10 +00:00
|
|
|
// if a backend is defined, return the loader directly
|
2024-11-08 20:54:25 +00:00
|
|
|
if o.backendString != "" {
|
|
|
|
|
return ml.backendLoader(opts...)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-31 22:01:10 +00:00
|
|
|
// Otherwise scan for backends in the asset directory
|
2023-05-11 14:34:16 +00:00
|
|
|
var err error
|
|
|
|
|
|
2024-05-14 23:17:02 +00:00
|
|
|
// get backends embedded in the binary
|
2025-07-22 14:31:04 +00:00
|
|
|
autoLoadBackends := []string{}
|
2024-05-13 09:37:52 +00:00
|
|
|
|
2024-05-14 23:17:02 +00:00
|
|
|
// append externalBackends supplied by the user via the CLI
|
2025-07-18 11:24:12 +00:00
|
|
|
for b := range ml.GetAllExternalBackends(o) {
|
2024-05-14 23:17:02 +00:00
|
|
|
autoLoadBackends = append(autoLoadBackends, b)
|
2025-07-22 14:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(autoLoadBackends) == 0 {
|
|
|
|
|
log.Error().Msg("No backends found")
|
|
|
|
|
return nil, fmt.Errorf("no backends found")
|
2023-07-20 20:10:12 +00:00
|
|
|
}
|
2024-01-07 23:37:02 +00:00
|
|
|
|
2024-05-14 23:17:02 +00:00
|
|
|
log.Debug().Msgf("Loading from the following backends (in order): %+v", autoLoadBackends)
|
|
|
|
|
|
2024-10-02 06:55:58 +00:00
|
|
|
log.Info().Msgf("Trying to load the model '%s' with the backend '%s'", o.modelID, autoLoadBackends)
|
2023-07-14 23:19:43 +00:00
|
|
|
|
2024-05-14 23:17:02 +00:00
|
|
|
for _, key := range autoLoadBackends {
|
2024-05-13 09:37:52 +00:00
|
|
|
log.Info().Msgf("[%s] Attempting to load", key)
|
2024-10-02 06:55:58 +00:00
|
|
|
options := append(opts, []Option{
|
2024-05-13 09:37:52 +00:00
|
|
|
WithBackendString(key),
|
2024-10-02 06:55:58 +00:00
|
|
|
}...)
|
2023-07-20 20:10:12 +00:00
|
|
|
|
2024-11-08 20:54:25 +00:00
|
|
|
model, modelerr := ml.backendLoader(options...)
|
2023-05-11 14:34:16 +00:00
|
|
|
if modelerr == nil && model != nil {
|
2024-05-13 09:37:52 +00:00
|
|
|
log.Info().Msgf("[%s] Loads OK", key)
|
2023-05-11 14:34:16 +00:00
|
|
|
return model, nil
|
|
|
|
|
} else if modelerr != nil {
|
2024-06-05 06:45:24 +00:00
|
|
|
err = errors.Join(err, fmt.Errorf("[%s]: %w", key, modelerr))
|
2024-05-13 09:37:52 +00:00
|
|
|
log.Info().Msgf("[%s] Fails: %s", key, modelerr.Error())
|
2023-07-17 21:58:15 +00:00
|
|
|
} else if model == nil {
|
2024-06-05 06:45:24 +00:00
|
|
|
err = errors.Join(err, fmt.Errorf("backend %s returned no usable model", key))
|
2024-05-13 09:37:52 +00:00
|
|
|
log.Info().Msgf("[%s] Fails: %s", key, "backend returned no usable model")
|
2023-05-11 14:34:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-01 18:58:11 +00:00
|
|
|
ml.Close() // make sure to release the lock in case of failure
|
|
|
|
|
|
2023-05-11 14:34:16 +00:00
|
|
|
return nil, fmt.Errorf("could not load model - all backends returned error: %s", err.Error())
|
|
|
|
|
}
|