xgae 0.1.14__py3-none-any.whl → 0.1.16__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.

xgae/cli_app.py CHANGED
@@ -56,7 +56,7 @@ async def cli() -> None:
56
56
  trace_id = langfuse.trace(name="xgae_cli").trace_id
57
57
 
58
58
  final_result = await engine.run_task_with_final_answer(
59
- task_message={"role": "user", "content": user_message},
59
+ task_message={'role': "user", 'content': user_message},
60
60
  trace_id=trace_id
61
61
  )
62
62
 
@@ -65,7 +65,7 @@ async def cli() -> None:
65
65
  print(f"\n📌 ASK INFO: {final_result['content']}")
66
66
  user_message = get_user_message("Enter ASK information (or 'exit' to quit)")
67
67
  final_result = await engine.run_task_with_final_answer(
68
- task_message={"role": "user", "content": user_message},
68
+ task_message={'role': "user", 'content': user_message},
69
69
  trace_id=trace_id
70
70
  )
71
71
 
@@ -17,16 +17,17 @@ class XGAMcpToolBox(XGAToolBox):
17
17
  custom_mcp_server_config: Optional[Dict[str, Any]] = None
18
18
  ):
19
19
  general_mcp_server_config = self._load_mcp_servers_config("mcpservers/xga_server.json")
20
- tool_box_mcp_server_config = general_mcp_server_config.get("mcpServers", {})
20
+ tool_box_mcp_server_config = general_mcp_server_config.get('mcpServers', {})
21
21
 
22
22
  if custom_mcp_server_config:
23
23
  tool_box_mcp_server_config.update(custom_mcp_server_config)
24
24
  elif custom_mcp_server_file:
25
25
  custom_mcp_server_config = self._load_mcp_servers_config(custom_mcp_server_file)
26
- custom_mcp_server_config = custom_mcp_server_config.get("mcpServers", {})
26
+ custom_mcp_server_config = custom_mcp_server_config.get('mcpServers', {})
27
27
  tool_box_mcp_server_config.update(custom_mcp_server_config)
28
28
 
29
29
  self._mcp_client = MultiServerMCPClient(tool_box_mcp_server_config)
30
+
30
31
  self.mcp_server_names: List[str] = [server_name for server_name in tool_box_mcp_server_config]
31
32
  self.mcp_tool_schemas: Dict[str, List[XGAToolSchema]] = {}
32
33
  self.task_tool_schemas: Dict[str, Dict[str,XGAToolSchema]] = {}
@@ -77,7 +78,7 @@ class XGAMcpToolBox(XGAToolBox):
77
78
  async def destroy_task_tool_box(self, task_id: str):
78
79
  tool_schemas = self.get_task_tool_schemas(task_id, type="general_tool")
79
80
  if len(tool_schemas) > 0:
80
- await self.call_tool(task_id, "end_task", {"task_id": task_id})
81
+ await self.call_tool(task_id, "end_task", {'task_id': task_id})
81
82
  self.task_tool_schemas.pop(task_id, None)
82
83
 
83
84
  @override
@@ -117,7 +118,7 @@ class XGAMcpToolBox(XGAToolBox):
117
118
  if mcp_tool:
118
119
  tool_args = args or {}
119
120
  if server_name == self.GENERAL_MCP_SERVER_NAME:
120
- tool_args = dict({"task_id": task_id}, **tool_args)
121
+ tool_args = dict({'task_id': task_id}, **tool_args)
121
122
  is_general_tool = True
122
123
 
123
124
  try:
@@ -151,9 +152,9 @@ class XGAMcpToolBox(XGAToolBox):
151
152
  input_schema['properties'].pop("task_id", None)
152
153
  if 'task_id' in input_schema['required']:
153
154
  input_schema['required'].remove('task_id')
154
- params_properties = input_schema.get("properties", {})
155
+ params_properties = input_schema.get('properties', {})
155
156
  for param_properties in params_properties.values():
156
- param_properties.pop("title", None)
157
+ param_properties.pop('title', None)
157
158
 
158
159
  metadata = tool.metadata or {}
