feat: update localrecall to support files with same names in the collections

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-03-19 17:45:13 +00:00
parent cc2d2838ca
commit 43c65ec7e8
6 changed files with 42 additions and 20 deletions
+1 -1
View File
@@ -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
+2 -4
View File
@@ -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=
+27 -9
View File
@@ -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 {
+7 -1
View File
@@ -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) {
+5
View File
@@ -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 {
-5
View File
@@ -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)))