package agent import ( "context" "fmt" "sort" "github.com/ollama/ollama/api" ) type ToolContext struct { WorkingDir string } type ToolResult struct { Content string WorkingDir string } type Tool interface { Name() string Description() string Schema() api.ToolFunction Execute(context.Context, ToolContext, map[string]any) (ToolResult, error) } type ApprovalRequired interface { RequiresApproval(map[string]any) bool } // ScopedTool is implemented by tools that need per-invocation approval // scoping beyond the tool name (e.g. shell commands scoped to the exact // command string). Tools that don't implement this are scoped by name only. type ScopedTool interface { ApprovalScope(args map[string]any) string } type Registry struct { tools map[string]Tool } func (r *Registry) Register(tool Tool) { if r == nil || tool == nil { return } if r.tools == nil { r.tools = make(map[string]Tool) } r.tools[tool.Name()] = tool } func (r *Registry) Get(name string) (Tool, bool) { if r == nil { return nil, false } tool, ok := r.tools[name] return tool, ok } func (r *Registry) Names() []string { if r == nil { return nil } names := make([]string, 0, len(r.tools)) for name := range r.tools { names = append(names, name) } sort.Strings(names) return names } func (r *Registry) Tools() api.Tools { if r == nil { return nil } names := r.Names() apiTools := make(api.Tools, 0, len(names)) for _, name := range names { tool := r.tools[name] apiTools = append(apiTools, api.Tool{ Type: "function", Function: tool.Schema(), }) } return apiTools } func (r *Registry) Execute(ctx context.Context, toolCtx ToolContext, call api.ToolCall) (ToolResult, error) { tool, ok := r.Get(call.Function.Name) if !ok { return ToolResult{}, fmt.Errorf("unknown tool: %s", call.Function.Name) } return tool.Execute(ctx, toolCtx, call.Function.Arguments.ToMap()) } func ToolRequiresApproval(tool Tool, args map[string]any) bool { if tool == nil { return false } if t, ok := tool.(ApprovalRequired); ok { return t.RequiresApproval(args) } return false }