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.
- {xgae-0.1.10 → xgae-0.1.12}/.env +8 -3
- {xgae-0.1.10 → xgae-0.1.12}/.idea/workspace.xml +17 -8
- {xgae-0.1.10 → xgae-0.1.12}/PKG-INFO +1 -1
- {xgae-0.1.10 → xgae-0.1.12}/pyproject.toml +1 -1
- {xgae-0.1.10 → xgae-0.1.12}/src/examples/agent/langgraph/react/react_agent.py +73 -44
- {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_human_in_loop.py +2 -4
- {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_simple.py +1 -2
- {xgae-0.1.10 → xgae-0.1.12}/src/examples/engine/run_user_prompt.py +9 -14
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/cli_app.py +2 -4
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/engine_base.py +3 -3
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/mcp_tool_box.py +4 -4
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/responser/non_stream_responser.py +31 -39
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/responser/responser_base.py +42 -40
- xgae-0.1.12/src/xgae/engine/responser/stream_responser.py +138 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/task_engine.py +79 -46
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/tools/without_general_tools_app.py +2 -3
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/__init__.py +2 -2
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/json_helpers.py +2 -2
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/llm_client.py +21 -19
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/setup_env.py +1 -0
- xgae-0.1.12/uv.lock +1386 -0
- xgae-0.1.10/src/xgae/engine/responser/stream_responser.py +0 -825
- xgae-0.1.10/uv.lock +0 -1386
- {xgae-0.1.10 → xgae-0.1.12}/.idea/.gitignore +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/ai_toolkit.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/misc.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/modules.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/vcs.xml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.idea/xgae.iml +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/.python-version +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/README.md +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/mcpservers/custom_servers.json +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/mcpservers/xga_server.json +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/mcpservers/xga_server_sse.json +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/examples/tools/custom_fault_tools_app.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/__init__.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/prompt_builder.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/engine/task_langfuse.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/misc.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/src/xgae/utils/xml_tool_parser.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/custom_tool_prompt_template.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/example/fault_user_prompt.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/gemini_system_prompt_template.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/general_tool_prompt_template.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/system_prompt_response_sample.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/templates/system_prompt_template.txt +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/test/test_langfuse.py +0 -0
- {xgae-0.1.10 → xgae-0.1.12}/test/test_litellm_langfuse.py +0 -0
{xgae-0.1.10 → xgae-0.1.12}/.env
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# LOG
|
|
2
|
-
LOG_LEVEL=
|
|
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
|
-
|
|
21
|
-
|
|
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.
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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$
|
|
214
|
-
<SUITE FILE_PATH="coverage/xgae$
|
|
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>
|
|
@@ -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
|
-
|
|
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("
|
|
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
|
-
"
|
|
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", "
|
|
61
|
+
graph_builder.add_edge("exec_task", "final_result")
|
|
61
62
|
|
|
62
63
|
graph_builder.add_conditional_edges(
|
|
63
|
-
"
|
|
64
|
+
"final_result",
|
|
64
65
|
self._next_condition,
|
|
65
66
|
{
|
|
66
|
-
"
|
|
67
|
-
"
|
|
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 = "
|
|
73
|
+
graph.name = "XGARectAgentGraph"
|
|
75
74
|
|
|
76
75
|
return graph
|
|
77
76
|
except Exception as e:
|
|
78
|
-
logging.error("Failed to create XGARectAgent
|
|
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 =
|
|
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" :
|
|
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 = [
|
|
93
|
-
custom_tools =
|
|
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
|
-
|
|
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
|
|
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
|
-
"
|
|
138
|
-
"
|
|
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["
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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", "
|
|
10
|
-
XGAResponseMsgType = Literal["user", "status", "tool", "assistant", "
|
|
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],
|
|
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"
|
|
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"
|
|
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:
|
|
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:
|
|
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,
|
|
15
|
-
|
|
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"
|
|
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"
|
|
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
|
-
|
|
52
|
-
|
|
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"
|
|
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
|
-
|
|
92
|
-
|
|
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"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|