xgae 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xgae might be problematic. Click here for more details.

@@ -1,16 +1,14 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
- from dataclasses import dataclass
5
- from typing import List, Dict, Any, Optional, Tuple, Union, Literal, Callable,TypedDict,AsyncGenerator
4
+
6
5
  from abc import ABC, abstractmethod
6
+ from dataclasses import dataclass
7
+ from typing import List, Dict, Any, Optional, Tuple, Union, Literal, Callable, TypedDict, AsyncGenerator
7
8
 
8
- from xgae.engine.xga_base import XGAToolResult, XGAResponseMsg, XGAToolBox
9
- # from xgae.utils.setup_env import langfuse
10
- from xgae.utils.json_helpers import (
11
- safe_json_parse,
12
- format_for_yield
13
- )
9
+ from xgae.engine.engine_base import XGAToolResult, XGAToolBox
10
+ from xgae.utils import langfuse
11
+ from xgae.utils.json_helpers import safe_json_parse,format_for_yield
14
12
  from xgae.utils.xml_tool_parser import XMLToolParser
15
13
 
16
14
  # Type alias for XML result adding strategy
@@ -19,17 +17,18 @@ XmlAddingStrategy = Literal["user_message", "assistant_message", "inline_edit"]
19
17
  # Type alias for tool execution strategy
20
18
  ToolExecutionStrategy = Literal["sequential", "parallel"]
21
19
 
22
- class TaskResponseContext(TypedDict, total=False):
20
+ class TaskResponserContext(TypedDict, total=False):
23
21
  is_stream: bool
24
22
  task_id: str
25
23
  task_run_id: str
26
24
  trace_id: str
25
+ root_span_id: str
27
26
  model_name: str
28
- max_xml_tool_calls: int
27
+ max_xml_tool_calls: int # LLM generate max_xml_tool limit, 0 is no limit
29
28
  add_response_msg_func: Callable
30
29
  tool_box: XGAToolBox
31
- tool_execution_strategy: Literal["sequential", "parallel"]
32
- xml_adding_strategy: Literal["user_message", "assistant_message", "inline_edit"]
30
+ tool_execution_strategy: ToolExecutionStrategy
31
+ xml_adding_strategy: XmlAddingStrategy
33
32
 
34
33
 
35
34
  class TaskRunContinuousState(TypedDict, total=False):
@@ -37,17 +36,6 @@ class TaskRunContinuousState(TypedDict, total=False):
37
36
  auto_continue_count: int
38
37
  auto_continue: bool
39
38
 
40
- class Span:
41
- def end(self, **kwargs):
42
- pass
43
-
44
- class Trace:
45
- def event(self, **kwargs):
46
- pass
47
-
48
- def span(self, **kwargs):
49
- return Span()
50
-
51
39
 
52
40
  @dataclass
53
41
  class ToolExecutionContext:
@@ -63,21 +51,27 @@ class ToolExecutionContext:
63
51
 
64
52
 
65
53
  class TaskResponseProcessor(ABC):
66
- def __init__(self, response_context: TaskResponseContext):
54
+ def __init__(self, response_context: TaskResponserContext):
67
55
  self.response_context = response_context
68
56
 
69
- self.task_id = response_context.get("task_id", "")
70
- self.task_run_id = response_context.get("task_run_id", "")
57
+ self.task_id = response_context.get("task_id")
58
+ self.task_run_id = response_context.get("task_run_id")
59
+ self.tool_execution_strategy = self.response_context.get("tool_execution_strategy", "parallel")
60
+ self.xml_adding_strategy = self.response_context.get("xml_adding_strategy", "user_message")
61
+ self.max_xml_tool_calls = self.response_context.get("max_xml_tool_calls", 0)
62
+
63
+ self.trace_context = {
64
+ "trace_id": self.response_context.get("trace_id"),
65
+ "parent_span_id": self.response_context.get("root_span_id"),
66
+ }
71
67
 
