xgae 0.1.10__tar.gz → 0.1.12__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 (50) hide show
  1. {xgae-0.1.10 → xgae-0.1.12}/.env +8 -3
  2. {xgae-0.1.10 → xgae-0.1.12}/.idea/workspace.xml +17 -8
  3. {xgae-0.1.10 → xgae-0.1.12}/PKG-INFO +1 -1
  4. {xgae-0.1.10 → xgae-0.1.12}/pyproject.toml +1 -1
  5. {xgae-0.1.10 → xgae-0.1.12}/src/examples/agent/langgraph/react/react_agent.py +73 -44
  6. {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_human_in_loop.py +2 -4
  7. {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_simple.py +1 -2
  8. {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_user_prompt.py +9 -14
  9. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/cli_app.py +2 -4
  10. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/engine_base.py +3 -3
  11. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/mcp_tool_box.py +4 -4
  12. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/responser/non_stream_responser.py +31 -39
  13. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/responser/responser_base.py +42 -40
  14. xgae-0.1.12/src/xgae/engine/responser/stream_responser.py +138 -0
  15. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/task_engine.py +79 -46
  16. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/tools/without_general_tools_app.py +2 -3
  17. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/__init__.py +2 -2
  18. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/json_helpers.py +2 -2
  19. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/llm_client.py +21 -19
  20. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/setup_env.py +1 -0
  21. xgae-0.1.12/uv.lock +1386 -0
  22. xgae-0.1.10/src/xgae/engine/responser/stream_responser.py +0 -825
  23. xgae-0.1.10/uv.lock +0 -1386
  24. {xgae-0.1.10 → xgae-0.1.12}/.idea/.gitignore +0 -0
  25. {xgae-0.1.10 → xgae-0.1.12}/.idea/ai_toolkit.xml +0 -0
  26. {xgae-0.1.10 → xgae-0.1.12}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  27. {xgae-0.1.10 → xgae-0.1.12}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  28. {xgae-0.1.10 → xgae-0.1.12}/.idea/misc.xml +0 -0
  29. {xgae-0.1.10 → xgae-0.1.12}/.idea/modules.xml +0 -0
  30. {xgae-0.1.10 → xgae-0.1.12}/.idea/vcs.xml +0 -0
  31. {xgae-0.1.10 → xgae-0.1.12}/.idea/xgae.iml +0 -0
  32. {xgae-0.1.10 → xgae-0.1.12}/.python-version +0 -0
  33. {xgae-0.1.10 → xgae-0.1.12}/README.md +0 -0
  34. {xgae-0.1.10 → xgae-0.1.12}/mcpservers/custom_servers.json +0 -0
  35. {xgae-0.1.10 → xgae-0.1.12}/mcpservers/xga_server.json +0 -0
  36. {xgae-0.1.10 → xgae-0.1.12}/mcpservers/xga_server_sse.json +0 -0
  37. {xgae-0.1.10 → xgae-0.1.12}/src/examples/tools/custom_fault_tools_app.py +0 -0
  38. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/__init__.py +0 -0
  39. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/prompt_builder.py +0 -0
  40. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/task_langfuse.py +0 -0
  41. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/misc.py +0 -0
  42. {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/xml_tool_parser.py +0 -0
  43. {xgae-0.1.10 → xgae-0.1.12}/templates/custom_tool_prompt_template.txt +0 -0
  44. {xgae-0.1.10 → xgae-0.1.12}/templates/example/fault_user_prompt.txt +0 -0
  45. {xgae-0.1.10 → xgae-0.1.12}/templates/gemini_system_prompt_template.txt +0 -0
  46. {xgae-0.1.10 → xgae-0.1.12}/templates/general_tool_prompt_template.txt +0 -0
  47. {xgae-0.1.10 → xgae-0.1.12}/templates/system_prompt_response_sample.txt +0 -0
  48. {xgae-0.1.10 → xgae-0.1.12}/templates/system_prompt_template.txt +0 -0
  49. {xgae-0.1.10 → xgae-0.1.12}/test/test_langfuse.py +0 -0
  50. {xgae-0.1.10 → xgae-0.1.12}/test/test_litellm_langfuse.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # LOG
2
- LOG_LEVEL=INFO
2
+ LOG_LEVEL=DEBUG
3
3
  LOG_FILE=log/xgae.log
4
4
  LOG_ENABLE=True
5
5
 
@@ -15,10 +15,15 @@ LLM_API_KEY=
15
15
  LLM_MAX_TOKENS=16384
16
16
  LLM_TEMPERATURE=0.7
17
17
  LLM_MAX_RETRIES=2
18
+ LLM_STREAM=True
19
+ LLM_ENABLE_THINKING=False
20
+
18
21
  LLM_LANGFUSE_ENABLE=False
19
22
 
20
- # TASK
21
- MAX_AUTO_RUN = 15
23
+
24
+ # TASK_ENGINE
25
+ MAX_AUTO_RUN=15
26
+ USE_ASSISTANT_CHUNK_MSG=False
22
27
 
23
28
 
24
29
 
@@ -29,9 +29,11 @@
29
29
  "keyToString": {
30
30
  "ModuleVcsDetector.initialDetectionPerformed": "true",
31
31
  "Python.__init__.executor": "Run",
32
- "Python.llm_client.executor": "Run",
32
+ "Python.cli_app.executor": "Run",
33
+ "Python.llm_client.executor": "Debug",
33
34
  "Python.mcp_tool_box.executor": "Run",
34
35
  "Python.message_tools_app.executor": "Run",
36
+ "Python.react_agent.executor": "Run",
35
37
  "Python.responser_base.executor": "Run",
36
38
  "Python.run_engine_with_human_in_loop.executor": "Run",
37
39
  "Python.run_human_in_loop.executor": "Run",
@@ -62,6 +64,7 @@
62
64
  }]]></component>
63
65
  <component name="RecentsManager">
64
66
  <key name="MoveFile.RECENT_KEYS">
67
+ <recent name="$PROJECT_DIR$/src/examples/agent/langgraph/react" />
65
68
  <recent name="$PROJECT_DIR$/src/examples" />
66
69
  <recent name="$PROJECT_DIR$/src/examples/engine" />
67
70
  <recent name="$PROJECT_DIR$/src/xgae/engine/responser" />
@@ -172,7 +175,11 @@
172
175
  <workItem from="1756082326044" duration="23657000" />
173
176
  <workItem from="1756168626188" duration="52435000" />
174
177
  <workItem from="1756305726553" duration="2819000" />
175
- <workItem from="1756340066915" duration="14276000" />
178
+ <workItem from="1756340066915" duration="19032000" />
179
+ <workItem from="1756371469253" duration="1657000" />
180
+ <workItem from="1756383619553" duration="2663000" />
181
+ <workItem from="1756386338550" duration="12000" />
182
+ <workItem from="1756429146558" duration="24412000" />
176
183
  </task>
177
184
  <servers />
178
185
  </component>
@@ -191,26 +198,28 @@
191
198
  </breakpoint-manager>
192
199
  </component>
193
200
  <component name="com.intellij.coverage.CoverageDataManagerImpl">
194
- <SUITE FILE_PATH="coverage/xgae$test_litellm_langfuse.coverage" NAME="test_litellm_langfuse Coverage Results" MODIFIED="1756196476262" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
195
201
  <SUITE FILE_PATH="coverage/xgae$task_langfuse.coverage" NAME="task_langfuse Coverage Results" MODIFIED="1756306181167" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
202
+ <SUITE FILE_PATH="coverage/xgae$test_litellm_langfuse.coverage" NAME="test_litellm_langfuse Coverage Results" MODIFIED="1756196476262" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
196
203
  <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$" />
197
- <SUITE FILE_PATH="coverage/xgae$run_simple.coverage" NAME="run_simple Coverage Results" MODIFIED="1756359842556" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
204
+ <SUITE FILE_PATH="coverage/xgae$run_simple.coverage" NAME="run_simple Coverage Results" MODIFIED="1756452256460" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
198
205
  <SUITE FILE_PATH="coverage/xgae$run_human_in_loop.coverage" NAME="run_human_in_loop Coverage Results" MODIFIED="1756279131815" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
199
206
  <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$" />
207
+ <SUITE FILE_PATH="coverage/xgae$cli_app.coverage" NAME="cli_app Coverage Results" MODIFIED="1756372555094" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
200
208
  <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$" />
201
209
  <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$" />
202
210
  <SUITE FILE_PATH="coverage/xgae$__init__.coverage" NAME="__init__ Coverage Results" MODIFIED="1756345820610" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
211
+ <SUITE FILE_PATH="coverage/xgae$react_agent.coverage" NAME="react_agent Coverage Results" MODIFIED="1756372008835" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
203
212
  <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$" />
204
213
  <SUITE FILE_PATH="coverage/xgae$test_langfuse.coverage" NAME="test_langfuse Coverage Results" MODIFIED="1756196410142" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
205
- <SUITE FILE_PATH="coverage/xgae$run_task_engine.coverage" NAME="run_task_engine Coverage Results" MODIFIED="1756341371676" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
214
+ <SUITE FILE_PATH="coverage/xgae$run_task_engine.coverage" NAME="run_task_engine Coverage Results" MODIFIED="1756459820516" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
206
215
  <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$" />
207
216
  <SUITE FILE_PATH="coverage/xgae$mcp_tool_box.coverage" NAME="mcp_tool_box Coverage Results" MODIFIED="1756274403389" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
208
217
  <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$" />
209
218
  <SUITE FILE_PATH="coverage/xgae$setup_env.coverage" NAME="setup_env Coverage Results" MODIFIED="1756273791782" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
210
- <SUITE FILE_PATH="coverage/xgae$run_user_prompt.coverage" NAME="run_user_prompt Coverage Results" MODIFIED="1756279512361" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
219
+ <SUITE FILE_PATH="coverage/xgae$run_user_prompt.coverage" NAME="run_user_prompt Coverage Results" MODIFIED="1756453021298" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
211
220
  <SUITE FILE_PATH="coverage/xgae$xgae_cli.coverage" NAME="xgae_cli Coverage Results" MODIFIED="1756347000850" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
212
221
  <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$" />
213
- <SUITE FILE_PATH="coverage/xgae$task_engine.coverage" NAME="task_engine Coverage Results" MODIFIED="1756308226461" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
214
- <SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1756308364265" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
222
+ <SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1756429245697" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
223
+ <SUITE FILE_PATH="coverage/xgae$task_engine.coverage" NAME="task_engine Coverage Results" MODIFIED="1756454873435" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
215
224
  </component>
216
225
  </project>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog==6.9.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xgae"
3
- version = "0.1.10"
3
+ version = "0.1.12"
4
4
  description = "Extreme General Agent Engine"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -12,26 +12,28 @@ from xgae.engine.mcp_tool_box import XGAMcpToolBox
12
12
  from xgae.utils.setup_env import setup_langfuse, setup_logging
13
13
  from xgae.utils import handle_error
14
14
  from xgae.utils.misc import read_file
15
+ from xgae.engine.task_engine import XGATaskEngine
15
16
 
16
17
  class TaskState(TypedDict, total=False):
17
18
  """State definition for the agent orchestration graph"""
18
19
  messages: Annotated[Sequence[BaseMessage], add_messages]
19
20
  user_input: str
20
21
  next_node: str
21
- context: Dict[str, Any]
22
+ agent_context: Dict[str, Any]
22
23
  system_prompt: str
23
24
  custom_tools: List[str]
24
25
  general_tools: List[str]
25
26
  task_result: XGATaskResult
26
- formatted_result: XGATaskResult
27
27
  iteration_count: int
28
28
 
29
29
  langfuse = setup_langfuse()
30
30
 
31
31
  class XGAReactAgent:
32
32
  MAX_TASK_RETRY = 2
33
+
33
34
  def __init__(self):
34
- self.tool_box = XGAMcpToolBox()
35
+ self.tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
36
+ self.graph = None
35
37
 
36
38
  async def _create_graph(self) -> StateGraph:
37
39
  try:
@@ -41,8 +43,7 @@ class XGAReactAgent:
41
43
  graph_builder.add_node("supervisor", self._supervisor_node)
42
44
  graph_builder.add_node("select_tool", self._select_tool_node)
43
45
  graph_builder.add_node("exec_task", self._exec_task_node)
44
- graph_builder.add_node("eval_result", self._eval_result_node)
45
- graph_builder.add_node("format_result", self._format_result_node)
46
+ graph_builder.add_node("final_result", self._final_result_node)
46
47
 
47
48
  # Add edges
48
49
  graph_builder.add_edge(START, "supervisor")
@@ -52,69 +53,98 @@ class XGAReactAgent:
52
53
  {
53
54
  "select_tool": "select_tool",
54
55
  "exec_task": "exec_task",
55
- "format_result": "format_result"
56
+ "end": END
56
57
  }
57
58
  )
58
59
 
59
60
  graph_builder.add_edge("select_tool", "exec_task")
60
- graph_builder.add_edge("exec_task", "eval_result")
61
+ graph_builder.add_edge("exec_task", "final_result")
61
62
 
62
63
  graph_builder.add_conditional_edges(
63
- "eval_result",
64
+ "final_result",
64
65
  self._next_condition,
65
66
  {
66
- "retry": "supervisor",
67
- "format_result": "format_result",
67
+ "supervisor": "supervisor",
68
+ "end": END
68
69
  }
69
70
  )
70
71
 
71
- graph_builder.add_edge("format_result", END)
72
-
73
72
  graph = graph_builder.compile(checkpointer=MemorySaver())
74
- graph.name = "XGARectAgent"
73
+ graph.name = "XGARectAgentGraph"
75
74
 
76
75
  return graph
77
76
  except Exception as e:
78
- logging.error("Failed to create XGARectAgent graph: %s", str(e))
77
+ logging.error("Failed to create XGARectAgent Graph: %s", str(e))
79
78
  raise
80
79
 
80
+ def _search_system_prompt(self, user_input: str) -> str:
81
+ # You should search RAG use user_input, fetch COT or Prompt for your business
82
+ system_prompt = None if "fault" not in user_input else read_file("templates/example/fault_user_prompt.txt")
83
+ return system_prompt
81
84
 
82
85
  async def _supervisor_node(self, state: TaskState) -> Dict[str, Any]:
83
86
  user_input = state.get("user_input", "")
84
- system_prompt = None if "fault" in user_input else read_file("templates/example/fault_user_prompt.txt")
87
+ system_prompt = self._search_system_prompt(user_input)
88
+
89
+ general_tools = [] if system_prompt else ["*"]
90
+ custom_tools = ["*"] if system_prompt else []
91
+
92
+ next_node = "select_tool" if system_prompt else "exec_task"
85
93
  return {
86
94
  "system_prompt" : system_prompt,
87
- "next_node" : "select_tool",
95
+ "next_node" : next_node,
96
+ "general_tools": general_tools,
97
+ "custom_tools": custom_tools,
88
98
  }
89
99
 
100
+ def _select_custom_tools(self, system_prompt: str) -> list[str]:
101
+ custom_tools = ["*"] if system_prompt else []
102
+ return custom_tools
103
+
90
104
  async def _select_tool_node(self, state: TaskState) -> Dict[str, Any]:
91
105
  system_prompt = state.get("system_prompt",None)
92
- general_tools = ["*"] if system_prompt else []
93
- custom_tools = ["*"] if not system_prompt else []
106
+ general_tools = []
107
+ custom_tools = self._select_custom_tools(system_prompt)
94
108
  return {
95
109
  "general_tools" : general_tools,
96
110
  "custom_tools" : custom_tools,
97
111
  }
98
112
 
99
113
  async def _exec_task_node(self, state: TaskState) -> Dict[str, Any]:
100
- task_result = XGATaskResult(type="answer", content="test task result")
114
+ user_input = state.get("user_input", "")
115
+ system_prompt = state.get("system_prompt",None)
116
+ general_tools = state.get("general_tools",[])
117
+ custom_tools = state.get("custom_tools",[])
118
+ is_system_prompt = True if system_prompt is not None else False
119
+ try:
120
+ logging.info(f"🔥 XGATaskEngine: run_task_with_final_answer: user_input={user_input}, general_tools={general_tools}, custom_tools={custom_tools}, is_system_prompt={is_system_prompt}")
121
+ engine = XGATaskEngine(tool_box=self.tool_box,
122
+ general_tools=general_tools,
123
+ custom_tools=custom_tools,
124
+ system_prompt=system_prompt)
125
+ task_result = await engine.run_task_with_final_answer(
126
+ task_message={"role": "user", "content": user_input}
127
+ )
128
+ except Exception as e:
129
+ logging.error("Failed to execute task: %s", str(e))
130
+ task_result = XGATaskResult(type="error", content="Failed to execute task")
131
+
132
+ iteration_count = state.get("iteration_count", 0) + 1
101
133
  return {
102
- "task_result" : task_result
134
+ "task_result" : task_result,
135
+ "iteration_count": iteration_count,
103
136
  }
104
137
 
105
- async def _eval_result_node(self, state: TaskState) -> Dict[str, Any]:
138
+ async def _final_result_node(self, state: TaskState) -> Dict[str, Any]:
139
+ iteration_count = state.get("iteration_count")
140
+ task_result = state.get("task_result")
106
141
  next_node = "end"
142
+ if iteration_count < self.MAX_TASK_RETRY and task_result["type"] == "error":
143
+ next_node = "supervisor"
107
144
  return {
108
145
  "next_node" : next_node
109
146
  }
110
147
 
111
- async def _format_result_node(self, state: TaskState) -> Dict[str, Any]:
112
- formatted_result = state.get("task_result")
113
- return {
114
- "formatted_result" : formatted_result,
115
- "messages": state["messages"] + [AIMessage(content=f"")]
116
- }
117
-
118
148
  def _next_condition(self, state: TaskState) -> str:
119
149
  next_node = state.get("next_node")
120
150
  return next_node
@@ -134,13 +164,8 @@ class XGAReactAgent:
134
164
  "messages": [HumanMessage(content=f"information for: {user_input}")],
135
165
  "user_input": user_input,
136
166
  "next_node": None,
137
- "tasks": [],
138
- "context": "",
139
- "current_task": None,
140
- "next_task": None,
141
- "formatted_result": "",
142
- "final_error_info": "",
143
- "iteration_count": 1
167
+ "agent_context": {},
168
+ "iteration_count": 0
144
169
  }
