fix: do not show invalid backends (#6058)

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2025-08-14 13:01:56 +02:00
committed by GitHub
parent bf60ca5bf0
commit b52bfaf1b3
5 changed files with 120 additions and 48 deletions

View File

@@ -30,12 +30,11 @@ func findLLamaCPPBackend(backendSystemPath string) (string, error) {
log.Debug().Msgf("System backends: %v", backends)
backendPath := ""
for b, path := range backends {
if b == "llama-cpp" {
backendPath = filepath.Dir(path)
break
}
backend, ok := backends.Get("llama-cpp")
if !ok {
return "", errors.New("llama-cpp backend not found, install it first")
}
backendPath = filepath.Dir(backend.RunFile)
if backendPath == "" {
return "", errors.New("llama-cpp backend not found, install it first")

View File

@@ -66,7 +66,7 @@ func InstallBackendFromGallery(galleries []config.Gallery, systemState *system.S
if err != nil {
return err
}
if _, ok := backends[name]; ok {
if backends.Exists(name) {
return nil
}
}
@@ -239,55 +239,99 @@ func DeleteBackendFromSystem(basePath string, name string) error {
return os.RemoveAll(backendDirectory)
}
func ListSystemBackends(basePath string) (map[string]string, error) {
backends, err := os.ReadDir(basePath)
type SystemBackend struct {
Name string
RunFile string
IsMeta bool
Metadata *BackendMetadata
}
type SystemBackends map[string]SystemBackend
func (b SystemBackends) Exists(name string) bool {
_, ok := b[name]
return ok
}
func (b SystemBackends) Get(name string) (SystemBackend, bool) {
backend, ok := b[name]
return backend, ok
}
func (b SystemBackends) GetAll() []SystemBackend {
backends := make([]SystemBackend, 0)
for _, backend := range b {
backends = append(backends, backend)
}
return backends
}
func ListSystemBackends(basePath string) (SystemBackends, error) {
potentialBackends, err := os.ReadDir(basePath)
if err != nil {
return nil, err
}
backendsNames := make(map[string]string)
backends := make(SystemBackends)
for _, backend := range backends {
if backend.IsDir() {
runFile := filepath.Join(basePath, backend.Name(), runFile)
for _, potentialBackend := range potentialBackends {
if potentialBackend.IsDir() {
potentialBackendRunFile := filepath.Join(basePath, potentialBackend.Name(), runFile)
var metadata *BackendMetadata
// If metadata file does not exist, we just use the directory name
// and we do not fill the other metadata (such as potential backend Aliases)
metadataFilePath := filepath.Join(basePath, backend.Name(), metadataFile)
metadataFilePath := filepath.Join(basePath, potentialBackend.Name(), metadataFile)
if _, err := os.Stat(metadataFilePath); os.IsNotExist(err) {
metadata = &BackendMetadata{
Name: backend.Name(),
Name: potentialBackend.Name(),
}
} else {
// Check for alias in metadata
metadata, err = readBackendMetadata(filepath.Join(basePath, backend.Name()))
metadata, err = readBackendMetadata(filepath.Join(basePath, potentialBackend.Name()))
if err != nil {
return nil, err
}
}
if !backends.Exists(potentialBackend.Name()) {
// We don't want to override aliases if already set, and if we are meta backend
if _, err := os.Stat(potentialBackendRunFile); err == nil {
backends[potentialBackend.Name()] = SystemBackend{
Name: potentialBackend.Name(),
RunFile: potentialBackendRunFile,
IsMeta: false,
Metadata: metadata,
}
}
}
if metadata == nil {
continue
}
if _, exists := backendsNames[backend.Name()]; !exists {
// We don't want to override aliases if already set, and if we are meta backend
if _, err := os.Stat(runFile); err == nil {
backendsNames[backend.Name()] = runFile
} else {
backendsNames[backend.Name()] = ""
if metadata.Alias != "" {
backends[metadata.Alias] = SystemBackend{
Name: metadata.Alias,
RunFile: potentialBackendRunFile,
IsMeta: false,
Metadata: metadata,
}
}
if metadata.Alias != "" {
backendsNames[metadata.Alias] = runFile
if metadata.MetaBackendFor != "" {
backends[metadata.Name] = SystemBackend{
Name: metadata.Name,
RunFile: filepath.Join(basePath, metadata.MetaBackendFor, runFile),
IsMeta: true,
Metadata: metadata,
}
}
}
}
return backendsNames, nil
return backends, nil
}
func RegisterBackends(basePath string, modelLoader *model.ModelLoader) error {
@@ -296,9 +340,9 @@ func RegisterBackends(basePath string, modelLoader *model.ModelLoader) error {
return err
}
for name, runFile := range backends {
log.Debug().Str("name", name).Str("runFile", runFile).Msg("Registering backend")
modelLoader.SetExternalBackend(name, runFile)
for _, backend := range backends {
log.Debug().Str("name", backend.Name).Str("runFile", backend.RunFile).Msg("Registering backend")
modelLoader.SetExternalBackend(backend.Name, backend.RunFile)
}
return nil

View File

@@ -208,13 +208,16 @@ var _ = Describe("Gallery Backends", func() {
metaBackendPath := filepath.Join(tempDir, "meta-backend")
Expect(metaBackendPath).To(BeADirectory())
metaBackendPath = filepath.Join(tempDir, "meta-backend", "metadata.json")
Expect(metaBackendPath).To(BeARegularFile())
concreteBackendPath := filepath.Join(tempDir, "nvidia-backend")
Expect(concreteBackendPath).To(BeADirectory())
allBackends, err := ListSystemBackends(tempDir)
Expect(err).NotTo(HaveOccurred())
Expect(allBackends).To(HaveKey("meta-backend"))
Expect(allBackends).To(HaveKey("nvidia-backend"))
Expect(allBackends.Exists("meta-backend")).To(BeTrue())
Expect(allBackends.Exists("nvidia-backend")).To(BeTrue())
// Delete meta backend by name
err = DeleteBackendFromSystem(tempDir, "meta-backend")
@@ -284,9 +287,14 @@ var _ = Describe("Gallery Backends", func() {
allBackends, err := ListSystemBackends(tempDir)
Expect(err).NotTo(HaveOccurred())
Expect(allBackends).To(HaveKey("meta-backend"))
Expect(allBackends).To(HaveKey("nvidia-backend"))
Expect(allBackends["meta-backend"]).To(BeEmpty())
Expect(allBackends.Exists("meta-backend")).To(BeTrue())
Expect(allBackends.Exists("nvidia-backend")).To(BeTrue())
backend, ok := allBackends.Get("meta-backend")
Expect(ok).To(BeTrue())
Expect(backend.Metadata.MetaBackendFor).To(Equal("nvidia-backend"))
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, "nvidia-backend", "run.sh")))
Expect(backend.IsMeta).To(BeTrue())
// Delete meta backend by name
err = DeleteBackendFromSystem(tempDir, "meta-backend")
@@ -356,9 +364,11 @@ var _ = Describe("Gallery Backends", func() {
allBackends, err := ListSystemBackends(tempDir)
Expect(err).NotTo(HaveOccurred())
Expect(allBackends).To(HaveKey("meta-backend"))
Expect(allBackends).To(HaveKey("nvidia-backend"))
Expect(allBackends["meta-backend"]).To(Equal(filepath.Join(tempDir, "nvidia-backend", "run.sh")))
Expect(allBackends.Exists("meta-backend")).To(BeTrue())
Expect(allBackends.Exists("nvidia-backend")).To(BeTrue())
backend, ok := allBackends.Get("meta-backend")
Expect(ok).To(BeTrue())
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, "nvidia-backend", "run.sh")))
// Delete meta backend by name
err = DeleteBackendFromSystem(tempDir, "meta-backend")
@@ -402,13 +412,21 @@ var _ = Describe("Gallery Backends", func() {
Expect(err).NotTo(HaveOccurred())
// Should include both the meta backend name and concrete backend name
Expect(backends).To(HaveKey("meta-backend"))
Expect(backends).To(HaveKey("concrete-backend"))
Expect(backends.Exists("meta-backend")).To(BeTrue())
Expect(backends.Exists("concrete-backend")).To(BeTrue())
// meta-backend should point to concrete-backend
Expect(backends.Exists("meta-backend")).To(BeTrue())
backend, ok := backends.Get("meta-backend")
Expect(ok).To(BeTrue())
Expect(backend.Metadata.MetaBackendFor).To(Equal("concrete-backend"))
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, "concrete-backend", "run.sh")))
Expect(backend.IsMeta).To(BeTrue())
// meta-backend should be empty
Expect(backends["meta-backend"]).To(BeEmpty())
// concrete-backend should point to its own run.sh
Expect(backends["concrete-backend"]).To(Equal(filepath.Join(tempDir, "concrete-backend", "run.sh")))
backend, ok = backends.Get("concrete-backend")
Expect(ok).To(BeTrue())
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, "concrete-backend", "run.sh")))
})
})
@@ -457,8 +475,14 @@ var _ = Describe("Gallery Backends", func() {
// Check that the alias was recognized
backends, err := ListSystemBackends(tempDir)
Expect(err).ToNot(HaveOccurred())
Expect(backends).To(HaveKeyWithValue("test-alias", filepath.Join(tempDir, "test-backend", "run.sh")))
Expect(backends).To(HaveKeyWithValue("test-backend", filepath.Join(tempDir, "test-backend", "run.sh")))
Expect(backends.Exists("test-alias")).To(BeTrue())
Expect(backends.Exists("test-backend")).To(BeTrue())
b, ok := backends.Get("test-alias")
Expect(ok).To(BeTrue())
Expect(b.RunFile).To(Equal(filepath.Join(tempDir, "test-backend", "run.sh")))
b, ok = backends.Get("test-backend")
Expect(ok).To(BeTrue())
Expect(b.RunFile).To(Equal(filepath.Join(tempDir, "test-backend", "run.sh")))
})
})
@@ -497,10 +521,13 @@ var _ = Describe("Gallery Backends", func() {
backends, err := ListSystemBackends(tempDir)
Expect(err).NotTo(HaveOccurred())
Expect(backends).To(HaveLen(len(backendNames)))
Expect(backends.GetAll()).To(HaveLen(len(backendNames)))
for _, name := range backendNames {
Expect(backends).To(HaveKeyWithValue(name, filepath.Join(tempDir, name, "run.sh")))
Expect(backends.Exists(name)).To(BeTrue())
backend, ok := backends.Get(name)
Expect(ok).To(BeTrue())
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, name, "run.sh")))
}
})
@@ -528,7 +555,10 @@ var _ = Describe("Gallery Backends", func() {
backends, err := ListSystemBackends(tempDir)
Expect(err).NotTo(HaveOccurred())
Expect(backends).To(HaveKeyWithValue(alias, filepath.Join(tempDir, backendName, "run.sh")))
Expect(backends.Exists(alias)).To(BeTrue())
backend, ok := backends.Get(alias)
Expect(ok).To(BeTrue())
Expect(backend.RunFile).To(Equal(filepath.Join(tempDir, backendName, "run.sh")))
})
It("should return error when base path doesn't exist", func() {

View File

@@ -147,8 +147,7 @@ func AvailableBackends(galleries []config.Gallery, basePath string) (GalleryElem
if err != nil {
return false
}
_, exists := backends[backend.GetName()]
return exists
return backends.Exists(backend.GetName())
})
if err != nil {
return nil, err

View File

@@ -117,7 +117,7 @@ func (mgs *BackendEndpointService) ListBackendsEndpoint() func(c *fiber.Ctx) err
if err != nil {
return err
}
return c.JSON(backends)
return c.JSON(backends.GetAll())
}
}