72
- # Initialize the XML parser
73
- self.trace = Trace()
68
+ self.add_response_message = response_context.get("add_response_msg_func")
74
69
 
75
- self.add_message = response_context.get("add_context_msg")
76
- self._add_message_with_agent_info = self.add_message
77
70
  self.tool_box = response_context.get("tool_box")
78
-
79
71
  self.xml_parser = XMLToolParser()
80
72
 
73
+
74
+
81
75
  @abstractmethod
82
76
  async def process_response(self,
83
77
  llm_response: AsyncGenerator,
@@ -86,16 +80,6 @@ class TaskResponseProcessor(ABC):
86
80
  ) -> AsyncGenerator[Dict[str, Any], None]:
87
81
  pass
88
82
 
89
- async def _yield_message(self, message_obj: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
90
- """Helper to yield a message with proper formatting.
91
-
92
- Ensures that content and metadata are JSON strings for client compatibility.
93
- """
94
- if message_obj:
95
- return format_for_yield(message_obj)
96
- return None
97
-
98
-
99
83
 
100
84
  def _extract_xml_chunks(self, content: str) -> List[str]:
101
85
  """Extract complete XML chunks using start and end pattern matching."""
@@ -190,12 +174,12 @@ class TaskResponseProcessor(ABC):
190
174
  except Exception as e:
191
175
  logging.error(f"Error extracting XML chunks: {e}")
192
176
  logging.error(f"Content was: {content}")
193
- self.trace.event(name="error_extracting_xml_chunks", level="ERROR",
177
+ langfuse.create_event(trace_context=self.trace_context, name="error_extracting_xml_chunks", level="ERROR",
194
178
  status_message=(f"Error extracting XML chunks: {e}"), metadata={"content": content})
195
179
 
196
180
  return chunks
197
181
 
198
- def _parse_xml_tool_call(self, xml_chunk: str) -> Optional[Tuple[Dict[str, Any], Dict[str, Any]]]:
182
+ def _parse_xml_tool_call(self, xml_chunk: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
199
183
  """Parse XML chunk into tool call format and return parsing details.
200
184
 
201
185
  Returns:
@@ -215,6 +199,9 @@ class TaskResponseProcessor(ABC):
215
199
 
216
200
  # Take the first tool call (should only be one per chunk)
217
201
  xml_tool_call = parsed_calls[0]
202
+ if not xml_tool_call.function_name:
203
+ logging.error(f"xml_tool_call function name is empty: {xml_tool_call}")
204
+ return None
218
205
 
219
206
  # Convert to the expected format
220
207
  tool_call = {
@@ -237,7 +224,7 @@ class TaskResponseProcessor(ABC):
237
224
  except Exception as e:
238
225
  logging.error(f"Error parsing XML chunk: {e}")
239
226
  logging.error(f"XML chunk was: {xml_chunk}")
240
- self.trace.event(name="error_parsing_xml_chunk", level="ERROR",
227
+ langfuse.create_event(trace_context=self.trace_context, name="error_parsing_xml_chunk", level="ERROR",
241
228
  status_message=(f"Error parsing XML chunk: {e}"), metadata={"xml_chunk": xml_chunk})
242
229
  return None
243
230
 
@@ -263,45 +250,44 @@ class TaskResponseProcessor(ABC):
263
250
 
264
251
  except Exception as e:
265
252
  logging.error(f"Error parsing XML tool calls: {e}", exc_info=True)
266
- self.trace.event(name="error_parsing_xml_tool_calls", level="ERROR",
253
+ langfuse.create_event(trace_context=self.trace_context, name="error_parsing_xml_tool_calls", level="ERROR",
267
254
  status_message=(f"Error parsing XML tool calls: {e}"), metadata={"content": content})
268
255
 
269
256
  return parsed_data
270
257
 
271
- # Tool execution methods
258
+
272
259
  async def _execute_tool(self, tool_call: Dict[str, Any]) -> XGAToolResult:
273
260
  """Execute a single tool call and return the result."""
274
- span = self.trace.span(name=f"execute_tool.{tool_call['function_name']}", input=tool_call["arguments"])
275
- try:
276
- function_name = tool_call["function_name"]
277
- arguments = tool_call["arguments"]
278
-
279
- logging.info(f"Executing tool: {function_name} with arguments: {arguments}")
280
- self.trace.event(name="executing_tool", level="DEFAULT",
281
- status_message=(f"Executing tool: {function_name} with arguments: {arguments}"))
282
-
283
- if isinstance(arguments, str):
284
- try:
285
- arguments = safe_json_parse(arguments)
286
- except json.JSONDecodeError:
287
- arguments = {"text": arguments} # @todo modify
288
-
289
- # Get available functions from tool registry
290
- #available_functions = self.tool_registry.get_available_functions()
291
- result = None
292
- available_function_names = self.tool_box.get_task_tool_names(self.task_id)
293
- if function_name in available_function_names:
294
- result = await self.tool_box.call_tool(self.task_id, function_name, arguments)
295
- else:
296
- logging.error(f"Tool function '{function_name}' not found in registry")
297
- result = XGAToolResult(success=False, output=f"Tool function '{function_name}' not found")
298
- logging.info(f"Tool execution complete: {function_name} -> {result}")
299
- span.end(status_message="tool_executed", output=result)
300
- return result
301
- except Exception as e:
302
- logging.error(f"Error executing tool {tool_call['function_name']}: {str(e)}", exc_info=True)
303
- span.end(status_message="tool_execution_error", output=f"Error executing tool: {str(e)}", level="ERROR")
304
- return XGAToolResult(success=False, output=f"Error executing tool: {str(e)}")
261
+ with langfuse.start_as_current_span(trace_context=self.trace_context, name=f"execute_tool.{tool_call['function_name']}", input=tool_call["arguments"]
262
+ ) as exec_tool_span:
263
+ try:
264
+ function_name = tool_call["function_name"]
265
+ arguments = tool_call["arguments"]
266
+
267
+ logging.info(f"Executing tool: {function_name} with arguments: {arguments}")
268
+
269
+ if isinstance(arguments, str):
270
+ try:
271
+ arguments = safe_json_parse(arguments)
272
+ except json.JSONDecodeError:
273
+ arguments = {"text": arguments} # @todo modify
274
+
275
+ result = None
276
+ available_tool_names = self.tool_box.get_task_tool_names(self.task_id)
277
+ if function_name in available_tool_names:
278
+ result = await self.tool_box.call_tool(self.task_id, function_name, arguments)
279
+ else:
280
+ logging.error(f"Tool function '{function_name}' not found in registry")
281
+ result = XGAToolResult(success=False, output=f"Tool function '{function_name}' not found")
282
+ logging.info(f"Tool execution complete: {function_name} -> {result}")
283
+ exec_tool_span.update(status_message="tool_executed", output=result)
284
+
285
+ return result
286
+ except Exception as e:
287
+ logging.error(f"Error executing tool {tool_call['function_name']}: {str(e)}", exc_info=True)
288
+
289
+ exec_tool_span.update(status_message="tool_execution_error", output=f"Error executing tool: {str(e)}", level="ERROR")
290
+ return XGAToolResult(success=False, output=f"Error executing tool: {str(e)}")
305
291
 
306
292
  async def _execute_tools(
307
293
  self,
@@ -309,8 +295,6 @@ class TaskResponseProcessor(ABC):
309
295
  execution_strategy: ToolExecutionStrategy = "sequential"
310
296
  ) -> List[Tuple[Dict[str, Any], XGAToolResult]]:
311
297
  logging.info(f"Executing {len(tool_calls)} tools with strategy: {execution_strategy}")
312
- self.trace.event(name="executing_tools_with_strategy", level="DEFAULT",
313
- status_message=(f"Executing {len(tool_calls)} tools with strategy: {execution_strategy}"))
314
298
 
315
299
  if execution_strategy == "sequential":
316
300
  return await self._execute_tools_sequentially(tool_calls)
@@ -335,54 +319,40 @@ class TaskResponseProcessor(ABC):
335
319
  """
336
320
  if not tool_calls:
337
321
  return []
322
+ tool_names = [t.get('function_name', 'unknown') for t in tool_calls]
323
+ logging.info(f"Executing {len(tool_calls)} tools sequentially: {tool_names}")
324
+ langfuse.create_event(trace_context=self.trace_context, name="executing_tools_sequentially", level="DEFAULT",
325
+ status_message=(f"Executing {len(tool_calls)} tools sequentially: {tool_names}"))
338
326
 
339
- try:
340
- tool_names = [t.get('function_name', 'unknown') for t in tool_calls]
341
- logging.info(f"Executing {len(tool_calls)} tools sequentially: {tool_names}")
342
- self.trace.event(name="executing_tools_sequentially", level="DEFAULT",
343
- status_message=(f"Executing {len(tool_calls)} tools sequentially: {tool_names}"))
344
-
345
- results = []
346
- for index, tool_call in enumerate(tool_calls):
347
- tool_name = tool_call.get('function_name', 'unknown')
348
- logging.debug(f"Executing tool {index + 1}/{len(tool_calls)}: {tool_name}")
349
-
350
- try:
351
- result = await self._execute_tool(tool_call)
352
- results.append((tool_call, result))
353
- logging.debug(f"Completed tool {tool_name} with success={result.success}")
354
-
355
- # Check if this is a terminating tool (ask or complete)
356
- if tool_name in ['ask', 'complete']:
357
- logging.info(f"Terminating tool '{tool_name}' executed. Stopping further tool execution.")
358
- self.trace.event(name="terminating_tool_executed", level="DEFAULT", status_message=(
359
- f"Terminating tool '{tool_name}' executed. Stopping further tool execution."))
360
- break # Stop executing remaining tools
361
-
362
- except Exception as e:
363
- logging.error(f"Error executing tool {tool_name}: {str(e)}")
364
- self.trace.event(name="error_executing_tool", level="ERROR",
365
- status_message=(f"Error executing tool {tool_name}: {str(e)}"))
366
- error_result = XGAToolResult(success=False, output=f"Error executing tool: {str(e)}")
367
- results.append((tool_call, error_result))
368
-
369
- logging.info(f"Sequential execution completed for {len(results)} tools (out of {len(tool_calls)} total)")
370
- self.trace.event(name="sequential_execution_completed", level="DEFAULT", status_message=(
371
- f"Sequential execution completed for {len(results)} tools (out of {len(tool_calls)} total)"))
372
- return results
373
-
374
- except Exception as e:
375
- logging.error(f"Error in sequential tool execution: {str(e)}", exc_info=True)
376
- # Return partial results plus error results for remaining tools
377
- completed_results = results if 'results' in locals() else []
378
- completed_tool_names = [r[0].get('function_name', 'unknown') for r in completed_results]
379
- remaining_tools = [t for t in tool_calls if t.get('function_name', 'unknown') not in completed_tool_names]
327
+ results = []
328
+ for index, tool_call in enumerate(tool_calls):
329
+ tool_name = tool_call.get('function_name', 'unknown')
330
+ logging.debug(f"Executing tool {index + 1}/{len(tool_calls)}: {tool_name}")
380
331
 
381
- # Add error results for remaining tools
382
- error_results = [(tool, XGAToolResult(success=False, output=f"Execution error: {str(e)}"))
383
- for tool in remaining_tools]
332
+ try:
333
+ result = await self._execute_tool(tool_call)
334
+ results.append((tool_call, result))
335
+ logging.debug(f"Completed tool {tool_name} with success={result.success}")
336
+
337
+ # Check if this is a terminating tool (ask or complete)
338
+ if tool_name in ['ask', 'complete']:
339
+ logging.info(f"Terminating tool '{tool_name}' executed. Stopping further tool execution.")
340
+ # langfuse.create_event(trace_context=self.trace_context, name="terminating_tool_executed",
341
+ # level="DEFAULT", status_message=(f"Terminating tool '{tool_name}' executed. Stopping further tool execution."))
342
+ break # Stop executing remaining tools
343
+
344
+ except Exception as e:
345
+ logging.error(f"Error executing tool {tool_name}: {str(e)}")
346
+ langfuse.create_event(trace_context=self.trace_context, name="error_executing_tool", level="ERROR",
347
+ status_message=(f"Error executing tool {tool_name}: {str(e)}"))
348
+ error_result = XGAToolResult(success=False, output=f"Error executing tool: {str(e)}")
349
+ results.append((tool_call, error_result))
350
+
351
+ logging.info(f"Sequential execution completed for {len(results)} tools (out of {len(tool_calls)} total)")
352
+ # langfuse.create_event(trace_context=self.trace_context, name="sequential_execution_completed", level="DEFAULT",
353
+ # status_message=(f"Sequential execution completed for {len(results)} tools (out of {len(tool_calls)} total)"))
354
+ return results
384
355
 
385
- return completed_results + error_results
386
356
 
387
357
  async def _execute_tools_in_parallel(self, tool_calls: List[Dict[str, Any]]) -> List[Tuple[Dict[str, Any], XGAToolResult]]:
388
358
  if not tool_calls:
@@ -391,8 +361,8 @@ class TaskResponseProcessor(ABC):
391
361
  try:
392
362
  tool_names = [t.get('function_name', 'unknown') for t in tool_calls]
393
363
  logging.info(f"Executing {len(tool_calls)} tools in parallel: {tool_names}")
394
- self.trace.event(name="executing_tools_in_parallel", level="DEFAULT",
395
- status_message=(f"Executing {len(tool_calls)} tools in parallel: {tool_names}"))
364
+ # langfuse.create_event(trace_context=self.trace_context, name="executing_tools_in_parallel", level="DEFAULT",
365
+ # status_message=(f"Executing {len(tool_calls)} tools in parallel: {tool_names}"))
396
366
 
397
367
  # Create tasks for all tool calls
398
368
  tasks = [self._execute_tool(tool_call) for tool_call in tool_calls]
@@ -405,7 +375,7 @@ class TaskResponseProcessor(ABC):
405
375
  for i, (tool_call, result) in enumerate(zip(tool_calls, results)):
406
376
  if isinstance(result, Exception):
407
377
  logging.error(f"Error executing tool {tool_call.get('function_name', 'unknown')}: {str(result)}")
408
- self.trace.event(name="error_executing_tool", level="ERROR", status_message=(
378
+ langfuse.create_event(trace_context=self.trace_context, name="error_executing_tool", level="ERROR", status_message=(
409
379
  f"Error executing tool {tool_call.get('function_name', 'unknown')}: {str(result)}"))
410
380
  # Create error result
411
381
  error_result = XGAToolResult(success=False, output=f"Error executing tool: {str(result)}")
@@ -414,24 +384,23 @@ class TaskResponseProcessor(ABC):
414
384
  processed_results.append((tool_call, result))
415
385
 
416
386
  logging.info(f"Parallel execution completed for {len(tool_calls)} tools")
417
- self.trace.event(name="parallel_execution_completed", level="DEFAULT",
418
- status_message=(f"Parallel execution completed for {len(tool_calls)} tools"))
387
+ # langfuse.create_event(trace_context=self.trace_context, name="parallel_execution_completed", level="DEFAULT",
388
+ # status_message=(f"Parallel execution completed for {len(tool_calls)} tools"))
419
389
  return processed_results
420
390
 
421
391
  except Exception as e:
422
392
  logging.error(f"Error in parallel tool execution: {str(e)}", exc_info=True)
423
- self.trace.event(name="error_in_parallel_tool_execution", level="ERROR",
393
+ langfuse.create_event(trace_context=self.trace_context, name="error_in_parallel_tool_execution", level="ERROR",
424
394
  status_message=(f"Error in parallel tool execution: {str(e)}"))
425
395
  # Return error results for all tools if the gather itself fails
426
396
  return [(tool_call, XGAToolResult(success=False, output=f"Execution error: {str(e)}"))
427
397
  for tool_call in tool_calls]
428
398
 
429
- def _add_tool_result(
399
+ def _add_tool_messsage(
430
400
  self,
431
- thread_id: str,
432
401
  tool_call: Dict[str, Any],
433
402
  result: XGAToolResult,
434
- strategy: Union[XmlAddingStrategy, str] = "assistant_message",
403
+ strategy: XmlAddingStrategy = "assistant_message",
435
404
  assistant_message_id: Optional[str] = None,
436
405
  parsing_details: Optional[Dict[str, Any]] = None
437
406
  ) -> Optional[Dict[str, Any]]: # Return the full message object
@@ -443,63 +412,11 @@ class TaskResponseProcessor(ABC):
443
412
  if assistant_message_id:
444
413
  metadata["assistant_message_id"] = assistant_message_id
445
414
  logging.info(f"Linking tool result to assistant message: {assistant_message_id}")
446
- self.trace.event(name="linking_tool_result_to_assistant_message", level="DEFAULT",
447
- status_message=(f"Linking tool result to assistant message: {assistant_message_id}"))
448
415
 
449
416
  # --- Add parsing details to metadata if available ---
450
417
  if parsing_details:
451
418
  metadata["parsing_details"] = parsing_details
452
419
  logging.info("Adding parsing_details to tool result metadata")
453
- self.trace.event(name="adding_parsing_details_to_tool_result_metadata", level="DEFAULT",
454
- status_message=(f"Adding parsing_details to tool result metadata"),
455
- metadata={"parsing_details": parsing_details})
456
- # ---
457
-
458
- # Check if this is a native function call (has id field)
459
- if "id" in tool_call:
460
- # Format as a proper tool message according to OpenAI spec
461
- function_name = tool_call.get("function_name", "")
462
-
463
- # Format the tool result content - tool role needs string content
464
- if isinstance(result, str):
465
- content = result
466
- elif hasattr(result, 'output'):
467
- # If it's a XGAToolResult object
468
- if isinstance(result.output, dict) or isinstance(result.output, list):
469
- # If output is already a dict or list, convert to JSON string
470
- content = json.dumps(result.output)
471
- else:
472
- # Otherwise just use the string representation
473
- content = str(result.output)
474
- else:
475
- # Fallback to string representation of the whole result
476
- content = str(result)
477
-
478
- logging.info(f"Formatted tool result content: {content[:100]}...")
479
- self.trace.event(name="formatted_tool_result_content", level="DEFAULT",
480
- status_message=(f"Formatted tool result content: {content[:100]}..."))
481
-
482
- # Create the tool response message with proper format
483
- tool_message = {
484
- "role": "tool",
485
- "tool_call_id": tool_call["id"],
486
- "name": function_name,
487
- "content": content
488
- }
489
-
490
- logging.info(f"Adding native tool result for tool_call_id={tool_call['id']} with role=tool")
491
- self.trace.event(name="adding_native_tool_result_for_tool_call_id", level="DEFAULT", status_message=(
492
- f"Adding native tool result for tool_call_id={tool_call['id']} with role=tool"))
493
-
494
- # Add as a tool message to the conversation history
495
- # This makes the result visible to the LLM in the next turn
496
- message_obj = self.add_message(
497
- type="tool", # Special type for tool responses
498
- content=tool_message,
499
- is_llm_message=True,
500
- metadata=metadata
501
- )
502
- return message_obj # Return the full message object
503
420
 
504
421
  # For XML and other non-native tools, use the new structured format
505
422
  # Determine message role based on strategy
@@ -525,7 +442,7 @@ class TaskResponseProcessor(ABC):
525
442
  metadata = {}
526
443
  metadata['frontend_content'] = structured_result_for_frontend
527
444
 
528
- message_obj = self._add_message_with_agent_info(
445
+ message_obj = self.add_response_message(
529
446
  type="tool",
530
447
  content=result_message_for_llm, # Save the LLM-friendly version
531
448
  is_llm_message=True,
@@ -544,7 +461,7 @@ class TaskResponseProcessor(ABC):
544
461
  return message_obj # Return the modified message object
545
462
  except Exception as e:
546
463
  logging.error(f"Error adding tool result: {str(e)}", exc_info=True)
547
- self.trace.event(name="error_adding_tool_result", level="ERROR",
464
+ langfuse.create_event(trace_context=self.trace_context, name="error_adding_tool_result", level="ERROR",
548
465
  status_message=(f"Error adding tool result: {str(e)}"),
549
466
  metadata={"tool_call": tool_call, "result": result, "strategy": strategy,
550
467
  "assistant_message_id": assistant_message_id,
@@ -555,7 +472,7 @@ class TaskResponseProcessor(ABC):
555
472
  "role": "user",
556
473
  "content": str(result)
557
474
  }
558
- message_obj = self.add_message(
475
+ message_obj = self.add_response_message(
559
476
  type="tool",
560
477
  content=fallback_message,
561
478
  is_llm_message=True,
@@ -564,7 +481,7 @@ class TaskResponseProcessor(ABC):
564
481
  return message_obj # Return the full message object
565
482
  except Exception as e2:
566
483
  logging.error(f"Failed even with fallback message: {str(e2)}", exc_info=True)
567
- self.trace.event(name="failed_even_with_fallback_message", level="ERROR",
484
+ langfuse.create_event(trace_context=self.trace_context, name="failed_even_with_fallback_message", level="ERROR",
568
485
  status_message=(f"Failed even with fallback message: {str(e2)}"),
569
486
  metadata={"tool_call": tool_call, "result": result, "strategy": strategy,
570
487
  "assistant_message_id": assistant_message_id,
@@ -629,38 +546,29 @@ class TaskResponseProcessor(ABC):
629
546
  )
630
547
 
631
548
  # Set function_name and xml_tag_name fields
632
- if "xml_tag_name" in tool_call:
633
- context.xml_tag_name = tool_call["xml_tag_name"]
634
- context.function_name = tool_call.get("function_name", tool_call["xml_tag_name"])
635
- else:
636
- # For non-XML tools, use function name directly
637
- context.function_name = tool_call.get("function_name", "unknown")
638
- context.xml_tag_name = None
549
+ context.xml_tag_name = tool_call["xml_tag_name"]
550
+ context.function_name = tool_call["function_name"]
639
551
 
640
552
  return context
641
553
 
642
- def _yield_and_save_tool_started(self, context: ToolExecutionContext, thread_id: str, thread_run_id: str) -> \
643
- Optional[Dict[str, Any]]:
554
+ def _add_tool_start_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
644
555
  """Formats, saves, and returns a tool started status message."""
645
556
  tool_name = context.xml_tag_name or context.function_name
646
557
  content = {
647
558
  "role": "assistant", "status_type": "tool_started",
648
559
  "function_name": context.function_name, "xml_tag_name": context.xml_tag_name,
649
- "message": f"Starting execution of {tool_name}", "tool_index": context.tool_index,
650
- "tool_call_id": context.tool_call.get("id") # Include tool_call ID if native
560
+ "message": f"Starting execution of {tool_name}", "tool_index": context.tool_index # Include tool_call ID if native
651
561
  }
652
- metadata = {"thread_run_id": thread_run_id}
653
- saved_message_obj = self.add_message(
654
- type="status", content=content, is_llm_message=False, metadata=metadata
562
+
563
+ return self.add_response_message(
564
+ type="status", content=content, is_llm_message=False
655
565
  )
656
- return saved_message_obj # Return the full object (or None if saving failed)
657
566
 
658
- def _yield_and_save_tool_completed(self, context: ToolExecutionContext, tool_message_id: Optional[str],
659
- thread_id: str, thread_run_id: str) -> Optional[Dict[str, Any]]:
567
+ def _add_tool_completed_message(self, context: ToolExecutionContext, tool_message_id: Optional[str]) -> Optional[Dict[str, Any]]:
660
568
  """Formats, saves, and returns a tool completed/failed status message."""
661
569
  if not context.result:
662
570
  # Delegate to error saving if result is missing (e.g., execution failed)
663
- return self._yield_and_save_tool_error(context, thread_id, thread_run_id)
571
+ return self._add_tool_error_message(context)
664
572
 
665
573
  tool_name = context.xml_tag_name or context.function_name
666
574
  status_type = "tool_completed" if context.result.success else "tool_failed"
@@ -672,7 +580,7 @@ class TaskResponseProcessor(ABC):
672
580
  "message": message_text, "tool_index": context.tool_index,
673
581
  "tool_call_id": context.tool_call.get("id")
674
582
  }
675
- metadata = {"thread_run_id": thread_run_id}
583
+ metadata = {}
676
584
  # Add the *actual* tool result message ID to the metadata if available and successful
677
585
  if context.result.success and tool_message_id:
678
586
  metadata["linked_tool_result_message_id"] = tool_message_id
@@ -681,17 +589,15 @@ class TaskResponseProcessor(ABC):
681
589
  if context.function_name in ['ask', 'complete']:
682
590
  metadata["agent_should_terminate"] = "true"
683
591
  logging.info(f"Marking tool status for '{context.function_name}' with termination signal.")
684
- self.trace.event(name="marking_tool_status_for_termination", level="DEFAULT", status_message=(
685
- f"Marking tool status for '{context.function_name}' with termination signal."))
592
+ # langfuse.create_event(trace_context=self.trace_context, name="marking_tool_status_for_termination", level="DEFAULT", status_message=(
593
+ # f"Marking tool status for '{context.function_name}' with termination signal."))
686
594
  # <<< END ADDED >>>
687
595
 
688
- saved_message_obj = self.add_message(
596
+ return self.add_response_message(
689
597
  type="status", content=content, is_llm_message=False, metadata=metadata
690
598
  )
691
- return saved_message_obj
692
599
 
693
- def _yield_and_save_tool_error(self, context: ToolExecutionContext, thread_id: str, thread_run_id: str) -> \
694
- Optional[Dict[str, Any]]:
600
+ def _add_tool_error_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
695
601
  """Formats, saves, and returns a tool error status message."""
696
602
  error_msg = str(context.error) if context.error else "Unknown error during tool execution"
697
603
  tool_name = context.xml_tag_name or context.function_name
@@ -702,9 +608,9 @@ class TaskResponseProcessor(ABC):
702
608
  "tool_index": context.tool_index,
703
609
  "tool_call_id": context.tool_call.get("id")
704
610
  }
705
- metadata = {"thread_run_id": thread_run_id}
611
+
706
612
  # Save the status message with is_llm_message=False
707
- saved_message_obj = self.add_message(
708
- type="status", content=content, is_llm_message=False, metadata=metadata
613
+ return self.add_response_message(
614
+ type="status", content=content, is_llm_message=False
709
615
  )
710
- return saved_message_obj
616
+