package tools import ( "context" "encoding/json" "fmt" "log/slog" "sync" "git.bebrik.xyz/Dan4ick/ZovOS_AI/internal/ai" ) // ToolHandler is the function signature for tool implementations. type ToolHandler func(ctx context.Context, args json.RawMessage) (string, error) // ToolDef defines a tool with its metadata and handler. type ToolDef struct { ai.Tool Handler ToolHandler } // Registry manages all available tools. type Registry struct { mu sync.RWMutex tools map[string]ToolDef log *slog.Logger } // NewRegistry creates a new tool registry. func NewRegistry(log *slog.Logger) *Registry { return &Registry{ tools: make(map[string]ToolDef), log: log, } } // Register adds a tool to the registry. func (r *Registry) Register(def ToolDef) { r.mu.Lock() defer r.mu.Unlock() r.tools[def.Name] = def r.log.Info("registered tool", "name", def.Name) } // Execute runs a tool by name with the given arguments. func (r *Registry) Execute(ctx context.Context, name string, argsJSON string) (string, error) { r.mu.RLock() td, ok := r.tools[name] r.mu.RUnlock() if !ok { return "", fmt.Errorf("unknown tool: %s", name) } r.log.Info("executing tool", "name", name, "args", argsJSON) result, err := td.Handler(ctx, json.RawMessage(argsJSON)) if err != nil { r.log.Error("tool execution failed", "name", name, "error", err) return fmt.Sprintf("Error: %v", err), nil // return error as result, not as Go error } r.log.Debug("tool result", "name", name, "result_len", len(result)) return result, nil } // List returns AI tool definitions for all registered tools. func (r *Registry) List() []ai.Tool { r.mu.RLock() defer r.mu.RUnlock() result := make([]ai.Tool, 0, len(r.tools)) for _, td := range r.tools { result = append(result, td.Tool) } return result } // Names returns names of all registered tools. func (r *Registry) Names() []string { r.mu.RLock() defer r.mu.RUnlock() names := make([]string, 0, len(r.tools)) for name := range r.tools { names = append(names, name) } return names }