xgae 0.1.5__tar.gz → 0.1.7__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.

Files changed (44) hide show
  1. {xgae-0.1.5 → xgae-0.1.7}/.env +2 -0
  2. {xgae-0.1.5 → xgae-0.1.7}/.idea/workspace.xml +19 -4
  3. {xgae-0.1.5 → xgae-0.1.7}/PKG-INFO +1 -1
  4. xgae-0.1.7/mcpservers/xga_server.json +11 -0
  5. {xgae-0.1.5 → xgae-0.1.7}/pyproject.toml +2 -1
  6. xgae-0.1.7/src/examples/run_human_in_loop.py +29 -0
  7. xgae-0.1.7/src/examples/run_simple.py +12 -0
  8. xgae-0.1.7/src/examples/run_user_prompt.py +30 -0
  9. xgae-0.1.5/src/xgae/engine/xga_base.py → xgae-0.1.7/src/xgae/engine/engine_base.py +6 -9
  10. xgae-0.1.5/src/xgae/engine/xga_mcp_tool_box.py → xgae-0.1.7/src/xgae/engine/mcp_tool_box.py +11 -5
  11. xgae-0.1.5/src/xgae/engine/xga_prompt_builder.py → xgae-0.1.7/src/xgae/engine/prompt_builder.py +3 -2
  12. xgae-0.1.7/src/xgae/engine/responser/non_stream_responser.py +108 -0
  13. xgae-0.1.5/src/xgae/engine/responser/xga_responser_base.py → xgae-0.1.7/src/xgae/engine/responser/responser_base.py +124 -218
  14. xgae-0.1.5/src/xgae/engine/responser/xga_stream_responser.py → xgae-0.1.7/src/xgae/engine/responser/stream_responser.py +51 -55
  15. xgae-0.1.5/src/xgae/engine/xga_engine.py → xgae-0.1.7/src/xgae/engine/task_engine.py +166 -146
  16. xgae-0.1.7/src/xgae/tools/without_general_tools_app.py +48 -0
  17. xgae-0.1.7/src/xgae/utils/__init__.py +13 -0
  18. {xgae-0.1.5 → xgae-0.1.7}/src/xgae/utils/llm_client.py +8 -3
  19. xgae-0.1.5/src/xgae/utils/utils.py → xgae-0.1.7/src/xgae/utils/misc.py +0 -8
  20. xgae-0.1.7/src/xgae/utils/setup_env.py +80 -0
  21. {xgae-0.1.5 → xgae-0.1.7}/src/xgae/utils/xml_tool_parser.py +4 -7
  22. xgae-0.1.7/templates/example_user_prompt.txt +22 -0
  23. {xgae-0.1.5 → xgae-0.1.7}/uv.lock +1 -1
  24. xgae-0.1.5/src/xgae/engine/responser/xga_non_stream_responser.py +0 -216
  25. xgae-0.1.5/src/xgae/utils/setup_env.py +0 -93
  26. xgae-0.1.5/templates/scp_test_prompt.txt +0 -21
  27. {xgae-0.1.5 → xgae-0.1.7}/.idea/.gitignore +0 -0
  28. {xgae-0.1.5 → xgae-0.1.7}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  29. {xgae-0.1.5 → xgae-0.1.7}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  30. {xgae-0.1.5 → xgae-0.1.7}/.idea/misc.xml +0 -0
  31. {xgae-0.1.5 → xgae-0.1.7}/.idea/modules.xml +0 -0
  32. {xgae-0.1.5 → xgae-0.1.7}/.idea/vcs.xml +0 -0
  33. {xgae-0.1.5 → xgae-0.1.7}/.idea/xgae.iml +0 -0
  34. {xgae-0.1.5 → xgae-0.1.7}/.python-version +0 -0
  35. {xgae-0.1.5 → xgae-0.1.7}/README.md +0 -0
  36. {xgae-0.1.5 → xgae-0.1.7}/mcpservers/custom_servers.json +0 -0
  37. /xgae-0.1.5/mcpservers/xga_server.json → /xgae-0.1.7/mcpservers/xga_server_sse.json +0 -0
  38. {xgae-0.1.5 → xgae-0.1.7}/src/xgae/__init__.py +0 -0
  39. {xgae-0.1.5 → xgae-0.1.7}/src/xgae/utils/json_helpers.py +0 -0
  40. {xgae-0.1.5 → xgae-0.1.7}/templates/custom_tool_prompt_template.txt +0 -0
  41. {xgae-0.1.5 → xgae-0.1.7}/templates/gemini_system_prompt_template.txt +0 -0
  42. {xgae-0.1.5 → xgae-0.1.7}/templates/general_tool_prompt_template.txt +0 -0
  43. {xgae-0.1.5 → xgae-0.1.7}/templates/system_prompt_response_sample.txt +0 -0
  44. {xgae-0.1.5 → xgae-0.1.7}/templates/system_prompt_template.txt +0 -0
