11package agent
22
33import (
4- "bufio"
54 "context"
6- "database/sql"
75 "encoding/json"
86 "fmt"
9- "log"
10- "os"
117 "strings"
128
13- "github.com/honganh1206/clue/conversation "
9+ "github.com/honganh1206/clue/api "
1410 "github.com/honganh1206/clue/inference"
15- "github.com/honganh1206/clue/prompts"
11+ "github.com/honganh1206/clue/message"
12+ "github.com/honganh1206/clue/server/data/conversation"
1613 "github.com/honganh1206/clue/tools"
17- _ "github.com/mattn/go-sqlite3"
1814)
1915
20- func Gen (conversationID string , modelConfig inference.ModelConfig , db * sql.DB ) error {
21- model , err := inference .Init (modelConfig )
22- if err != nil {
23- log .Fatalf ("Failed to initialize model: %s" , err .Error ())
24- }
25-
26- scanner := bufio .NewScanner (os .Stdin )
27- getUserMsg := func () (string , bool ) {
28- if ! scanner .Scan () {
29- return "" , false
30- }
31- return scanner .Text (), true
32- }
33-
34- toolDefs := []tools.ToolDefinition {tools .ReadFileDefinition , tools .ListFilesDefinition , tools .EditFileDefinition }
35-
36- var a * Agent
37- var conv * conversation.Conversation
38-
39- if conversationID != "" {
40- conv , err = conversation .Load (conversationID , db )
41- if err != nil {
42- return err
43- }
44- } else {
45- conv , err = conversation .New ()
46- if err != nil {
47- return err
48- }
49- }
50- a = New (model , getUserMsg , conv , toolDefs , prompts .ClaudeSystemPrompt (), db )
51-
52- // In production, use Background() as the final root context()
53- // For dev env, TODO for temporary scaffolding
54- err = a .run (context .TODO ())
55-
56- if err != nil {
57- return err
58- }
59-
60- return nil
61- }
62-
6316type Agent struct {
6417 model inference.Model
6518 getUserMessage func () (string , bool )
6619 tools []tools.ToolDefinition
6720 promptPath string
6821 conversation * conversation.Conversation
69- // FIXME: CRUD operations should be on its own, not a field in Agent
70- db * sql.DB
22+ client * api.Client
7123}
7224
73- func New (model inference.Model , getUserMsg func () (string , bool ), conversation * conversation.Conversation , tools []tools.ToolDefinition , promptPath string , db * sql. DB ) * Agent {
25+ func New (model inference.Model , getUserMsg func () (string , bool ), conversation * conversation.Conversation , tools []tools.ToolDefinition , promptPath string , client * api. Client ) * Agent {
7426 return & Agent {
7527 model : model ,
7628 getUserMessage : getUserMsg ,
7729 tools : tools ,
7830 promptPath : promptPath ,
7931 conversation : conversation ,
80- db : db ,
32+ client : client ,
8133 }
8234}
8335
@@ -100,7 +52,7 @@ func getModelColor(modelName string) string {
10052 }
10153}
10254
103- func (a * Agent ) run (ctx context.Context ) error {
55+ func (a * Agent ) Run (ctx context.Context ) error {
10456 modelName := a .model .Name ()
10557 colorCode := getModelColor (modelName )
10658 resetCode := "\u001b [0m"
@@ -111,40 +63,35 @@ func (a *Agent) run(ctx context.Context) error {
11163
11264 for {
11365 if readUserInput {
114-
11566 fmt .Print ("\u001b [94m>\u001b [0m " )
11667 userInput , ok := a .getUserMessage ()
11768 if ! ok {
11869 break
11970 }
12071
121- userMsg := conversation.MessageRequest {
122- MessageParam : conversation.MessageParam {
123- Role : conversation .UserRole ,
124- Content : []conversation.ContentBlock {conversation .NewTextContentBlock (userInput )},
125- },
72+ userMsg := & message.Message {
73+ Role : message .UserRole ,
74+ Content : []message.ContentBlockUnion {message .NewTextContentBlock (userInput )},
12675 }
127- a .conversation .Append (userMsg .MessageParam )
76+
77+ a .conversation .Append (userMsg )
12878 a .saveConversation ()
12979 }
13080
131- // TODO: Update with something interactive
132- // fmt.Printf("\u001b[93m%s\u001b[0m: ", modelName)
133-
134- agentMsg , err := a .model .RunInference (ctx , a .conversation .Messages , a .tools )
81+ agentMsg , err := a .model .CompleteStream (ctx , a .conversation .Messages , a .tools )
13582 if err != nil {
13683 return err
13784 }
13885
139- a .conversation .Append (agentMsg . MessageParam )
86+ a .conversation .Append (agentMsg )
14087 a .saveConversation ()
14188
142- toolResults := []conversation. ContentBlock {}
89+ toolResults := []message. ContentBlockUnion {}
14390
144- for _ , content := range agentMsg .Content {
145- switch c := content .( type ) {
146- case conversation. ToolUseContentBlock :
147- result := a .executeTool (c .ID , c .Name , c .Input )
91+ for _ , c := range agentMsg .Content {
92+ switch c . Type {
93+ case message . ToolUseType :
94+ result := a .executeTool (c .OfToolUseBlock . ID , c .OfToolUseBlock . Name , c . OfToolUseBlock .Input )
14895 toolResults = append (toolResults , result )
14996 }
15097 }
@@ -156,24 +103,21 @@ func (a *Agent) run(ctx context.Context) error {
156103
157104 readUserInput = false
158105
159- toolResultMsg := conversation.MessageRequest {
160- MessageParam : conversation.MessageParam {
161- Role : conversation .UserRole ,
162- Content : toolResults ,
163- },
106+ toolResultMsg := & message.Message {
107+ Role : message .UserRole ,
108+ Content : toolResults ,
164109 }
165110
166- a .conversation .Append (toolResultMsg . MessageParam )
111+ a .conversation .Append (toolResultMsg )
167112 a .saveConversation ()
168113 }
169114
170115 return nil
171116}
172117
173- func (a * Agent ) executeTool (id , name string , input json.RawMessage ) conversation. ContentBlock {
118+ func (a * Agent ) executeTool (id , name string , input json.RawMessage ) message. ContentBlockUnion {
174119 var toolDef tools.ToolDefinition
175120 var found bool
176-
177121 for _ , tool := range a .tools {
178122 if tool .Name == name {
179123 toolDef = tool
@@ -185,29 +129,29 @@ func (a *Agent) executeTool(id, name string, input json.RawMessage) conversation
185129 if ! found {
186130 // TODO: Return proper error type
187131 errorMsg := "tool not found"
188- return conversation .NewToolResultContentBlock (id , errorMsg , true )
132+ return message .NewToolResultContentBlock (id , errorMsg , true )
189133 }
190134
191135 fmt .Printf ("\u001b [92mtool\u001b [0m: %s(%s)\n " , name , input )
136+ println ()
192137
193138 response , err := toolDef .Function (input )
194139
195140 if err != nil {
196- return conversation .NewToolResultContentBlock (id , err .Error (), true )
141+ return message .NewToolResultContentBlock (id , err .Error (), true )
197142 }
198143
199- return conversation .NewToolResultContentBlock (id , response , true )
144+ return message .NewToolResultContentBlock (id , response , false )
200145}
201146
202147func (a * Agent ) saveConversation () error {
203- // FIXME: Very drafty. Consider moving the db field out of Agent struct?
204- err := a .conversation . SaveTo (a .db )
205- if err != nil {
206- // 4. Log any errors from history.Save to os.Stderr and return the error.
207- fmt . Fprintf ( os . Stderr , "Warning: could not save conversation to DB: %v \n " , err )
208- return err
148+ if len ( a . conversation . Messages ) > 0 {
149+ err := a .client . SaveConversation (a .conversation )
150+ if err != nil {
151+ fmt . Printf ( "DEBUG: Failed conversation details - ConversationID: %s \n " , a . conversation . ID )
152+ return err
153+ }
209154 }
210155
211156 return nil
212-
213157}
0 commit comments