mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -05:00
A tool that shadows bash can now declare an execution descriptor: its class, a title and a note for the model, and the skill kinds whose runtime it supplies. The host reads it to build its text and to decide which skills need isolated execution, instead of knowing add-on IDs. The field is optional and needs manifest API 2. The host now speaks API 2 and still accepts add-ons that declare only API 1, so manifests that declare nothing new keep working unchanged. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
103 lines
3.8 KiB
Go
103 lines
3.8 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package contract
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
)
|
|
|
|
// MinExecutionAPI is the first manifest API version that knows the execution
|
|
// descriptor. A manifest that declares one must accept it.
|
|
const MinExecutionAPI uint64 = 2
|
|
|
|
// 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
|
|
}
|
|
return ShadowedSkills(skills)
|
|
}
|