xgae 0.3.1__tar.gz → 0.3.3__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.1 → xgae-0.3.3}/CHANGELOG.md +14 -2
- xgae-0.3.3/PKG-INFO +36 -0
- xgae-0.3.3/README.md +20 -0
- xgae-0.3.3/examples/agent/are/example_mcp_apps.json +11 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/README.md +44 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/env.example +43 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/mcp_tool_executor.py +133 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/xga_agent.py +290 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/xga_agent_factory.py +26 -0
- xgae-0.3.3/examples/agent/are/simulation/agents/xga/xga_tool_box.py +110 -0
- xgae-0.3.3/examples/agent/are/simulation/scenarios/scenario_bomc_fault/scenario.py +184 -0
- {xgae-0.3.1 → xgae-0.3.3}/pyproject.toml +5 -5
- {xgae-0.3.1 → xgae-0.3.3}/uv.lock +5 -5
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/mcp_tool_box.py +8 -6
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/prompt_builder.py +3 -2
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/llm_client.py +3 -0
- xgae-0.3.1/PKG-INFO +0 -14
- xgae-0.3.1/README.md +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/.env +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/.python-version +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/agent/langgraph/reflection/agent_base.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/agent/langgraph/reflection/custom_prompt_rag.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/agent/langgraph/reflection/reflection_agent.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/agent/langgraph/reflection/result_eval_agent.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/agent/langgraph/reflection/run_agent_app.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/engine/run_custom_and_agent_tools.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/engine/run_general_tools.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/engine/run_human_in_loop.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/engine/run_simple.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/tools/custom_fault_tools_app.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/examples/tools/simu_a2a_tools_app.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/mcpservers/custom_servers.json +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/mcpservers/xga_server.json +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/mcpservers/xga_server_sse.json +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/agent_tool_prompt_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/custom_tool_prompt_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/example/fault_user_prompt.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/example/result_eval_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/gemini_system_prompt_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/general_tool_prompt_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/system_prompt_response_sample.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/templates/system_prompt_template.txt +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/test/test_chroma.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/test/test_langfuse.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/test/test_litellm_langfuse.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/__init__.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/engine_base.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/responser/non_stream_responser.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/responser/responser_base.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/responser/stream_responser.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/task_engine.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine/task_langfuse.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/engine_cli_app.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/gaia2/are_engine.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/tools/without_general_tools_app.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/__init__.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/json_helpers.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/misc.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/setup_env.py +0 -0
- {xgae-0.3.1 → xgae-0.3.3}/xgae/utils/xml_tool_parser.py +0 -0
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
## [0.3.
|
|
1
|
+
## [0.3.3] - 2025-10-30
|
|
2
2
|
### Added
|
|
3
|
-
-
|
|
3
|
+
- GAIA2 ARE Scenario: scenario_bomc_fault
|
|
4
|
+
- GAIA2 ARE MCP: Support Custom MCP Apps, example_mcp_apps.json
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
## [0.3.2] - 2025-10-24
|
|
8
|
+
### Added
|
|
9
|
+
- GAIA2 ARE Example: XGAAreAgent, XGAAreToolBox
|
|
10
|
+
### Modified
|
|
11
|
+
- ARETaskEngine
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.3.0] - 2025-10-22
|
|
15
|
+
### Added
|
|
16
|
+
- Support GAIA2 ARE: ARETaskEngine
|
|
17
|
+
|
|
6
18
|
|
|
7
19
|
## [0.2.4] - 2025-9-23
|
|
8
20
|
### Modified
|
xgae-0.3.3/PKG-INFO
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xgae
|
|
3
|
+
Version: 0.3.3
|
|
4
|
+
Summary: Extreme General Agent Engine
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: colorlog>=6.9.0
|
|
7
|
+
Requires-Dist: langchain-mcp-adapters>=0.1.9
|
|
8
|
+
Requires-Dist: langfuse==2.60.9
|
|
9
|
+
Requires-Dist: litellm>=1.71.1
|
|
10
|
+
Requires-Dist: mcp>=1.11.0
|
|
11
|
+
Provides-Extra: examples
|
|
12
|
+
Requires-Dist: chromadb==1.1.0; extra == 'examples'
|
|
13
|
+
Requires-Dist: langchain-community==0.3.29; extra == 'examples'
|
|
14
|
+
Requires-Dist: langgraph==0.6.5; extra == 'examples'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
## XGAE: Extreme General Agent Engine
|
|
18
|
+
### Functional Features
|
|
19
|
+
- Support Custom prompt and external MCP Tools
|
|
20
|
+
- Support Langfuse
|
|
21
|
+
- Support Langgraph
|
|
22
|
+
- Support GAIA2 ARE agent and Custom MCP Apps
|
|
23
|
+
- Support Human-in-Loop in agent
|
|
24
|
+
- Can Use A2A protocol call A2A Agent as tool by 'xgaproxy' project
|
|
25
|
+
- Can Use E2B or Daytona Sandbox of 'xgatools' project
|
|
26
|
+
|
|
27
|
+
### Non-Functional Features
|
|
28
|
+
- Faster than SUNA Engine's speed
|
|
29
|
+
- Architecture is lighter than SUNA Engine
|
|
30
|
+
- Separate tools from Agent Engine
|
|
31
|
+
|
|
32
|
+
### Examples
|
|
33
|
+
- langgraph: Build React mode Langgraph Agent by XGA Engine
|
|
34
|
+
- are: Build GAIA2 ARE Agent by XGA Engine
|
|
35
|
+
- engine: Use XGA Engine in various scenarios
|
|
36
|
+
- tools: Simulation tools for example and test
|
xgae-0.3.3/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## XGAE: Extreme General Agent Engine
|
|
2
|
+
### Functional Features
|
|
3
|
+
- Support Custom prompt and external MCP Tools
|
|
4
|
+
- Support Langfuse
|
|
5
|
+
- Support Langgraph
|
|
6
|
+
- Support GAIA2 ARE agent and Custom MCP Apps
|
|
7
|
+
- Support Human-in-Loop in agent
|
|
8
|
+
- Can Use A2A protocol call A2A Agent as tool by 'xgaproxy' project
|
|
9
|
+
- Can Use E2B or Daytona Sandbox of 'xgatools' project
|
|
10
|
+
|
|
11
|
+
### Non-Functional Features
|
|
12
|
+
- Faster than SUNA Engine's speed
|
|
13
|
+
- Architecture is lighter than SUNA Engine
|
|
14
|
+
- Separate tools from Agent Engine
|
|
15
|
+
|
|
16
|
+
### Examples
|
|
17
|
+
- langgraph: Build React mode Langgraph Agent by XGA Engine
|
|
18
|
+
- are: Build GAIA2 ARE Agent by XGA Engine
|
|
19
|
+
- engine: Use XGA Engine in various scenarios
|
|
20
|
+
- tools: Simulation tools for example and test
|
|
@@ -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
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Certificate paths may vary on different types of machines.
|
|
2
|
+
ARE_SIMULATION_SSL_CERT_PATH=
|
|
3
|
+
ARE_SIMULATION_SSL_KEY_PATH=
|
|
4
|
+
|
|
5
|
+
# ARE_SIMULATION UI server.
|
|
6
|
+
ARE_SIMULATION_SERVER_HOSTNAME=localhost
|
|
7
|
+
ARE_SIMULATION_SERVER_PORT=8080
|
|
8
|
+
|
|
9
|
+
# ARE_SIMULATION UI client.
|
|
10
|
+
ARE_SIMULATION_CLIENT_HOSTNAME=localhost
|
|
11
|
+
ARE_SIMULATION_CLIENT_PORT=8088
|
|
12
|
+
ARE_SIMULATION_CLIENT_BACKEND_URL=https://${ARE_SIMULATION_SERVER_HOSTNAME}:${ARE_SIMULATION_SERVER_PORT}
|
|
13
|
+
|
|
14
|
+
# Scenario discovery
|
|
15
|
+
# Format: "dir1,dir2,dir3,..."
|
|
16
|
+
# Example: "/path/to/custom/scenarios,/another/path"
|
|
17
|
+
ARE_SCENARIO_DIRECTORIES="/Users/sharkystar/DevProjects/are"
|
|
18
|
+
|
|
19
|
+
# Models.
|
|
20
|
+
ALL_MODELS=""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DEMO_FS_PATH="hf://datasets/meta-agents-research-environments/gaia2_filesystem/demo_filesystem"
|
|
24
|
+
|
|
25
|
+
MCP_APPS_JSON_PATH=/Users/sharkystar/DevProjects/are/mcpservers/example_mcp_apps.json
|
|
26
|
+
|
|
27
|
+
### XGA ENV ###
|
|
28
|
+
# LOG
|
|
29
|
+
LOG_LEVEL=INFO
|
|
30
|
+
LOG_FILE=log/xgae.log
|
|
31
|
+
LOG_ENABLE=True
|
|
32
|
+
|
|
33
|
+
LLM_MAX_TOKENS=16384
|
|
34
|
+
LLM_TEMPERATURE=0
|
|
35
|
+
LLM_MAX_RETRIES=1
|
|
36
|
+
LLM_STREAM=True
|
|
37
|
+
LLM_ENABLE_THINKING=False
|
|
38
|
+
|
|
39
|
+
LLM_LANGFUSE_ENABLE=False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# TASK_ENGINE
|
|
43
|
+
USE_ASSISTANT_CHUNK_MSG=False
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
'success': not mcp_result.isError,
|
|
84
|
+
}
|
|
85
|
+
if mcp_result.isError:
|
|
86
|
+
result['output'] = mcp_result.content[0].text
|
|
87
|
+
else:
|
|
88
|
+
result['output'] = 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):
|
|
97
|
+
try:
|
|
98
|
+
_result:dict = json.loads(org_result)
|
|
99
|
+
output = _result.get('output', None)
|
|
100
|
+
success = _result.get('success', None)
|
|
101
|
+
|
|
102
|
+
if success and output:
|
|
103
|
+
result = XGAToolResult(success=bool(success), output=str(output))
|
|
104
|
+
except:
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
async def sample_task(name, duration):
|
|
112
|
+
print(f"Task '{name}' Begin,time = {duration} ")
|
|
113
|
+
await asyncio.sleep(duration)
|
|
114
|
+
return f"Task '{name}' Finished"
|
|
115
|
+
|
|
116
|
+
async def main():
|
|
117
|
+
try:
|
|
118
|
+
# result = AsyncToolExecutor.run_and_wait(sample_task("mytool", 1))
|
|
119
|
+
# print(f"Result1: {result}")
|
|
120
|
+
|
|
121
|
+
result = call_mcp_tool("http://localhost:17070/sse", "get_alarm", {"ip":"13.0.0.13"})
|
|
122
|
+
result = convert_mcp_tool_result(result)
|
|
123
|
+
print(f"Result2: {result}")
|
|
124
|
+
|
|
125
|
+
result = call_mcp_tool("http://localhost:17070/sse", "get_alarm1", {"ip":"13.0.0.13"})
|
|
126
|
+
result = convert_mcp_tool_result(result)
|
|
127
|
+
print(f"Result3: {result}")
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
print(f"Thread Fail: {e}")
|
|
131
|
+
|
|
132
|
+
with asyncio.Runner() as runner:
|
|
133
|
+
runner.run(main())
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from typing_extensions import override
|
|
5
|
+
|
|
6
|
+
from are.simulation.agents.are_simulation_agent import AgentStoppedException
|
|
7
|
+
from are.simulation.agents.llm.types import MMObservation
|
|
8
|
+
from are.simulation.agents.multimodal import Attachment, attachments_to_pil
|
|
9
|
+
from are.simulation.agents.agent_log import (
|
|
10
|
+
LLMInputLog,
|
|
11
|
+
LLMOutputThoughtActionLog,
|
|
12
|
+
StepLog,
|
|
13
|
+
StopLog,
|
|
14
|
+
SystemPromptLog,
|
|
15
|
+
ThoughtLog,
|
|
16
|
+
ToolCallLog,
|
|
17
|
+
FinalAnswerLog
|
|
18
|
+
)
|
|
19
|
+
from are.simulation.agents.default_agent.base_agent import (
|
|
20
|
+
BaseAgent,
|
|
21
|
+
RunningState,
|
|
22
|
+
get_offset_from_time_config_mode
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from xgae.utils.llm_client import LLMConfig
|
|
26
|
+
from xgae.gaia2.are_engine import ARETaskEngine
|
|
27
|
+
from are.simulation.agents.xga.xga_tool_box import XGAAreToolBox
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def pre_run_task_check(agent, iterations: int, llm_messages: List[Dict[str, Any]]):
|
|
31
|
+
try:
|
|
32
|
+
agent.logger.info(f"\n\n------ Starting Run Task Iteration {iterations}... ------")
|
|
33
|
+
|
|
34
|
+
if iterations == 0:
|
|
35
|
+
agent.system_prompt = agent.task_engine.task_prompt
|
|
36
|
+
agent.logger.info(f"------ SYSTEM_PROMPT ------ \n{agent.system_prompt}\n")
|
|
37
|
+
agent.append_agent_log(
|
|
38
|
+
SystemPromptLog(
|
|
39
|
+
content = agent.system_prompt,
|
|
40
|
+
timestamp = agent.make_timestamp(),
|
|
41
|
+
agent_id = agent.agent_id,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
agent.append_agent_log(
|
|
46
|
+
StepLog(
|
|
47
|
+
iteration = agent.iterations,
|
|
48
|
+
timestamp = agent.make_timestamp(),
|
|
49
|
+
agent_id = agent.agent_id,
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if agent.stop_event.is_set():
|
|
54
|
+
agent.logger.info(f"pre_run_task_check[{iterations}]: Recv Stop Event before condition, raise AgentStoppedException")
|
|
55
|
+
raise AgentStoppedException("Agent stopped.")
|
|
56
|
+
|
|
57
|
+
# Execute a pre_step() function if it exists
|
|
58
|
+
for conditional_step in agent.conditional_pre_steps:
|
|
59
|
+
if conditional_step.condition is None or conditional_step.condition(agent):
|
|
60
|
+
conditional_step.function(agent)
|
|
61
|
+
|
|
62
|
+
if agent.stop_event.is_set():
|
|
63
|
+
agent.logger.info(f"pre_run_task_check[{iterations}]: Recv Stop Event after condition, raise AgentStoppedException")
|
|
64
|
+
raise AgentStoppedException("Agent stopped.")
|
|
65
|
+
|
|
66
|
+
# Begin step()
|
|
67
|
+
agent.append_agent_log(
|
|
68
|
+
LLMInputLog(
|
|
69
|
+
content = llm_messages,
|
|
70
|
+
timestamp = agent.make_timestamp(),
|
|
71
|
+
agent_id = agent.agent_id
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if agent.simulated_generation_time_config is not None:
|
|
76
|
+
if agent.pause_env is None:
|
|
77
|
+
raise ValueError("pause_env is not set")
|
|
78
|
+
agent.pause_env()
|
|
79
|
+
except Exception as e:
|
|
80
|
+
agent.log_error(e)
|
|
81
|
+
agent.logger.info(f"pre_run_task_check[{iterations}]: Exception Occur, Stop task")
|
|
82
|
+
agent.task_engine.stop_task()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def post_run_task_check(agent, iterations: int, llm_response: Dict[str, Any]):
|
|
86
|
+
try:
|
|
87
|
+
agent.logger.info(f"------ LLM Response Iteration [{iterations}] ------ \n{llm_response}\n")
|
|
88
|
+
|
|
89
|
+
llm_output = str(llm_response)
|
|
90
|
+
|
|
91
|
+
# Resume the environment after the generation of a thought/action if needed
|
|
92
|
+
if agent.simulated_generation_time_config is not None:
|
|
93
|
+
if agent.resume_env is None:
|
|
94
|
+
raise ValueError("resume_env is not set")
|
|
95
|
+
|
|
96
|
+
offset = get_offset_from_time_config_mode(
|
|
97
|
+
time_config = agent.simulated_generation_time_config,
|
|
98
|
+
completion_duration = 0,
|
|
99
|
+
)
|
|
100
|
+
agent.logger.info(f"post_run_task_check[{iterations}]: Resuming environment with {offset} offset")
|
|
101
|
+
agent.resume_env(offset)
|
|
102
|
+
|
|
103
|
+
metadata = {}
|
|
104
|
+
agent.append_agent_log(
|
|
105
|
+
LLMOutputThoughtActionLog(
|
|
106
|
+
content = llm_output,
|
|
107
|
+
timestamp = agent.make_timestamp(),
|
|
108
|
+
agent_id = agent.agent_id,
|
|
109
|
+
prompt_tokens = metadata.get("prompt_tokens", 0),
|
|
110
|
+
completion_tokens = metadata.get("completion_tokens", 0),
|
|
111
|
+
total_tokens = metadata.get("total_tokens", 0),
|
|
112
|
+
reasoning_tokens = metadata.get("reasoning_tokens", 0),
|
|
113
|
+
completion_duration = metadata.get("completion_duration", 0),
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
# end step()
|
|
117
|
+
|
|
118
|
+
if agent.stop_event.is_set():
|
|
119
|
+
agent.logger.info(f"post_run_task_check[{iterations}]: Recv Stop Event, raise AgentStoppedException")
|
|
120
|
+
raise AgentStoppedException("Agent stopped.")
|
|
121
|
+
|
|
122
|
+
# Execute a post_step() function if it exists (polling the Meta Agents Research Environments notifications for example)
|
|
123
|
+
for conditional_step in agent.conditional_post_steps:
|
|
124
|
+
if conditional_step.condition is None or conditional_step.condition(agent):
|
|
125
|
+
conditional_step.function(agent)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
agent.log_error(e)
|
|
128
|
+
agent.logger.info(f"post_run_task_check[{iterations}]: Exception Occur, Stop task")
|
|
129
|
+
agent.task_engine.stop_task()
|
|
130
|
+
finally:
|
|
131
|
+
if agent.simulated_generation_time_config and agent.resume_env:
|
|
132
|
+
agent.resume_env(0.0) # Resume without advancing time
|
|
133
|
+
|
|
134
|
+
agent.iterations += 1
|
|
135
|
+
agent.planning_counter += 1
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def terminate_task_check(agent, iterations: int) -> bool:
|
|
139
|
+
is_terminate_task = False
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
if agent.termination_step.condition:
|
|
143
|
+
is_terminate_task = agent.termination_step.condition(agent)
|
|
144
|
+
if is_terminate_task:
|
|
145
|
+
agent.logger.info(f"terminate_task_check[{iterations}]: termination_step.condition is True")
|
|
146
|
+
except Exception as e:
|
|
147
|
+
agent.log_error(e)
|
|
148
|
+
|
|
149
|
+
return is_terminate_task
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class XGAAreAgent(BaseAgent):
|
|
153
|
+
def __init__(self, **kwargs):
|
|
154
|
+
super().__init__(**kwargs)
|
|
155
|
+
self.task_engine: ARETaskEngine = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@override
|
|
159
|
+
def initialize(self, attachments: list[Attachment] | None = None, **kwargs) -> None:
|
|
160
|
+
self.logs = []
|
|
161
|
+
self.iterations = 0
|
|
162
|
+
self.planning_counter = 0
|
|
163
|
+
|
|
164
|
+
tool_box = XGAAreToolBox(self.tools)
|
|
165
|
+
|
|
166
|
+
_system_prompt = "\n\n".join(prompt for prompt in self.init_system_prompts.values())
|
|
167
|
+
pattern = r'<general_instructions>(.*?)</general_instructions>'
|
|
168
|
+
prompt_are_general = re.search(pattern, _system_prompt, re.DOTALL)
|
|
169
|
+
if prompt_are_general:
|
|
170
|
+
prompt_are_general = prompt_are_general.group(1).strip()
|
|
171
|
+
else:
|
|
172
|
+
prompt_are_general = _system_prompt
|
|
173
|
+
|
|
174
|
+
model_config = self.llm_engine.model_config
|
|
175
|
+
llm_config = LLMConfig(
|
|
176
|
+
model = model_config.model_name,
|
|
177
|
+
api_key = model_config.api_key,
|
|
178
|
+
api_base = model_config.endpoint
|
|
179
|
+
)
|
|
180
|
+
self.task_engine = ARETaskEngine(
|
|
181
|
+
agent = self,
|
|
182
|
+
agent_id = self.agent_id,
|
|
183
|
+
system_prompt = prompt_are_general,
|
|
184
|
+
max_auto_run = self.max_iterations,
|
|
185
|
+
llm_config = llm_config,
|
|
186
|
+
tool_box = tool_box,
|
|
187
|
+
pre_run_task_fn = pre_run_task_check,
|
|
188
|
+
post_run_task_fn = post_run_task_check,
|
|
189
|
+
terminate_task_fn = terminate_task_check,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Reload the agent state if logs are provided
|
|
193
|
+
start_logs = kwargs.pop("start_logs", [])
|
|
194
|
+
if start_logs:
|
|
195
|
+
self.replay(start_logs)
|
|
196
|
+
|
|
197
|
+
# Include additional image PILs directly into state stack.
|
|
198
|
+
if attachments:
|
|
199
|
+
images = attachments_to_pil(attachments)
|
|
200
|
+
self.action_executor.inject_state({f"image_{i}": image for i, image in enumerate(images)})
|
|
201
|
+
self.logger.debug(f"XGAAreAgent initialize: Injecting images into states for {len(images)} images")
|
|
202
|
+
self.logger.debug(f"XGAAreAgent initialize: New Keys {','.join(self.action_executor.state.keys())}")
|
|
203
|
+
|
|
204
|
+
self.initialized = True
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@override
|
|
208
|
+
def execute_agent_loop(self) -> str | None | MMObservation:
|
|
209
|
+
with asyncio.Runner() as runner:
|
|
210
|
+
runner.run(self.async_execute_agent_loop())
|
|
211
|
+
|
|
212
|
+
# We have reached a termination condition, execute the termination method
|
|
213
|
+
if self.termination_step.function is not None and not self.stop_event.is_set():
|
|
214
|
+
return self.termination_step.function(self)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async def async_execute_agent_loop(self) -> str | None | MMObservation:
|
|
218
|
+
chunks = []
|
|
219
|
+
async for chunk in self.task_engine.run_task(task_input={"role": "user", "content": self.task}):
|
|
220
|
+
chunks.append(chunk)
|
|
221
|
+
chunk_type = chunk['type']
|
|
222
|
+
if chunk_type== "status":
|
|
223
|
+
status_content = chunk['content']
|
|
224
|
+
status_type = status_content['status_type']
|
|
225
|
+
if status_type == "error":
|
|
226
|
+
error_msg = chunk.get('message')
|
|
227
|
+
self.logger.warning(f"XGAAreAgent execute_agent_loop: Fatal error - {error_msg}")
|
|
228
|
+
self.log_error(error_msg)
|
|
229
|
+
elif status_type == "stop":
|
|
230
|
+
error_msg = chunk.get('message')
|
|
231
|
+
self.logger.warning("XGAAreAgent execute_agent_loop: Agent stopped.")
|
|
232
|
+
self.append_agent_log(
|
|
233
|
+
StopLog(
|
|
234
|
+
content = f"Agent stopped - {error_msg}",
|
|
235
|
+
timestamp = self.make_timestamp(),
|
|
236
|
+
agent_id = self.agent_id,
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
elif status_type == "tool_started":
|
|
240
|
+
function_name = status_content['function_name']
|
|
241
|
+
self.append_agent_log(
|
|
242
|
+
ThoughtLog(
|
|
243
|
+
content = f"To complete task, should call '{function_name}' tool",
|
|
244
|
+
timestamp = self.make_timestamp(),
|
|
245
|
+
agent_id = self.agent_id,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
elif chunk_type == "tool":
|
|
249
|
+
tool_content = chunk['content']
|
|
250
|
+
tool_execution = tool_content.get('tool_execution')
|
|
251
|
+
self.append_agent_log(
|
|
252
|
+
ToolCallLog(
|
|
253
|
+
tool_name = tool_execution.get('function_name'),
|
|
254
|
+
tool_arguments = tool_execution.get('arguments'),
|
|
255
|
+
timestamp = self.make_timestamp(),
|
|
256
|
+
agent_id = self.agent_id,
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
#print(chunk)
|
|
260
|
+
|
|
261
|
+
final_result = self.task_engine.parse_final_result(chunks)
|
|
262
|
+
print(f"\n\nFINAL_RESULT: {final_result}")
|
|
263
|
+
|
|
264
|
+
# Send Final Result to user
|
|
265
|
+
args = {
|
|
266
|
+
'content': final_result['content']
|
|
267
|
+
}
|
|
268
|
+
self.tools['AgentUserInterface__send_message_to_user'](**args)
|
|
269
|
+
self.append_agent_log(
|
|
270
|
+
ToolCallLog(
|
|
271
|
+
tool_name = 'AgentUserInterface__send_message_to_user',
|
|
272
|
+
tool_arguments = args,
|
|
273
|
+
timestamp = self.make_timestamp(),
|
|
274
|
+
agent_id = self.agent_id,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Return Final Result
|
|
279
|
+
if final_result['type'] == "error":
|
|
280
|
+
self.custom_state["running_state"] = RunningState.FAILED
|
|
281
|
+
else:
|
|
282
|
+
self.custom_state["running_state"] = RunningState.TERMINATED
|
|
283
|
+
|
|
284
|
+
self.append_agent_log(
|
|
285
|
+
FinalAnswerLog(
|
|
286
|
+
content = final_result['content'],
|
|
287
|
+
timestamp = self.make_timestamp(),
|
|
288
|
+
agent_id = self.agent_id
|
|
289
|
+
)
|
|
290
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from are.simulation.agents.are_simulation_agent_config import ARESimulationReactBaseAgentConfig
|
|
2
|
+
from are.simulation.agents.default_agent.steps.are_simulation import get_are_simulation_update_pre_step
|
|
3
|
+
from are.simulation.agents.default_agent.termination_methods.are_simulation import get_gaia2_termination_step
|
|
4
|
+
from are.simulation.agents.default_agent.tools.json_action_executor import JsonActionExecutor
|
|
5
|
+
from are.simulation.agents.llm.llm_engine import LLMEngine
|
|
6
|
+
|
|
7
|
+
from agent.are.simulation.agents.xga.xga_agent import XGAAreAgent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def xga_simulation_react_xml_agent(
|
|
11
|
+
llm_engine: LLMEngine, base_agent_config: ARESimulationReactBaseAgentConfig
|
|
12
|
+
):
|
|
13
|
+
return XGAAreAgent(
|
|
14
|
+
llm_engine=llm_engine,
|
|
15
|
+
tools={},
|
|
16
|
+
system_prompts={
|
|
17
|
+
"system_prompt": str(base_agent_config.system_prompt),
|
|
18
|
+
},
|
|
19
|
+
termination_step=get_gaia2_termination_step(),
|
|
20
|
+
max_iterations=base_agent_config.max_iterations,
|
|
21
|
+
action_executor=JsonActionExecutor( # Just for compatible BaseAgent, useless
|
|
22
|
+
use_custom_logger=base_agent_config.use_custom_logger
|
|
23
|
+
),
|
|
24
|
+
conditional_pre_steps=[get_are_simulation_update_pre_step()],
|
|
25
|
+
use_custom_logger=base_agent_config.use_custom_logger,
|
|
26
|
+
)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from typing import List, Any, Dict, Optional
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
8
|
+
|
|
9
|
+
from xgae.engine.engine_base import XGAError, XGAToolSchema, XGAToolResult, XGAToolType
|
|
10
|
+
from xgae.engine.mcp_tool_box import XGAMcpToolBox
|
|
11
|
+
|
|
12
|
+
from are.simulation.tools import Tool
|
|
13
|
+
from are.simulation.agents.xga.mcp_tool_executor import convert_mcp_tool_result
|
|
14
|
+
|
|
15
|
+
class XGAAreToolBox(XGAMcpToolBox):
|
|
16
|
+
def __init__(self, are_tools: Dict[str, Tool]):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.are_tools = are_tools
|
|
19
|
+
self._is_loaded_are_tool_schemas = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
async def init_tool_schemas(self):
|
|
24
|
+
await self._load_mcp_tools_schema()
|
|
25
|
+
self._load_are_tools_schema()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@override
|
|
29
|
+
async def call_tool(self, task_id: str, tool_name: str, args: Optional[Dict[str, Any]] = None) -> XGAToolResult:
|
|
30
|
+
if tool_name == "end_task":
|
|
31
|
+
server_name = self.GENERAL_MCP_SERVER_NAME
|
|
32
|
+
else:
|
|
33
|
+
task_tool_schemas = self.task_tool_schemas.get(task_id, {})
|
|
34
|
+
tool_schema = task_tool_schemas.get(tool_name, None)
|
|
35
|
+
if tool_schema is None:
|
|
36
|
+
raise XGAError(f"MCP tool not found: '{tool_name}'")
|
|
37
|
+
server_name = tool_schema.server_name
|
|
38
|
+
|
|
39
|
+
tool_type = self._get_tool_type(server_name)
|
|
40
|
+
if tool_type == "custom": # ARE Tools
|
|
41
|
+
full_tool_name = server_name + "__" + tool_name
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
tool_result = self.are_tools[full_tool_name](**args)
|
|
45
|
+
result = convert_mcp_tool_result(str(tool_result))
|
|
46
|
+
except Exception as e:
|
|
47
|
+
error = f"Call ARE Tool '{tool_name}' error: {str(e)}"
|
|
48
|
+
logging.error(f"AreToolBox call_are_tool: {error}")
|
|
49
|
+
result = XGAToolResult(success=False, output=error)
|
|
50
|
+
else:
|
|
51
|
+
async with self._mcp_client.session(server_name) as session:
|
|
52
|
+
tools = await load_mcp_tools(session)
|
|
53
|
+
mcp_tool = next((t for t in tools if t.name == tool_name), None)
|
|
54
|
+
|
|
55
|
+
if mcp_tool:
|
|
56
|
+
tool_args = args or {}
|
|
57
|
+
|
|
58
|
+
if tool_type == "general" or tool_type == "agent":
|
|
59
|
+
tool_args = dict({'task_id': task_id}, **tool_args)
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
tool_result = await mcp_tool.arun(tool_args)
|
|
63
|
+
if tool_type == "general":
|
|
64
|
+
tool_result = json.loads(tool_result)
|
|
65
|
+
result = XGAToolResult(success=tool_result['success'], output=str(tool_result['output']))
|
|
66
|
+
else:
|
|
67
|
+
result = XGAToolResult(success=True, output=str(tool_result))
|
|
68
|
+
except Exception as e:
|
|
69
|
+
error = f"Call MCP Tool '{tool_name}' error: {str(e)}"
|
|
70
|
+
logging.error(f"AreToolBox call_mcp_tool: {error}")
|
|
71
|
+
result = XGAToolResult(success=False, output=error)
|
|
72
|
+
else:
|
|
73
|
+
error = f"No MCP tool found with name: {tool_name}"
|
|
74
|
+
logging.info(f"AreToolBox call_mcp_tool: error={error}")
|
|
75
|
+
result = XGAToolResult(success=False, output=error)
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def reload_mcp_tools_schema(self) -> None:
|
|
81
|
+
self._is_loaded_mcp_tool_schemas = False
|
|
82
|
+
self._is_loaded_are_tool_schemas = False
|
|
83
|
+
await self.init_tool_schemas()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _load_are_tools_schema(self) -> None:
|
|
87
|
+
if not self._is_loaded_are_tool_schemas:
|
|
88
|
+
for are_tool in self.are_tools.values():
|
|
89
|
+
full_tool_name = are_tool.name
|
|
90
|
+
server_name , tool_name = full_tool_name.split("__")
|
|
91
|
+
tool_type :XGAToolType = "custom"
|
|
92
|
+
input_schema = {
|
|
93
|
+
'properties': are_tool.inputs,
|
|
94
|
+
'required': []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
tool_schema = XGAToolSchema(tool_name = tool_name,
|
|
98
|
+
tool_type = tool_type,
|
|
99
|
+
server_name = server_name,
|
|
100
|
+
description = are_tool.description,
|
|
101
|
+
input_schema = input_schema,
|
|
102
|
+
metadata = {}
|
|
103
|
+
)
|
|
104
|
+
if server_name not in self.mcp_tool_schemas:
|
|
105
|
+
self.mcp_tool_schemas[server_name] = []
|
|
106
|
+
self.mcp_tool_schemas[server_name].append(tool_schema)
|
|
107
|
+
if server_name not in self.mcp_server_names:
|
|
108
|
+
self.mcp_server_names.append(server_name)
|
|
109
|
+
|
|
110
|
+
self._is_loaded_are_tool_schemas = True
|
|
@@ -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())
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "xgae"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "Extreme General Agent Engine"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"colorlog
|
|
9
|
-
"litellm
|
|
10
|
-
"mcp
|
|
8
|
+
"colorlog>=6.9.0",
|
|
9
|
+
"litellm>=1.71.1",
|
|
10
|
+
"mcp>=1.11.0",
|
|
11
11
|
"langfuse==2.60.9",
|
|
12
|
-
"langchain-mcp-adapters
|
|
12
|
+
"langchain-mcp-adapters>=0.1.9",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
|
|
@@ -3008,7 +3008,7 @@ wheels = [
|
|
|
3008
3008
|
|
|
3009
3009
|
[[package]]
|
|
3010
3010
|
name = "xgae"
|
|
3011
|
-
version = "0.3.
|
|
3011
|
+
version = "0.3.3"
|
|
3012
3012
|
source = { editable = "." }
|
|
3013
3013
|
dependencies = [
|
|
3014
3014
|
{ name = "colorlog" },
|
|
@@ -3028,13 +3028,13 @@ examples = [
|
|
|
3028
3028
|
[package.metadata]
|
|
3029
3029
|
requires-dist = [
|
|
3030
3030
|
{ name = "chromadb", marker = "extra == 'examples'", specifier = "==1.1.0" },
|
|
3031
|
-
{ name = "colorlog", specifier = "
|
|
3031
|
+
{ name = "colorlog", specifier = ">=6.9.0" },
|
|
3032
3032
|
{ name = "langchain-community", marker = "extra == 'examples'", specifier = "==0.3.29" },
|
|
3033
|
-
{ name = "langchain-mcp-adapters", specifier = "
|
|
3033
|
+
{ name = "langchain-mcp-adapters", specifier = ">=0.1.9" },
|
|
3034
3034
|
{ name = "langfuse", specifier = "==2.60.9" },
|
|
3035
3035
|
{ name = "langgraph", marker = "extra == 'examples'", specifier = "==0.6.5" },
|
|
3036
|
-
{ name = "litellm", specifier = "
|
|
3037
|
-
{ name = "mcp", specifier = "
|
|
3036
|
+
{ name = "litellm", specifier = ">=1.71.1" },
|
|
3037
|
+
{ name = "mcp", specifier = ">=1.11.0" },
|
|
3038
3038
|
]
|
|
3039
3039
|
provides-extras = ["examples"]
|
|
3040
3040
|
|
|
@@ -167,12 +167,14 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
167
167
|
param_properties.pop('title', None)
|
|
168
168
|
|
|
169
169
|
metadata = tool.metadata or {}
|
|
170
|
-
tool_schema = XGAToolSchema(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
tool_schema = XGAToolSchema(
|
|
171
|
+
tool_name = tool.name,
|
|
172
|
+
tool_type = tool_type,
|
|
173
|
+
server_name = server_name,
|
|
174
|
+
description = tool.description,
|
|
175
|
+
input_schema = input_schema,
|
|
176
|
+
metadata = metadata
|
|
177
|
+
)
|
|
176
178
|
self.mcp_tool_schemas[server_name].append(tool_schema)
|
|
177
179
|
|
|
178
180
|
self._is_loaded_mcp_tool_schemas = True
|
|
@@ -83,9 +83,10 @@ class XGAPromptBuilder():
|
|
|
83
83
|
tool_info = ""
|
|
84
84
|
for tool_schema in tool_schemas:
|
|
85
85
|
description = tool_schema.description if tool_schema.description else 'No description available'
|
|
86
|
-
tool_info += f"-
|
|
86
|
+
tool_info += f"- {tool_schema.tool_name}: {description}\n"
|
|
87
87
|
parameters = tool_schema.input_schema.get('properties', {})
|
|
88
|
-
tool_info += f"
|
|
88
|
+
tool_info += f" Parameters: {parameters}\n"
|
|
89
|
+
tool_info += "\n"
|
|
89
90
|
tool_prompt = tool_prompt.replace("{tool_schemas}", tool_info)
|
|
90
91
|
|
|
91
92
|
return tool_prompt
|
|
@@ -51,6 +51,9 @@ class LLMClient:
|
|
|
51
51
|
self._init_langfuse()
|
|
52
52
|
|
|
53
53
|
llm_config = llm_config or LLMConfig()
|
|
54
|
+
if llm_config.get('model') and llm_config.get('model_name') is None:
|
|
55
|
+
llm_config['model_name'] = llm_config.get('model')
|
|
56
|
+
|
|
54
57
|
self.max_retries = int(os.getenv('LLM_MAX_RETRIES', 1))
|
|
55
58
|
|
|
56
59
|
env_llm_model = os.getenv('LLM_MODEL', "openai/qwen3-235b-a22b")
|
xgae-0.3.1/PKG-INFO
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: xgae
|
|
3
|
-
Version: 0.3.1
|
|
4
|
-
Summary: Extreme General Agent Engine
|
|
5
|
-
Requires-Python: >=3.11
|
|
6
|
-
Requires-Dist: colorlog==6.9.0
|
|
7
|
-
Requires-Dist: langchain-mcp-adapters==0.1.9
|
|
8
|
-
Requires-Dist: langfuse==2.60.9
|
|
9
|
-
Requires-Dist: litellm==1.71.1
|
|
10
|
-
Requires-Dist: mcp==1.11.0
|
|
11
|
-
Provides-Extra: examples
|
|
12
|
-
Requires-Dist: chromadb==1.1.0; extra == 'examples'
|
|
13
|
-
Requires-Dist: langchain-community==0.3.29; extra == 'examples'
|
|
14
|
-
Requires-Dist: langgraph==0.6.5; extra == 'examples'
|
xgae-0.3.1/README.md
DELETED
|
File without changes
|
{xgae-0.3.1 → xgae-0.3.3}/.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
|