Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions core/gallery/backend_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package gallery
import "github.com/mudler/LocalAI/core/config"

type GalleryBackend struct {
Metadata `json:",inline" yaml:",inline"`
Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
Metadata `json:",inline" yaml:",inline"`
Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
CapabilitiesMap map[string]string `json:"capabilities,omitempty" yaml:"capabilities,omitempty"`
}

type GalleryBackends []*GalleryBackend
Expand All @@ -14,6 +15,10 @@ func (m *GalleryBackend) SetGallery(gallery config.Gallery) {
m.Gallery = gallery
}

func (m *GalleryBackend) IsMeta() bool {
return len(m.CapabilitiesMap) > 0
}

func (m *GalleryBackend) SetInstalled(installed bool) {
m.Installed = installed
}
Expand Down
111 changes: 104 additions & 7 deletions core/gallery/backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,70 @@
"path/filepath"

"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/core/system"
"github.com/mudler/LocalAI/pkg/model"
"github.com/mudler/LocalAI/pkg/oci"
"github.com/rs/zerolog/log"
)

const (
aliasFile = "alias"
metaFile = "meta"
runFile = "run.sh"
)

func findBestBackendFromMeta(backend *GalleryBackend, systemState *system.SystemState, backends GalleryElements[*GalleryBackend]) *GalleryBackend {
realBackend := backend.CapabilitiesMap[systemState.GPUVendor]
if realBackend == "" {
return nil
}

return backends.FindByName(realBackend)
}

// Installs a model from the gallery
func InstallBackendFromGallery(galleries []config.Gallery, name string, basePath string, downloadStatus func(string, string, string, float64)) error {
func InstallBackendFromGallery(galleries []config.Gallery, systemState *system.SystemState, name string, basePath string, downloadStatus func(string, string, string, float64)) error {
log.Debug().Interface("galleries", galleries).Str("name", name).Msg("Installing backend from gallery")

backends, err := AvailableBackends(galleries, basePath)
if err != nil {
return err
}

backend := FindGalleryElement(backends, name, basePath)
if backend == nil {
return fmt.Errorf("no model found with name %q", name)
return fmt.Errorf("no backend found with name %q", name)
}

if backend.IsMeta() {
log.Debug().Interface("systemState", systemState).Str("name", name).Msg("Backend is a meta backend")

// Then, let's try to find the best backend based on the capabilities map
bestBackend := findBestBackendFromMeta(backend, systemState, backends)
if bestBackend == nil {
return fmt.Errorf("no backend found with capabilities %q", backend.CapabilitiesMap)
}

log.Debug().Str("name", name).Str("bestBackend", bestBackend.Name).Msg("Installing backend from meta backend")

// Then, let's install the best backend
if err := InstallBackend(basePath, bestBackend, downloadStatus); err != nil {
return err
}

// we need now to create a path for the meta backend, with the alias to the installed ones so it can be used to remove it
metaBackendPath := filepath.Join(basePath, name)
if err := os.MkdirAll(metaBackendPath, 0750); err != nil {
return fmt.Errorf("failed to create meta backend path %q: %v", metaBackendPath, err)
}

// Then, let's create an meta file to point to the best backend
metaFile := filepath.Join(metaBackendPath, metaFile)
if err := os.WriteFile(metaFile, []byte(bestBackend.Name), 0644); err != nil {
return fmt.Errorf("failed to write meta file %q: %v", metaFile, err)
}

return nil
}

return InstallBackend(basePath, backend, downloadStatus)
Expand All @@ -32,6 +82,10 @@
return fmt.Errorf("failed to create base path: %v", err)
}

if config.IsMeta() {
return fmt.Errorf("meta backends cannot be installed directly")
}

name := config.Name

img, err := oci.GetImage(config.URI, "", nil, nil)
Expand All @@ -50,7 +104,7 @@

if config.Alias != "" {
// Write an alias file inside
aliasFile := filepath.Join(backendPath, "alias")
aliasFile := filepath.Join(backendPath, aliasFile)
if err := os.WriteFile(aliasFile, []byte(config.Alias), 0644); err != nil {
return fmt.Errorf("failed to write alias file %q: %v", aliasFile, err)
}
Expand All @@ -60,9 +114,52 @@
}

func DeleteBackendFromSystem(basePath string, name string) error {
backendFile := filepath.Join(basePath, name)
backendDirectory := filepath.Join(basePath, name)

// check if the backend dir exists
if _, err := os.Stat(backendDirectory); os.IsNotExist(err) {
// if doesn't exist, it might be an alias, so we need to check if we have a matching alias in
// all the backends in the basePath
backends, err := os.ReadDir(basePath)
if err != nil {
return err
}

for _, backend := range backends {
if backend.IsDir() {
aliasFile := filepath.Join(basePath, backend.Name(), aliasFile)
alias, err := os.ReadFile(aliasFile)
if err != nil {
return err
}
if string(alias) == name {
backendDirectory = filepath.Join(basePath, backend.Name())
break
}
}
}

if backendDirectory == "" {
return fmt.Errorf("no backend found with name %q", name)
}
}

// If it's a meta, delete also associated backend
metaFile := filepath.Join(backendDirectory, metaFile)
if _, err := os.Stat(metaFile); err == nil {
meta, err := os.ReadFile(metaFile)
if err != nil {
return err
}
metaBackendDirectory := filepath.Join(basePath, string(meta))
log.Debug().Str("backendDirectory", metaBackendDirectory).Msg("Deleting meta backend")
if _, err := os.Stat(metaBackendDirectory); os.IsNotExist(err) {
return fmt.Errorf("meta backend %q not found", string(meta))
}
os.RemoveAll(metaBackendDirectory)
}

return os.RemoveAll(backendFile)
return os.RemoveAll(backendDirectory)
}

func ListSystemBackends(basePath string) (map[string]string, error) {
Expand All @@ -75,10 +172,10 @@

for _, backend := range backends {
if backend.IsDir() {
runFile := filepath.Join(basePath, backend.Name(), "run.sh")
runFile := filepath.Join(basePath, backend.Name(), runFile)
backendsNames[backend.Name()] = runFile

aliasFile := filepath.Join(basePath, backend.Name(), "alias")
aliasFile := filepath.Join(basePath, backend.Name(), aliasFile)
if _, err := os.Stat(aliasFile); err == nil {
// read the alias file, and use it as key
alias, err := os.ReadFile(aliasFile)
Expand Down
Loading
Loading