145
170
 
146
171
  # Run the retrieval graph with proper configuration
@@ -149,7 +174,7 @@ class XGAReactAgent:
149
174
  final_state = await self.graph.ainvoke(initial_state, config=config)
150
175
 
151
176
  # Parse and return formatted results
152
- result = final_state["formatted_result"]
177
+ result = final_state["task_result"]
153
178
 
154
179
  logging.info("=" * 100)
155
180
  logging.info("User question: %s", user_input)
@@ -167,11 +192,15 @@ class XGAReactAgent:
167
192
  if __name__ == "__main__":
168
193
  setup_logging()
169
194
 
170
- agent = XGAReactAgent()
171
- user_inputs = [
172
- "Create a function to sort a list of numbers, sort [6,8,7,5]"
173
- , "sort [3,2,7,5]"
174
- ]
175
- for user_input in user_inputs:
176
- result = agent.generate(user_input)
177
- print(result)
195
+ async def main():
196
+ agent = XGAReactAgent()
197
+ user_inputs = [
198
+ #"locate 10.2.3.4 fault and solution",
199
+ "5+5",
200
+ ]
201
+ for user_input in user_inputs:
202
+ result = await agent.generate(user_input)
203
+ await asyncio.sleep(1)
204
+ print(result)
205
+
206
+ asyncio.run(main())
@@ -2,7 +2,7 @@ import asyncio
2
2
 