159
160
  tool_schema = XGAToolSchema(tool_name=tool.name,
@@ -172,24 +173,24 @@ class XGAMcpToolBox(XGAToolBox):
172
173
  def _load_mcp_servers_config(mcp_config_path: str) -> Dict[str, Any]:
173
174
  try:
174
175
  if os.path.exists(mcp_config_path):
175
- with open(mcp_config_path, 'r', encoding='utf-8') as f:
176
+ with open(mcp_config_path, 'r', encoding="utf-8") as f:
176
177
  server_config = json.load(f)
177
178
 
178
- for server_name, server_info in server_config["mcpServers"].items():
179
+ for server_name, server_info in server_config['mcpServers'].items():
179
180
  if "transport" not in server_info:
180
181
  if "url" in server_info:
181
- server_info["transport"] = "streamable_http" if "mcp" in server_info["url"] else "sse"
182
+ server_info['transport'] = "streamable_http" if "mcp" in server_info['url'] else "sse"
182
183
  else:
183
- server_info["transport"] = "stdio"
184
+ server_info['transport'] = "stdio"
184
185
 
185
186
  return server_config
186
187
  else:
187
188
  logging.warning(f"McpToolBox load_mcp_servers_config: MCP servers config file not found at: {mcp_config_path}")
188
- return {"mcpServers": {}}
189
+ return {'mcpServers': {}}
189
190
 
190
191
  except Exception as e:
191
192
  logging.error(f"McpToolBox load_mcp_servers_config: Failed to load MCP servers config: {e}")
192
- return {"mcpServers": {}}
193
+ return {'mcpServers': {}}
193
194
 
194
195
 
195
196
  if __name__ == "__main__":
@@ -34,19 +34,19 @@ class XGAPromptBuilder():
34
34
  openai_schemas = []
35
35
  for tool_schema in tool_schemas:
36
36
  openai_schema = {}
37
- openai_schema["type"] = "function"
38
37
  openai_function = {}
39
- openai_schema["function"] = openai_function
40
-
41
- openai_function["name"] = tool_schema.tool_name
42
- openai_function["description"] = tool_schema.description if tool_schema.description else 'No description available'
38
+ openai_schema['type'] = "function"
39
+ openai_schema['function'] = openai_function
43
40
 
41
+ openai_function['name'] = tool_schema.tool_name
42
+ openai_function['description'] = tool_schema.description if tool_schema.description else 'No description available'
44
43
  openai_parameters = {}
44
+ openai_function['parameters'] = openai_parameters
45
+
45
46
  input_schema = tool_schema.input_schema
46
- openai_function["parameters"] = openai_parameters
47
- openai_parameters["type"] = input_schema["type"]
48
- openai_parameters["properties"] = input_schema.get("properties", {})
49
- openai_parameters["required"] = input_schema["required"]
47
+ openai_parameters['type'] = input_schema['type']
48
+ openai_parameters['properties'] = input_schema.get('properties', {})
49
+ openai_parameters['required'] = input_schema['required']
50
50
 
51
51
  openai_schemas.append(openai_schema)
52
52
 
@@ -69,7 +69,7 @@ class XGAPromptBuilder():
69
69
  for tool_schema in tool_schemas:
70
70
  description = tool_schema.description if tool_schema.description else 'No description available'
71
71
  tool_info += f"- **{tool_schema.tool_name}**: {description}\n"
72
- parameters = tool_schema.input_schema.get("properties", {})
72
+ parameters = tool_schema.input_schema.get('properties', {})
73
73
  tool_info += f" Parameters: {parameters}\n"
74
74
  tool_prompt = tool_prompt.replace("{tool_schemas}", tool_info)
75
75
 
@@ -3,7 +3,7 @@ import logging
3
3
  from typing import List, Dict, Any, AsyncGenerator, override,Optional
4
4
 
5
5
  from xgae.utils import log_trace
6
- from xgae.utils.json_helpers import format_for_yield
6
+
7
7
 
8
8
  from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
9
9
 
@@ -20,12 +20,12 @@ class NonStreamTaskResponser(TaskResponseProcessor):
20
20
  llm_content = ""
21
21
  parsed_xml_data = []
22
22
  finish_reason = None
23
- llm_count = continuous_state.get("auto_continue_count")
23
+ auto_continue_count = continuous_state['auto_continue_count']
24
24
 
25
25
  try:
26
26
  if hasattr(llm_response, 'choices') and llm_response.choices:
27
27
  if hasattr(llm_response.choices[0], 'finish_reason'):
28
- finish_reason = llm_response.choices[0].finish_reason
28
+ finish_reason = llm_response.choices[0].finish_reason # LLM finish reason: ‘stop' , 'length'
29
29
  logging.info(f"NonStreamResp: LLM response finish_reason={finish_reason}")
30
30
 
31
31
  response_message = llm_response.choices[0].message if hasattr(llm_response.choices[0], 'message') else None
@@ -35,24 +35,22 @@ class NonStreamTaskResponser(TaskResponseProcessor):
35
35
 
36
36
  parsed_xml_data = self._parse_xml_tool_calls(llm_content)
37
37
  if self.max_xml_tool_calls > 0 and len(parsed_xml_data) > self.max_xml_tool_calls:
38
- logging.warning(f"NonStreamResp: Truncate content, parsed_xml_data length={len(parsed_xml_data)} limit over max_xml_tool_calls={self.max_xml_tool_calls}")
38
+ logging.warning(f"NonStreamResp: Over XML Tool Limit, finish_reason='xml_tool_limit_reached', "
39
+ f"parsed_xml_data_len={len(parsed_xml_data)}")
39
40
  parsed_xml_data = parsed_xml_data[:self.max_xml_tool_calls]
40
41
  finish_reason = "xml_tool_limit_reached"
41
42
 
42
- self.root_span.event(name=f"non_stream_processor_start[{self.task_no}]({llm_count})", level="DEFAULT",
43
- status_message=f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_execution_strategy}, "
43
+ self.root_span.event(name=f"non_stream_processor_start[{self.task_no}]({auto_continue_count})", level="DEFAULT",
44
+ status_message=f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_exec_strategy}, "
44
45
  f"parsed_xml_data_len={len(parsed_xml_data)}, llm_content_len={len(llm_content)}")