@@ -15,6 +15,8 @@ LLM_MAX_TOKENS=16384
15
15
  LLM_TEMPERATURE=0.7
16
16
  LLM_MAX_RETRIES=2
17
17
 
18
+ # TASK
19
+ MAX_AUTO_RUN = 15
18
20
 
19
21
 
20
22
 
@@ -29,8 +29,15 @@
29
29
  "keyToString": {
30
30
  "ModuleVcsDetector.initialDetectionPerformed": "true",
31
31
  "Python.llm_client.executor": "Run",
32
+ "Python.message_tools_app.executor": "Run",
33
+ "Python.responser_base.executor": "Run",
34
+ "Python.run_engine_with_human_in_loop.executor": "Run",
35
+ "Python.run_simple.executor": "Run",
36
+ "Python.run_task_engine.executor": "Run",
37
+ "Python.run_user_prompt.executor": "Run",
32
38
  "Python.run_xga_engine.executor": "Run",
33
39
  "Python.setup_env.executor": "Run",
40
+ "Python.task_engine.executor": "Run",
34
41
  "Python.utils.executor": "Run",
35
42
  "Python.xga_engine.executor": "Run",
36
43
  "Python.xga_mcp_tool_box.executor": "Debug",
@@ -86,7 +93,7 @@
86
93
  <option name="INPUT_FILE" value="" />
87
94
  <method v="2" />
88
95
  </configuration>
89
- <configuration name="run_xga_engine" type="PythonConfigurationType" factoryName="Python">
96
+ <configuration name="run_task_engine" type="PythonConfigurationType" factoryName="Python">
90
97
  <module name="xgae" />
91
98
  <option name="ENV_FILES" value="" />
92
99
  <option name="INTERPRETER_OPTIONS" value="" />
@@ -110,7 +117,7 @@
110
117
  <ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
111
118
  </ENTRIES>
112
119
  </EXTENSION>
113
- <option name="SCRIPT_NAME" value="$PROJECT_DIR$/src/xgae/engine/xga_engine.py" />
120
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/src/xgae/engine/task_engine.py" />
114
121
  <option name="PARAMETERS" value="" />
115
122
  <option name="SHOW_COMMAND_LINE" value="false" />
116
123
  <option name="EMULATE_TERMINAL" value="false" />
@@ -152,6 +159,8 @@
152
159
  <workItem from="1755668525673" duration="14877000" />
153
160
  <workItem from="1755700523844" duration="24000" />
154
161
  <workItem from="1755737435202" duration="48139000" />
162
+ <workItem from="1756044658912" duration="1248000" />
163
+ <workItem from="1756082326044" duration="23338000" />
155
164
  </task>
156
165
  <servers />
157
166
  </component>
@@ -171,11 +180,17 @@
171
180
  </component>
172
181
  <component name="com.intellij.coverage.CoverageDataManagerImpl">
173
182
  <SUITE FILE_PATH="coverage/xgae$xga_engine.coverage" NAME="xga_engine Coverage Results" MODIFIED="1755580277172" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
183
+ <SUITE FILE_PATH="coverage/xgae$run_simple.coverage" NAME="run_simple Coverage Results" MODIFIED="1756111714718" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
184
+ <SUITE FILE_PATH="coverage/xgae$run_xga_engine.coverage" NAME="run_task_engine Coverage Results" MODIFIED="1756111613459" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
185
+ <SUITE FILE_PATH="coverage/xgae$message_tools_app.coverage" NAME="message_tools_app Coverage Results" MODIFIED="1756094157566" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
186
+ <SUITE FILE_PATH="coverage/xgae$run_engine_with_human_in_loop.coverage" NAME="run_engine_with_human_in_loop Coverage Results" MODIFIED="1756089269027" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
174
187
  <SUITE FILE_PATH="coverage/xgae$xga_prompt_builder.coverage" NAME="xga_prompt_builder Coverage Results" MODIFIED="1755587456555" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
175
- <SUITE FILE_PATH="coverage/xgae$run_xga_engine.coverage" NAME="run_xga_engine Coverage Results" MODIFIED="1755854764155" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
188
+ <SUITE FILE_PATH="coverage/xgae$responser_base.coverage" NAME="responser_base Coverage Results" MODIFIED="1756103040764" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
176
189
  <SUITE FILE_PATH="coverage/xgae$utils.coverage" NAME="utils Coverage Results" MODIFIED="1755226923439" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
177
190
  <SUITE FILE_PATH="coverage/xgae$setup_env.coverage" NAME="setup_env Coverage Results" MODIFIED="1755657717310" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
191
+ <SUITE FILE_PATH="coverage/xgae$run_user_prompt.coverage" NAME="run_user_prompt Coverage Results" MODIFIED="1756089624828" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
178
192
  <SUITE FILE_PATH="coverage/xgae$xga_mcp_tool_box.coverage" NAME="xga_mcp_tool_box Coverage Results" MODIFIED="1755583099719" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
179
- <SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1755565705235" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
193
+ <SUITE FILE_PATH="coverage/xgae$task_engine.coverage" NAME="task_engine Coverage Results" MODIFIED="1756102942600" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
194
+ <SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1756112044142" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
180
195
  </component>
181
196
  </project>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog>=6.9.0
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers":{
3
+ "xga_general": {
4
+ "command": "uv",
5
+ "args": [
6
+ "run",
7
+ "xgae-tools"
8
+ ]
9
+ }
10
+ }
11
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xgae"
3
- version = "0.1.5"
3
+ version = "0.1.7"
4
4
  description = "Extreme General Agent Engine"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -21,3 +21,4 @@ build-backend = "hatchling.build"