3
3
  from xgae.engine.mcp_tool_box import XGAMcpToolBox
4
4
  from xgae.engine.task_engine import XGATaskEngine
5
- from xgae.utils.llm_client import LLMConfig
5
+
6
6
  from xgae.utils.misc import read_file
7
7
 
8
8
  from xgae.utils.setup_env import setup_langfuse, setup_logging
@@ -18,9 +18,7 @@ async def main() -> None:
18
18
  engine = XGATaskEngine(tool_box=tool_box,
19
19
  general_tools=[],
20
20
  custom_tools=["*"],
21
- llm_config=LLMConfig(stream=False),
22
- system_prompt=system_prompt,
23
- max_auto_run=8)
21
+ system_prompt=system_prompt)
24
22
 
25
23
  # Two task run in same langfuse trace
26
24
  trace_id = langfuse.trace(name="xgae_example_run_human_in_loop").trace_id
@@ -1,14 +1,13 @@
1
1
  import asyncio
2
2
 
3
3
  from xgae.engine.task_engine import XGATaskEngine
4
- from xgae.utils.llm_client import LLMConfig
5
4
 
6
5
  from xgae.utils.setup_env import setup_logging
7
6
 
8
7
  setup_logging()
9
8
 
10
9
  async def main() -> None:
11
- engine = XGATaskEngine(llm_config=LLMConfig(stream=False), max_auto_run=3)
10
+ engine = XGATaskEngine()
12
11
 