45
46
 
46
- if len(llm_content) == 0:
47
- logging.warning(f"NonStreamResp: LLM response_message llm_content is empty")
48
-
49
47
  message_data = {"role": "assistant", "content": llm_content}
50
48
  assistant_msg = self.add_response_message(type="assistant", content=message_data, is_llm_message=True)
51
49
  yield assistant_msg
52
50
 
53
51
  tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
54
52
  if len(tool_calls_to_execute) > 0:
55
- tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_execution_strategy)
53
+ tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_exec_strategy)
56
54
 
57
55
  tool_index = 0
58
56
  for i, (returned_tool_call, tool_result) in enumerate(tool_results):
@@ -64,37 +62,37 @@ class NonStreamTaskResponser(TaskResponseProcessor):
64
62
  tool_context = self._create_tool_context(tool_call, tool_index, assistant_msg_id, parsing_details, tool_result)
65
63
 
66
64
  tool_start_msg = self._add_tool_start_message(tool_context)
67
- yield format_for_yield(tool_start_msg)
65
+ yield tool_start_msg
68
66
 
69
67
  tool_message = self._add_tool_messsage(tool_call, tool_result, self.xml_adding_strategy, assistant_msg_id, parsing_details)
70
68
 
71
69
  tool_completed_msg = self._add_tool_completed_message(tool_context, tool_message['message_id'])
72
- yield format_for_yield(tool_completed_msg)
70
+ yield tool_completed_msg
73
71
 
74
- yield format_for_yield(tool_message)
72
+ yield tool_message
75
73
 
76
- if tool_completed_msg["metadata"].get("agent_should_terminate") == "true":
74
+ if tool_context.function_name in ['ask', 'complete']:
77
75
  finish_reason = "completed"
78
76
  break
79
77
 
80
78
  tool_index += 1
81
79
  else:
82
80
  finish_reason = "non_tool_call"
83
- logging.warning(f"NonStreamResp: tool_calls is empty, No Tool need to call !")
81
+ logging.warning(f"NonStreamResp: finish_reason='non_tool_call', No Tool need to call !")
84
82
 
85
83
  if finish_reason:
86
- finish_content = {"status_type": "finish", "finish_reason": finish_reason}
84
+ finish_content = {'status_type': "finish", 'finish_reason': finish_reason}
87
85
  finish_msg = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
88
- yield format_for_yield(finish_msg)
86
+ yield finish_msg
89
87
  except Exception as e:
90
88
  trace = log_trace(e, f"NonStreamResp: Process response llm_content:\n {llm_content}")
91
89
  self.root_span.event(name="non_stream_process_response_error", level="ERROR",
92
90
  status_message=f"Process non-streaming response error: {e}",
93
91
  metadata={"content": llm_content, "trace": trace})
94
92
 
95
- content = {"role": "system", "status_type": "error", "message": f"Process non-streaming response error: {e}"}
93
+ content = {'role': "system", 'status_type': "error", 'message': f"Process non-streaming response error: {e}"}
96
94
  error_msg = self.add_response_message(type="status", content=content, is_llm_message=False)
97
- yield format_for_yield(error_msg)
95
+ yield error_msg
98
96
 
99
97
  raise # Use bare 'raise' to preserve the original exception with its traceback
100
98
 
@@ -28,8 +28,7 @@ class TaskResponserContext(TypedDict, total=False):
28
28
  model_name: str
29
29
  max_xml_tool_calls: int # LLM generate max_xml_tool limit, 0 is no limit
30
30
  use_assistant_chunk_msg: bool
31
- tool_execution_strategy: ToolExecutionStrategy
32
- tool_execute_on_stream: bool
31
+ tool_exec_strategy: ToolExecutionStrategy
33
32
  xml_adding_strategy: XmlAddingStrategy
34
33
  add_response_msg_func: Callable
35
34
  create_response_msg_func: Callable
@@ -49,9 +48,9 @@ class ToolExecutionContext:
49
48
  """Context for a tool execution including call details, result, and display info."""
50
49
  tool_call: Dict[str, Any]
51
50
  tool_index: int
52
- function_name: Optional[str] = None
51
+ function_name: str
52
+ xml_tag_name: str
53
53
  result: Optional[XGAToolResult] = None
54
- xml_tag_name: Optional[str] = None
55
54
  error: Optional[Exception] = None
56
55
  assistant_message_id: Optional[str] = None
57
56
  parsing_details: Optional[Dict[str, Any]] = None
@@ -61,21 +60,22 @@ class TaskResponseProcessor(ABC):
61
60
  def __init__(self, response_context: TaskResponserContext):
62
61
  self.response_context = response_context
63
62
 
64
- self.task_id = response_context.get("task_id")
65
- self.task_run_id = response_context.get("task_run_id")
66
- self.task_no = response_context.get("task_no")
67
- self.tool_execution_strategy = self.response_context.get("tool_execution_strategy", "parallel")
68
- self.xml_adding_strategy = self.response_context.get("xml_adding_strategy", "user_message")
69
- self.max_xml_tool_calls = self.response_context.get("max_xml_tool_calls", 0)
70
- self.tool_execute_on_stream = response_context.get("tool_execute_on_stream", False)
63
+ self.task_id = response_context['task_id']
64
+ self.task_run_id = response_context['task_run_id']
65
+ self.task_no = response_context['task_no']
66
+ self.tool_exec_strategy = response_context['tool_exec_strategy']
67
+ self.xml_adding_strategy = response_context['xml_adding_strategy']
68
+ self.max_xml_tool_calls = response_context['max_xml_tool_calls']
71
69
 
72
- task_langfuse = response_context.get("task_langfuse")
70
+ self.add_response_message = response_context['add_response_msg_func']
71
+ self.create_response_message = response_context['create_response_msg_func']
72
+ self.tool_box = response_context['tool_box']
73
+
74
+ task_langfuse = response_context['task_langfuse']
73
75
  self.root_span = task_langfuse.root_span
74
- self.add_response_message = response_context.get("add_response_msg_func")
75
- self.create_response_message = response_context.get("create_response_msg_func")
76
76
 
77
- self.tool_box = response_context.get("tool_box")
78
- self.xml_parser = XMLToolParser()
77
+ self.xml_parser = XMLToolParser()
78
+
79
79
 
80
80
 
81
81
  @abstractmethod
