|
|
package services |
|
|
|
|
|
import ( |
|
|
"context" |
|
|
"encoding/json" |
|
|
"errors" |
|
|
"fmt" |
|
|
"os" |
|
|
|
|
|
"github.com/mudler/LocalAI/core/config" |
|
|
"github.com/mudler/LocalAI/core/gallery" |
|
|
"github.com/mudler/LocalAI/pkg/model" |
|
|
"github.com/mudler/LocalAI/pkg/system" |
|
|
"github.com/mudler/LocalAI/pkg/utils" |
|
|
"github.com/mudler/xlog" |
|
|
"gopkg.in/yaml.v2" |
|
|
) |
|
|
|
|
|
const ( |
|
|
processingMessage = "processing file: %s. Total: %s. Current: %s" |
|
|
) |
|
|
|
|
|
func (g *GalleryService) modelHandler(op *GalleryOp[gallery.GalleryModel, gallery.ModelConfig], cl *config.ModelConfigLoader, systemState *system.SystemState) error { |
|
|
utils.ResetDownloadTimers() |
|
|
|
|
|
|
|
|
if op.Context != nil { |
|
|
select { |
|
|
case <-op.Context.Done(): |
|
|
g.UpdateStatus(op.ID, &GalleryOpStatus{ |
|
|
Cancelled: true, |
|
|
Processed: true, |
|
|
Message: "cancelled", |
|
|
GalleryElementName: op.GalleryElementName, |
|
|
}) |
|
|
return op.Context.Err() |
|
|
default: |
|
|
} |
|
|
} |
|
|
|
|
|
g.UpdateStatus(op.ID, &GalleryOpStatus{Message: fmt.Sprintf("processing model: %s", op.GalleryElementName), Progress: 0, Cancellable: true}) |
|
|
|
|
|
|
|
|
progressCallback := func(fileName string, current string, total string, percentage float64) { |
|
|
|
|
|
if op.Context != nil { |
|
|
select { |
|
|
case <-op.Context.Done(): |
|
|
return |
|
|
default: |
|
|
} |
|
|
} |
|
|
g.UpdateStatus(op.ID, &GalleryOpStatus{Message: fmt.Sprintf(processingMessage, fileName, total, current), FileName: fileName, Progress: percentage, TotalFileSize: total, DownloadedFileSize: current, Cancellable: true}) |
|
|
utils.DisplayDownloadFunction(fileName, current, total, percentage) |
|
|
} |
|
|
|
|
|
err := processModelOperation(op, systemState, g.modelLoader, g.appConfig.EnforcePredownloadScans, g.appConfig.AutoloadBackendGalleries, progressCallback) |
|
|
if err != nil { |
|
|
|
|
|
if op.Context != nil && errors.Is(err, op.Context.Err()) { |
|
|
g.UpdateStatus(op.ID, &GalleryOpStatus{ |
|
|
Cancelled: true, |
|
|
Processed: true, |
|
|
Message: "cancelled", |
|
|
GalleryElementName: op.GalleryElementName, |
|
|
}) |
|
|
return err |
|
|
} |
|
|
return err |
|
|
} |
|
|
|
|
|
|
|
|
if op.Context != nil { |
|
|
select { |
|
|
case <-op.Context.Done(): |
|
|
g.UpdateStatus(op.ID, &GalleryOpStatus{ |
|
|
Cancelled: true, |
|
|
Processed: true, |
|
|
Message: "cancelled", |
|
|
GalleryElementName: op.GalleryElementName, |
|
|
}) |
|
|
return op.Context.Err() |
|
|
default: |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
err = cl.LoadModelConfigsFromPath(systemState.Model.ModelsPath, g.appConfig.ToConfigLoaderOptions()...) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
err = cl.Preload(systemState.Model.ModelsPath) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
g.UpdateStatus(op.ID, |
|
|
&GalleryOpStatus{ |
|
|
Deletion: op.Delete, |
|
|
Processed: true, |
|
|
GalleryElementName: op.GalleryElementName, |
|
|
Message: "completed", |
|
|
Progress: 100, |
|
|
Cancellable: false}) |
|
|
|
|
|
return nil |
|
|
} |
|
|
|
|
|
func installModelFromRemoteConfig(ctx context.Context, systemState *system.SystemState, modelLoader *model.ModelLoader, req gallery.GalleryModel, downloadStatus func(string, string, string, float64), enforceScan, automaticallyInstallBackend bool, backendGalleries []config.Gallery) error { |
|
|
config, err := gallery.GetGalleryConfigFromURLWithContext[gallery.ModelConfig](ctx, req.URL, systemState.Model.ModelsPath) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
config.Files = append(config.Files, req.AdditionalFiles...) |
|
|
|
|
|
installedModel, err := gallery.InstallModel(ctx, systemState, req.Name, &config, req.Overrides, downloadStatus, enforceScan) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
if automaticallyInstallBackend && installedModel.Backend != "" { |
|
|
if err := gallery.InstallBackendFromGallery(ctx, backendGalleries, systemState, modelLoader, installedModel.Backend, downloadStatus, false); err != nil { |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
return nil |
|
|
} |
|
|
|
|
|
type galleryModel struct { |
|
|
gallery.GalleryModel `yaml:",inline"` |
|
|
ID string `json:"id"` |
|
|
} |
|
|
|
|
|
func processRequests(systemState *system.SystemState, modelLoader *model.ModelLoader, enforceScan, automaticallyInstallBackend bool, galleries []config.Gallery, backendGalleries []config.Gallery, requests []galleryModel) error { |
|
|
ctx := context.Background() |
|
|
var err error |
|
|
for _, r := range requests { |
|
|
utils.ResetDownloadTimers() |
|
|
if r.ID == "" { |
|
|
err = installModelFromRemoteConfig(ctx, systemState, modelLoader, r.GalleryModel, utils.DisplayDownloadFunction, enforceScan, automaticallyInstallBackend, backendGalleries) |
|
|
|
|
|
} else { |
|
|
err = gallery.InstallModelFromGallery( |
|
|
ctx, galleries, backendGalleries, systemState, modelLoader, r.ID, r.GalleryModel, utils.DisplayDownloadFunction, enforceScan, automaticallyInstallBackend) |
|
|
} |
|
|
} |
|
|
return err |
|
|
} |
|
|
|
|
|
func ApplyGalleryFromFile(systemState *system.SystemState, modelLoader *model.ModelLoader, enforceScan, automaticallyInstallBackend bool, galleries []config.Gallery, backendGalleries []config.Gallery, s string) error { |
|
|
dat, err := os.ReadFile(s) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
var requests []galleryModel |
|
|
|
|
|
if err := yaml.Unmarshal(dat, &requests); err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
return processRequests(systemState, modelLoader, enforceScan, automaticallyInstallBackend, galleries, backendGalleries, requests) |
|
|
} |
|
|
|
|
|
func ApplyGalleryFromString(systemState *system.SystemState, modelLoader *model.ModelLoader, enforceScan, automaticallyInstallBackend bool, galleries []config.Gallery, backendGalleries []config.Gallery, s string) error { |
|
|
var requests []galleryModel |
|
|
err := json.Unmarshal([]byte(s), &requests) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
return processRequests(systemState, modelLoader, enforceScan, automaticallyInstallBackend, galleries, backendGalleries, requests) |
|
|
} |
|
|
|
|
|
|
|
|
func processModelOperation( |
|
|
op *GalleryOp[gallery.GalleryModel, gallery.ModelConfig], |
|
|
systemState *system.SystemState, |
|
|
modelLoader *model.ModelLoader, |
|
|
enforcePredownloadScans bool, |
|
|
automaticallyInstallBackend bool, |
|
|
progressCallback func(string, string, string, float64), |
|
|
) error { |
|
|
ctx := op.Context |
|
|
if ctx == nil { |
|
|
ctx = context.Background() |
|
|
} |
|
|
|
|
|
|
|
|
select { |
|
|
case <-ctx.Done(): |
|
|
return ctx.Err() |
|
|
default: |
|
|
} |
|
|
|
|
|
switch { |
|
|
case op.Delete: |
|
|
return gallery.DeleteModelFromSystem(systemState, op.GalleryElementName) |
|
|
case op.GalleryElement != nil: |
|
|
installedModel, err := gallery.InstallModel( |
|
|
ctx, systemState, op.GalleryElement.Name, |
|
|
op.GalleryElement, |
|
|
op.Req.Overrides, |
|
|
progressCallback, enforcePredownloadScans) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
if automaticallyInstallBackend && installedModel.Backend != "" { |
|
|
xlog.Debug("Installing backend", "backend", installedModel.Backend) |
|
|
if err := gallery.InstallBackendFromGallery(ctx, op.BackendGalleries, systemState, modelLoader, installedModel.Backend, progressCallback, false); err != nil { |
|
|
return err |
|
|
} |
|
|
} |
|
|
return nil |
|
|
case op.GalleryElementName != "": |
|
|
return gallery.InstallModelFromGallery(ctx, op.Galleries, op.BackendGalleries, systemState, modelLoader, op.GalleryElementName, op.Req, progressCallback, enforcePredownloadScans, automaticallyInstallBackend) |
|
|
default: |
|
|
return installModelFromRemoteConfig(ctx, systemState, modelLoader, op.Req, progressCallback, enforcePredownloadScans, automaticallyInstallBackend, op.BackendGalleries) |
|
|
} |
|
|
} |
|
|
|