xgae 0.1.14__tar.gz → 0.1.15__tar.gz
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-0.1.14 → xgae-0.1.15}/PKG-INFO +1 -1
- {xgae-0.1.14 → xgae-0.1.15}/pyproject.toml +2 -2
- xgae-0.1.15/release.md +12 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/mcp_tool_box.py +7 -6
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/prompt_builder.py +9 -8
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/responser/non_stream_responser.py +12 -11
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/responser/responser_base.py +76 -92
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/responser/stream_responser.py +67 -60
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/task_engine.py +92 -93
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/task_langfuse.py +11 -11
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/json_helpers.py +0 -29
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/llm_client.py +23 -23
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/xml_tool_parser.py +7 -7
- xgae-0.1.15/uv.lock +1386 -0
- xgae-0.1.14/.idea/.gitignore +0 -8
- xgae-0.1.14/.idea/ai_toolkit.xml +0 -6
- xgae-0.1.14/.idea/inspectionProfiles/Project_Default.xml +0 -15
- xgae-0.1.14/.idea/inspectionProfiles/profiles_settings.xml +0 -6
- xgae-0.1.14/.idea/misc.xml +0 -7
- xgae-0.1.14/.idea/modules.xml +0 -8
- xgae-0.1.14/.idea/vcs.xml +0 -4
- xgae-0.1.14/.idea/workspace.xml +0 -200
- xgae-0.1.14/.idea/xgae.iml +0 -11
- xgae-0.1.14/uv.lock +0 -1386
- {xgae-0.1.14 → xgae-0.1.15}/.env +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/.python-version +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/README.md +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/mcpservers/custom_servers.json +0 -0
- /xgae-0.1.14/mcpservers/xga_server_stdio.json → /xgae-0.1.15/mcpservers/xga_server.json +0 -0
- /xgae-0.1.14/mcpservers/xga_server.json → /xgae-0.1.15/mcpservers/xga_server_sse.json +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/agent/langgraph/react/react_agent.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/engine/run_general_tools.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/engine/run_human_in_loop.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/engine/run_simple.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/engine/run_user_prompt.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/examples/tools/custom_fault_tools_app.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/__init__.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/cli_app.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/engine/engine_base.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/tools/without_general_tools_app.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/__init__.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/misc.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/src/xgae/utils/setup_env.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/custom_tool_prompt_template.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/example/fault_user_prompt.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/gemini_system_prompt_template.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/general_tool_prompt_template.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/system_prompt_response_sample.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/templates/system_prompt_template.txt +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/test/test_langfuse.py +0 -0
- {xgae-0.1.14 → xgae-0.1.15}/test/test_litellm_langfuse.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "xgae"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.15"
|
|
4
4
|
description = "Extreme General Agent Engine"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -18,7 +18,7 @@ requires = ["hatchling"]
|
|
|
18
18
|
build-backend = "hatchling.build"
|
|
19
19
|
|
|
20
20
|
[tool.hatch.build]
|
|
21
|
-
exclude = ["log/*"]
|
|
21
|
+
exclude = ["log/*", ".idea/*"]
|
|
22
22
|
|
|
23
23
|
[project.scripts]
|
|
24
24
|
xgae = "xgae.cli_app:main"
|
xgae-0.1.15/release.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Relese Changelog
|
|
2
|
+
## [0.1.15] - 2025-9-1
|
|
3
|
+
### Target
|
|
4
|
+
- Saved for StreamResponser tool_exec_on_stream mode, next release will be abolished
|
|
5
|
+
### Changed
|
|
6
|
+
- Improve French translation (#377).
|
|
7
|
+
### Fixed
|
|
8
|
+
- Fix finish_reason judge logic
|
|
9
|
+
|
|
10
|
+
## [0.1.15] - 2025-8-31
|
|
11
|
+
### Target
|
|
12
|
+
- First complete version
|
|
@@ -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(
|
|
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(
|
|
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]] = {}
|
|
@@ -178,18 +179,18 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
178
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[
|
|
182
|
+
server_info['transport'] = "streamable_http" if "mcp" in server_info['url'] else "sse"
|
|
182
183
|
else:
|
|
183
|
-
server_info[
|
|
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 {
|
|
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 {
|
|
193
|
+
return {'mcpServers': {}}
|
|
193
194
|
|
|
194
195
|
|
|
195
196
|
if __name__ == "__main__":
|
|
@@ -34,19 +34,20 @@ 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[
|
|
38
|
+
openai_schema['type'] = "function"
|
|
39
|
+
openai_schema['function'] = openai_function
|
|
40
40
|
|
|
41
|
-
openai_function[
|
|
42
|
-
openai_function[
|
|
41
|
+
openai_function['name'] = tool_schema.tool_name
|
|
42
|
+
openai_function['description'] = tool_schema.description if tool_schema.description else 'No description available'
|
|
43
43
|
|
|
44
44
|
openai_parameters = {}
|
|
45
|
+
openai_function['parameters'] = openai_parameters
|
|
46
|
+
|
|
45
47
|
input_schema = tool_schema.input_schema
|
|
46
|
-
|
|
47
|
-
openai_parameters[
|
|
48
|
-
openai_parameters[
|
|
49
|
-
openai_parameters["required"] = input_schema["required"]
|
|
48
|
+
openai_parameters['type'] = input_schema['type']
|
|
49
|
+
openai_parameters['properties'] = input_schema.get('properties', {})
|
|
50
|
+
openai_parameters['required'] = input_schema['required']
|
|
50
51
|
|
|
51
52
|
openai_schemas.append(openai_schema)
|
|
52
53
|
|
|
@@ -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
|
-
|
|
6
|
+
|
|
7
7
|
|
|
8
8
|
from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
|
|
9
9
|
|
|
@@ -25,7 +25,7 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
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,12 +35,13 @@ 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:
|
|
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
43
|
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.
|
|
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
47
|
if len(llm_content) == 0:
|
|
@@ -52,7 +53,7 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
52
53
|
|
|
53
54
|
tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
|
|
54
55
|
if len(tool_calls_to_execute) > 0:
|
|
55
|
-
tool_results = await self._execute_tools(tool_calls_to_execute, self.
|
|
56
|
+
tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_exec_strategy)
|
|
56
57
|
|
|
57
58
|
tool_index = 0
|
|
58
59
|
for i, (returned_tool_call, tool_result) in enumerate(tool_results):
|
|
@@ -64,16 +65,16 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
64
65
|
tool_context = self._create_tool_context(tool_call, tool_index, assistant_msg_id, parsing_details, tool_result)
|
|
65
66
|
|
|
66
67
|
tool_start_msg = self._add_tool_start_message(tool_context)
|
|
67
|
-
yield
|
|
68
|
+
yield tool_start_msg
|
|
68
69
|
|
|
69
70
|
tool_message = self._add_tool_messsage(tool_call, tool_result, self.xml_adding_strategy, assistant_msg_id, parsing_details)
|
|
70
71
|
|
|
71
72
|
tool_completed_msg = self._add_tool_completed_message(tool_context, tool_message['message_id'])
|
|
72
|
-
yield
|
|
73
|
+
yield tool_completed_msg
|
|
73
74
|
|
|
74
|
-
yield
|
|
75
|
+
yield tool_message
|
|
75
76
|
|
|
76
|
-
if
|
|
77
|
+
if tool_context.function_name in ['ask', 'complete']:
|
|
77
78
|
finish_reason = "completed"
|
|
78
79
|
break
|
|
79
80
|
|
|
@@ -85,7 +86,7 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
85
86
|
if finish_reason:
|
|
86
87
|
finish_content = {"status_type": "finish", "finish_reason": finish_reason}
|
|
87
88
|
finish_msg = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
|
|
88
|
-
yield
|
|
89
|
+
yield finish_msg
|
|
89
90
|
except Exception as e:
|
|
90
91
|
trace = log_trace(e, f"NonStreamResp: Process response llm_content:\n {llm_content}")
|
|
91
92
|
self.root_span.event(name="non_stream_process_response_error", level="ERROR",
|
|
@@ -94,7 +95,7 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
94
95
|
|
|
95
96
|
content = {"role": "system", "status_type": "error", "message": f"Process non-streaming response error: {e}"}
|
|
96
97
|
error_msg = self.add_response_message(type="status", content=content, is_llm_message=False)
|
|
97
|
-
yield
|
|
98
|
+
yield error_msg
|
|
98
99
|
|
|
99
100
|
raise # Use bare 'raise' to preserve the original exception with its traceback
|
|
100
101
|
|
|
@@ -28,8 +28,8 @@ 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
|
-
|
|
32
|
-
|
|
31
|
+
tool_exec_strategy: ToolExecutionStrategy
|
|
32
|
+
tool_exec_on_stream: bool
|
|
33
33
|
xml_adding_strategy: XmlAddingStrategy
|
|
34
34
|
add_response_msg_func: Callable
|
|
35
35
|
create_response_msg_func: Callable
|
|
@@ -49,9 +49,9 @@ class ToolExecutionContext:
|
|
|
49
49
|
"""Context for a tool execution including call details, result, and display info."""
|
|
50
50
|
tool_call: Dict[str, Any]
|
|
51
51
|
tool_index: int
|
|
52
|
-
function_name:
|
|
52
|
+
function_name: str
|
|
53
|
+
xml_tag_name: str
|
|
53
54
|
result: Optional[XGAToolResult] = None
|
|
54
|
-
xml_tag_name: Optional[str] = None
|
|
55
55
|
error: Optional[Exception] = None
|
|
56
56
|
assistant_message_id: Optional[str] = None
|
|
57
57
|
parsing_details: Optional[Dict[str, Any]] = None
|
|
@@ -61,21 +61,23 @@ class TaskResponseProcessor(ABC):
|
|
|
61
61
|
def __init__(self, response_context: TaskResponserContext):
|
|
62
62
|
self.response_context = response_context
|
|
63
63
|
|
|
64
|
-
self.task_id
|
|
65
|
-
self.task_run_id
|
|
66
|
-
self.task_no
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
69
|
-
self.
|
|
70
|
-
self.
|
|
64
|
+
self.task_id = response_context['task_id']
|
|
65
|
+
self.task_run_id = response_context['task_run_id']
|
|
66
|
+
self.task_no = response_context['task_no']
|
|
67
|
+
self.tool_exec_strategy = response_context['tool_exec_strategy']
|
|
68
|
+
self.tool_exec_on_stream = response_context['tool_exec_on_stream']
|
|
69
|
+
self.xml_adding_strategy = response_context['xml_adding_strategy']
|
|
70
|
+
self.max_xml_tool_calls = response_context['max_xml_tool_calls']
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
self.add_response_message = response_context['add_response_msg_func']
|
|
73
|
+
self.create_response_message = response_context['create_response_msg_func']
|
|
74
|
+
self.tool_box = response_context['tool_box']
|
|
75
|
+
|
|
76
|
+
task_langfuse = response_context['task_langfuse']
|
|
73
77
|
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
78
|
|
|
77
|
-
self.
|
|
78
|
-
|
|
79
|
+
self.xml_parser = XMLToolParser()
|
|
80
|
+
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
@abstractmethod
|
|
@@ -209,16 +211,16 @@ class TaskResponseProcessor(ABC):
|
|
|
209
211
|
|
|
210
212
|
# Convert to the expected format
|
|
211
213
|
tool_call = {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
'function_name' : xml_tool_call.function_name,
|
|
215
|
+
'xml_tag_name' : xml_tool_call.function_name.replace("_", "-"), # For backwards compatibility
|
|
216
|
+
'arguments' : xml_tool_call.parameters
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
# Include the parsing details
|
|
218
220
|
parsing_details = xml_tool_call.parsing_details
|
|
219
|
-
parsing_details[
|
|
221
|
+
parsing_details['raw_xml'] = xml_tool_call.raw_xml
|
|
220
222
|
|
|
221
|
-
logging.debug(f"Parsed new format tool call: {tool_call}")
|
|
223
|
+
logging.debug(f"TaskProcessor parse_xml_tool_call: Parsed new format tool call: {tool_call}")
|
|
222
224
|
return tool_call, parsing_details
|
|
223
225
|
|
|
224
226
|
# If not the expected <function_calls><invoke> format, return None
|
|
@@ -260,7 +262,7 @@ class TaskResponseProcessor(ABC):
|
|
|
260
262
|
|
|
261
263
|
async def _execute_tool(self, tool_call: Dict[str, Any]) -> XGAToolResult:
|
|
262
264
|
"""Execute a single tool call and return the result."""
|
|
263
|
-
function_name = tool_call
|
|
265
|
+
function_name = tool_call['function_name']
|
|
264
266
|
exec_tool_span = self.root_span.span(name=f"execute_tool.{function_name}", input=tool_call["arguments"])
|
|
265
267
|
try:
|
|
266
268
|
arguments = tool_call["arguments"]
|
|
@@ -328,7 +330,7 @@ class TaskResponseProcessor(ABC):
|
|
|
328
330
|
|
|
329
331
|
results = []
|
|
330
332
|
for index, tool_call in enumerate(tool_calls):
|
|
331
|
-
tool_name = tool_call
|
|
333
|
+
tool_name = tool_call['function_name']
|
|
332
334
|
logging.info(f"TaskProcessor execute_tools_sequentially: Executing tool '{tool_name}', sequence={index + 1}/{tool_num}")
|
|
333
335
|
result = await self._execute_tool(tool_call)
|
|
334
336
|
results.append((tool_call, result))
|
|
@@ -358,7 +360,7 @@ class TaskResponseProcessor(ABC):
|
|
|
358
360
|
logging.warning("TaskProcessor execute_tools_in_parallel: tool_calls is empty")
|
|
359
361
|
return []
|
|
360
362
|
|
|
361
|
-
tool_names = [t
|
|
363
|
+
tool_names = [t['function_name'] for t in tool_calls]
|
|
362
364
|
tool_num = len(tool_calls)
|
|
363
365
|
if tool_num > 1:
|
|
364
366
|
logging.info(f"TaskProcessor execute_tools_in_parallel: Executing {tool_num} tools sequentially: {tool_names}")
|
|
@@ -384,17 +386,6 @@ class TaskResponseProcessor(ABC):
|
|
|
384
386
|
assistant_message_id: Optional[str] = None,
|
|
385
387
|
parsing_details: Optional[Dict[str, Any]] = None
|
|
386
388
|
) -> 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
389
|
# Create two versions of the structured result
|
|
399
390
|
# Rich version for the frontend
|
|
400
391
|
result_for_frontend = self._create_structured_tool_result(tool_call, result, parsing_details, for_llm=False)
|
|
@@ -403,21 +394,24 @@ class TaskResponseProcessor(ABC):
|
|
|
403
394
|
|
|
404
395
|
# Add the message with the appropriate role to the conversation history
|
|
405
396
|
# This allows the LLM to see the tool result in subsequent interactions
|
|
397
|
+
role = "user" if strategy == "user_message" else "assistant"
|
|
406
398
|
content = {
|
|
407
|
-
|
|
408
|
-
|
|
399
|
+
'role': role,
|
|
400
|
+
'content': json.dumps(result_for_llm)
|
|
409
401
|
}
|
|
410
402
|
|
|
403
|
+
metadata = {}
|
|
404
|
+
if assistant_message_id:
|
|
405
|
+
metadata["assistant_message_id"] = assistant_message_id
|
|
406
|
+
|
|
407
|
+
if parsing_details:
|
|
408
|
+
metadata["parsing_details"] = parsing_details
|
|
409
|
+
|
|
411
410
|
metadata['frontend_content'] = result_for_frontend
|
|
412
411
|
|
|
413
|
-
tool_message = self.add_response_message(
|
|
414
|
-
type="tool",
|
|
415
|
-
content=content,
|
|
416
|
-
is_llm_message=True,
|
|
417
|
-
metadata=metadata
|
|
418
|
-
)
|
|
412
|
+
tool_message = self.add_response_message(type="tool", content=content, is_llm_message=True, metadata=metadata)
|
|
419
413
|
|
|
420
|
-
# Let's
|
|
414
|
+
# Let's result_for_frontend the message for yielding.
|
|
421
415
|
yield_message = tool_message.copy()
|
|
422
416
|
yield_message['content'] = result_for_frontend
|
|
423
417
|
|
|
@@ -429,7 +423,7 @@ class TaskResponseProcessor(ABC):
|
|
|
429
423
|
result: XGAToolResult,
|
|
430
424
|
parsing_details: Optional[Dict[str, Any]] = None,
|
|
431
425
|
for_llm: bool = False) -> Dict[str, Any]:
|
|
432
|
-
function_name = tool_call
|
|
426
|
+
function_name = tool_call['function_name']
|
|
433
427
|
xml_tag_name = tool_call.get("xml_tag_name")
|
|
434
428
|
arguments = tool_call.get("arguments", {})
|
|
435
429
|
tool_call_id = tool_call.get("id")
|
|
@@ -449,15 +443,14 @@ class TaskResponseProcessor(ABC):
|
|
|
449
443
|
output_to_use = output
|
|
450
444
|
|
|
451
445
|
structured_result = {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"error": None if result.success else result.output
|
|
446
|
+
'tool_execution': {
|
|
447
|
+
'function_name' : function_name,
|
|
448
|
+
'xml_tag_name' : xml_tag_name,
|
|
449
|
+
'arguments' : arguments,
|
|
450
|
+
'result' : {
|
|
451
|
+
'success' : result.success,
|
|
452
|
+
'output' : output_to_use,
|
|
453
|
+
'error' : None if result.success else result.output
|
|
461
454
|
},
|
|
462
455
|
}
|
|
463
456
|
}
|
|
@@ -474,26 +467,25 @@ class TaskResponseProcessor(ABC):
|
|
|
474
467
|
) -> ToolExecutionContext:
|
|
475
468
|
"""Create a tool execution context with display name and parsing details populated."""
|
|
476
469
|
return ToolExecutionContext(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
result=result
|
|
470
|
+
tool_call = tool_call,
|
|
471
|
+
tool_index = tool_index,
|
|
472
|
+
function_name = tool_call['function_name'],
|
|
473
|
+
xml_tag_name = tool_call['xml_tag_name'],
|
|
474
|
+
assistant_message_id = assistant_message_id,
|
|
475
|
+
parsing_details = parsing_details,
|
|
476
|
+
result = result
|
|
484
477
|
)
|
|
485
478
|
|
|
486
479
|
|
|
487
480
|
def _add_tool_start_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
|
|
488
481
|
"""Formats, saves, and returns a tool started status message."""
|
|
489
|
-
tool_name = context.xml_tag_name or context.function_name
|
|
490
482
|
content = {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
483
|
+
'status_type' : "tool_started",
|
|
484
|
+
'role' : "assistant",
|
|
485
|
+
'function_name' : context.function_name,
|
|
486
|
+
'xml_tag_name' : context.xml_tag_name,
|
|
487
|
+
'message' : f"Starting execution of {context.function_name}",
|
|
488
|
+
'tool_index' : context.tool_index
|
|
497
489
|
}
|
|
498
490
|
|
|
499
491
|
return self.add_response_message(type="status", content=content, is_llm_message=False)
|
|
@@ -503,42 +495,34 @@ class TaskResponseProcessor(ABC):
|
|
|
503
495
|
if not context.result:
|
|
504
496
|
return self._add_tool_error_message(context)
|
|
505
497
|
|
|
506
|
-
tool_name = context.xml_tag_name or context.function_name
|
|
507
498
|
status_type = "tool_completed" if context.result.success else "tool_failed"
|
|
508
|
-
message_text = f"Tool {
|
|
499
|
+
message_text = f"Tool {context.function_name} {'completed successfully' if context.result.success else 'failed'}"
|
|
509
500
|
|
|
510
501
|
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")
|
|
502
|
+
"status_type" : status_type,
|
|
503
|
+
"role" : "assistant",
|
|
504
|
+
"function_name" : context.function_name,
|
|
505
|
+
"xml_tag_name" : context.xml_tag_name,
|
|
506
|
+
"message" : message_text,
|
|
507
|
+
"tool_index" : context.tool_index
|
|
518
508
|
}
|
|
519
509
|
|
|
520
510
|
metadata = {}
|
|
521
|
-
|
|
522
|
-
|
|
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"
|
|
511
|
+
if tool_message_id:
|
|
512
|
+
metadata["tool_result_message_id"] = tool_message_id
|
|
527
513
|
|
|
528
514
|
return self.add_response_message(type="status", content=content, is_llm_message=False, metadata=metadata)
|
|
529
515
|
|
|
530
516
|
def _add_tool_error_message(self, context: ToolExecutionContext) -> Optional[Dict[str, Any]]:
|
|
531
517
|
"""Formats, saves, and returns a tool error status message."""
|
|
532
|
-
error_msg = str(context.error) if context.error else "
|
|
533
|
-
tool_name = context.xml_tag_name or context.function_name
|
|
518
|
+
error_msg = str(context.error) if context.error else "Tool execution unknown exception"
|
|
534
519
|
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"
|
|
540
|
-
"tool_index": context.tool_index
|
|
541
|
-
"tool_call_id": context.tool_call.get("id")
|
|
520
|
+
"status_type" : "tool_error",
|
|
521
|
+
"role" : "assistant",
|
|
522
|
+
"function_name" : context.function_name,
|
|
523
|
+
"xml_tag_name" : context.xml_tag_name,
|
|
524
|
+
"message" : f"Executing tool {context.function_name} exception: {error_msg}",
|
|
525
|
+
"tool_index" : context.tool_index
|
|
542
526
|
}
|
|
543
527
|
|
|
544
528
|
return self.add_response_message(type="status", content=content, is_llm_message=False)
|