21
21
  exclude = ["log/*"]
22
22
 
23
23
  [project.scripts]
24
+ xgae-tools = "xgae.tools.without_general_tools_app:main"
@@ -0,0 +1,29 @@
1
+ import asyncio
2
+
3
+ from xgae.engine.mcp_tool_box import XGAMcpToolBox
4
+ from xgae.engine.task_engine import XGATaskEngine
5
+ from xgae.utils.llm_client import LLMConfig
6
+ from xgae.utils.misc import read_file
7
+
8
+
9
+ async def main() -> None:
10
+ tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
11
+ system_prompt = read_file("templates/example_user_prompt.txt")
12
+ engine = XGATaskEngine(tool_box=tool_box,
13
+ general_tools=[],
14
+ custom_tools=["*"],
15
+ llm_config=LLMConfig(stream=False),
16
+ system_prompt=system_prompt,
17
+ max_auto_run=8)
18
+
19
+ user_input = "locate fault and solution"
20
+ final_result = await engine.run_task_with_final_answer(task_message={"role": "user", "content": user_input})
21
+ print("FINAL RESULT:", final_result)
22
+
23
+ if final_result["type"] == "ask":
24
+ print("====== Wait for user input ... ======")
25
+ user_input = "ip=10.0.1.1"
26
+ final_result = await engine.run_task_with_final_answer(task_message={"role": "user", "content": user_input})
27
+ print("FINAL RESULT:", final_result)
28
+
29
+ asyncio.run(main())
@@ -0,0 +1,12 @@
1
+ import asyncio
2
+
3
+ from xgae.engine.task_engine import XGATaskEngine
4
+ from xgae.utils.llm_client import LLMConfig
5
+
6
+
7
+ async def main() -> None:
8
+ engine = XGATaskEngine(llm_config=LLMConfig(stream=False), max_auto_run=1)
9
+ final_result = await engine.run_task_with_final_answer(task_message={"role": "user", "content": "1+1"})
10
+ print("FINAL RESULT:", final_result)
11
+
12
+ asyncio.run(main())
@@ -0,0 +1,30 @@
1
+ import asyncio
2
+
3
+ from xgae.engine.mcp_tool_box import XGAMcpToolBox
4
+ from xgae.engine.task_engine import XGATaskEngine
5
+ from xgae.utils.llm_client import LLMConfig
6
+ from xgae.utils.misc import read_file
7
+
8
+
9
+ async def main() -> None:
10
+ tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
11
+ system_prompt = read_file("templates/example_user_prompt.txt")
12
+ engine = XGATaskEngine(tool_box=tool_box,
13
+ general_tools=[],
14
+ custom_tools=["*"],
15
+ llm_config=LLMConfig(stream=False),
16
+ system_prompt=system_prompt,
17
+ max_auto_run=8)
18
+
19
+ user_input = "locate 10.2.3.4 fault and solution"
20
+ is_final_result = False
21
+
22
+ if is_final_result:
23
+ final_result = await engine.run_task_with_final_answer(task_message={"role": "user", "content": user_input})
24
+ print("FINAL RESULT:", final_result)
25
+ else:
26
+ # Get All Task Process Message
27
+ async for chunk in engine.run_task(task_message={"role": "user", "content": user_input}):
28
+ print(chunk)
29
+
30
+ asyncio.run(main())
@@ -6,18 +6,15 @@ class XGAError(Exception):
6
6
  """Custom exception for errors in the XGA system."""
