mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 12:33:16 -05:00
An add-on that keeps small notes for the person can now name the index files that the host puts in the prompt of a chat. The declaration is capabilities.contextFiles: at most one file for each scope (user, agent, channel), each a path under Memory/ that ends in .md, with an optional byte cap that can only lower the host's 2 KiB cap. The field is new, so a manifest that uses it must accept manifest API 2. The add-on only names paths. The host reads them as the person, so the declaration gives the add-on no access to files. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
85 lines
3.6 KiB
Go
85 lines
3.6 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package contract
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("The context files declaration", func() {
|
|
notes := []ContextFile{
|
|
{Scope: ScopeUser, Path: "Memory/index.md"},
|
|
{Scope: ScopeAgent, Path: "Memory/agents/{agent}/index.md", Cap: 1024},
|
|
{Scope: ScopeChannel, Path: "Memory/channels/{channel}/index.md"},
|
|
}
|
|
withFiles := func(files []ContextFile) Manifest {
|
|
m, err := ParseManifest(fixture())
|
|
Expect(err).NotTo(HaveOccurred())
|
|
m.API = VersionRange{Min: 2, Max: 2}
|
|
m.Capabilities.ContextFiles = files
|
|
return m
|
|
}
|
|
host := func() HostCompatibility {
|
|
h := compatible()
|
|
h.API, h.APIMin = HostAPIVersion, HostAPIMin
|
|
return h
|
|
}
|
|
|
|
It("is valid with one file for each scope", func() {
|
|
Expect(ValidateManifest(withFiles(notes), host())).To(Succeed())
|
|
})
|
|
|
|
It("needs a manifest that accepts API 2 to declare context files", func() {
|
|
m := withFiles(notes)
|
|
m.API = VersionRange{Min: 1, Max: 1}
|
|
Expect(ValidateManifest(m, host())).To(MatchError(ContainSubstring("api 2")))
|
|
})
|
|
|
|
It("refuses a path outside the Memory folder, an unsafe path and a wrong placeholder", func() {
|
|
for _, bad := range []ContextFile{
|
|
{Scope: ScopeUser, Path: "Documents/index.md"},
|
|
{Scope: ScopeUser, Path: "Memory/../Documents/index.md"},
|
|
{Scope: ScopeUser, Path: "/Memory/index.md"},
|
|
{Scope: ScopeUser, Path: "Memory/index.txt"},
|
|
{Scope: ScopeUser, Path: "Memory/{agent}/index.md"},
|
|
{Scope: ScopeUser, Path: "Memory//index.md"},
|
|
{Scope: ScopeUser, Path: "Memory\\index.md"},
|
|
{Scope: ScopeUser, Path: "Memory/in\x00dex.md"},
|
|
{Scope: ScopeAgent, Path: "Memory/agents/index.md"},
|
|
{Scope: ScopeAgent, Path: "Memory/agents/{channel}/index.md"},
|
|
{Scope: ScopeChannel, Path: "Memory/{channel}/{channel}/index.md"},
|
|
{Scope: ScopeChannel, Path: "Memory/{x}/{channel}/index.md"},
|
|
{Scope: ScopeUser, Path: "Memory/" + strings.Repeat("a", 200) + ".md"},
|
|
{Scope: "everyone", Path: "Memory/index.md"},
|
|
{Scope: "", Path: "Memory/index.md"},
|
|
} {
|
|
Expect(ValidateManifest(withFiles([]ContextFile{bad}), host())).NotTo(Succeed(), "%+v", bad)
|
|
}
|
|
})
|
|
|
|
It("refuses a cap that is negative or above the host's index cap", func() {
|
|
for _, c := range []int{-1, MaxContextFileBytes + 1} {
|
|
Expect(ValidateManifest(withFiles([]ContextFile{{Scope: ScopeUser, Path: "Memory/index.md", Cap: c}}), host())).NotTo(Succeed())
|
|
}
|
|
Expect(ValidateManifest(withFiles([]ContextFile{{Scope: ScopeUser, Path: "Memory/index.md", Cap: MaxContextFileBytes}}), host())).To(Succeed())
|
|
})
|
|
|
|
It("refuses two files for one scope and more files than scopes", func() {
|
|
twice := []ContextFile{{Scope: ScopeUser, Path: "Memory/index.md"}, {Scope: ScopeUser, Path: "Memory/other.md"}}
|
|
Expect(ValidateManifest(withFiles(twice), host())).NotTo(Succeed())
|
|
many := append(append([]ContextFile{}, notes...), ContextFile{Scope: ScopeUser, Path: "Memory/x.md"})
|
|
Expect(ValidateManifest(withFiles(many), host())).NotTo(Succeed())
|
|
})
|
|
|
|
It("reads capabilities.contextFiles from JSON and refuses an unknown field in it", func() {
|
|
var c Capabilities
|
|
Expect(json.Unmarshal([]byte(`{"service":true,"contextFiles":[{"scope":"user","path":"Memory/index.md","cap":2048}]}`), &c)).To(Succeed())
|
|
Expect(c.ContextFiles).To(Equal([]ContextFile{{Scope: ScopeUser, Path: "Memory/index.md", Cap: 2048}}))
|
|
Expect(json.Unmarshal([]byte(`{"service":true,"contextFiles":[{"scope":"user","path":"Memory/index.md","all":true}]}`), &c)).NotTo(Succeed())
|
|
Expect(json.Unmarshal([]byte(`{"service":true,"ContextFiles":[]}`), &c)).NotTo(Succeed())
|
|
})
|
|
})
|