xgae 0.3.2__tar.gz → 0.3.4__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.3.2 → xgae-0.3.4}/CHANGELOG.md +14 -0
- {xgae-0.3.2 → xgae-0.3.4}/PKG-INFO +2 -2
- {xgae-0.3.2 → xgae-0.3.4}/README.md +1 -1
- xgae-0.3.4/examples/agent/are/README.md +44 -0
- xgae-0.3.2/examples/agent/are/simulation/agents/xga/xga_agent.py → xgae-0.3.4/examples/agent/are/are/simulation/agents/xga/are_agent.py +23 -14
- xgae-0.3.2/examples/agent/are/simulation/agents/xga/xga_agent_factory.py → xgae-0.3.4/examples/agent/are/are/simulation/agents/xga/are_agent_factory.py +1 -1
- xgae-0.3.4/examples/agent/are/are/simulation/agents/xga/are_prompt_builder.py +19 -0
- xgae-0.3.2/examples/agent/are/simulation/agents/xga/xga_tool_box.py → xgae-0.3.4/examples/agent/are/are/simulation/agents/xga/are_tool_box.py +4 -3
- xgae-0.3.4/examples/agent/are/are/simulation/agents/xga/mcp_tool_executor.py +134 -0
- xgae-0.3.4/examples/agent/are/are/simulation/scenarios/scenario_bomc_fault/scenario.py +184 -0
- xgae-0.3.4/examples/agent/are/are_modify_files.zip +0 -0
- {xgae-0.3.2/examples/agent/are/simulation/agents/xga → xgae-0.3.4/examples/agent/are}/env.example +1 -0
- xgae-0.3.4/examples/agent/are/mcpservers/example_mcp_apps.json +11 -0
- xgae-0.3.4/examples/agent/are/templates/custom_tool_prompt_template.txt +24 -0
- xgae-0.3.4/examples/agent/are/templates/general_tool_prompt_template.txt +51 -0
- xgae-0.3.4/mcpservers/xga_server.json +11 -0
- {xgae-0.3.2 → xgae-0.3.4}/pyproject.toml +1 -1
- {xgae-0.3.2 → xgae-0.3.4}/uv.lock +1 -1
- {xgae-0.3.2 → xgae-0.3.4}/xgae/gaia2/are_engine.py +3 -0
- xgae-0.3.2/examples/agent/are/simulation/agents/xga/README.md +0 -30
- {xgae-0.3.2 → xgae-0.3.4}/.env +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/.python-version +0 -0
- {xgae-0.3.2 → xgae-0.3.4/examples/agent/are}/mcpservers/xga_server.json +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/agent/langgraph/reflection/agent_base.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/agent/langgraph/reflection/custom_prompt_rag.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/agent/langgraph/reflection/reflection_agent.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/agent/langgraph/reflection/result_eval_agent.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/agent/langgraph/reflection/run_agent_app.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/engine/run_custom_and_agent_tools.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/engine/run_general_tools.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/engine/run_human_in_loop.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/engine/run_simple.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/tools/custom_fault_tools_app.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/examples/tools/simu_a2a_tools_app.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/mcpservers/custom_servers.json +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/mcpservers/xga_server_sse.json +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/agent_tool_prompt_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/custom_tool_prompt_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/example/fault_user_prompt.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/example/result_eval_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/gemini_system_prompt_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/general_tool_prompt_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/system_prompt_response_sample.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/templates/system_prompt_template.txt +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/test/test_chroma.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/test/test_langfuse.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/test/test_litellm_langfuse.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/__init__.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/engine_base.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/mcp_tool_box.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/prompt_builder.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/responser/non_stream_responser.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/responser/responser_base.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/responser/stream_responser.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/task_engine.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine/task_langfuse.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/engine_cli_app.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/tools/without_general_tools_app.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/__init__.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/json_helpers.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/llm_client.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/misc.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/setup_env.py +0 -0
- {xgae-0.3.2 → xgae-0.3.4}/xgae/utils/xml_tool_parser.py +0 -0
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [0.3.4] - 2025-11-1
|
|
2
|
+
- GAIA2 ARE Example: XGAArePromptBuilder, Use MCP tool format general tool construct prompt
|
|
3
|
+
### Modified
|
|
4
|
+
- GAIA2 ARE Example: Refact code struct and class name
|
|
5
|
+
- GAIA2 ARE Example: Optimize prompt template
|
|
6
|
+
- ARETaskEngine: add prompt_builder init parameter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## [0.3.3] - 2025-10-30
|
|
10
|
+
### Added
|
|
11
|
+
- GAIA2 ARE Scenario: scenario_bomc_fault
|
|
12
|
+
- GAIA2 ARE MCP: Support Custom MCP Apps, example_mcp_apps.json
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
## [0.3.2] - 2025-10-24
|
|
2
16
|
### Added
|
|
3
17
|
- GAIA2 ARE Example: XGAAreAgent, XGAAreToolBox
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xgae
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Extreme General Agent Engine
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: colorlog>=6.9.0
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
- Support Custom prompt and external MCP Tools
|
|
20
20
|
- Support Langfuse
|
|
21
21
|
- Support Langgraph
|
|
22
|
-
- Support GAIA2 ARE agent
|
|
22
|
+
- Support GAIA2 ARE agent and Custom MCP Apps
|
|
23
23
|
- Support Human-in-Loop in agent
|
|
24
24
|
- Can Use A2A protocol call A2A Agent as tool by 'xgaproxy' project
|
|
25
25
|
- Can Use E2B or Daytona Sandbox of 'xgatools' project
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- Support Custom prompt and external MCP Tools
|
|
4
4
|
- Support Langfuse
|
|
5
5
|
- Support Langgraph
|
|
6
|
-
- Support GAIA2 ARE agent
|
|
6
|
+
- Support GAIA2 ARE agent and Custom MCP Apps
|
|
7
7
|
- Support Human-in-Loop in agent
|
|
8
8
|
- Can Use A2A protocol call A2A Agent as tool by 'xgaproxy' project
|
|
9
9
|
- Can Use E2B or Daytona Sandbox of 'xgatools' project
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## GAIA2 ARE Support
|
|
2
|
+
### How to add XGAE to ARE Project
|
|
3
|
+
- add xgae==0.3.2 to ARE requirements.txt
|
|
4
|
+
- uv pip install -r requirements.txt
|
|
5
|
+
- modify ARE 'AgentBuilder' and 'AgentConfigBuilder' class, add "xga" type agent :
|
|
6
|
+
```
|
|
7
|
+
File: agent_builder.py
|
|
8
|
+
class AgentBuilder:
|
|
9
|
+
def list_agents(self) -> list[str]:
|
|
10
|
+
return ["default", "xga"]
|
|
11
|
+
|
|
12
|
+
def build():
|
|
13
|
+
...
|
|
14
|
+
agent_name = agent_config.get_agent_name()
|
|
15
|
+
if agent_name in ["default", "xga"]:
|
|
16
|
+
# add xga agent code
|
|
17
|
+
|
|
18
|
+
File: agent_config_builder.py
|
|
19
|
+
class AgentConfigBuilder:
|
|
20
|
+
def build():
|
|
21
|
+
if agent_name in["default", "xga"]:
|
|
22
|
+
```
|
|
23
|
+
- modify ARE 'MCPApp' :
|
|
24
|
+
```
|
|
25
|
+
File: mcp_app.py
|
|
26
|
+
class MCPApp:
|
|
27
|
+
def _call_tool(self, tool_name: str, **kwargs) -> str:
|
|
28
|
+
try:
|
|
29
|
+
...
|
|
30
|
+
from are.simulation.agents.xga.mcp_tool_executor import call_mcp_tool
|
|
31
|
+
result = call_mcp_tool(self.server_url, tool_name, kwargs, 10)
|
|
32
|
+
return str(result)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
- modify ARE .env, add XGAE .env config, refer to env.example
|
|
39
|
+
- copy XGAE package 'mcpservers' and 'templates' directory to ARE project root
|
|
40
|
+
|
|
41
|
+
### Run XGA Agent in ARE
|
|
42
|
+
```
|
|
43
|
+
uv run are-run -e -s scenario_find_image_file -a xga --model openai/qwen3-235b-a22b --provider llama-api --output_dir ./output
|
|
44
|
+
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import asyncio
|
|
2
3
|
from typing import List, Dict, Any
|
|
3
4
|
from typing_extensions import override
|
|
4
5
|
|
|
@@ -23,8 +24,8 @@ from are.simulation.agents.default_agent.base_agent import (
|
|
|
23
24
|
|
|
24
25
|
from xgae.utils.llm_client import LLMConfig
|
|
25
26
|
from xgae.gaia2.are_engine import ARETaskEngine
|
|
26
|
-
from
|
|
27
|
-
|
|
27
|
+
from are.simulation.agents.xga.are_tool_box import XGAAreToolBox
|
|
28
|
+
from are.simulation.agents.xga.are_prompt_builder import XGAArePromptBuilder
|
|
28
29
|
|
|
29
30
|
def pre_run_task_check(agent, iterations: int, llm_messages: List[Dict[str, Any]]):
|
|
30
31
|
try:
|
|
@@ -165,10 +166,11 @@ class XGAAreAgent(BaseAgent):
|
|
|
165
166
|
_system_prompt = "\n\n".join(prompt for prompt in self.init_system_prompts.values())
|
|
166
167
|
pattern = r'<general_instructions>(.*?)</general_instructions>'
|
|
167
168
|
prompt_are_general = re.search(pattern, _system_prompt, re.DOTALL)
|
|
169
|
+
prompt_header = "#CORE IDENTITY & CAPABILITIES\n"
|
|
168
170
|
if prompt_are_general:
|
|
169
|
-
prompt_are_general = prompt_are_general.group(1).strip()
|
|
171
|
+
prompt_are_general = prompt_header + prompt_are_general.group(1).strip() + "\n\n"
|
|
170
172
|
else:
|
|
171
|
-
prompt_are_general = _system_prompt
|
|
173
|
+
prompt_are_general = prompt_header + _system_prompt + "\n\n"
|
|
172
174
|
|
|
173
175
|
model_config = self.llm_engine.model_config
|
|
174
176
|
llm_config = LLMConfig(
|
|
@@ -176,6 +178,9 @@ class XGAAreAgent(BaseAgent):
|
|
|
176
178
|
api_key = model_config.api_key,
|
|
177
179
|
api_base = model_config.endpoint
|
|
178
180
|
)
|
|
181
|
+
|
|
182
|
+
prompt_builder = XGAArePromptBuilder(prompt_are_general)
|
|
183
|
+
|
|
179
184
|
self.task_engine = ARETaskEngine(
|
|
180
185
|
agent = self,
|
|
181
186
|
agent_id = self.agent_id,
|
|
@@ -183,6 +188,7 @@ class XGAAreAgent(BaseAgent):
|
|
|
183
188
|
max_auto_run = self.max_iterations,
|
|
184
189
|
llm_config = llm_config,
|
|
185
190
|
tool_box = tool_box,
|
|
191
|
+
prompt_builder = prompt_builder,
|
|
186
192
|
pre_run_task_fn = pre_run_task_check,
|
|
187
193
|
post_run_task_fn = post_run_task_check,
|
|
188
194
|
terminate_task_fn = terminate_task_check,
|
|
@@ -205,8 +211,8 @@ class XGAAreAgent(BaseAgent):
|
|
|
205
211
|
|
|
206
212
|
@override
|
|
207
213
|
def execute_agent_loop(self) -> str | None | MMObservation:
|
|
208
|
-
|
|
209
|
-
|
|
214
|
+
with asyncio.Runner() as runner:
|
|
215
|
+
runner.run(self.async_execute_agent_loop())
|
|
210
216
|
|
|
211
217
|
# We have reached a termination condition, execute the termination method
|
|
212
218
|
if self.termination_step.function is not None and not self.stop_event.is_set():
|
|
@@ -235,15 +241,18 @@ class XGAAreAgent(BaseAgent):
|
|
|
235
241
|
agent_id = self.agent_id,
|
|
236
242
|
)
|
|
237
243
|
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
elif chunk_type == "assistant":
|
|
245
|
+
llm_content = chunk['content']['content']
|
|
246
|
+
if "<thought>" in llm_content:
|
|
247
|
+
thought_content = re.search(r'<thought>(.*?)</thought>', llm_content, re.DOTALL).group(1).strip()
|
|
248
|
+
if thought_content:
|
|
249
|
+
self.append_agent_log(
|
|
250
|
+
ThoughtLog(
|
|
251
|
+
content = thought_content,
|
|
252
|
+
timestamp = self.make_timestamp(),
|
|
253
|
+
agent_id = self.agent_id,
|
|
254
|
+
)
|
|
245
255
|
)
|
|
246
|
-
)
|
|
247
256
|
elif chunk_type == "tool":
|
|
248
257
|
tool_content = chunk['content']
|
|
249
258
|
tool_execution = tool_content.get('tool_execution')
|
|
@@ -4,7 +4,7 @@ from are.simulation.agents.default_agent.termination_methods.are_simulation impo
|
|
|
4
4
|
from are.simulation.agents.default_agent.tools.json_action_executor import JsonActionExecutor
|
|
5
5
|
from are.simulation.agents.llm.llm_engine import LLMEngine
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from are.simulation.agents.xga.are_agent import XGAAreAgent
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def xga_simulation_react_xml_agent(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing_extensions import override
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
from xgae.engine.engine_base import XGAToolSchema
|
|
5
|
+
from xgae.engine.prompt_builder import XGAPromptBuilder
|
|
6
|
+
|
|
7
|
+
class XGAArePromptBuilder(XGAPromptBuilder):
|
|
8
|
+
def __init__(self, system_prompt: Optional[str] = None):
|
|
9
|
+
super().__init__(system_prompt)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
def build_general_tool_prompt(self, tool_schemas: List[XGAToolSchema]) -> str:
|
|
14
|
+
tool_prompt = self.build_mcp_tool_prompt("templates/general_tool_prompt_template.txt", tool_schemas)
|
|
15
|
+
return tool_prompt
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
5
|
from typing_extensions import override
|
|
6
6
|
|
|
7
7
|
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
@@ -10,6 +10,7 @@ from xgae.engine.engine_base import XGAError, XGAToolSchema, XGAToolResult, XGAT
|
|
|
10
10
|
from xgae.engine.mcp_tool_box import XGAMcpToolBox
|
|
11
11
|
|
|
12
12
|
from are.simulation.tools import Tool
|
|
13
|
+
from are.simulation.agents.xga.mcp_tool_executor import convert_mcp_tool_result
|
|
13
14
|
|
|
14
15
|
class XGAAreToolBox(XGAMcpToolBox):
|
|
15
16
|
def __init__(self, are_tools: Dict[str, Tool]):
|
|
@@ -41,11 +42,11 @@ class XGAAreToolBox(XGAMcpToolBox):
|
|
|
41
42
|
|
|
42
43
|
try:
|
|
43
44
|
tool_result = self.are_tools[full_tool_name](**args)
|
|
44
|
-
result =
|
|
45
|
+
result = convert_mcp_tool_result(str(tool_result))
|
|
45
46
|
except Exception as e:
|
|
46
47
|
error = f"Call ARE Tool '{tool_name}' error: {str(e)}"
|
|
47
48
|
logging.error(f"AreToolBox call_are_tool: {error}")
|
|
48
|
-
result = XGAToolResult(success=False, output=
|
|
49
|
+
result = XGAToolResult(success=False, output=str(e))
|
|
49
50
|
else:
|
|
50
51
|
async with self._mcp_client.session(server_name) as session:
|
|
51
52
|
tools = await load_mcp_tools(session)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from concurrent.futures import Future as ConcurrentFuture
|
|
7
|
+
from typing import Any, Optional, Coroutine, List, Dict
|
|
8
|
+
|
|
9
|
+
from mcp.client.session import ClientSession
|
|
10
|
+
from mcp.client.sse import sse_client
|
|
11
|
+
|
|
12
|
+
from xgae.engine.engine_base import XGAToolResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AsyncToolExecutor:
|
|
16
|
+
_instance = None
|
|
17
|
+
_loop = None
|
|
18
|
+
_thread = None
|
|
19
|
+
_lock = threading.Lock()
|
|
20
|
+
|
|
21
|
+
def __new__(cls):
|
|
22
|
+
with cls._lock:
|
|
23
|
+
if cls._instance is None:
|
|
24
|
+
cls._instance = super().__new__(cls)
|
|
25
|
+
cls._init_event_loop()
|
|
26
|
+
return cls._instance
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def _init_event_loop(cls):
|
|
31
|
+
if cls._loop is None or cls._loop.is_closed():
|
|
32
|
+
cls._loop = asyncio.new_event_loop()
|
|
33
|
+
|
|
34
|
+
def start_loop(loop):
|
|
35
|
+
asyncio.set_event_loop(loop)
|
|
36
|
+
try:
|
|
37
|
+
loop.run_forever()
|
|
38
|
+
finally:
|
|
39
|
+
if not loop.is_closed():
|
|
40
|
+
loop.close()
|
|
41
|
+
|
|
42
|
+
cls._thread = threading.Thread(target=start_loop, args=(cls._loop,), daemon=True)
|
|
43
|
+
cls._thread.start()
|
|
44
|
+
|
|
45
|
+
time.sleep(0.1)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def submit(cls, coro: Coroutine) -> ConcurrentFuture:
|
|
49
|
+
if cls._loop is None or cls._loop.is_closed():
|
|
50
|
+
cls._init_event_loop()
|
|
51
|
+
|
|
52
|
+
return asyncio.run_coroutine_threadsafe(coro, cls._loop)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def run_and_wait(cls, coro: Coroutine, timeout: Optional[float] = None) -> Any:
|
|
56
|
+
future = cls.submit(coro)
|
|
57
|
+
return future.result(timeout=timeout)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def get_loop(cls):
|
|
61
|
+
if cls._loop is None or cls._loop.is_closed():
|
|
62
|
+
cls._init_event_loop()
|
|
63
|
+
return cls._loop
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def shutdown(cls):
|
|
67
|
+
if cls._loop is not None and not cls._loop.is_closed():
|
|
68
|
+
cls._loop.call_soon_threadsafe(cls._loop.stop)
|
|
69
|
+
|
|
70
|
+
from mcp.types import CallToolResult
|
|
71
|
+
|
|
72
|
+
async def _mcp_sse_tool_call(url: str, tool_name: str, arguments: dict[str, Any]) -> CallToolResult:
|
|
73
|
+
async with sse_client(url, sse_read_timeout=5) as streams:
|
|
74
|
+
async with ClientSession(*streams) as session:
|
|
75
|
+
await session.initialize()
|
|
76
|
+
return await session.call_tool(tool_name, arguments)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def call_mcp_tool(url: str, tool_name: str, arguments: dict[str, Any], timeout: Optional[float] = None) -> Any:
|
|
80
|
+
future = AsyncToolExecutor.submit(_mcp_sse_tool_call(url, tool_name, arguments))
|
|
81
|
+
mcp_result:CallToolResult = future.result(timeout=timeout)
|
|
82
|
+
result = {
|
|
83
|
+
'isError': mcp_result.isError,
|
|
84
|
+
}
|
|
85
|
+
if mcp_result.isError:
|
|
86
|
+
result['content'] = mcp_result.content[0].text
|
|
87
|
+
else:
|
|
88
|
+
result['content'] = mcp_result.structuredContent['result']
|
|
89
|
+
|
|
90
|
+
return json.dumps(result)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def convert_mcp_tool_result(org_result: str)->XGAToolResult:
|
|
94
|
+
result = XGAToolResult(success=True, output=str(org_result))
|
|
95
|
+
|
|
96
|
+
if org_result and isinstance(org_result, str) and "isError" in org_result:
|
|
97
|
+
try:
|
|
98
|
+
_result:dict = json.loads(org_result)
|
|
99
|
+
content = _result.get('content', None)
|
|
100
|
+
isError = _result.get('isError', None)
|
|
101
|
+
|
|
102
|
+
if content and isError:
|
|
103
|
+
success = not bool(isError)
|
|
104
|
+
result = XGAToolResult(success=bool(success), output=str(content))
|
|
105
|
+
except:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
async def sample_task(name, duration):
|
|
113
|
+
print(f"Task '{name}' Begin,time = {duration} ")
|
|
114
|
+
await asyncio.sleep(duration)
|
|
115
|
+
return f"Task '{name}' Finished"
|
|
116
|
+
|
|
117
|
+
async def main():
|
|
118
|
+
try:
|
|
119
|
+
# result = AsyncToolExecutor.run_and_wait(sample_task("mytool", 1))
|
|
120
|
+
# print(f"Result1: {result}")
|
|
121
|
+
|
|
122
|
+
result = call_mcp_tool("http://localhost:17070/sse", "get_alarm", {"ip":"13.0.0.13"})
|
|
123
|
+
result = convert_mcp_tool_result(result)
|
|
124
|
+
print(f"Result2: {result}")
|
|
125
|
+
|
|
126
|
+
result = call_mcp_tool("http://localhost:17070/sse", "get_alarm1", {"ip":"13.0.0.13"})
|
|
127
|
+
result = convert_mcp_tool_result(result)
|
|
128
|
+
print(f"Result3: {result}")
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"Thread Fail: {e}")
|
|
132
|
+
|
|
133
|
+
with asyncio.Runner() as runner:
|
|
134
|
+
runner.run(main())
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the terms described in the LICENSE file in
|
|
5
|
+
# the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import datetime
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from dotenv import load_dotenv
|
|
15
|
+
from are.simulation.apps.agent_user_interface import AgentUserInterface
|
|
16
|
+
from are.simulation.apps.mcp.mcp_app import MCPApp
|
|
17
|
+
from are.simulation.scenarios.scenario import Scenario, ScenarioValidationResult
|
|
18
|
+
from are.simulation.scenarios.utils.env_utils import expand_env_vars
|
|
19
|
+
|
|
20
|
+
from are.simulation.scenarios.utils.registry import register_scenario
|
|
21
|
+
from are.simulation.types import EventRegisterer, event_registered
|
|
22
|
+
|
|
23
|
+
# Set up logger
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Load environment variables from .env file
|
|
27
|
+
load_dotenv()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@register_scenario("scenario_bomc_fault")
|
|
32
|
+
class ScenarioBomcFault(Scenario):
|
|
33
|
+
start_time: float | None = datetime.datetime.now().timestamp()
|
|
34
|
+
duration: float | None = None
|
|
35
|
+
|
|
36
|
+
def init_and_populate_apps(self, *args, **kwargs) -> None:
|
|
37
|
+
|
|
38
|
+
# get apps from the demo
|
|
39
|
+
agui = AgentUserInterface()
|
|
40
|
+
self.apps = [agui]
|
|
41
|
+
|
|
42
|
+
# form task from universe params
|
|
43
|
+
self.start_time = datetime.datetime.now().timestamp()
|
|
44
|
+
|
|
45
|
+
# Load additional MCP apps from JSON file if specified
|
|
46
|
+
self._load_mcp_apps_from_json()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build_events_flow(self) -> None:
|
|
50
|
+
"""Define the sequence of events that will occur during the scenario"""
|
|
51
|
+
|
|
52
|
+
agui = self.get_typed_app(AgentUserInterface)
|
|
53
|
+
|
|
54
|
+
with EventRegisterer.capture_mode():
|
|
55
|
+
# User event: User requests task creation
|
|
56
|
+
event1 = agui.send_message_to_agent(
|
|
57
|
+
content="locate 10.0.0.1 fault and solution",
|
|
58
|
+
).depends_on(None, delay_seconds=2)
|
|
59
|
+
|
|
60
|
+
oracle1 = (
|
|
61
|
+
agui.send_message_to_user(
|
|
62
|
+
content="Fault Cause: Phone Recharge Application Crash; Solution: Restart Phone Recharge Application",
|
|
63
|
+
)
|
|
64
|
+
.oracle()
|
|
65
|
+
.depends_on(event1, delay_seconds=10)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
self.events = [event1]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def validate(self, env) -> ScenarioValidationResult:
|
|
73
|
+
"""
|
|
74
|
+
Validate that the scenario completed successfully.
|
|
75
|
+
|
|
76
|
+
Check that the agent properly interacted with our custom app.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
aui = env.get_app("AgentUserInterface")
|
|
80
|
+
msg = aui.get_last_message_from_agent()
|
|
81
|
+
content = msg.content
|
|
82
|
+
if "Phone Recharge Application Crash" in content:
|
|
83
|
+
return ScenarioValidationResult(success=True)
|
|
84
|
+
else:
|
|
85
|
+
return ScenarioValidationResult(success=False)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
return ScenarioValidationResult(success=False, exception=e)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_mcp_apps_from_json(self) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Load additional MCP apps from a JSON file specified in the environment variables.
|
|
93
|
+
|
|
94
|
+
The JSON file should follow the Claude MCP definition format with a ``mcpServers``
|
|
95
|
+
key containing server configurations. Environment variables in the JSON are
|
|
96
|
+
expanded using the current environment.
|
|
97
|
+
|
|
98
|
+
:return: None
|
|
99
|
+
"""
|
|
100
|
+
# Get the JSON file path from environment variables
|
|
101
|
+
mcp_apps_json_path = os.environ.get("MCP_APPS_JSON_PATH")
|
|
102
|
+
|
|
103
|
+
if not mcp_apps_json_path:
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
# Ensure self.apps is initialized
|
|
107
|
+
if not hasattr(self, "apps") or self.apps is None:
|
|
108
|
+
self.apps = []
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
with open(mcp_apps_json_path, "r") as f:
|
|
112
|
+
mcp_config = json.load(f)
|
|
113
|
+
|
|
114
|
+
if "mcpServers" not in mcp_config:
|
|
115
|
+
logger.warning(
|
|
116
|
+
f"No 'mcpServers' key found in MCP apps JSON file: {mcp_apps_json_path}"
|
|
117
|
+
)
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
# Create MCP apps from the JSON configuration
|
|
121
|
+
for server_name, server_config in mcp_config["mcpServers"].items():
|
|
122
|
+
mcp_app = self._create_mcp_app(server_name, server_config)
|
|
123
|
+
if mcp_app:
|
|
124
|
+
self.apps.append(mcp_app)
|
|
125
|
+
logger.info(f"Added MCP app: {server_name}")
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error(f"Error loading MCP apps from JSON: {e}", exc_info=True)
|
|
129
|
+
|
|
130
|
+
def _create_mcp_app(self, name: str, config: dict[str, Any]) -> MCPApp | None:
|
|
131
|
+
"""
|
|
132
|
+
Create an MCPApp instance from a server configuration.
|
|
133
|
+
|
|
134
|
+
Supports two types of MCP servers:
|
|
135
|
+
|
|
136
|
+
1. Local servers with command and arguments
|
|
137
|
+
2. Remote SSE servers with URL and headers
|
|
138
|
+
|
|
139
|
+
Environment variables in the configuration are expanded before creating the app.
|
|
140
|
+
|
|
141
|
+
:param name: The name of the MCP app
|
|
142
|
+
:type name: str
|
|
143
|
+
:param config: The server configuration from the JSON file
|
|
144
|
+
:type config: dict[str, Any]
|
|
145
|
+
:return: An MCPApp instance or None if creation fails
|
|
146
|
+
:rtype: MCPApp | None
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
# Expand environment variables in the configuration
|
|
150
|
+
expanded_config = expand_env_vars(config, allowed=["HF_TOKEN"])
|
|
151
|
+
|
|
152
|
+
# Handle SSE remote servers
|
|
153
|
+
if expanded_config.get("type") == "sse" or "url" in expanded_config:
|
|
154
|
+
return MCPApp(
|
|
155
|
+
name=name,
|
|
156
|
+
server_url=expanded_config.get("url"),
|
|
157
|
+
sse_headers=expanded_config.get("headers", {}),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Handle local servers with command and args
|
|
161
|
+
elif "command" in expanded_config:
|
|
162
|
+
return MCPApp(
|
|
163
|
+
name=name,
|
|
164
|
+
server_command=expanded_config.get("command"),
|
|
165
|
+
server_args=expanded_config.get("args", []),
|
|
166
|
+
server_env=expanded_config.get("env", {}),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
else:
|
|
170
|
+
logger.warning(f"Unsupported MCP server configuration for {name}")
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error(f"Error creating MCP app {name}: {e}", exc_info=True)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Before Run Scenario, should xage project , execute command:
|
|
179
|
+
# 1. Vailid Success: uv run example-fault-tools
|
|
180
|
+
# 2. Vailid Fail: uv run example-fault-tools --alarmtype 2
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
from are.simulation.scenarios.utils.cli_utils import run_and_validate
|
|
183
|
+
|
|
184
|
+
run_and_validate(ScenarioBomcFault())
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# AVAILABLE MCP TOOLS
|
|
2
|
+
You have access to external MCP (Model Context Protocol) server tools.
|
|
3
|
+
MCP tools can be called directly using their native function names in the standard function calling format:
|
|
4
|
+
|
|
5
|
+
## MCP TOOL SCHEMAS
|
|
6
|
+
{tool_schemas}
|
|
7
|
+
|
|
8
|
+
## CRITICAL MCP TOOL RULES
|
|
9
|
+
When you use ANY MCP (Model Context Protocol) tools:
|
|
10
|
+
1. Never call a MCP tool not in 'Available MCP tools' list
|
|
11
|
+
2. If call MCP tool result 'success' is false, call 'complete' tool to end task, don't call 'ask' tool
|
|
12
|
+
3. ALWAYS read and use the EXACT results returned by the MCP tool
|
|
13
|
+
4. For search tools: ONLY cite URLs, sources, and information from the actual search results
|
|
14
|
+
5. For any tool: Base your response entirely on the tool's output - do NOT add external information
|
|
15
|
+
6. DO NOT fabricate, invent, hallucinate, or make up any sources, URLs, or data
|
|
16
|
+
7. If you need more information, call the MCP tool again with different parameters
|
|
17
|
+
8. When writing reports/summaries: Reference ONLY the data from MCP tool results
|
|
18
|
+
9. If the MCP tool doesn't return enough information, explicitly state this limitation
|
|
19
|
+
10. Always double-check that every fact, URL, and reference comes from the MCP tool output
|
|
20
|
+
|
|
21
|
+
IMPORTANT: MCP tool results are your PRIMARY and ONLY source of truth for external data!
|
|
22
|
+
NEVER supplement MCP results with your training data or make assumptions beyond what the tools provide.
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# FUNDAMENTAL RULES FOR TASK EXECUTION
|
|
2
|
+
1. COMMUNICATION: Only message the user when completely done or if the task is impossible.
|
|
3
|
+
2. EXECUTION: Work silently, complete tasks fully, no progress updates.
|
|
4
|
+
3. COMPLIANCE: Follow user instructions exactly, ask for clarification only if the environment does not provide enough information.
|
|
5
|
+
4. PROBLEM SOLVING: Try alternative approaches before reporting failure.
|
|
6
|
+
5. INFORMATION: Use available tools to gather missing information before asking user.
|
|
7
|
+
6. AMBIGUITY: Execute all clear and unambiguous parts of a request immediately. When you encounter ambiguities, contradictions, or impossible elements, finish unambiguous subtasks and then stop and explicitly ask the user for clarification before proceeding with those specific parts.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# FORMAT SPECIFICATION
|
|
11
|
+
## RESPONSE FORMAT SPECIFICATION
|
|
12
|
+
<thought>
|
|
13
|
+
[Your reasoning in plain text]
|
|
14
|
+
</thought>
|
|
15
|
+
|
|
16
|
+
<function_calls>
|
|
17
|
+
<invoke name="function_name">
|
|
18
|
+
<parameter name="param_name">param_value</parameter>
|
|
19
|
+
...
|
|
20
|
+
</invoke>
|
|
21
|
+
</function_calls>
|
|
22
|
+
|
|
23
|
+
String and scalar parameters should be specified as-is, while lists and objects should use JSON format.
|
|
24
|
+
|
|
25
|
+
## THOUGHT RULES
|
|
26
|
+
- Always explain your reasoning in natural language before the Action.
|
|
27
|
+
- Never include tool call details inside the Thought, only in the Action.
|
|
28
|
+
|
|
29
|
+
## RESPONSE EXAMPLE
|
|
30
|
+
<thought>
|
|
31
|
+
I need to look up the current weather before answering, so I will call the weather tool with the city name.
|
|
32
|
+
<thought>
|
|
33
|
+
|
|
34
|
+
<function_calls>
|
|
35
|
+
<invoke name="query_weather">
|
|
36
|
+
<parameter name="city_name">Beijing</parameter>
|
|
37
|
+
</invoke>
|
|
38
|
+
<function_calls>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# AVAILABLE SYSTEM TOOLS
|
|
42
|
+
|
|
43
|
+
## SYSTEM TOOL SCHEMAS:
|
|
44
|
+
{tool_schemas}
|
|
45
|
+
## CRITICAL GENERAL TOOL RULES
|
|
46
|
+
- Use the exact function names from schema above
|
|
47
|
+
- Include all required parameters as specified in the schema
|
|
48
|
+
- Format complex data (objects, arrays) as JSON strings within the parameter tags
|
|
49
|
+
- Boolean values should be "true" or "false" (lowercase)
|
|
50
|
+
- If 'ask' tool answer is not match, call 'complete' tool end task, never call 'ask' tool again
|
|
51
|
+
|
|
@@ -4,6 +4,7 @@ from typing_extensions import override
|
|
|
4
4
|
|
|
5
5
|
from xgae.engine.engine_base import XGAToolBox
|
|
6
6
|
from xgae.engine.task_engine import XGATaskEngine
|
|
7
|
+
from xgae.engine.prompt_builder import XGAPromptBuilder
|
|
7
8
|
from xgae.utils.llm_client import LLMConfig
|
|
8
9
|
|
|
9
10
|
|
|
@@ -15,6 +16,7 @@ class ARETaskEngine(XGATaskEngine):
|
|
|
15
16
|
max_auto_run: int,
|
|
16
17
|
llm_config: Optional[LLMConfig] = None,
|
|
17
18
|
tool_box: Optional[XGAToolBox] = None,
|
|
19
|
+
prompt_builder: Optional[XGAPromptBuilder] = None,
|
|
18
20
|
pre_run_task_fn : Callable[[Any, int, List[Dict[str, Any]]], Any] = None,
|
|
19
21
|
post_run_task_fn : Callable[[Any, int, Dict[str, Any]], Any] = None,
|
|
20
22
|
terminate_task_fn : Callable[[Any, int], bool] = None,
|
|
@@ -26,6 +28,7 @@ class ARETaskEngine(XGATaskEngine):
|
|
|
26
28
|
max_auto_run = max_auto_run,
|
|
27
29
|
llm_config = llm_config,
|
|
28
30
|
tool_box = tool_box,
|
|
31
|
+
prompt_builder = prompt_builder,
|
|
29
32
|
)
|
|
30
33
|
self.agent = agent
|
|
31
34
|
self.pre_run_task_fn = pre_run_task_fn
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
## GAIA2 ARE Support
|
|
2
|
-
### How to add XGAE to ARE Project
|
|
3
|
-
- add xgae==0.3.2 to ARE requirements.txt
|
|
4
|
-
- uv pip install -r requirements.txt
|
|
5
|
-
- modify ARE 'AgentBuilder' and 'AgentConfigBuilder' class, add "xga" type agent
|
|
6
|
-
```
|
|
7
|
-
File: agent_builder.py
|
|
8
|
-
class AgentBuilder:
|
|
9
|
-
def list_agents(self) -> list[str]:
|
|
10
|
-
return ["default", "xga"]
|
|
11
|
-
|
|
12
|
-
def build():
|
|
13
|
-
...
|
|
14
|
-
agent_name = agent_config.get_agent_name()
|
|
15
|
-
if agent_name in ["default", "xga"]:
|
|
16
|
-
# add xga agent code
|
|
17
|
-
|
|
18
|
-
File: agent_config_builder.py
|
|
19
|
-
class AgentConfigBuilder:
|
|
20
|
-
def build():
|
|
21
|
-
if agent_name in["default", "xga"]:
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
- modify ARE .env, add XGAE .env config, refer to env.example
|
|
25
|
-
- copy XGAE package 'mcpservers' and 'templates' directory to ARE project root
|
|
26
|
-
|
|
27
|
-
### Run XGA Agent in ARE
|
|
28
|
-
```
|
|
29
|
-
uv run are-run -e -s scenario_find_image_file -a xga --model openai/qwen3-235b-a22b --provider llama-api --output_dir ./output
|
|
30
|
-
```
|
{xgae-0.3.2 → xgae-0.3.4}/.env
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|