Files
LocalAI/core/services/studio/contextfiles/limits.go
T
Ettore Di Giacinto b64b35b5a4 feat(studio): add a generic loader for context files
A source declares a scope (user, agent or channel), a home-relative index
path and a byte cap. The loader reads the indexes that apply to a chat and
returns one bounded block under a fixed header, plus a key that changes with
it. Details of a topic are read next to the index; the path is built from
the registry and the chat, so the model can only name a topic.

The package only reads. A Writer interface lets a later change plug in the
save path, and CheckIndex and CheckProfile return the current entries when a
write would pass a cap, so the model merges them.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 09:28:57 +00:00

54 lines
1.7 KiB
Go

package contextfiles
import (
"fmt"
"strings"
)
// OverCapError means a write would make a file larger than its cap. Current
// holds what the file has now, so the writer can show it to the model, which
// must merge the entries instead of adding to them.
type OverCapError struct {
What string // "index" or "profile"
Lines, Len int // size of the refused text
MaxLines int // 0: no line limit
MaxBytes int
Current string
}
func (e *OverCapError) Error() string {
limit := fmt.Sprintf("%d bytes", e.MaxBytes)
if e.MaxLines > 0 {
limit = fmt.Sprintf("%d lines and %s", e.MaxLines, limit)
}
return fmt.Sprintf("the %s would be too long (%d lines, %d bytes; the limit is %s): merge or drop entries, then save the whole %s again. Current entries:\n%s",
e.What, e.Lines, e.Len, limit, e.What, e.Current)
}
func countLines(b []byte) int {
n := 0
for line := range strings.SplitSeq(string(b), "\n") {
if strings.TrimSpace(line) != "" {
n++
}
}
return n
}
// CheckIndex checks a new index of the source against its caps. Over a cap it
// returns an *OverCapError that carries the current index.
func CheckIndex(src Source, current, next []byte) error {
if lines := countLines(next); lines > MaxIndexLines || len(next) > src.limit() {
return &OverCapError{What: "index", Lines: lines, Len: len(next), MaxLines: MaxIndexLines, MaxBytes: src.limit(), Current: string(current)}
}
return nil
}
// CheckProfile checks a new agent profile against MaxProfileBytes.
func CheckProfile(current, next []byte) error {
if len(next) > MaxProfileBytes {
return &OverCapError{What: "profile", Lines: countLines(next), Len: len(next), MaxBytes: MaxProfileBytes, Current: string(current)}
}
return nil
}