@@ -209,16 +209,16 @@ class TaskResponseProcessor(ABC):
209
209
 
210
210
  # Convert to the expected format
211
211
  tool_call = {
212
- "function_name": xml_tool_call.function_name,
213
- "xml_tag_name": xml_tool_call.function_name.replace('_', '-'), # For backwards compatibility
214
- "arguments": xml_tool_call.parameters
212
+ 'function_name' : xml_tool_call.function_name,
213
+ 'xml_tag_name' : xml_tool_call.function_name.replace("_", "-"), # For backwards compatibility
214
+ 'arguments' : xml_tool_call.parameters
215
215
  }
216
216
 
217
217
  # Include the parsing details
218
218
  parsing_details = xml_tool_call.parsing_details
219
- parsing_details["raw_xml"] = xml_tool_call.raw_xml
219
+ parsing_details['raw_xml'] = xml_tool_call.raw_xml
220
220
 
221
- logging.debug(f"Parsed new format tool call: {tool_call}")
221
+ logging.debug(f"TaskProcessor parse_xml_tool_call: Parsed new format tool call: {tool_call}")
222
222
  return tool_call, parsing_details
223
223
 
224
224
  # If not the expected <function_calls><invoke> format, return None
@@ -260,10 +260,10 @@ class TaskResponseProcessor(ABC):
260
260
 
261
261
  async def _execute_tool(self, tool_call: Dict[str, Any]) -> XGAToolResult:
262
262
  """Execute a single tool call and return the result."""
263
- function_name = tool_call.get("function_name", "empty_function")
263
+ function_name = tool_call['function_name']
264
264
  exec_tool_span = self.root_span.span(name=f"execute_tool.{function_name}", input=tool_call["arguments"])
265
265
  try:
266
- arguments = tool_call["arguments"]
266
+ arguments = tool_call.get('arguments', {})
267
267
  if isinstance(arguments, str):
268
268
  try:
269
269
  arguments = safe_json_parse(arguments)
@@ -319,7 +319,7 @@ class TaskResponseProcessor(ABC):
319
319
  logging.warning("TaskProcessor execute_tools_sequentially: tool_calls is empty")
320
320
  return []
321
321
 
322
- tool_names = [t.get('function_name', 'unknown') for t in tool_calls]
322
+ tool_names = [tc['function_name'] for tc in tool_calls]
323
323
  tool_num = len(tool_calls)
324
324
  if tool_num > 1:
325
325
  logging.info(f"TaskProcessor execute_tools_sequentially: Executing {tool_num} tools sequentially: {tool_names}")
@@ -328,13 +328,13 @@ class TaskResponseProcessor(ABC):
328
328
 
329
329
  results = []
330
330
  for index, tool_call in enumerate(tool_calls):
331
- tool_name = tool_call.get('function_name', 'unknown')
331
+ tool_name = tool_call['function_name']
332
332
  logging.info(f"TaskProcessor execute_tools_sequentially: Executing tool '{tool_name}', sequence={index + 1}/{tool_num}")
333
333
  result = await self._execute_tool(tool_call)
334
334
  results.append((tool_call, result))
335
335
 
336
336
  # Check if this is a terminating tool (ask or complete)
337
- if tool_name in ['ask', 'complete']:
337
+ if tool_name in ["ask", "complete"]:
338
338
  if len(results) < tool_num:
339
339
  logging.info(f"TaskProcessor execute_tools_sequentially: Terminating tool '{tool_name}' executed, Stopping further tool execution.")
340
340
  self.root_span.event(name="task_process_terminate_tool_executed", level="DEFAULT",
@@ -358,7 +358,7 @@ class TaskResponseProcessor(ABC):
358
358
  logging.warning("TaskProcessor execute_tools_in_parallel: tool_calls is empty")
359
359
  return []
360
360
 
361
- tool_names = [t.get('function_name', 'unknown') for t in tool_calls]
361
+ tool_names = [tc['function_name'] for tc in tool_calls]
362
362
  tool_num = len(tool_calls)
363
363
  if tool_num > 1:
364
364
  logging.info(f"TaskProcessor execute_tools_in_parallel: Executing {tool_num} tools sequentially: {tool_names}")
@@ -384,17 +384,6 @@ class TaskResponseProcessor(ABC):
384
384
  assistant_message_id: Optional[str] = None,
385
385
  parsing_details: Optional[Dict[str, Any]] = None
386
386
  ) -> Optional[Dict[str, Any]]: # Return the full message object
