package tools
import (
"context"
"encoding/json"
"fmt"
"sort"
"assistant/config"
"assistant/llm"
"assistant/memory"
)
// Tool is a registry entry: schema + runner.
type Tool struct {
Name string
Description string
Parameters json.RawMessage
Run func(ctx context.Context, args json.RawMessage) (string, error)
}
// Registry maps tool name to definition.
type Registry map[string]Tool
// ToChatTools builds Ollama /api/chat tool definitions for enabled tools.
func (r Registry) ToChatTools() []llm.ChatTool {
names := make([]string, 0, len(r))
for n := range r {
names = append(names, n)
}
sort.Strings(names)
out := make([]llm.ChatTool, 0, len(r))
for _, n := range names {
t := r[n]
out = append(out, llm.ChatTool{
Type: "function",
Function: llm.ToolFunction{
Name: t.Name,
Description: t.Description,
Parameters: t.Parameters,
},
})
}
return out
}
// BuildRegistry constructs tools from config. store may be nil when db-backed tools are disabled.
func BuildRegistry(cfg *config.Config, store *memory.Store) (Registry, error) {
reg := Registry{}
if cfg.Tools.Weather.Enabled {
w := newWeatherTool(cfg.Tools.Weather)
reg[w.Name] = w
}
if cfg.Tools.News.Enabled {
for _, t := range newNewsTools(cfg.Tools.News) {
reg[t.Name] = t
}
}
if cfg.Tools.Tasks.Enabled {
if store == nil {
return nil, fmt.Errorf("tasks tool enabled but memory store is nil")
}
for _, t := range taskTools(store) {
reg[t.Name] = t
}
}
if cfg.Tools.Memory.Enabled {
if store == nil {
return nil, fmt.Errorf("memory tool enabled but memory store is nil")
}
for _, t := range memoryTools(store) {
reg[t.Name] = t
}
}
if cfg.Tools.Code.Enabled {
codeTools, err := newCodeTool(cfg.Tools.Code)
if err != nil {
return nil, err
}
for i := range codeTools {
t := codeTools[i]
reg[t.Name] = t
}
}
return reg, nil
}