7
7
  pass
8
8
 
9
+ XGAMsgStatusType = Literal["error", "finish", "tool_error", "tool_started", "tool_completed", "tool_failed", "thread_run_start", "thread_run_end", "assistant_response_start", "assistant_response_end"]
10
+ XGAResponseMsgType = Literal["user", "status", "tool", "assistant"]
9
11
 
10
- class XGAResponseMsg(TypedDict, total=False):
11
- type: Literal["user", "status", "tool", "assistant", "assistant_response_end"]
12
- content: Union[Dict[str, Any], List[Any], str]
12
+ class XGAResponseMessage(TypedDict, total=False):
13
+ message_id: str
14
+ type: XGAResponseMsgType
13
15
  is_llm_message: bool
16
+ content: Union[Dict[str, Any], List[Any], str]
14
17
  metadata: Dict[str, Any]
15
- message_id: str
16
- task_id: str
17
- task_run_id: str
18
- trace_id: str
19
- session_id: Optional[str]
20
- agent_id: Optional[str]
21
18
 
22
19
  class XGATaskResult(TypedDict, total=False):
23
20
  type: Literal["ask", "answer", "error"]
@@ -7,8 +7,8 @@ from typing import List, Any, Dict, Optional, Literal, override
7
7
  from langchain_mcp_adapters.client import MultiServerMCPClient
8
8
  from langchain_mcp_adapters.tools import load_mcp_tools
9
9
 
10
- from xgae.engine.xga_base import XGAError, XGAToolSchema, XGAToolBox, XGAToolResult
11
- from xgae.utils.setup_env import langfuse
10
+ from xgae.engine.engine_base import XGAError, XGAToolSchema, XGAToolBox, XGAToolResult
11
+ from xgae.utils import langfuse
12
12
 
13
13
  class XGAMcpToolBox(XGAToolBox):
14
14
  GENERAL_MCP_SERVER_NAME = "xga_general"
@@ -38,7 +38,7 @@ class XGAMcpToolBox(XGAToolBox):
38
38
  async def creat_task_tool_box(self, task_id: str, general_tools: List[str], custom_tools: List[str]):
39
39
  task_tool_schemas = {}
40
40
  general_tool_schemas = self.mcp_tool_schemas.get(XGAMcpToolBox.GENERAL_MCP_SERVER_NAME, {})
