package tools import ( "context" "encoding/json" "fmt" "sort" coretypes "github.com/enmanuel/agents/pkg/llm" ) // Registry holds available tools keyed by name. type Registry struct { tools map[string]Tool } // NewRegistry creates an empty registry. func NewRegistry() *Registry { return &Registry{tools: make(map[string]Tool)} } // Register adds a tool to the registry. func (r *Registry) Register(t Tool) { r.tools[t.Def.Name] = t } // Get looks up a tool by name. func (r *Registry) Get(name string) (Tool, bool) { t, ok := r.tools[name] return t, ok } // Names returns all registered tool names in sorted order. func (r *Registry) Names() []string { names := make([]string, 0, len(r.tools)) for k := range r.tools { names = append(names, k) } sort.Strings(names) return names } // Len returns the number of registered tools. func (r *Registry) Len() int { return len(r.tools) } // Execute looks up a tool by name and runs it. Returns an error result if not found. func (r *Registry) Execute(ctx context.Context, name string, argsJSON string) Result { t, ok := r.tools[name] if !ok { return Result{Err: fmt.Errorf("tool %q not found", name)} } var args map[string]any if argsJSON != "" { if err := json.Unmarshal([]byte(argsJSON), &args); err != nil { return Result{Err: fmt.Errorf("parse args for %q: %w", name, err)} } } return t.Exec(ctx, args) } // ToLLMSpecs converts all registered tools to the LLM-compatible ToolSpec format. // This is a pure transformation — no side effects. func (r *Registry) ToLLMSpecs() []coretypes.ToolSpec { specs := make([]coretypes.ToolSpec, 0, len(r.tools)) for _, name := range r.Names() { t := r.tools[name] specs = append(specs, defToLLMSpec(t.Def)) } return specs } // defToLLMSpec converts a pure Def to an LLM ToolSpec with JSON Schema. func defToLLMSpec(d Def) coretypes.ToolSpec { properties := make(map[string]any, len(d.Parameters)) required := make([]string, 0) for _, p := range d.Parameters { properties[p.Name] = map[string]any{ "type": p.Type, "description": p.Description, } if p.Required { required = append(required, p.Name) } } schema := map[string]any{ "type": "object", "properties": properties, } if len(required) > 0 { schema["required"] = required } return coretypes.ToolSpec{ Name: d.Name, Description: d.Description, InputSchema: schema, } }