From 7a36e8d967337dcbe1eb71612f7e7b0a5a470c08 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Fri, 10 Oct 2025 16:35:05 +0200 Subject: [PATCH] chore(ui): skip duplicated entries in search list (#6425) Signed-off-by: Ettore Di Giacinto --- core/gallery/backend_types.go | 6 ++++++ core/http/routes/ui_api.go | 40 ++++++++++++++++++++++++++--------- gallery/index.yaml | 2 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/core/gallery/backend_types.go b/core/gallery/backend_types.go index d0ff1a0ce..a8b930be1 100644 --- a/core/gallery/backend_types.go +++ b/core/gallery/backend_types.go @@ -1,6 +1,8 @@ package gallery import ( + "fmt" + "github.com/mudler/LocalAI/core/config" "github.com/mudler/LocalAI/pkg/system" "github.com/rs/zerolog/log" @@ -72,3 +74,7 @@ func (m *GalleryBackend) GetDescription() string { func (m *GalleryBackend) GetTags() []string { return m.Tags } + +func (m GalleryBackend) ID() string { + return fmt.Sprintf("%s@%s", m.Gallery.Name, m.Name) +} diff --git a/core/http/routes/ui_api.go b/core/http/routes/ui_api.go index edb7a1435..87dcb44c0 100644 --- a/core/http/routes/ui_api.go +++ b/core/http/routes/ui_api.go @@ -159,15 +159,25 @@ func RegisterUIAPIRoutes(app *fiber.App, cl *config.ModelConfigLoader, appConfig models = models.Paginate(pageNum, itemsNum) } - // Convert models to JSON-friendly format + // Convert models to JSON-friendly format and deduplicate by ID modelsJSON := make([]fiber.Map, 0, len(models)) + seenIDs := make(map[string]bool) + for _, m := range models { - galleryID := fmt.Sprintf("%s@%s", m.Gallery.Name, m.Name) - currentlyProcessing := opcache.Exists(galleryID) + modelID := m.ID() + + // Skip duplicate IDs to prevent Alpine.js x-for errors + if seenIDs[modelID] { + log.Debug().Msgf("Skipping duplicate model ID: %s", modelID) + continue + } + seenIDs[modelID] = true + + currentlyProcessing := opcache.Exists(modelID) jobID := "" isDeletionOp := false if currentlyProcessing { - jobID = opcache.Get(galleryID) + jobID = opcache.Get(modelID) status := galleryService.GetStatus(jobID) if status != nil && status.Deletion { isDeletionOp = true @@ -177,7 +187,7 @@ func RegisterUIAPIRoutes(app *fiber.App, cl *config.ModelConfigLoader, appConfig _, trustRemoteCodeExists := m.Overrides["trust_remote_code"] modelsJSON = append(modelsJSON, fiber.Map{ - "id": m.ID(), + "id": modelID, "name": m.Name, "description": m.Description, "icon": m.Icon, @@ -430,15 +440,25 @@ func RegisterUIAPIRoutes(app *fiber.App, cl *config.ModelConfigLoader, appConfig backends = backends.Paginate(pageNum, itemsNum) } - // Convert backends to JSON-friendly format + // Convert backends to JSON-friendly format and deduplicate by ID backendsJSON := make([]fiber.Map, 0, len(backends)) + seenBackendIDs := make(map[string]bool) + for _, b := range backends { - galleryID := fmt.Sprintf("%s@%s", b.Gallery.Name, b.Name) - currentlyProcessing := opcache.Exists(galleryID) + backendID := b.ID() + + // Skip duplicate IDs to prevent Alpine.js x-for errors + if seenBackendIDs[backendID] { + log.Debug().Msgf("Skipping duplicate backend ID: %s", backendID) + continue + } + seenBackendIDs[backendID] = true + + currentlyProcessing := opcache.Exists(backendID) jobID := "" isDeletionOp := false if currentlyProcessing { - jobID = opcache.Get(galleryID) + jobID = opcache.Get(backendID) status := galleryService.GetStatus(jobID) if status != nil && status.Deletion { isDeletionOp = true @@ -446,7 +466,7 @@ func RegisterUIAPIRoutes(app *fiber.App, cl *config.ModelConfigLoader, appConfig } backendsJSON = append(backendsJSON, fiber.Map{ - "id": galleryID, + "id": backendID, "name": b.Name, "description": b.Description, "icon": b.Icon, diff --git a/gallery/index.yaml b/gallery/index.yaml index b11943630..f39cbf072 100644 --- a/gallery/index.yaml +++ b/gallery/index.yaml @@ -20767,7 +20767,7 @@ uri: "huggingface://ggerganov/whisper.cpp/ggml-small.en-q5_1.bin" sha256: bfdff4894dcb76bbf647d56263ea2a96645423f1669176f4844a1bf8e478ad30 - !!merge <<: *whisper - name: "whisper-small" + name: "whisper-small-en" overrides: parameters: model: ggml-small.en.bin