93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// ── Registry ────────────────────────────────
|
|
|
|
var registry = map[string]Tool{}
|
|
|
|
// Register adds a tool to the global registry. Called at init time.
|
|
func Register(t Tool) {
|
|
def := t.Definition()
|
|
if _, exists := registry[def.Name]; exists {
|
|
panic("tools.Register: duplicate tool name: " + def.Name)
|
|
}
|
|
registry[def.Name] = t
|
|
log.Printf("🔧 Registered tool: %s", def.Name)
|
|
}
|
|
|
|
// Get returns a tool by name, or nil if not found.
|
|
func Get(name string) Tool {
|
|
return registry[name]
|
|
}
|
|
|
|
// AllDefinitions returns tool definitions for all registered tools.
|
|
// Used to populate the `tools` field in LLM requests.
|
|
func AllDefinitions() []ToolDef {
|
|
defs := make([]ToolDef, 0, len(registry))
|
|
for _, t := range registry {
|
|
defs = append(defs, t.Definition())
|
|
}
|
|
return defs
|
|
}
|
|
|
|
// ── Execution ───────────────────────────────
|
|
|
|
// ExecuteCall runs a single tool call and returns a ToolResult.
|
|
// Never returns an error — failures are encoded in the result content
|
|
// so the LLM can see what went wrong and adjust.
|
|
func ExecuteCall(ctx context.Context, execCtx ExecutionContext, call ToolCall) ToolResult {
|
|
tool := Get(call.Name)
|
|
if tool == nil {
|
|
return ToolResult{
|
|
ToolCallID: call.ID,
|
|
Name: call.Name,
|
|
Content: jsonError(fmt.Sprintf("unknown tool: %s", call.Name)),
|
|
IsError: true,
|
|
}
|
|
}
|
|
|
|
result, err := tool.Execute(ctx, execCtx, call.Arguments)
|
|
if err != nil {
|
|
log.Printf("Tool %s error: %v", call.Name, err)
|
|
return ToolResult{
|
|
ToolCallID: call.ID,
|
|
Name: call.Name,
|
|
Content: jsonError(err.Error()),
|
|
IsError: true,
|
|
}
|
|
}
|
|
|
|
return ToolResult{
|
|
ToolCallID: call.ID,
|
|
Name: call.Name,
|
|
Content: result,
|
|
}
|
|
}
|
|
|
|
// ExecuteAll runs multiple tool calls and returns all results.
|
|
func ExecuteAll(ctx context.Context, execCtx ExecutionContext, calls []ToolCall) []ToolResult {
|
|
results := make([]ToolResult, len(calls))
|
|
for i, call := range calls {
|
|
results[i] = ExecuteCall(ctx, execCtx, call)
|
|
}
|
|
return results
|
|
}
|
|
|
|
// HasTools returns true if any tools are registered.
|
|
func HasTools() bool {
|
|
return len(registry) > 0
|
|
}
|
|
|
|
// ── Helpers ─────────────────────────────────
|
|
|
|
func jsonError(msg string) string {
|
|
b, _ := json.Marshal(map[string]string{"error": msg})
|
|
return string(b)
|
|
}
|