xgae 0.1.1__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.1/.env ADDED
@@ -0,0 +1,21 @@
1
+ # LOG
2
+ LOG_LEVEL=INFO
3
+ LOG_FILE=log/xgae.log
4
+
5
+ # LANGFUSE
6
+ LANGFUSE_PUBLIC_KEY=
7
+ LANGFUSE_SECRET_KEY=
8
+ LANGFUSE_HOST=https://cloud.langfuse.com
9
+
10
+ # LLM
11
+ LLM_MODEL=openai/qwen3-235b-a22b
12
+ LLM_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1
13
+ LLM_API_KEY=
14
+ LLM_MAX_TOKENS=16384
15
+ LLM_TEMPERATURE=0.7
16
+ LLM_MAX_RETRIES=2
17
+
18
+
19
+
20
+
21
+
@@ -0,0 +1,8 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
@@ -0,0 +1,15 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
5
+ <option name="ourVersions">
6
+ <value>
7
+ <list size="2">
8
+ <item index="0" class="java.lang.String" itemvalue="3.8" />
9
+ <item index="1" class="java.lang.String" itemvalue="3.11" />
10
+ </list>
11
+ </value>
12
+ </option>
13
+ </inspection_tool>
14
+ </profile>
15
+ </component>
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.13 (xgae)" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at ~/DevelopSpace/xgae/.venv" project-jdk-type="Python SDK" />
7
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/xgae.iml" filepath="$PROJECT_DIR$/.idea/xgae.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings" defaultProject="true" />
4
+ </project>
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="SELECTIVE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="0f1352ac-e106-4041-9704-6775ec0c7096" name="Changes" comment="" />
8
+ <option name="SHOW_DIALOG" value="false" />
9
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
11
+ <option name="LAST_RESOLUTION" value="IGNORE" />
12
+ </component>
13
+ <component name="FileTemplateManagerImpl">
14
+ <option name="RECENT_TEMPLATES">
15
+ <list>
16
+ <option value="Python Script" />
17
+ </list>
18
+ </option>
19
+ </component>
20
+ <component name="ProjectColorInfo">{
21
+ &quot;associatedIndex&quot;: 7
22
+ }</component>
23
+ <component name="ProjectId" id="31InXSONOzq9VTdmbkoD0F0ho9q" />
24
+ <component name="ProjectViewState">
25
+ <option name="hideEmptyMiddlePackages" value="true" />
26
+ <option name="showLibraryContents" value="true" />
27
+ </component>
28
+ <component name="PropertiesComponent">{
29
+ &quot;keyToString&quot;: {
30
+ &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
31
+ &quot;Python.llm_client.executor&quot;: &quot;Run&quot;,
32
+ &quot;Python.utils.executor&quot;: &quot;Run&quot;,
33
+ &quot;Python.xga_mcp_tool_box.executor&quot;: &quot;Run&quot;,
34
+ &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
35
+ &quot;last_opened_file_path&quot;: &quot;/Users/goosezzy/DevelopSpace/xgae&quot;,
36
+ &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
37
+ &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
38
+ &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
39
+ &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
40
+ &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
41
+ &quot;settings.editor.selected.configurable&quot;: &quot;preferences.general&quot;,
42
+ &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
43
+ }
44
+ }</component>
45
+ <component name="RecentsManager">
46
+ <key name="MoveFile.RECENT_KEYS">
47
+ <recent name="$PROJECT_DIR$/src/xgae/engine/responser" />
48
+ </key>
49
+ </component>
50
+ <component name="RunManager">
51
+ <configuration default="true" type="PythonConfigurationType" factoryName="Python">
52
+ <module name="xgae" />
53
+ <option name="ENV_FILES" value="" />
54
+ <option name="INTERPRETER_OPTIONS" value="" />
55
+ <option name="PARENT_ENVS" value="true" />
56
+ <envs>
57
+ <env name="PYTHONUNBUFFERED" value="1" />
58
+ </envs>
59
+ <option name="SDK_HOME" value="" />
60
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
61
+ <option name="IS_MODULE_SDK" value="false" />
62
+ <option name="ADD_CONTENT_ROOTS" value="true" />
63
+ <option name="ADD_SOURCE_ROOTS" value="true" />
64
+ <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
65
+ <EXTENSION ID="net.ashald.envfile">
66
+ <option name="IS_ENABLED" value="false" />
67
+ <option name="IS_SUBST" value="false" />
68
+ <option name="IS_PATH_MACRO_SUPPORTED" value="false" />
69
+ <option name="IS_IGNORE_MISSING_FILES" value="false" />
70
+ <option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
71
+ <ENTRIES>
72
+ <ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
73
+ </ENTRIES>
74
+ </EXTENSION>
75
+ <option name="SCRIPT_NAME" value="" />
76
+ <option name="PARAMETERS" value="" />
77
+ <option name="SHOW_COMMAND_LINE" value="false" />
78
+ <option name="EMULATE_TERMINAL" value="false" />
79
+ <option name="MODULE_MODE" value="false" />
80
+ <option name="REDIRECT_INPUT" value="false" />
81
+ <option name="INPUT_FILE" value="" />
82
+ <method v="2" />
83
+ </configuration>
84
+ </component>
85
+ <component name="SharedIndexes">
86
+ <attachedChunks>
87
+ <set>
88
+ <option value="bundled-js-predefined-d6986cc7102b-09060db00ec0-JavaScript-PY-251.26927.90" />
89
+ <option value="bundled-python-sdk-41e8cd69c857-64d779b69b7a-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-251.26927.90" />
90
+ </set>
91
+ </attachedChunks>
92
+ </component>
93
+ <component name="TaskManager">
94
+ <task active="true" id="Default" summary="Default task">
95
+ <changelist id="0f1352ac-e106-4041-9704-6775ec0c7096" name="Changes" comment="" />
96
+ <created>1755220495138</created>
97
+ <option name="number" value="Default" />
98
+ <option name="presentableId" value="Default" />
99
+ <updated>1755220495138</updated>
100
+ <workItem from="1755220497295" duration="14755000" />
101
+ <workItem from="1755239458855" duration="4586000" />
102
+ <workItem from="1755244406907" duration="6284000" />
103
+ <workItem from="1755262332599" duration="306000" />
104
+ <workItem from="1755270285722" duration="12068000" />
105
+ <workItem from="1755283728206" duration="1511000" />
106
+ <workItem from="1755286552901" duration="3130000" />
107
+ <workItem from="1755307767865" duration="337000" />
108
+ <workItem from="1755308113420" duration="2825000" />
109
+ <workItem from="1755321931380" duration="8531000" />
110
+ <workItem from="1755330830188" duration="229000" />
111
+ <workItem from="1755396985976" duration="6435000" />
112
+ <workItem from="1755415614018" duration="461000" />
113
+ <workItem from="1755416476555" duration="256000" />
114
+ <workItem from="1755419142521" duration="150000" />
115
+ <workItem from="1755429851003" duration="290000" />
116
+ <workItem from="1755430331676" duration="21000" />
117
+ </task>
118
+ <servers />
119
+ </component>
120
+ <component name="TypeScriptGeneratedFilesManager">
121
+ <option name="version" value="3" />
122
+ </component>
123
+ <component name="com.intellij.coverage.CoverageDataManagerImpl">
124
+ <SUITE FILE_PATH="coverage/xgae$xga_mcp_tool_box.coverage" NAME="xga_mcp_tool_box Coverage Results" MODIFIED="1755430037796" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
125
+ <SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1755247632274" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
126
+ <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$" />
127
+ </component>
128
+ </project>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
7
+ </content>
8
+ <orderEntry type="jdk" jdkName="Python 3.13 virtualenv at ~/DevelopSpace/xgae/.venv" jdkType="Python SDK" />
9
+ <orderEntry type="sourceFolder" forTests="false" />
10
+ </component>
11
+ </module>
@@ -0,0 +1 @@
1
+ 3.13
xgae-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: xgae
3
+ Version: 0.1.1
4
+ Summary: Extreme General Agent Engine
5
+ Requires-Python: >=3.13
6
+ Requires-Dist: colorlog>=6.9.0
7
+ Requires-Dist: langchain-mcp-adapters>=0.1.4
8
+ Requires-Dist: langfuse>=2.60.5
9
+ Requires-Dist: langgraph>=0.3.21
10
+ Requires-Dist: litellm>=1.74.8
11
+ Requires-Dist: mcp>=1.12.1
xgae-0.1.1/README.md ADDED
File without changes
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers":{
3
+ "bomc_fault": {
4
+ "url": "http://localhost:17070/sse",
5
+ "transport": "sse"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers":{
3
+ "xga_general": {
4
+ "url": "http://localhost:16060/sse",
5
+ "transport": "sse"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "xgae"
3
+ version = "0.1.1"
4
+ description = "Extreme General Agent Engine"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "colorlog>=6.9.0",
9
+ "langchain-mcp-adapters>=0.1.4",
10
+ "langgraph>=0.3.21",
11
+ "litellm>=1.74.8",
12
+ "mcp>=1.12.1",
13
+ "langfuse>=2.60.5",
14
+ ]
15
+
16
+ [build-system]
17
+ requires = ["hatchling"]
18
+ build-backend = "hatchling.build"
19
+
20
+ [tool.hatch.build]
21
+ exclude = ["log/*"]
22
+
23
+ [project.scripts]
File without changes
@@ -0,0 +1,46 @@
1
+ from typing import Union, Optional, Dict, List, Any, Literal
2
+ from dataclasses import dataclass
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+
7
+ @dataclass
8
+ class XGAMessage:
9
+ message_id: str
10
+ type: Literal["status", "tool", "assistant", "assistant_response_end"]
11
+ is_llm_message: bool
12
+ content: Union[Dict[str, Any], List[Any], str]
13
+ metadata: Optional[Dict[str, Any]]
14
+ session_id: Optional[str]
15
+ agent_id: Optional[str]
16
+ task_id: Optional[str]
17
+
18
+ @dataclass
19
+ class XGAToolSchema:
20
+ tool_name: str
21
+ server_name: str
22
+ description: str
23
+ input_schema: Optional[str]
24
+
25
+
26
+ @dataclass
27
+ class XGAToolResult:
28
+ success: bool
29
+ output: str
30
+
31
+ class XGAToolBox(ABC):
32
+ @abstractmethod
33
+ async def creat_task_tool_box(self, task_id: str, general_tools: List[str], custom_tools: List[str]):
34
+ pass
35
+
36
+ @abstractmethod
37
+ async def destroy_task_tool_box(self, task_id: str):
38
+ pass
39
+
40
+ @abstractmethod
41
+ def get_task_tool_schemas(self, task_id: str, type: Literal["general_tool", "custom_tool"]) -> List[XGAToolSchema]:
42
+ pass
43
+
44
+ @abstractmethod
45
+ async def call_tool(self, task_id: str, tool_name: str, args: Optional[Dict[str, Any]] = None) -> XGAToolResult:
46
+ pass
@@ -0,0 +1,69 @@
1
+
2
+ from typing import List, Any, Dict, Optional, AsyncGenerator
3
+ from uuid import uuid4
4
+
5
+ from xgae.engine.xga_base import XGAMessage, XGAToolSchema, XGAToolBox
6
+ from xgae.utils.llm_client import LLMClient
7
+ from xgae.utils.setup_env import langfuse
8
+ from xga_prompt_builder import XGAPromptBuilder
9
+ from xga_mcp_tool_box import XGAMcpToolBox
10
+
11
+ class XGAEngine():
12
+ def __init__(self,
13
+ session_id: Optional[str] = None,
14
+ trace_id: Optional[str] = None,
15
+ agent_id: Optional[str] = None,
16
+ llm_config: Optional[Dict[str, Any]] = None,
17
+ prompt_builder: Optional[XGAPromptBuilder] = None,
18
+ tool_box: Optional[XGAToolBox] = None):
19
+ self.session_id = session_id if session_id else f"xga_sid_{uuid4()}"
20
+ self.agent_id = agent_id
21
+
22
+ self.messages: List[XGAMessage] = []
23
+ self.llm_client = LLMClient(llm_config)
24
+ self.model_name = self.llm_client.model_name
25
+ self.is_stream = self.llm_client.is_stream
26
+
27
+ self.prompt_builder = prompt_builder or XGAPromptBuilder()
28
+ self.tool_box = tool_box or XGAMcpToolBox()
29
+
30
+ self.task_id = None
31
+ self.trace_id = trace_id if trace_id else langfuse.create_trace_id()
32
+
33
+
34
+ async def run_task(self,
35
+ task_messages: List[Dict[str, Any]],
36
+ task_id: Optional[str],
37
+ prompt_template: Optional[str] = None,
38
+ general_tools: Optional[List[str]] = ["*"],
39
+ custom_tools: Optional[List[str]] = []) -> AsyncGenerator:
40
+ try:
41
+ self.task_id = task_id if task_id else f"xga_task_{uuid4()}"
42
+ await self.tool_box.creat_task_tool_box(self.task_id, general_tools, custom_tools)
43
+ system_prompt = await self._build_system_prompt(prompt_template, general_tools, custom_tools)
44
+ yield system_prompt
45
+
46
+ finally:
47
+ await self.tool_box.destroy_task_tool_box(self.task_id)
48
+
49
+
50
+ def _run_task_once(self):
51
+ pass
52
+
53
+ async def _build_system_prompt(self, prompt_template: str, general_tools: List[str], custom_tools: List[str]) -> str:
54
+ self.task_tool_schemas: Dict[str, XGAToolSchema] = {}
55
+ system_prompt = self.prompt_builder.build_system_prompt(self.model_name, prompt_template)
56
+
57
+ tool_schemas = await self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
58
+ system_prompt = self.prompt_builder.build_general_tool_prompt(self.model_name, system_prompt, tool_schemas)
59
+
60
+ tool_schemas = await self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
61
+ system_prompt = self.prompt_builder.build_custom_tool_prompt(self.model_name, system_prompt, tool_schemas)
62
+
63
+ return system_prompt
64
+
65
+ def add_message(self, message: XGAMessage):
66
+ message.message_id = f"xga_msg_{uuid4()}"
67
+ message.session_id = self.session_id
68
+ message.agent_id = self.agent_id
69
+ self.messages.append(message)
@@ -0,0 +1,192 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from typing import List, Any, Dict, Optional, Literal, override
5
+
6
+ from langchain_mcp_adapters.client import MultiServerMCPClient
7
+ from langchain_mcp_adapters.tools import load_mcp_tools
8
+
9
+ from xgae.engine.xga_base import XGAToolSchema, XGAToolBox, XGAToolResult
10
+ from xgae.utils.setup_env import XGAError
11
+
12
+
13
+ class XGAMcpToolBox(XGAToolBox):
14
+ GENERAL_MCP_SERVER_NAME = "xga_general"
15
+
16
+ def __init__(self,
17
+ custom_mcp_server_config: Optional[Dict[str, Any]] = None,
18
+ custom_mcp_server_file: Optional[str] = None):
19
+ general_mcp_server_config = self._load_mcp_servers_config("mcpservers/xga_server.json")
20
+ tool_box_mcp_server_config = general_mcp_server_config.get("mcpServers", {})
21
+
22
+ if custom_mcp_server_config:
23
+ tool_box_mcp_server_config.update(custom_mcp_server_config)
24
+ elif custom_mcp_server_file:
25
+ custom_mcp_server_config = self._load_mcp_servers_config(custom_mcp_server_file)
26
+ custom_mcp_server_config = custom_mcp_server_config.get("mcpServers", {})
27
+ tool_box_mcp_server_config.update(custom_mcp_server_config)
28
+
29
+ self._mcp_client = MultiServerMCPClient(tool_box_mcp_server_config)
30
+ self.mcp_server_names: List[str] = [server_name for server_name in tool_box_mcp_server_config]
31
+ self.mcp_tool_schemas: Dict[str, List[XGAToolSchema]] = {}
32
+ self.task_tool_schemas: Dict[str, Dict[str,XGAToolSchema]] = {}
33
+
34
+ @override
35
+ async def creat_task_tool_box(self, task_id: str, general_tools: List[str], custom_tools: List[str]):
36
+ task_tool_schemas = {}
37
+ general_tool_schemas = self.mcp_tool_schemas.get(XGAMcpToolBox.GENERAL_MCP_SERVER_NAME, {})
38
+ if len(general_tools) > 0 and general_tools[0] == "*":
39
+ task_tool_schemas = {tool_schema.tool_name: tool_schema for tool_schema in general_tool_schemas}
40
+ else:
41
+ for tool_schema in general_tool_schemas:
42
+ if tool_schema.tool_name in general_tools:
43
+ task_tool_schemas[tool_schema.tool_name] = tool_schema
44
+ task_tool_schemas.pop("end_task")
45
+
46
+ for server_tool_name in custom_tools:
47
+ parts = server_tool_name.split(".")
48
+ if len(parts) != 2:
49
+ continue
50
+ custom_server_name, custom_tool_name = parts
51
+ if (not custom_server_name ) or (not custom_tool_name):
52
+ continue
53
+
54
+ custom_tool_schemas = self.mcp_tool_schemas.get(custom_server_name, None)
55
+ if custom_tool_schemas is None:
56
+ continue
57
+ if custom_tool_name == "*":
58
+ custom_tool_schema_d = {tool_schema.tool_name: tool_schema for tool_schema in custom_tool_schemas}
59
+ task_tool_schemas.update(custom_tool_schema_d)
60
+ else:
61
+ for tool_schema in custom_tool_schemas:
62
+ if custom_tool_name == tool_schema.tool_name:
63
+ task_tool_schemas[custom_tool_name] = tool_schema
64
+
65
+
66
+ self.task_tool_schemas[task_id] = task_tool_schemas
67
+
68
+ @override
69
+ async def destroy_task_tool_box(self, task_id: str):
70
+ tool_schemas = self.get_task_tool_schemas(task_id, type="general_tool")
71
+ if len(tool_schemas) > 0:
72
+ await self.call_tool(task_id, "end_task", {"task_id": task_id})
73
+ self.task_tool_schemas.pop(task_id, None)
74
+
75
+ @override
76
+ def get_task_tool_schemas(self, task_id: str, type: Literal["general_tool", "custom_tool"]) -> List[XGAToolSchema]:
77
+ task_tool_schemas = []
78
+
79
+ all_task_tool_schemas = self.task_tool_schemas.get(task_id, {})
80
+ for tool_schema in all_task_tool_schemas.values():
81
+ if type == "general_tool" and tool_schema.server_name == self.GENERAL_MCP_SERVER_NAME:
82
+ task_tool_schemas.append(tool_schema)
83
+ elif type == "custom_tool" and tool_schema.server_name != self.GENERAL_MCP_SERVER_NAME:
84
+ task_tool_schemas.append(tool_schema)
85
+
86
+ return task_tool_schemas
87
+
88
+ @override
89
+ async def call_tool(self, task_id: str, tool_name: str, args: Optional[Dict[str, Any]] = None) -> XGAToolResult:
90
+ if tool_name == "end_task":
91
+ server_name = self.GENERAL_MCP_SERVER_NAME
92
+ else:
93
+ task_tool_schemas = self.task_tool_schemas.get(task_id, {})
94
+ tool_schema = task_tool_schemas.get(tool_name, None)
95
+ if tool_schema is None:
96
+ raise XGAError(f"MCP tool not found: '{tool_name}'")
97
+ server_name = tool_schema.server_name
98
+
99
+ async with self._mcp_client.session(server_name) as session:
100
+ tools = await load_mcp_tools(session)
101
+ mcp_tool = next((t for t in tools if t.name == tool_name), None)
102
+
103
+ if mcp_tool:
104
+ tool_args = args or {}
105
+ if server_name == self.GENERAL_MCP_SERVER_NAME:
106
+ pass
107
+ #tool_args["task_id"] = task_id #xga general tool, first param must be task_id
108
+ else:
109
+ tool_args = args
110
+
111
+ try:
112
+ tool_result = await mcp_tool.arun(tool_args)
113
+ result = XGAToolResult(success=True, output=str(tool_result))
114
+ except Exception as e:
115
+ error = f"Call mcp tool '{tool_name}' error: {str(e)}"
116
+ logging.error(f"XGAMcpToolBox.call_tool: {error}")
117
+ result = XGAToolResult(success=False, output=error)
118
+ else:
119
+ error = f"No MCP tool found with name: {tool_name}"
120
+ logging.info(f"XGAMcpToolBox.call_tool: error={error}")
121
+ result = XGAToolResult(success=False, output=error)
122
+
123
+ return result
124
+
125
+ async def load_mcp_tools_schema(self)-> None:
126
+ for server_name in self.mcp_server_names:
127
+ self.mcp_tool_schemas[server_name] = []
128
+ mcp_tools = await self._mcp_client.get_tools(server_name=server_name)
129
+ for tool in mcp_tools:
130
+ input_schema = tool.args_schema
131
+ if server_name == self.GENERAL_MCP_SERVER_NAME:
132
+ input_schema = str(tool.args_schema) # @todo remove task_id param
133
+ input_schema_str = str(input_schema) # @todo convert input tool.args_schema
134
+ tool_schema = XGAToolSchema(tool_name=tool.name,
135
+ server_name=server_name,
136
+ description=tool.description,
137
+ input_schema=input_schema_str)
138
+ self.mcp_tool_schemas[server_name].append(tool_schema)
139
+
140
+ @staticmethod
141
+ def _load_mcp_servers_config(mcp_config_path: str) -> Dict[str, Any]:
142
+ try:
143
+ if os.path.exists(mcp_config_path):
144
+ with open(mcp_config_path, 'r', encoding='utf-8') as f:
145
+ server_config = json.load(f)
146
+
147
+ for server_name, server_info in server_config["mcpServers"].items():
148
+ if "transport" not in server_info:
149
+ if "url" in server_info:
150
+ server_info["transport"] = "streamable_http" if "mcp" in server_info["url"] else "sse"
151
+ else:
152
+ server_info["transport"] = "stdio"
153
+
154
+ return server_config
155
+ else:
156
+ logging.warning("MCP servers config file not found at: %s", mcp_config_path)
157
+ return {"mcpServers": {}}
158
+
159
+ except Exception as e:
160
+ logging.error("Failed to load MCP servers config: %s", str(e))
161
+ return {"mcpServers": {}}
162
+
163
+
164
+ if __name__ == "__main__":
165
+ import asyncio
166
+ from dataclasses import asdict
167
+ async def main():
168
+ task_id = "task1"
169
+ mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
170
+ #mcp_tool_box = XGAMcpToolBox()
171
+ await mcp_tool_box.load_mcp_tools_schema()
172
+ await mcp_tool_box.creat_task_tool_box(task_id=task_id, general_tools=["*"], custom_tools=["bomc_fault.*"])
173
+ tool_schemas = mcp_tool_box.get_task_tool_schemas(task_id, "general_tool")
174
+ print("general_tools_schemas" + "*"*50)
175
+ for tool_schema in tool_schemas:
176
+ print(asdict(tool_schema))
177
+ print()
178
+
179
+ tool_schemas = mcp_tool_box.get_task_tool_schemas(task_id, "custom_tool")
180
+ print("custom_tools_schemas" + "*" * 50)
181
+ for tool_schema in tool_schemas:
182
+ print(asdict(tool_schema))
183
+ print()
184
+
185
+ result = await mcp_tool_box.call_tool(task_id=task_id, tool_name="web_search", args={"task_id": task_id, "query": "查询天津天气"})
186
+ print(f"call web_search result: {result}")
187
+
188
+ result = await mcp_tool_box.call_tool(task_id=task_id, tool_name="complete", args={"task_id": task_id})
189
+ print(f"call complete result: {result}")
190
+
191
+ await mcp_tool_box.destroy_task_tool_box(task_id)
192
+ asyncio.run(main())
@@ -0,0 +1,38 @@
1
+ import datetime
2
+
3
+ from typing import Optional, List
4
+
5
+ from xga_base import XGAToolSchema
6
+ from xgae.utils.setup_env import read_file, XGAError
7
+
8
+
9
+ class XGAPromptBuilder():
10
+ def __init__(self,
11
+ prompt_template: Optional[str] = None,
12
+ prompt_template_file: Optional[str] = None):
13
+ self.system_prompt_template = None
14
+ if prompt_template:
15
+ self.system_prompt_template = prompt_template
16
+ elif prompt_template_file:
17
+ self.system_prompt_template = read_file(prompt_template_file)
18
+ else:
19
+ _system_prompt_template = read_file("templates/system_prompt_template.txt")
20
+ self.system_prompt_template = _system_prompt_template.format(
21
+ current_date=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d'),
22
+ current_time=datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S'),
23
+ current_year=datetime.datetime.now(datetime.timezone.utc).strftime('%Y')
24
+ )
25
+
26
+
27
+ def build_system_prompt(self, model_name:str, prompt_template: Optional[str]=None)-> str:
28
+ system_prompt = prompt_template if prompt_template else self.system_prompt_template
29
+
30
+ return system_prompt
31
+
32
+
33
+ def build_general_tool_prompt(self, model_name:str, prompt_template: str, tool_schemas:List[XGAToolSchema])-> str:
34
+ pass
35
+
36
+
37
+ def build_custom_tool_prompt(self, model_name:str, prompt_template: str, tool_schemas:List[XGAToolSchema])-> str:
38
+ pass