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/packages/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// ToolPreviewer is optionally implemented by tools that can describe their
29// exact effect without applying it. Confirmation UIs use the preview before
30// allowing a side-effecting call to proceed.
31type ToolPreviewer interface {
32 Preview(ctx context.Context, args json.RawMessage) (ToolResult, error)
33}
34
35// ToolPolicyError marks a tool-local policy refusal without changing its text.
36// It does not imply that other calls in the same batch were rolled back.
37type ToolPolicyError struct{ Err error }
38
39func (e *ToolPolicyError) Error() string { return e.Err.Error() }
40func (e *ToolPolicyError) Unwrap() error { return e.Err }
41
42// ToolResult is the outcome of Tool.Execute.
43type ToolResult struct {
44 // Content is sent back to the LLM (text and/or images).
45 Content []provider.Content
46 // IsError marks this result as an error to the LLM.
47 IsError bool
48 // Status optionally refines an error result for lifecycle observers.
49 // Tools that return cancellation as content rather than an error may set
50 // cancelled or timed_out; policy refusals may set blocked.
51 Status string
52 // ActivateTools names previously deferred tools that become available
53 // after this result. Unknown names are ignored by the agent.
54 ActivateTools []string
55 // Details is arbitrary data for UIs and logs; not sent to the LLM.
56 Details any
57}
58
59// Registry is a name->Tool map.
60type Registry map[string]Tool
61
62// NewRegistry builds a Registry from a list of tools.
63func NewRegistry(tools ...Tool) Registry {
64 r := Registry{}
65 for _, t := range tools {
66 r[t.Name()] = t
67 }
68 return r
69}
70
71// Specs returns the tool definitions to advertise to the LLM.
72// Sorted by tool name so the order is stable across requests. This
73// is load-bearing for provider-side prompt caching: providers
74// prefix-match tool definitions, and Go's map iteration order is
75// randomized per call, which would otherwise bust the cache every
76// single turn.
77func (r Registry) Specs() []provider.Tool {
78 names := make([]string, 0, len(r))
79 for name := range r {
80 names = append(names, name)
81 }
82 sort.Strings(names)
83 out := make([]provider.Tool, 0, len(r))
84 for _, name := range names {
85 t := r[name]
86 deferred := false
87 if d, ok := t.(interface{ Deferred() bool }); ok {
88 deferred = d.Deferred()
89 }
90 out = append(out, provider.Tool{
91 Name: t.Name(),
92 Description: t.Description(),
93 Schema: t.Schema(),
94 Deferred: deferred,
95 })
96 }
97 return out
98}
99
100// Get looks up a tool by name.
101func (r Registry) Get(name string) (Tool, error) {
102 t, ok := r[name]
103 if !ok {
104 return nil, fmt.Errorf("unknown tool %q", name)
105 }
106 return t, nil
107}