13
12
  final_result = await engine.run_task_with_final_answer(
14
13
  task_message={"role": "user", "content": "1+7"}
@@ -2,12 +2,11 @@ import asyncio
2
2
 
3
3
  from xgae.engine.mcp_tool_box import XGAMcpToolBox
4
4
  from xgae.engine.task_engine import XGATaskEngine
5
- from xgae.utils.llm_client import LLMConfig
6
5
  from xgae.utils.misc import read_file
7
6
 
8
7
  from xgae.utils.setup_env import setup_logging
9
8
 
10
- setup_logging()
9
+ setup_logging(log_level="ERROR")
11
10
 
12
11
  async def main() -> None:
13
12
  # Before Run Exec: uv run custom_fault_tools
@@ -17,19 +16,15 @@ async def main() -> None:
17
16
  engine = XGATaskEngine(tool_box=tool_box,
18
17
  general_tools=[],
19
18
  custom_tools=["*"],
20
- llm_config=LLMConfig(stream=False),
21
- system_prompt=system_prompt,
22
- max_auto_run=8)
19
+ system_prompt=system_prompt)
23
20
 
24
21
  user_input = "locate 10.2.3.4 fault and solution"
25
- is_final_result = False
26
-
27
- if is_final_result:
28
- final_result = await engine.run_task_with_final_answer(task_message={"role": "user", "content": user_input})
29
- print("FINAL RESULT:", final_result)
30
- else:
31
- # Get All Task Process Message
32
- async for chunk in engine.run_task(task_message={"role": "user", "content": user_input}):
33
- print(chunk)
22
+ chunks = []
23
+ async for chunk in engine.run_task(task_message={"role": "user", "content": user_input}):
24
+ chunks.append(chunk)
25
+ print(chunk)
26
+
27
+ final_result = engine.parse_final_result(chunks)
28
+ print(f"\n\nFINAL_RESULT: {final_result}")
34
29
 
