From 43c65ec7e88aff9bbd5b5a17c7eded4e72cdcd91 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Thu, 19 Mar 2026 17:45:13 +0000 Subject: [PATCH] feat: update localrecall to support files with same names in the collections Signed-off-by: Ettore Di Giacinto --- go.mod | 2 +- go.sum | 6 ++---- webui/collections/inprocess.go | 36 +++++++++++++++++++++++-------- webui/collections/rag_provider.go | 8 ++++++- webui/collections_backend_http.go | 5 +++++ webui/collections_handlers.go | 5 ----- 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 02be071..a7f0306 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/jung-kurt/gofpdf v1.16.2 github.com/modelcontextprotocol/go-sdk v1.2.0 github.com/mudler/cogito v0.9.5-0.20260315222927-63abdec7189b - github.com/mudler/localrecall v0.5.9-0.20260314221856-96d63875cc47 + github.com/mudler/localrecall v0.5.9-0.20260319170742-933f68603f62 github.com/mudler/skillserver v0.0.5-0.20260221145827-0639a82c8f49 github.com/mudler/xlog v0.0.5 github.com/onsi/ginkgo/v2 v2.27.5 diff --git a/go.sum b/go.sum index 080bfc0..9f51b9e 100644 --- a/go.sum +++ b/go.sum @@ -297,12 +297,10 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= -github.com/mudler/cogito v0.9.2-0.20260225234859-b76691637703 h1:DVYONTwYj14uNvT1wumq3lV2Kl4cQ9vl+NAlkMHpdp4= -github.com/mudler/cogito v0.9.2-0.20260225234859-b76691637703/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4= github.com/mudler/cogito v0.9.5-0.20260315222927-63abdec7189b h1:A74T2Lauvg61KodYqsjTYDY05kPLcW+efVZjd23dghU= github.com/mudler/cogito v0.9.5-0.20260315222927-63abdec7189b/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4= -github.com/mudler/localrecall v0.5.9-0.20260314221856-96d63875cc47 h1:fHIkLJgfcYDF4bhwVZdfFcQB2HVw93ClOQSKRci8qQs= -github.com/mudler/localrecall v0.5.9-0.20260314221856-96d63875cc47/go.mod h1:TZVXQI840MqjDtilBLc7kfmnctK4oNf1IR+cE68zno8= +github.com/mudler/localrecall v0.5.9-0.20260319170742-933f68603f62 h1:KVTEukvLlQXKZx1C1ZLru+ahaiECLF+7v2caK8vauJ0= +github.com/mudler/localrecall v0.5.9-0.20260319170742-933f68603f62/go.mod h1:/d2bG9H8G/HzsnXTTQl2bOD+ui74XwpeiSDJ+2gdkGc= github.com/mudler/skillserver v0.0.5-0.20260221145827-0639a82c8f49 h1:dAF1ALXqqapRZo80x56BIBBcPrPbRNerbd66rdyO8J4= github.com/mudler/skillserver v0.0.5-0.20260221145827-0639a82c8f49/go.mod h1:z3yFhcL9bSykmmh6xgGu0hyoItd4CnxgtWMEWw8uFJU= github.com/mudler/xlog v0.0.5 h1:2unBuVC5rNGhCC86UaA94TElWFml80NL5XLK+kAmNuU= diff --git a/webui/collections/inprocess.go b/webui/collections/inprocess.go index 661cb4b..dba8251 100644 --- a/webui/collections/inprocess.go +++ b/webui/collections/inprocess.go @@ -71,17 +71,25 @@ func (b *backendInProcess) Upload(collection, filename string, fileBody io.Reade if !exists { return fmt.Errorf("collection not found: %s", collection) } - filePath := filepath.Join(b.cfg.FileAssets, filename) - out, err := os.Create(filePath) + // Write to a temp file; kb.Store will copy it into the correct UUID + // subdirectory under the collection's asset dir. + tmpDir, err := os.MkdirTemp("", "localagi-upload") if err != nil { return err } - defer out.Close() - if _, err := io.Copy(out, fileBody); err != nil { + defer os.RemoveAll(tmpDir) + tmpPath := filepath.Join(tmpDir, filename) + out, err := os.Create(tmpPath) + if err != nil { return err } + if _, err := io.Copy(out, fileBody); err != nil { + out.Close() + return err + } + out.Close() now := time.Now().Format(time.RFC3339) - return kb.Store(filePath, map[string]string{"created_at": now}) + return kb.Store(tmpPath, map[string]string{"created_at": now}) } func (b *backendInProcess) ListEntries(collection string) ([]string, error) { @@ -91,7 +99,12 @@ func (b *backendInProcess) ListEntries(collection string) ([]string, error) { if !exists { return nil, fmt.Errorf("collection not found: %s", collection) } - return kb.ListDocuments(), nil + keys := kb.ListDocuments() + entries := make([]string, len(keys)) + for i, k := range keys { + entries[i] = filepath.Base(k) + } + return entries, nil } func (b *backendInProcess) GetEntryContent(collection, entry string) (string, int, error) { @@ -112,8 +125,8 @@ func (b *backendInProcess) Search(collection, query string, maxResults int) ([]S return nil, fmt.Errorf("collection not found: %s", collection) } if maxResults <= 0 { - entries := kb.ListDocuments() - if len(entries) >= 5 { + keys := kb.ListDocuments() + if len(keys) >= 5 { maxResults = 5 } else { maxResults = 1 @@ -158,7 +171,12 @@ func (b *backendInProcess) DeleteEntry(collection, entry string) ([]string, erro if err := kb.RemoveEntry(entry); err != nil { return nil, err } - return kb.ListDocuments(), nil + keys := kb.ListDocuments() + entries := make([]string, len(keys)) + for i, k := range keys { + entries[i] = filepath.Base(k) + } + return entries, nil } func (b *backendInProcess) AddSource(collection, url string, intervalMin int) error { diff --git a/webui/collections/rag_provider.go b/webui/collections/rag_provider.go index dc1bf9a..548d13f 100644 --- a/webui/collections/rag_provider.go +++ b/webui/collections/rag_provider.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/mudler/LocalAGI/core/agent" "github.com/mudler/LocalAGI/core/state" "github.com/mudler/localrecall/rag" @@ -107,7 +108,12 @@ func (a *internalCompactionAdapter) ListEntries() ([]string, error) { if kb == nil { return nil, fmt.Errorf("collection not available") } - return kb.ListDocuments(), nil + keys := kb.ListDocuments() + entries := make([]string, len(keys)) + for i, k := range keys { + entries[i] = filepath.Base(k) + } + return entries, nil } func (a *internalCompactionAdapter) GetEntryContent(entry string) (content string, chunkCount int, err error) { diff --git a/webui/collections_backend_http.go b/webui/collections_backend_http.go index e00348a..695e2dc 100644 --- a/webui/collections_backend_http.go +++ b/webui/collections_backend_http.go @@ -1,6 +1,7 @@ package webui import ( + "fmt" "io" "os" "path/filepath" @@ -114,6 +115,10 @@ func (b *collectionsBackendHTTP) ListSources(collection string) ([]CollectionSou return out, nil } +func (b *collectionsBackendHTTP) GetEntryFilePath(collection, entry string) (string, error) { + return "", fmt.Errorf("GetEntryFilePath is not supported via HTTP backend") +} + func (b *collectionsBackendHTTP) EntryExists(collection, entry string) bool { entries, err := b.client.ListEntries(collection) if err != nil { diff --git a/webui/collections_handlers.go b/webui/collections_handlers.go index 6a2c7d7..d18d351 100644 --- a/webui/collections_handlers.go +++ b/webui/collections_handlers.go @@ -142,11 +142,6 @@ func (app *App) uploadFile(backend CollectionsBackend) func(c *fiber.Ctx) error } defer f.Close() - if backend.EntryExists(name, file.Filename) { - xlog.Info("Entry already exists") - return c.Status(fiber.StatusBadRequest).JSON(collectionsErrorResponse(errCodeConflict, "Entry already exists", fmt.Sprintf("File '%s' has already been uploaded to collection '%s'", file.Filename, name))) - } - if err := backend.Upload(name, file.Filename, f); err != nil { if status := collectionErrStatus(err, name); status == fiber.StatusNotFound { return c.Status(status).JSON(collectionsErrorResponse(errCodeNotFound, "Collection not found", fmt.Sprintf("Collection '%s' does not exist", name)))