tool.go RawHistoryFolder
1// Package core implements the agent loop, tool runtime, and session
2// persistence. It is provider-agnostic: it talks to an LLM only through
3// the provider.Client interface.
4package core
5
6import (
7 "context"
8 "encoding/json"
9 "fmt"
10 "sort"
11
12 "github.com/patriceckhart/zot/internal/provider"
13)
14
15// Tool is a capability the agent can invoke.
16type Tool interface {
17 // Name is the unique tool id shown to the LLM.
18 Name() string
19 // Description is a one-line summary shown to the LLM.
20 Description() string
21 // Schema is a JSON Schema object for Execute's args.
22 Schema() json.RawMessage
23 // Execute runs the tool. progress may be called any number of times
24 // with partial textual output (for UIs); it is not sent to the LLM.
25 Execute(ctx context.Context, args json.RawMessage, progress func(string)) (ToolResult, error)
26}
27
28// ToolResult is the outcome of Tool.Execute.
29type ToolResult struct {
30 // Content is sent back to the LLM (text and/or images).
31 Content []provider.Content
32 // IsError marks this result as an error to the LLM.
33 IsError bool
34 // Details is arbitrary data for UIs and logs; not sent to the LLM.
35 Details any
36}
37
38// Registry is a name->Tool map.
39type Registry map[string]Tool
40
41// NewRegistry builds a Registry from a list of tools.
42func NewRegistry(tools ...Tool) Registry {
43 r := Registry{}
44 for _, t := range tools {
45 r[t.Name()] = t
46 }
47 return r
48}
49
50// Specs returns the tool definitions to advertise to the LLM.
51// Sorted by tool name so the order is stable across requests. This
52// is load-bearing for provider-side prompt caching: providers
53// prefix-match tool definitions, and Go's map iteration order is
54// randomized per call, which would otherwise bust the cache every
55// single turn.
56func (r Registry) Specs() []provider.Tool {
57 names := make([]string, 0, len(r))
58 for name := range r {
59 names = append(names, name)
60 }
61 sort.Strings(names)
62 out := make([]provider.Tool, 0, len(r))
63 for _, name := range names {
64 t := r[name]
65 out = append(out, provider.Tool{
66 Name: t.Name(),
67 Description: t.Description(),
68 Schema: t.Schema(),
69 })
70 }
71 return out
72}
73
74// Get looks up a tool by name.
75func (r Registry) Get(name string) (Tool, error) {
76 t, ok := r[name]
77 if !ok {
78 return nil, fmt.Errorf("unknown tool %q", name)
79 }
80 return t, nil
81}