387
- tool_message = None
388
-
389
- metadata = {}
390
- if assistant_message_id:
391
- metadata["assistant_message_id"] = assistant_message_id
392
-
393
- if parsing_details:
394
- metadata["parsing_details"] = parsing_details
395
-
396
- role = "user" if strategy == "user_message" else "assistant"
397
-
398
387
  # Create two versions of the structured result
399
388
  # Rich version for the frontend
400
389
  result_for_frontend = self._create_structured_tool_result(tool_call, result, parsing_details, for_llm=False)
@@ -403,21 +392,24 @@ class TaskResponseProcessor(ABC):
403
392
 
404
393
  # Add the message with the appropriate role to the conversation history
405
394
  # This allows the LLM to see the tool result in subsequent interactions
395
+ role = "user" if strategy == "user_message" else "assistant"
406
396
  content = {
407
- "role": role,
408
- "content": json.dumps(result_for_llm)
397
+ 'role': role,
398
+ 'content': json.dumps(result_for_llm)
409
399
  }
410
400
 
401
+ metadata = {}
402
+ if assistant_message_id:
403
+ metadata['assistant_message_id'] = assistant_message_id
404
+
405
+ if parsing_details:
406
+ metadata['parsing_details'] = parsing_details
407
+
411
408
  metadata['frontend_content'] = result_for_frontend
412
409
 
413
- tool_message = self.add_response_message(
414
- type="tool",
415
- content=content,
416
- is_llm_message=True,
417
- metadata=metadata
418
- )
410
+ tool_message = self.add_response_message(type="tool", content=content, is_llm_message=True, metadata=metadata)
419
411
 
420
- # Let's reconstruct the message for yielding.
412
+ # Let's result_for_frontend the message for yielding.
421
413
  yield_message = tool_message.copy()
422
414
  yield_message['content'] = result_for_frontend
423
415
 
@@ -429,10 +421,9 @@ class TaskResponseProcessor(ABC):
429
421
  result: XGAToolResult,
430
422
  parsing_details: Optional[Dict[str, Any]] = None,
431
423
  for_llm: bool = False) -> Dict[str, Any]:
432
- function_name = tool_call.get("function_name", "unknown")
433
- xml_tag_name = tool_call.get("xml_tag_name")
434
- arguments = tool_call.get("arguments", {})
435
- tool_call_id = tool_call.get("id")
424
+ function_name = tool_call['function_name']
425
+ xml_tag_name = tool_call['xml_tag_name']
426
+ arguments = tool_call.get('arguments', {})
436
427
 
437
428
  # Process the output - if it's a JSON string, parse it back to an object
438
429
  output = result.output
@@ -449,15 +440,14 @@ class TaskResponseProcessor(ABC):
449
440
  output_to_use = output
450
441
 