35
30
  asyncio.run(main())
@@ -3,7 +3,7 @@ import sys
3
3
 
4
4
  from xgae.engine.mcp_tool_box import XGAMcpToolBox
5
5
  from xgae.engine.task_engine import XGATaskEngine
6
- from xgae.utils.llm_client import LLMConfig
6
+
7
7
  from xgae.utils.misc import read_file
8
8
 
9
9
  from xgae.utils.setup_env import setup_langfuse, setup_env_logging
@@ -50,9 +50,7 @@ async def cli() -> None:
50
50
  engine = XGATaskEngine(tool_box=tool_box,
51
51
  general_tools=general_tools,
52
52
  custom_tools=custom_tools,
53
- llm_config=LLMConfig(stream=False),
54
- system_prompt=system_prompt,
55
- max_auto_run=8)
53
+ system_prompt=system_prompt)
56
54
 
57
55
  # Two task run in same langfuse trace
58
56
  trace_id = langfuse.trace(name="xgae_cli").trace_id
@@ -6,14 +6,14 @@ 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", "assistant_complete"]
9
+ XGAMsgStatusType = Literal["error", "finish", "tool_started", "tool_completed", "tool_error", "tool_failed"]
10
+ XGAResponseMsgType = Literal["user", "status", "tool", "assistant", "assistant_chunk"]
11
11
 
