blob: 8ff5d6b9c22f00679324d4ff9b0388d4f9d4bf55 [file] [log] [blame]
Sketch🕴️305f8172026-02-27 13:58:43 +04001package loop
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "strings"
8 "time"
9
10 "dodo.cloud/neo/tools"
11
12 "github.com/anthropics/anthropic-sdk-go"
13)
14
15type AnthropicAgent struct {
16 reg tools.Registry
17 client anthropic.Client
18}
19
20func (a *AnthropicAgent) Run(todo *ToDo) error {
21 sp := fmt.Sprintf(systemPrompt, ToDoJSONSchema())
22 var toolParams []anthropic.ToolParam
23 for _, t := range a.reg.All() {
24 schema, err := GetToolSchema(t.InputSchema())
25 if err != nil {
26 return err
27 }
28 toolParams = append(toolParams, anthropic.ToolParam{
29 Name: t.Name(),
30 Description: anthropic.String(t.Description()),
31 InputSchema: schema,
32 })
33 }
34 tools := make([]anthropic.ToolUnionParam, len(toolParams))
35 for i, toolParam := range toolParams {
36 tools[i] = anthropic.ToolUnionParam{OfTool: &toolParam}
37 }
38 k, err := json.MarshalIndent(todo, "", "\t")
39 if err != nil {
40 return err
41 }
42 for {
43 items := findActionableItems(todo, "assistant")
44 if len(items) == 0 {
45 time.Sleep(30 * time.Second)
46 continue
47 }
48 var itemIds []string
49 var messages []anthropic.MessageParam
50 for _, i := range items {
51 itemIds = append(itemIds, i.ID)
52 }
53 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock(string(k))))
54 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock(
55 fmt.Sprintf("Work on TODO item: %s", strings.Join(itemIds, ", ")))))
56 for {
57 resp, err := a.client.Messages.New(context.TODO(), anthropic.MessageNewParams{
58 MaxTokens: 10240,
59 System: []anthropic.TextBlockParam{
60 {Text: sp},
61 },
62 Messages: messages,
63 Model: anthropic.ModelClaudeOpus4_6,
64 Tools: tools,
65 })
66 if err != nil {
67 return err
68 }
69 fmt.Printf("--- STOP_REASON: %s\n", resp.StopReason)
70 messages = append(messages, resp.ToParam())
71
72 var toolResults []anthropic.ContentBlockParamUnion
73 for _, block := range resp.Content {
74 switch v := block.AsAny().(type) {
75 case anthropic.TextBlock:
76 fmt.Printf("AI: %s\n", v.Text)
77 case anthropic.ToolUseBlock:
78 t := a.reg.Get(v.Name)
79 if t == nil {
80 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, fmt.Sprintf("unknown tool %q", v.Name), true))
81 continue
82 }
83 args := v.JSON.Input.Raw()
84 fmt.Printf("CALLING TOOL: %s %s\n", v.Name, args)
85 out, err := t.Call(string(args))
86 if err != nil {
87 fmt.Printf("ERR: %s\n", err.Error())
88 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, err.Error(), true))
89 } else {
90 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, out, false))
91 }
92 }
93 }
94 if len(toolResults) == 0 {
95 break
96 }
97 messages = append(messages, anthropic.NewUserMessage(toolResults...))
98 }
99 for _, i := range items {
100 i.AssignedTo = "user"
101 }
102 }
103}