41
- if len(general_tools) > 0 and general_tools[0] == "*":
41
+ if "*" in general_tools:
42
42
  task_tool_schemas = {tool_schema.tool_name: tool_schema for tool_schema in general_tool_schemas}
43
43
  else:
44
44
  for tool_schema in general_tool_schemas:
@@ -46,6 +46,12 @@ class XGAMcpToolBox(XGAToolBox):
46
46
  task_tool_schemas[tool_schema.tool_name] = tool_schema
47
47
  task_tool_schemas.pop("end_task", None)
48
48
 
49
+ if len(custom_tools) == 1 and custom_tools[0] == "*":
50
+ custom_tools = []
51
+ for server_name in self.mcp_server_names:
52
+ if server_name != XGAMcpToolBox.GENERAL_MCP_SERVER_NAME:
53
+ custom_tools.append(f"{server_name}.*")
54
+
49
55
  for server_tool_name in custom_tools:
50
56
  parts = server_tool_name.split(".")
51
57
  if len(parts) != 2:
@@ -58,8 +64,8 @@ class XGAMcpToolBox(XGAToolBox):
58
64
  if custom_tool_schemas is None:
59
65
  continue
60
66
  if custom_tool_name == "*":
61
- custom_tool_schema_d = {tool_schema.tool_name: tool_schema for tool_schema in custom_tool_schemas}
62
- task_tool_schemas.update(custom_tool_schema_d)
67
+ custom_tool_schema_dict = {tool_schema.tool_name: tool_schema for tool_schema in custom_tool_schemas}
68
+ task_tool_schemas.update(custom_tool_schema_dict)
63
69
  else:
64
70
  for tool_schema in custom_tool_schemas:
65
71
  if custom_tool_name == tool_schema.tool_name:
@@ -1,9 +1,10 @@
1
1
  import json
2
2
  import datetime
3
+
3
4
  from typing import Optional, List
4
5
 
5
- from xga_base import XGAToolSchema, XGAError
6
- from xgae.utils.utils import read_file, format_file_with_args
6
+ from xgae.engine.engine_base import XGAToolSchema, XGAError
7
+ from xgae.utils.misc import read_file, format_file_with_args
7
8
 
8
9
 
9
10
  class XGAPromptBuilder():
