mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -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>
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package contract
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// exactObjectFields checks keys before encoding/json can match struct fields
|
|
// case-insensitively. strictJSON retains duplicate, trailing and size/depth bounds.
|
|
// Only the immediate object is checked: localized maps keep their locale keys.
|
|
func exactObjectFields(b []byte, max int64, keys ...string) (map[string]json.RawMessage, error) {
|
|
var fields map[string]json.RawMessage
|
|
if err := strictJSON(b, &fields, max); err != nil {
|
|
return nil, err
|
|
}
|
|
for key := range fields {
|
|
if !slices.Contains(keys, key) {
|
|
return nil, fmt.Errorf("noncanonical or unknown JSON key %q", key)
|
|
}
|
|
}
|
|
return fields, nil
|
|
}
|
|
|
|
// rejectExtensionAliases narrows exact spelling to the new declaration entry
|
|
// keys, without changing historical spelling behavior for unrelated fields.
|
|
func rejectExtensionAliases(b []byte, keys ...string) error {
|
|
var fields map[string]json.RawMessage
|
|
if err := strictJSON(b, &fields, DefaultLimits().ManifestBytes); err != nil {
|
|
return err
|
|
}
|
|
for key := range fields {
|
|
for _, canonical := range keys {
|
|
if key != canonical && strings.EqualFold(key, canonical) {
|
|
return fmt.Errorf("noncanonical extension key %q", key)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Capabilities) UnmarshalJSON(b []byte) error {
|
|
if err := rejectExtensionAliases(b, "executionProvider", "hostOperations", "network", "contextFiles"); err != nil {
|
|
return err
|
|
}
|
|
type wire Capabilities
|
|
var v wire
|
|
if err := strictJSON(b, &v, DefaultLimits().ManifestBytes); err != nil {
|
|
return err
|
|
}
|
|
*c = Capabilities(v)
|
|
return nil
|
|
}
|
|
|
|
func (u *UI) UnmarshalJSON(b []byte) error {
|
|
if err := rejectExtensionAliases(b, "homeCommands", "modelActions"); err != nil {
|
|
return err
|
|
}
|
|
type wire UI
|
|
var v wire
|
|
if err := strictJSON(b, &v, DefaultLimits().ManifestBytes); err != nil {
|
|
return err
|
|
}
|
|
*u = UI(v)
|
|
return nil
|
|
}
|
|
|
|
func (p *ExecutionProvider) UnmarshalJSON(b []byte) error {
|
|
if _, err := exactObjectFields(b, DefaultLimits().ManifestBytes, "tools", "statusTool"); err != nil {
|
|
return err
|
|
}
|
|
type wire ExecutionProvider
|
|
var v wire
|
|
if err := strictJSON(b, &v, DefaultLimits().ManifestBytes); err != nil {
|
|
return err
|
|
}
|
|
*p = ExecutionProvider(v)
|
|
return nil
|
|
}
|
|
|
|
func (o *HostOperations) UnmarshalJSON(b []byte) error {
|
|
if _, err := exactObjectFields(b, DefaultLimits().ManifestBytes, "required", "optional"); err != nil {
|
|
return err
|
|
}
|
|
type wire HostOperations
|
|
var v wire
|
|
if err := strictJSON(b, &v, DefaultLimits().ManifestBytes); err != nil {
|
|
return err
|
|
}
|
|
*o = HostOperations(v)
|
|
return nil
|
|
}
|