12
12
  class XGAResponseMessage(TypedDict, total=False):
13
13
  message_id: str
14
14
  type: XGAResponseMsgType
15
15
  is_llm_message: bool
16
- content: Union[Dict[str, Any], List[Any], str]
16
+ content: Union[Dict[str, Any], str]
17
17
  metadata: Dict[str, Any]
18
18
 
19
19
  class XGATaskResult(TypedDict, total=False):
@@ -124,11 +124,11 @@ class XGAMcpToolBox(XGAToolBox):
124
124
  result = XGAToolResult(success=True, output=str(tool_result))
125
125
  except Exception as e:
126
126
  error = f"Call mcp tool '{tool_name}' error: {str(e)}"
127
- logging.error(f"XGAMcpToolBox.call_tool: {error}")
127
+ logging.error(f"McpToolBox call_tool: {error}")
128
128
  result = XGAToolResult(success=False, output=error)
129
129
  else:
130
130
  error = f"No MCP tool found with name: {tool_name}"
131
- logging.info(f"XGAMcpToolBox.call_tool: error={error}")
131
+ logging.info(f"McpToolBox call_tool: error={error}")
132
132
  result = XGAToolResult(success=False, output=error)
133
133
 
134
134
  return result
@@ -179,11 +179,11 @@ class XGAMcpToolBox(XGAToolBox):
179
179
 
