mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -05:00
No release ever shipped add-ons, so there is no installed base on api 1.
The manifest must now declare api {"min": 2, "max": 2}; a manifest with
api 1, no api or a wider range is refused with a message that names the
range.
Every field that was gated on api 2 is now part of the contract, so the
Min*API constants and their gates are gone, and so are HostAPIMin and
the apiMin host field. Three older-manifest shims go with them: the
resource listing the host implied for one add-on, the legacyArgs hint
for arguments an older version of a tool took, and the tests that
checked the gates. All shipped manifests and the test fixtures declare
api 2.
Assisted-by: Claude:claude-sonnet-5-5 go test
105 lines
3.7 KiB
Go
105 lines
3.7 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package contract
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
)
|
|
|
|
// ExecutionClassCommands is the class of a tool that runs shell commands and
|
|
// code: the tool that shadows bash.
|
|
const ExecutionClassCommands = "commands"
|
|
|
|
// Kinds of skills a commands provider can supply the runtime for.
|
|
const (
|
|
// SkillKindDocuments are the skills of the document slots (see
|
|
// DocumentSkill). They need a provider that reports isolated execution.
|
|
SkillKindDocuments = "documents"
|
|
)
|
|
|
|
// Limits of the text the host shows for an execution provider.
|
|
const (
|
|
MaxExecutionTitleChars = 40
|
|
MaxExecutionNoteChars = 600
|
|
)
|
|
|
|
// ExecutionDescriptor is what an add-on declares about the tool that runs
|
|
// commands. It carries no authority. Whether commands are ready and isolated
|
|
// is what the provider reports at run time (ExecutionStatus); the host decides
|
|
// from that report and never from the add-on's ID.
|
|
type ExecutionDescriptor struct {
|
|
// Class is ExecutionClassCommands.
|
|
Class string `json:"class"`
|
|
// Title names the provider in the text the host shows the person and the
|
|
// model, for example "Sandbox". It falls back to the add-on's title.
|
|
Title string `json:"title,omitempty"`
|
|
// Note is one plain sentence for the model when the provider reports
|
|
// isolated execution, such as the programs its environment has.
|
|
Note string `json:"note,omitempty"`
|
|
// Skills says which skills run on this provider.
|
|
Skills *ExecutionSkills `json:"skills,omitempty"`
|
|
}
|
|
|
|
// ExecutionSkills declares the skills whose runtime the provider supplies.
|
|
type ExecutionSkills struct {
|
|
// ProvidesRuntimeFor lists skill kinds (SkillKindDocuments). The host
|
|
// offers the add-on's skills of those kinds only while the provider
|
|
// reports isolated execution.
|
|
ProvidesRuntimeFor []string `json:"providesRuntimeFor,omitempty"`
|
|
}
|
|
|
|
// validate checks the descriptor of tool d. A nil descriptor is valid.
|
|
func (e *ExecutionDescriptor) validate(d ToolDefinition) error {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
if e.Class != ExecutionClassCommands || d.Shadows != "bash" {
|
|
return fmt.Errorf("tool %s: an execution descriptor has class %q and belongs to a tool that shadows bash", d.Name, ExecutionClassCommands)
|
|
}
|
|
if e.Title != "" && !plainText(e.Title, MaxExecutionTitleChars) {
|
|
return fmt.Errorf("tool %s: invalid execution title", d.Name)
|
|
}
|
|
if e.Note != "" && !plainText(e.Note, MaxExecutionNoteChars) {
|
|
return fmt.Errorf("tool %s: invalid execution note", d.Name)
|
|
}
|
|
if e.Skills != nil {
|
|
for _, k := range e.Skills.ProvidesRuntimeFor {
|
|
if k != SkillKindDocuments {
|
|
return fmt.Errorf("tool %s: unknown skill kind %q", d.Name, k)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CommandsTool returns the tool of the definitions that shadows bash.
|
|
func CommandsTool(defs []ToolDefinition) (ToolDefinition, bool) {
|
|
i := slices.IndexFunc(defs, func(d ToolDefinition) bool { return d.Shadows == "bash" && ValidateShadow(d) == nil })
|
|
if i < 0 {
|
|
return ToolDefinition{}, false
|
|
}
|
|
return defs[i], true
|
|
}
|
|
|
|
// IsolatedSkills lists the skills of an add-on that need its own commands
|
|
// provider to report isolated execution: the document-slot skills of an
|
|
// add-on with a commands tool, unless its descriptor says it does not provide
|
|
// the runtime for documents. A commands tool without a descriptor (a manifest
|
|
// of API 1) counts as providing it, so such an add-on keeps the check.
|
|
func IsolatedSkills(defs []ToolDefinition, skills []SkillDefinition) []string {
|
|
tool, ok := CommandsTool(defs)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if e := tool.Execution; e != nil && (e.Skills == nil || !slices.Contains(e.Skills.ProvidesRuntimeFor, SkillKindDocuments)) {
|
|
return nil
|
|
}
|
|
var out []string
|
|
for _, s := range SlotsOf(skills) {
|
|
if s.Kind == SlotKindDocuments {
|
|
out = append(out, s.Name)
|
|
}
|
|
}
|
|
return out
|
|
}
|