@@ -108,19 +108,25 @@ func (a *Agent) TTSEnabled() bool {
108108
109109// Process runs the agentic loop. onToolCall is called before each tool execution (may be nil).
110110func (a * Agent ) Process (ctx context.Context , chatID int64 , userMsg llm.Message , onToolCall func (toolName string )) (string , error ) {
111+ tr := newRequestTrace ()
111112 queryText := messageText (userMsg )
112113
113114 // Store user message; embed it if semantic store + MCP embeddings are both available.
115+ endEmbed := tr .begin ("embed" )
114116 queryEmb := a .storeUserMessage (ctx , chatID , userMsg , queryText )
117+ endEmbed ()
115118
116119 // Auto-compact if needed
117120 if a .compacter != nil && NeedsCompaction (a .store , chatID ) {
118121 a .logger .Info ("auto-compacting conversation" , "chat_id" , chatID )
122+ endCompact := tr .begin ("compact" )
119123 if err := a .compacter .Compact (ctx , chatID , a .store ); err != nil {
120124 a .logger .Warn ("auto compaction failed" , "err" , err )
121125 }
126+ endCompact ()
122127 }
123128
129+ endToolsSel := tr .begin ("tools_select" )
124130 var tools []llm.Tool
125131 if a .mcp != nil {
126132 tools = a .mcp .LLMToolsForQuery (ctx , queryText )
@@ -131,25 +137,38 @@ func (a *Agent) Process(ctx context.Context, chatID int64, userMsg llm.Message,
131137 if a .filesystem != nil {
132138 tools = append (tools , filesystemTools ()... )
133139 }
140+ endToolsSel ()
134141
142+ endCross := tr .begin ("cross_session" )
135143 crossSessionCtx := a .buildCrossSessionContext (ctx , chatID , queryEmb )
144+ endCross ()
136145
137146 // Check response cache before calling the LLM.
138- if cached , ok := a .cache .Get (chatID , queryEmb ); ok {
147+ endCache := tr .begin ("cache" )
148+ cached , cacheHit := a .cache .Get (chatID , queryEmb )
149+ endCache ()
150+ if cacheHit {
139151 a .logger .Info ("cache hit" , "chat_id" , chatID )
140152 a .store .AddMessage (chatID , llm.Message {Role : "assistant" , Content : cached })
153+ tr .log (a .logger , chatID , "cache_hit" )
141154 return cached , nil
142155 }
143156
144157 for i := 0 ; i < maxToolIterations ; i ++ {
158+ endHist := tr .begin (fmt .Sprintf ("iter%d_history" , i ))
145159 history := a .getHistory (chatID , queryEmb )
160+ endHist ()
146161
147162 sysPrompt := "Current date and time: " + time .Now ().Format ("Monday, 2 January 2006, 15:04 MST" ) + "\n \n " + a .sysPrompt
148163 if crossSessionCtx != "" {
149164 sysPrompt += "\n \n " + crossSessionCtx
150165 }
166+
167+ endLLM := tr .begin (fmt .Sprintf ("iter%d_llm" , i ))
151168 resp , err := a .router .Chat (ctx , history , sysPrompt , tools )
169+ endLLM ()
152170 if err != nil {
171+ tr .log (a .logger , chatID , "llm_error" )
153172 return "" , fmt .Errorf ("llm: %w" , err )
154173 }
155174
@@ -162,6 +181,7 @@ func (a *Agent) Process(ctx context.Context, chatID int64, userMsg llm.Message,
162181 if i == 0 {
163182 a .cache .Set (chatID , queryEmb , resp .Content )
164183 }
184+ tr .log (a .logger , chatID , "ok" )
165185 return resp .Content , nil
166186 }
167187
@@ -171,25 +191,35 @@ func (a *Agent) Process(ctx context.Context, chatID int64, userMsg llm.Message,
171191 ToolCalls : resp .ToolCalls ,
172192 })
173193
194+ endTools := tr .begin (fmt .Sprintf ("iter%d_tools" , i ))
174195 a .executeToolCalls (ctx , chatID , resp .ToolCalls , onToolCall )
196+ endTools ()
175197 }
176198
199+ tr .log (a .logger , chatID , "max_iterations" )
177200 return "" , fmt .Errorf ("exceeded maximum tool iterations (%d)" , maxToolIterations )
178201}
179202
180203// ProcessStream is like Process but streams the final text response via onChunk.
181204// Tool-calling iterations remain synchronous. onChunk receives the accumulated text so far.
182205func (a * Agent ) ProcessStream (ctx context.Context , chatID int64 , userMsg llm.Message , onToolCall func (string ), onChunk func (accumulated string )) (string , error ) {
206+ tr := newRequestTrace ()
183207 queryText := messageText (userMsg )
208+
209+ endEmbed := tr .begin ("embed" )
184210 queryEmb := a .storeUserMessage (ctx , chatID , userMsg , queryText )
211+ endEmbed ()
185212
186213 if a .compacter != nil && NeedsCompaction (a .store , chatID ) {
187214 a .logger .Info ("auto-compacting conversation" , "chat_id" , chatID )
215+ endCompact := tr .begin ("compact" )
188216 if err := a .compacter .Compact (ctx , chatID , a .store ); err != nil {
189217 a .logger .Warn ("auto compaction failed" , "err" , err )
190218 }
219+ endCompact ()
191220 }
192221
222+ endToolsSel := tr .begin ("tools_select" )
193223 var tools []llm.Tool
194224 if a .mcp != nil {
195225 tools = a .mcp .LLMToolsForQuery (ctx , queryText )
@@ -200,24 +230,37 @@ func (a *Agent) ProcessStream(ctx context.Context, chatID int64, userMsg llm.Mes
200230 if a .filesystem != nil {
201231 tools = append (tools , filesystemTools ()... )
202232 }
233+ endToolsSel ()
203234
235+ endCross := tr .begin ("cross_session" )
204236 crossSessionCtx := a .buildCrossSessionContext (ctx , chatID , queryEmb )
237+ endCross ()
205238
206- if cached , ok := a .cache .Get (chatID , queryEmb ); ok {
239+ endCache := tr .begin ("cache" )
240+ cached , cacheHit := a .cache .Get (chatID , queryEmb )
241+ endCache ()
242+ if cacheHit {
207243 a .logger .Info ("cache hit" , "chat_id" , chatID )
208244 a .store .AddMessage (chatID , llm.Message {Role : "assistant" , Content : cached })
245+ tr .log (a .logger , chatID , "cache_hit" )
209246 return cached , nil
210247 }
211248
212249 for i := 0 ; i < maxToolIterations ; i ++ {
250+ endHist := tr .begin (fmt .Sprintf ("iter%d_history" , i ))
213251 history := a .getHistory (chatID , queryEmb )
252+ endHist ()
253+
214254 sysPrompt := "Current date and time: " + time .Now ().Format ("Monday, 2 January 2006, 15:04 MST" ) + "\n \n " + a .sysPrompt
215255 if crossSessionCtx != "" {
216256 sysPrompt += "\n \n " + crossSessionCtx
217257 }
218258
259+ endLLM := tr .begin (fmt .Sprintf ("iter%d_llm" , i ))
219260 ch , err := a .router .ChatStream (ctx , history , sysPrompt , tools )
220261 if err != nil {
262+ endLLM ()
263+ tr .log (a .logger , chatID , "llm_error" )
221264 return "" , fmt .Errorf ("llm: %w" , err )
222265 }
223266
@@ -227,6 +270,8 @@ func (a *Agent) ProcessStream(ctx context.Context, chatID int64, userMsg llm.Mes
227270
228271 for chunk := range ch {
229272 if chunk .Err != nil {
273+ endLLM ()
274+ tr .log (a .logger , chatID , "stream_error" )
230275 return "" , fmt .Errorf ("llm stream: %w" , chunk .Err )
231276 }
232277 if chunk .Delta != "" {
@@ -242,12 +287,14 @@ func (a *Agent) ProcessStream(ctx context.Context, chatID int64, userMsg llm.Mes
242287 }
243288 }
244289 }
290+ endLLM ()
245291
246292 if len (toolCalls ) == 0 {
247293 a .store .AddMessage (chatID , llm.Message {Role : "assistant" , Content : accumulated })
248294 if i == 0 {
249295 a .cache .Set (chatID , queryEmb , accumulated )
250296 }
297+ tr .log (a .logger , chatID , "ok" )
251298 return accumulated , nil
252299 }
253300
@@ -258,9 +305,12 @@ func (a *Agent) ProcessStream(ctx context.Context, chatID int64, userMsg llm.Mes
258305 ToolCalls : toolCalls ,
259306 })
260307
308+ endTools := tr .begin (fmt .Sprintf ("iter%d_tools" , i ))
261309 a .executeToolCalls (ctx , chatID , toolCalls , onToolCall )
310+ endTools ()
262311 }
263312
313+ tr .log (a .logger , chatID , "max_iterations" )
264314 return "" , fmt .Errorf ("exceeded maximum tool iterations (%d)" , maxToolIterations )
265315}
266316
0 commit comments