180
180
  return server_config
181
181
  else:
182
- logging.warning("MCP servers config file not found at: %s", mcp_config_path)
182
+ logging.warning(f"McpToolBox load_mcp_servers_config: MCP servers config file not found at: {mcp_config_path}")
183
183
  return {"mcpServers": {}}
184
184
 
185
185
  except Exception as e:
186
- logging.error("Failed to load MCP servers config: %s", str(e))
186
+ logging.error(f"McpToolBox load_mcp_servers_config: Failed to load MCP servers config: {e}")
187
187
  return {"mcpServers": {}}
188
188
 
189
189
 
@@ -1,6 +1,8 @@
1
1
  import logging
2
2
 
3
3
  from typing import List, Dict, Any, AsyncGenerator, override,Optional
4
+
5
+ from xgae.utils import handle_error
4
6
  from xgae.utils.json_helpers import format_for_yield
5
7
 
6
8
  from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
@@ -11,22 +13,20 @@ class NonStreamTaskResponser(TaskResponseProcessor):
11
13
  super().__init__(response_context)
12
14
 
13
15
  @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
+ async def process_response(self,
17
+ llm_response: Any,prompt_messages: List[Dict[str, Any]],
18
+ continuous_state: TaskRunContinuousState
19
+ ) -> AsyncGenerator[Dict[str, Any], None]:
16
20
  llm_content = ""
17
21
  parsed_xml_data = []
18
22
  finish_reason = None
19
23
  llm_count = continuous_state.get("auto_continue_count")
20
24
 
21
25
  try:
22
- # Extract finish_reason, content, tool calls
23
26
  if hasattr(llm_response, 'choices') and llm_response.choices:
24
27
  if hasattr(llm_response.choices[0], 'finish_reason'):
25
28
  finish_reason = llm_response.choices[0].finish_reason
26
- logging.info(f"NonStreamTask:LLM response finish_reason={finish_reason}")
27
-
28
- self.root_span.event(name=f"non_stream_processor_start[{self.task_no}]({llm_count})", level="DEFAULT",
29
- status_message=(f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_execution_strategy}"))
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
32
32
  if response_message:
@@ -35,27 +35,23 @@ 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"NonStreamTask:Truncate content, parsed_xml_data length {len(parsed_xml_data)} limit over max_xml_tool_calls={self.max_xml_tool_calls}")
39
- xml_chunks = self._extract_xml_chunks(llm_content)[:self.max_xml_tool_calls]
40
- if xml_chunks:
41
- last_chunk = xml_chunks[-1]
42
- last_chunk_pos = llm_content.find(last_chunk)
43
- if last_chunk_pos >= 0:
44
- llm_content = llm_content[:last_chunk_pos + len(last_chunk)]
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}")
45
39
  parsed_xml_data = parsed_xml_data[:self.max_xml_tool_calls]
