Spaces:
Configuration error
Configuration error
package startup | |
import ( | |
"errors" | |
"fmt" | |
"os" | |
"path/filepath" | |
"strings" | |
"github.com/mudler/LocalAI/core/config" | |
"github.com/mudler/LocalAI/core/gallery" | |
"github.com/mudler/LocalAI/embedded" | |
"github.com/mudler/LocalAI/pkg/downloader" | |
"github.com/mudler/LocalAI/pkg/utils" | |
"github.com/rs/zerolog/log" | |
) | |
// InstallModels will preload models from the given list of URLs and galleries | |
// It will download the model if it is not already present in the model path | |
// It will also try to resolve if the model is an embedded model YAML configuration | |
func InstallModels(galleries []config.Gallery, modelLibraryURL string, modelPath string, enforceScan bool, downloadStatus func(string, string, string, float64), models ...string) error { | |
// create an error that groups all errors | |
var err error | |
lib, _ := embedded.GetRemoteLibraryShorteners(modelLibraryURL, modelPath) | |
for _, url := range models { | |
// As a best effort, try to resolve the model from the remote library | |
// if it's not resolved we try with the other method below | |
if modelLibraryURL != "" { | |
if lib[url] != "" { | |
log.Debug().Msgf("[startup] model configuration is defined remotely: %s (%s)", url, lib[url]) | |
url = lib[url] | |
} | |
} | |
url = embedded.ModelShortURL(url) | |
uri := downloader.URI(url) | |
switch { | |
case embedded.ExistsInModelsLibrary(url): | |
modelYAML, e := embedded.ResolveContent(url) | |
// If we resolve something, just save it to disk and continue | |
if e != nil { | |
log.Error().Err(e).Msg("error resolving model content") | |
err = errors.Join(err, e) | |
continue | |
} | |
log.Debug().Msgf("[startup] resolved embedded model: %s", url) | |
md5Name := utils.MD5(url) | |
modelDefinitionFilePath := filepath.Join(modelPath, md5Name) + ".yaml" | |
if e := os.WriteFile(modelDefinitionFilePath, modelYAML, 0600); err != nil { | |
log.Error().Err(e).Str("filepath", modelDefinitionFilePath).Msg("error writing model definition") | |
err = errors.Join(err, e) | |
} | |
case uri.LooksLikeOCI(): | |
log.Debug().Msgf("[startup] resolved OCI model to download: %s", url) | |
// convert OCI image name to a file name. | |
ociName := strings.TrimPrefix(url, downloader.OCIPrefix) | |
ociName = strings.TrimPrefix(ociName, downloader.OllamaPrefix) | |
ociName = strings.ReplaceAll(ociName, "/", "__") | |
ociName = strings.ReplaceAll(ociName, ":", "__") | |
// check if file exists | |
if _, e := os.Stat(filepath.Join(modelPath, ociName)); errors.Is(e, os.ErrNotExist) { | |
modelDefinitionFilePath := filepath.Join(modelPath, ociName) | |
e := uri.DownloadFile(modelDefinitionFilePath, "", 0, 0, func(fileName, current, total string, percent float64) { | |
utils.DisplayDownloadFunction(fileName, current, total, percent) | |
}) | |
if e != nil { | |
log.Error().Err(e).Str("url", url).Str("filepath", modelDefinitionFilePath).Msg("error downloading model") | |
err = errors.Join(err, e) | |
} | |
} | |
log.Info().Msgf("[startup] installed model from OCI repository: %s", ociName) | |
case uri.LooksLikeURL(): | |
log.Debug().Msgf("[startup] downloading %s", url) | |
// Extract filename from URL | |
fileName, e := uri.FilenameFromUrl() | |
if e != nil { | |
log.Warn().Err(e).Str("url", url).Msg("error extracting filename from URL") | |
err = errors.Join(err, e) | |
continue | |
} | |
modelPath := filepath.Join(modelPath, fileName) | |
if e := utils.VerifyPath(fileName, modelPath); e != nil { | |
log.Error().Err(e).Str("filepath", modelPath).Msg("error verifying path") | |
err = errors.Join(err, e) | |
continue | |
} | |
// check if file exists | |
if _, e := os.Stat(modelPath); errors.Is(e, os.ErrNotExist) { | |
e := uri.DownloadFile(modelPath, "", 0, 0, func(fileName, current, total string, percent float64) { | |
utils.DisplayDownloadFunction(fileName, current, total, percent) | |
}) | |
if e != nil { | |
log.Error().Err(e).Str("url", url).Str("filepath", modelPath).Msg("error downloading model") | |
err = errors.Join(err, e) | |
} | |
} | |
default: | |
if _, e := os.Stat(url); e == nil { | |
log.Debug().Msgf("[startup] resolved local model: %s", url) | |
// copy to modelPath | |
md5Name := utils.MD5(url) | |
modelYAML, e := os.ReadFile(url) | |
if e != nil { | |
log.Error().Err(e).Str("filepath", url).Msg("error reading model definition") | |
err = errors.Join(err, e) | |
continue | |
} | |
modelDefinitionFilePath := filepath.Join(modelPath, md5Name) + ".yaml" | |
if e := os.WriteFile(modelDefinitionFilePath, modelYAML, 0600); e != nil { | |
log.Error().Err(err).Str("filepath", modelDefinitionFilePath).Msg("error loading model: %s") | |
err = errors.Join(err, e) | |
} | |
} else { | |
// Check if it's a model gallery, or print a warning | |
e, found := installModel(galleries, url, modelPath, downloadStatus, enforceScan) | |
if e != nil && found { | |
log.Error().Err(err).Msgf("[startup] failed installing model '%s'", url) | |
err = errors.Join(err, e) | |
} else if !found { | |
log.Warn().Msgf("[startup] failed resolving model '%s'", url) | |
err = errors.Join(err, fmt.Errorf("failed resolving model '%s'", url)) | |
} | |
} | |
} | |
} | |
return err | |
} | |
func installModel(galleries []config.Gallery, modelName, modelPath string, downloadStatus func(string, string, string, float64), enforceScan bool) (error, bool) { | |
models, err := gallery.AvailableGalleryModels(galleries, modelPath) | |
if err != nil { | |
return err, false | |
} | |
model := gallery.FindModel(models, modelName, modelPath) | |
if model == nil { | |
return err, false | |
} | |
if downloadStatus == nil { | |
downloadStatus = utils.DisplayDownloadFunction | |
} | |
log.Info().Str("model", modelName).Str("license", model.License).Msg("installing model") | |
err = gallery.InstallModelFromGallery(galleries, modelName, modelPath, gallery.GalleryModel{}, downloadStatus, enforceScan) | |
if err != nil { | |
return err, true | |
} | |
return nil, true | |
} | |