@@ -0,0 +1,108 @@
1
+ import logging
2
+
3
+ from typing import List, Dict, Any, AsyncGenerator, override,Optional
4
+
5
+ from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
6
+ from xgae.utils import langfuse
7
+ from xgae.utils.json_helpers import format_for_yield
8
+
9
+ class NonStreamTaskResponser(TaskResponseProcessor):
10
+ def __init__(self, response_context: TaskResponserContext):
11
+ super().__init__(response_context)
12
+
13
+ @override
14
+ async def process_response(self,llm_response: Any,prompt_messages: List[Dict[str, Any]],
15
+ continuous_state: Optional[TaskRunContinuousState] = None) -> AsyncGenerator[Dict[str, Any], None]:
16
+ llm_content = ""
17
+ parsed_xml_data = []
18
+ finish_reason = None
19
+
20
+ try:
21
+ # Extract finish_reason, content, tool calls
22
+ if hasattr(llm_response, 'choices') and llm_response.choices:
23
+ if hasattr(llm_response.choices[0], 'finish_reason'):
24
+ finish_reason = llm_response.choices[0].finish_reason
25
+ logging.info(f"NonStreamTask:LLM response finish_reason={finish_reason}")
26
+
27
+ langfuse.create_event(trace_context=self.trace_context, name="non_stream_processor_start", level="DEFAULT",
28
+ status_message=(f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_execution_strategy}"))
29
+
30
+ response_message = llm_response.choices[0].message if hasattr(llm_response.choices[0], 'message') else None
31
+ if response_message:
32
+ if hasattr(response_message, 'content') and response_message.content:
33
+ llm_content = response_message.content
34
+
35
+ parsed_xml_data = self._parse_xml_tool_calls(llm_content)
36
+ if self.max_xml_tool_calls > 0 and len(parsed_xml_data) > self.max_xml_tool_calls:
37
+ logging.warning(f"NonStreamTask:Truncate content, parsed_xml_data length {len(parsed_xml_data)} limit over max_xml_tool_calls={self.max_xml_tool_calls}")
38
+ xml_chunks = self._extract_xml_chunks(llm_content)[:self.max_xml_tool_calls]
39
+ if xml_chunks:
40
+ last_chunk = xml_chunks[-1]
41
+ last_chunk_pos = llm_content.find(last_chunk)
42
+ if last_chunk_pos >= 0:
43
+ llm_content = llm_content[:last_chunk_pos + len(last_chunk)]
44
+ parsed_xml_data = parsed_xml_data[:self.max_xml_tool_calls]
45
+ finish_reason = "xml_tool_limit_reached"
46
+ logging.info(f"NonStreamTask:LLM response finish_reason={finish_reason}")
47
+ else:
48
+ logging.warning(f"NonStreamTask:LLM response_message is empty")
49
+
50
+ message_data = {"role": "assistant", "content": llm_content, "index": -1} # index=-1, full llm_content
51
+ assistant_msg = self.add_response_message(type="assistant", content=message_data, is_llm_message=True)
52
+ yield assistant_msg
53
+
54
+ tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
55
+ if len(tool_calls_to_execute) > 0:
56
+ logging.info(f"NonStreamTask:Executing {len(tool_calls_to_execute)} tools with strategy: {self.tool_execution_strategy}")
57
+
58
+ tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_execution_strategy)
59
+
60
+ tool_index = 0
61
+ for i, (returned_tool_call, tool_result) in enumerate(tool_results):
62
+ parsed_xml_item = parsed_xml_data[i]
63
+ tool_call = parsed_xml_item['tool_call']
64
+ parsing_details = parsed_xml_item['parsing_details']
65
+ assistant_msg_id = assistant_msg['message_id'] if assistant_msg else None
66
+
67
+ tool_context = self._create_tool_context(tool_call, tool_index, assistant_msg_id, parsing_details)
68
+ tool_context.result = tool_result
69
+
70
+ tool_start_msg = self._add_tool_start_message(tool_context)
71
+ yield format_for_yield(tool_start_msg)
72
+
73
+ tool_message = self._add_tool_messsage(tool_call, tool_result, self.xml_adding_strategy, assistant_msg_id, parsing_details)
74
+
75
+ tool_completed_msg = self._add_tool_completed_message(tool_context, tool_message['message_id'])
76
+ yield format_for_yield(tool_completed_msg)
77
+
78
+ yield format_for_yield(tool_message)
79
+
80
+ if tool_completed_msg["metadata"].get("agent_should_terminate") == "true":
81
+ finish_reason = "completed"
82
+ break
83
+ tool_index += 1
84
+
85
+ if finish_reason:
86
+ finish_content = {"status_type": "finish", "finish_reason": finish_reason}
87
+ finish_msg_obj = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
88
+ if finish_msg_obj:
89
+ yield format_for_yield(finish_msg_obj)
90
+
91
+ except Exception as e:
92
+ logging.error(f"NonStreamTask: Error processing non-streaming response: {llm_content}")
93
+ langfuse.create_event(trace_context=self.trace_context, name="error_processing_non_streaming_response", level="ERROR",
94
+ status_message=(f"Error processing non-streaming response: {str(e)}"))
95
+
96
+ content = {"role": "system", "status_type": "error", "message": str(e)}
97
+ err_msg = self.add_response_message(ype="status", content=content,is_llm_message=False)
98
+ if err_msg:
99
+ yield format_for_yield(err_msg)
100
+
101
+ # Re-raise the same exception (not a new one) to ensure proper error propagation
102
+ logging.critical(f"NonStreamTask: Re-raising error to stop further processing: {str(e)}")
103
+ langfuse.create_event(trace_context=self.trace_context, name="re_raising_error_to_stop_further_processing", level="CRITICAL",
104
+ status_message=(f"Re-raising error to stop further processing: {str(e)}"))
105
+ raise # Use bare 'raise' to preserve the original exception with its traceback
106
+
107
+
108
+