46
40
  finish_reason = "xml_tool_limit_reached"
47
- logging.info(f"NonStreamTask:LLM response finish_reason={finish_reason}")
48
- else:
49
- logging.warning(f"NonStreamTask:LLM response_message is empty")
50
41
 
51
- message_data = {"role": "assistant", "content": llm_content} # index=-1, full llm_content
52
- assistant_msg = self.add_response_message(type="assistant_complete", content=message_data, is_llm_message=True)
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}, "
44
+ f"parsed_xml_data_len={len(parsed_xml_data)}, llm_content_len={len(llm_content)}")
45
+
46
+ if len(llm_content) == 0:
47
+ logging.warning(f"NonStreamResp: LLM response_message llm_content is empty")
48
+
49
+ message_data = {"role": "assistant", "content": llm_content}
50
+ assistant_msg = self.add_response_message(type="assistant", content=message_data, is_llm_message=True)
53
51
  yield assistant_msg
54
52
 
55
53
  tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
56
54
  if len(tool_calls_to_execute) > 0:
57
- logging.info(f"NonStreamTask:Executing {len(tool_calls_to_execute)} tools with strategy: {self.tool_execution_strategy}")
58
-
59
55
  tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_execution_strategy)
60
56
 
61
57
  tool_index = 0
@@ -81,31 +77,27 @@ class NonStreamTaskResponser(TaskResponseProcessor):
81
77
  if tool_completed_msg["metadata"].get("agent_should_terminate") == "true":
82
78
  finish_reason = "completed"
83
79
  break
80
+
84
81
  tool_index += 1
85
82
  else:
86
83
  finish_reason = "non_tool_call"
87
- logging.warning(f"NonStreamTask: tool_calls is empty, No Tool need to call !")
84
+ logging.warning(f"NonStreamResp: tool_calls is empty, No Tool need to call !")
88
85
 
89
86
  if finish_reason:
90
87
  finish_content = {"status_type": "finish", "finish_reason": finish_reason}
91
- finish_msg_obj = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
92
- if finish_msg_obj:
93
- yield format_for_yield(finish_msg_obj)
94
-
88
+ finish_msg = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
89
+ yield format_for_yield(finish_msg)
95
90
  except Exception as e:
96
- logging.error(f"NonStreamTask: Error processing non-streaming response: {llm_content}")
97
- self.root_span.event(name="error_processing_non_streaming_response", level="ERROR",
98
- status_message=(f"Error processing non-streaming response: {str(e)}"))
99
-
100
- content = {"role": "system", "status_type": "error", "message": str(e)}
101
- err_msg = self.add_response_message(ype="status", content=content,is_llm_message=False)
102
- if err_msg:
103
- yield format_for_yield(err_msg)
104
-
105
- # Re-raise the same exception (not a new one) to ensure proper error propagation
106
- logging.critical(f"NonStreamTask: Re-raising error to stop further processing: {str(e)}")
107
- self.root_span.event(name="re_raising_error_to_stop_further_processing", level="CRITICAL",
108
- status_message=(f"Re-raising error to stop further processing: {str(e)}"))
91
+ logging.error(f"NonStreamResp: Process response llm_content: {llm_content}")
92
+ handle_error(e)
93
+ self.root_span.event(name="non_stream_process_response_error", level="ERROR",
94
+ status_message=f"Process non-streaming response error: {e}",
95
+ metadata={"content": llm_content})
96
+
97
+ content = {"role": "system", "status_type": "error", "message": f"Process non-streaming response error: {e}"}
98
+ error_msg = self.add_response_message(type="status", content=content, is_llm_message=False)
99
+ yield format_for_yield(error_msg)
100
+
109
101
  raise # Use bare 'raise' to preserve the original exception with its traceback
110
102
 
111
103