diff --git a/internal/tools/builtin.go b/internal/tools/builtin.go new file mode 100644 index 0000000..eb91cf4 --- /dev/null +++ b/internal/tools/builtin.go @@ -0,0 +1,48 @@ +package tools + +import ( + "context" + "io" + "net/http" + "strings" + "time" +) + +func RegisterBuiltins(r *Registry) { + r.Register(&Tool{ + Name: "web_fetch", + Description: "Fetch content from a URL", + Parameters: []Param{ + {Name: "url", Type: "string", Required: true, Description: "URL to fetch"}, + }, + Execute: webFetch, + }) + + r.Register(&Tool{ + Name: "web_search", + Description: "Search the web for information", + Parameters: []Param{ + {Name: "query", Type: "string", Required: true, Description: "search query"}, + }, + Execute: webSearch, + }) +} + +func webFetch(ctx context.Context, args string) (string, error) { + client := &http.Client{Timeout: 30 * time.Second} + resp, err := client.Get(strings.TrimSpace(args)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + return string(body), nil +} + +func webSearch(ctx context.Context, args string) (string, error) { + return "Web search not yet implemented. Query: " + args, nil +} diff --git a/internal/tools/registry.go b/internal/tools/registry.go new file mode 100644 index 0000000..57880f0 --- /dev/null +++ b/internal/tools/registry.go @@ -0,0 +1,62 @@ +package tools + +import ( + "context" + "sync" +) + +type ToolFunc func(ctx context.Context, args string) (string, error) + +type Tool struct { + Name string `json:"name"` + Description string `json:"description"` + Parameters []Param `json:"parameters"` + Execute ToolFunc `json:"-"` +} + +type Param struct { + Name string `json:"name"` + Type string `json:"type"` + Required bool `json:"required"` + Description string `json:"description"` +} + +type Registry struct { + mu sync.Mutex + tools map[string]*Tool +} + +func NewRegistry() *Registry { + return &Registry{tools: make(map[string]*Tool)} +} + +func (r *Registry) Register(t *Tool) { + r.mu.Lock() + defer r.mu.Unlock() + r.tools[t.Name] = t +} + +func (r *Registry) Get(name string) (*Tool, bool) { + r.mu.Lock() + defer r.mu.Unlock() + t, ok := r.tools[name] + return t, ok +} + +func (r *Registry) List() []*Tool { + r.mu.Lock() + defer r.mu.Unlock() + out := make([]*Tool, 0, len(r.tools)) + for _, t := range r.tools { + out = append(out, t) + } + return out +} + +func (r *Registry) Execute(ctx context.Context, name, args string) (string, error) { + t, ok := r.Get(name) + if !ok { + return "", nil + } + return t.Execute(ctx, args) +}