451
442
  structured_result = {
452
- "tool_execution": {
453
- "function_name": function_name,
454
- "xml_tag_name": xml_tag_name,
455
- "tool_call_id": tool_call_id,
456
- "arguments": arguments,
457
- "result": {
458
- "success": result.success,
459
- "output": output_to_use,
460
- "error": None if result.success else result.output
443
+ 'tool_execution': {
444
+ 'function_name' : function_name,
445
+ 'xml_tag_name' : xml_tag_name,
446
+ 'arguments' : arguments,
447
+ 'result' : {
448
+ 'success' : result.success,
449
+ 'output' : output_to_use,
450
+ 'error' : None if result.success else result.output
461
451
  },
462
452
  }
463
453
  }
@@ -474,26 +464,25 @@ class TaskResponseProcessor(ABC):
474
464
  ) -> ToolExecutionContext:
475
465
  """Create a tool execution context with display name and parsing details populated."""
476
466
  return ToolExecutionContext(
477
- function_name=tool_call.get("function_name"),
478
- tool_call=tool_call,
479
- tool_index=tool_index,
480
- assistant_message_id=assistant_message_id,
481
- parsing_details=parsing_details,
482
- xml_tag_name=tool_call.get("xml_tag_name"),
483
- result=result,
467
+ tool_call = tool_call,
468
+ tool_index = tool_index,
469
+ function_name = tool_call['function_name'],
470
+ xml_tag_name = tool_call['xml_tag_name'],
471
+ assistant_message_id = assistant_message_id,
472
+ parsing_details = parsing_details,
473
+ result = result
484
474
  )
485
475
 
486
476
 
487
477
  def _add_tool_start_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
488
478
  """Formats, saves, and returns a tool started status message."""
489
- tool_name = context.xml_tag_name or context.function_name
490
479
  content = {
491
- "status_type": "tool_started",
492
- "role": "assistant",
493
- "function_name": context.function_name,
494
- "xml_tag_name": context.xml_tag_name,
495
- "message": f"Starting execution of {tool_name}",
496
- "tool_index": context.tool_index
480
+ 'status_type' : "tool_started",
481
+ 'role' : "assistant",
482
+ 'function_name' : context.function_name,
483
+ 'xml_tag_name' : context.xml_tag_name,
484
+ 'message' : f"Starting execution of {context.function_name}",
485
+ 'tool_index' : context.tool_index
497
486
  }
498
487
 
499
488
  return self.add_response_message(type="status", content=content, is_llm_message=False)
@@ -503,42 +492,34 @@ class TaskResponseProcessor(ABC):
503
492
  if not context.result:
504
493
  return self._add_tool_error_message(context)
505
494
 
506
- tool_name = context.xml_tag_name or context.function_name
507
495
  status_type = "tool_completed" if context.result.success else "tool_failed"
508
- message_text = f"Tool {tool_name} {'completed successfully' if context.result.success else 'failed'}"
496
+ message_text = f"Tool {context.function_name} {'completed successfully' if context.result.success else 'failed'}"
509
497
 
510
498
  content = {
511
- "status_type": status_type,
512
- "role": "assistant",
513
- "function_name": context.function_name,
514
- "xml_tag_name": context.xml_tag_name,
515
- "message": message_text,
516
- "tool_index": context.tool_index,
517
- "tool_call_id": context.tool_call.get("id")
499
+ 'status_type' : status_type,
500
+ 'role' : "assistant",
501
+ 'function_name' : context.function_name,
502
+ 'xml_tag_name' : context.xml_tag_name,
503
+ 'message' : message_text,
504
+ 'tool_index' : context.tool_index
518
505
  }
519
506
 
520
507
  metadata = {}
521
- # Add the *actual* tool result message ID to the metadata if available and successful
522
- if context.result.success and tool_message_id:
523
- metadata["linked_tool_result_message_id"] = tool_message_id
524
-
525
- if context.function_name in ['ask', 'complete']:
526
- metadata["agent_should_terminate"] = "true"
508
+ if tool_message_id:
509
+ metadata['tool_result_message_id'] = tool_message_id
527
510
 
528
511
  return self.add_response_message(type="status", content=content, is_llm_message=False, metadata=metadata)
529
512
 
530
513
  def _add_tool_error_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
531
514
  """Formats, saves, and returns a tool error status message."""
532
- error_msg = str(context.error) if context.error else "Unknown error during tool execution"
533
- tool_name = context.xml_tag_name or context.function_name
515
+ error_msg = str(context.error) if context.error else "Tool execution unknown exception"
534
516
  content = {
535
- "status_type": "tool_error",
536
- "role": "assistant",
537
- "function_name": context.function_name,
538
- "xml_tag_name": context.xml_tag_name,
539
- "message": f"Error executing tool {tool_name}: {error_msg}",
540
- "tool_index": context.tool_index,
541
- "tool_call_id": context.tool_call.get("id")
517
+ 'status_type' : "tool_error",
518
+ 'role' : "assistant",
519
+ 'function_name' : context.function_name,
520
+ 'xml_tag_name' : context.xml_tag_name,
521
+ 'message' : f"Executing tool {context.function_name} exception: {error_msg}",
522
+ 'tool_index' : context.tool_index
542
523
  }
543
524
 
544
525
  return self.add_response_message(type="status", content=content, is_llm_message=False)