xgae 0.1.1__tar.gz → 0.1.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.1.1 → xgae-0.1.3}/.idea/misc.xml +1 -1
- {xgae-0.1.1 → xgae-0.1.3}/.idea/workspace.xml +78 -28
- {xgae-0.1.1 → xgae-0.1.3}/.idea/xgae.iml +1 -1
- {xgae-0.1.1 → xgae-0.1.3}/PKG-INFO +1 -1
- {xgae-0.1.1 → xgae-0.1.3}/pyproject.toml +1 -1
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/engine/xga_base.py +5 -2
- xgae-0.1.3/src/xgae/engine/xga_engine.py +99 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/engine/xga_mcp_tool_box.py +41 -21
- xgae-0.1.3/src/xgae/engine/xga_prompt_builder.py +97 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/utils/llm_client.py +2 -5
- xgae-0.1.3/src/xgae/utils/setup_env.py +96 -0
- xgae-0.1.3/src/xgae/utils/utils.py +42 -0
- xgae-0.1.3/templates/custom_tool_prompt_template.txt +25 -0
- xgae-0.1.3/templates/gemini_system_prompt_template.txt +1742 -0
- xgae-0.1.3/templates/general_tool_prompt_template.txt +25 -0
- xgae-0.1.3/templates/scp_test_prompt.txt +21 -0
- xgae-0.1.3/templates/system_prompt_response_sample.txt +1252 -0
- xgae-0.1.3/templates/system_prompt_template.txt +1106 -0
- xgae-0.1.1/src/xgae/engine/xga_engine.py +0 -69
- xgae-0.1.1/src/xgae/engine/xga_prompt_builder.py +0 -38
- xgae-0.1.1/src/xgae/utils/setup_env.py +0 -108
- {xgae-0.1.1 → xgae-0.1.3}/.env +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.idea/.gitignore +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.idea/modules.xml +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.idea/vcs.xml +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/.python-version +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/README.md +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/mcpservers/custom_servers.json +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/mcpservers/xga_server.json +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/__init__.py +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/engine/responser/xga_non_stream_responser.py +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/engine/responser/xga_responser_utils.py +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/src/xgae/engine/responser/xga_stream_reponser.py +0 -0
- {xgae-0.1.1 → xgae-0.1.3}/uv.lock +0 -0
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
<component name="Black">
|
|
4
4
|
<option name="sdkName" value="Python 3.13 (xgae)" />
|
|
5
5
|
</component>
|
|
6
|
-
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at ~/
|
|
6
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at ~/DevProjects/xga/xgae/.venv" project-jdk-type="Python SDK" />
|
|
7
7
|
</project>
|
|
@@ -25,23 +25,27 @@
|
|
|
25
25
|
<option name="hideEmptyMiddlePackages" value="true" />
|
|
26
26
|
<option name="showLibraryContents" value="true" />
|
|
27
27
|
</component>
|
|
28
|
-
<component name="PropertiesComponent"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
29
|
+
"keyToString": {
|
|
30
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
31
|
+
"Python.llm_client.executor": "Run",
|
|
32
|
+
"Python.run_xga_engine.executor": "Run",
|
|
33
|
+
"Python.setup_env.executor": "Run",
|
|
34
|
+
"Python.utils.executor": "Run",
|
|
35
|
+
"Python.xga_engine.executor": "Run",
|
|
36
|
+
"Python.xga_mcp_tool_box.executor": "Debug",
|
|
37
|
+
"Python.xga_prompt_builder.executor": "Debug",
|
|
38
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
39
|
+
"last_opened_file_path": "/Users/sharkystar/DevProjects/xga/xgae",
|
|
40
|
+
"node.js.detected.package.eslint": "true",
|
|
41
|
+
"node.js.detected.package.tslint": "true",
|
|
42
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
43
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
44
|
+
"nodejs_package_manager_path": "npm",
|
|
45
|
+
"settings.editor.selected.configurable": "com.intellij.pycharm.community.ide.impl.configuration.PythonContentEntriesConfigurable",
|
|
46
|
+
"vue.rearranger.settings.migration": "true"
|
|
43
47
|
}
|
|
44
|
-
}
|
|
48
|
+
}]]></component>
|
|
45
49
|
<component name="RecentsManager">
|
|
46
50
|
<key name="MoveFile.RECENT_KEYS">
|
|
47
51
|
<recent name="$PROJECT_DIR$/src/xgae/engine/responser" />
|
|
@@ -57,6 +61,7 @@
|
|
|
57
61
|
<env name="PYTHONUNBUFFERED" value="1" />
|
|
58
62
|
</envs>
|
|
59
63
|
<option name="SDK_HOME" value="" />
|
|
64
|
+
<option name="SDK_NAME" value="Python 3.13 virtualenv at ~/DevProjects/xga/xgae/.venv" />
|
|
60
65
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
61
66
|
<option name="IS_MODULE_SDK" value="false" />
|
|
62
67
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
@@ -81,6 +86,39 @@
|
|
|
81
86
|
<option name="INPUT_FILE" value="" />
|
|
82
87
|
<method v="2" />
|
|
83
88
|
</configuration>
|
|
89
|
+
<configuration name="run_xga_engine" type="PythonConfigurationType" factoryName="Python">
|
|
90
|
+
<module name="xgae" />
|
|
91
|
+
<option name="ENV_FILES" value="" />
|
|
92
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
93
|
+
<option name="PARENT_ENVS" value="true" />
|
|
94
|
+
<envs>
|
|
95
|
+
<env name="PYTHONUNBUFFERED" value="1" />
|
|
96
|
+
</envs>
|
|
97
|
+
<option name="SDK_HOME" value="" />
|
|
98
|
+
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
99
|
+
<option name="IS_MODULE_SDK" value="true" />
|
|
100
|
+
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
101
|
+
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
102
|
+
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
103
|
+
<EXTENSION ID="net.ashald.envfile">
|
|
104
|
+
<option name="IS_ENABLED" value="false" />
|
|
105
|
+
<option name="IS_SUBST" value="false" />
|
|
106
|
+
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
|
|
107
|
+
<option name="IS_IGNORE_MISSING_FILES" value="false" />
|
|
108
|
+
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
|
|
109
|
+
<ENTRIES>
|
|
110
|
+
<ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
|
|
111
|
+
</ENTRIES>
|
|
112
|
+
</EXTENSION>
|
|
113
|
+
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/src/xgae/engine/xga_engine.py" />
|
|
114
|
+
<option name="PARAMETERS" value="" />
|
|
115
|
+
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
116
|
+
<option name="EMULATE_TERMINAL" value="false" />
|
|
117
|
+
<option name="MODULE_MODE" value="false" />
|
|
118
|
+
<option name="REDIRECT_INPUT" value="false" />
|
|
119
|
+
<option name="INPUT_FILE" value="" />
|
|
120
|
+
<method v="2" />
|
|
121
|
+
</configuration>
|
|
84
122
|
</component>
|
|
85
123
|
<component name="SharedIndexes">
|
|
86
124
|
<attachedChunks>
|
|
@@ -104,25 +142,37 @@
|
|
|
104
142
|
<workItem from="1755270285722" duration="12068000" />
|
|
105
143
|
<workItem from="1755283728206" duration="1511000" />
|
|
106
144
|
<workItem from="1755286552901" duration="3130000" />
|
|
107
|
-
<workItem from="
|
|
108
|
-
<workItem from="
|
|
109
|
-
<workItem from="
|
|
110
|
-
<workItem from="
|
|
111
|
-
<workItem from="
|
|
112
|
-
<workItem from="
|
|
113
|
-
<workItem from="
|
|
114
|
-
<workItem from="1755419142521" duration="150000" />
|
|
115
|
-
<workItem from="1755429851003" duration="290000" />
|
|
116
|
-
<workItem from="1755430331676" duration="21000" />
|
|
145
|
+
<workItem from="1755409529770" duration="670000" />
|
|
146
|
+
<workItem from="1755478595168" duration="20000" />
|
|
147
|
+
<workItem from="1755479257156" duration="16895000" />
|
|
148
|
+
<workItem from="1755525531052" duration="13030000" />
|
|
149
|
+
<workItem from="1755585869510" duration="5796000" />
|
|
150
|
+
<workItem from="1755593112104" duration="786000" />
|
|
151
|
+
<workItem from="1755611972189" duration="6282000" />
|
|
117
152
|
</task>
|
|
118
153
|
<servers />
|
|
119
154
|
</component>
|
|
120
155
|
<component name="TypeScriptGeneratedFilesManager">
|
|
121
156
|
<option name="version" value="3" />
|
|
122
157
|
</component>
|
|
158
|
+
<component name="XDebuggerManager">
|
|
159
|
+
<breakpoint-manager>
|
|
160
|
+
<default-breakpoints>
|
|
161
|
+
<breakpoint type="python-exception">
|
|
162
|
+
<properties notifyOnTerminate="true" exception="BaseException">
|
|
163
|
+
<option name="notifyOnTerminate" value="true" />
|
|
164
|
+
</properties>
|
|
165
|
+
</breakpoint>
|
|
166
|
+
</default-breakpoints>
|
|
167
|
+
</breakpoint-manager>
|
|
168
|
+
</component>
|
|
123
169
|
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
|
124
|
-
<SUITE FILE_PATH="coverage/xgae$
|
|
125
|
-
<SUITE FILE_PATH="coverage/xgae$
|
|
170
|
+
<SUITE FILE_PATH="coverage/xgae$xga_engine.coverage" NAME="xga_engine Coverage Results" MODIFIED="1755580277172" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
171
|
+
<SUITE FILE_PATH="coverage/xgae$xga_prompt_builder.coverage" NAME="xga_prompt_builder Coverage Results" MODIFIED="1755587456555" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
172
|
+
<SUITE FILE_PATH="coverage/xgae$run_xga_engine.coverage" NAME="run_xga_engine Coverage Results" MODIFIED="1755657339432" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
126
173
|
<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$" />
|
|
174
|
+
<SUITE FILE_PATH="coverage/xgae$setup_env.coverage" NAME="setup_env Coverage Results" MODIFIED="1755565531587" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
175
|
+
<SUITE FILE_PATH="coverage/xgae$xga_mcp_tool_box.coverage" NAME="xga_mcp_tool_box Coverage Results" MODIFIED="1755583099719" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
176
|
+
<SUITE FILE_PATH="coverage/xgae$llm_client.coverage" NAME="llm_client Coverage Results" MODIFIED="1755565705235" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
127
177
|
</component>
|
|
128
178
|
</project>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
6
6
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
|
7
7
|
</content>
|
|
8
|
-
<orderEntry type="jdk" jdkName="Python 3.13 virtualenv at ~/
|
|
8
|
+
<orderEntry type="jdk" jdkName="Python 3.13 virtualenv at ~/DevProjects/xga/xgae/.venv" jdkType="Python SDK" />
|
|
9
9
|
<orderEntry type="sourceFolder" forTests="false" />
|
|
10
10
|
</component>
|
|
11
11
|
</module>
|
|
@@ -2,7 +2,9 @@ from typing import Union, Optional, Dict, List, Any, Literal
|
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class XGAError(Exception):
|
|
6
|
+
"""Custom exception for errors in the XGA system."""
|
|
7
|
+
pass
|
|
6
8
|
|
|
7
9
|
@dataclass
|
|
8
10
|
class XGAMessage:
|
|
@@ -20,7 +22,8 @@ class XGAToolSchema:
|
|
|
20
22
|
tool_name: str
|
|
21
23
|
server_name: str
|
|
22
24
|
description: str
|
|
23
|
-
input_schema:
|
|
25
|
+
input_schema: Dict[str, Any]
|
|
26
|
+
metadata: Optional[Dict[str, Any]]
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
@dataclass
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import List, Any, Dict, Optional, AsyncGenerator
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from xgae.engine.xga_base import XGAMessage, 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
|
+
|
|
12
|
+
class XGATaskEngine():
|
|
13
|
+
def __init__(self,
|
|
14
|
+
session_id: Optional[str] = None,
|
|
15
|
+
task_id: Optional[str] = None,
|
|
16
|
+
agent_id: Optional[str] = None,
|
|
17
|
+
trace_id: Optional[str] = None,
|
|
18
|
+
system_prompt: Optional[str] = None,
|
|
19
|
+
llm_config: Optional[Dict[str, Any]] = None,
|
|
20
|
+
prompt_builder: Optional[XGAPromptBuilder] = None,
|
|
21
|
+
tool_box: Optional[XGAToolBox] = None):
|
|
22
|
+
self.session_id = session_id if session_id else f"xga_sid_{uuid4()}"
|
|
23
|
+
self.task_id = task_id if task_id else f"xga_task_{uuid4()}"
|
|
24
|
+
self.agent_id = agent_id
|
|
25
|
+
self.trace_id = trace_id if trace_id else langfuse.create_trace_id()
|
|
26
|
+
|
|
27
|
+
self.messages: List[XGAMessage] = []
|
|
28
|
+
self.llm_client = LLMClient(llm_config)
|
|
29
|
+
self.model_name = self.llm_client.model_name
|
|
30
|
+
self.is_stream = self.llm_client.is_stream
|
|
31
|
+
|
|
32
|
+
self.prompt_builder = prompt_builder or XGAPromptBuilder(system_prompt)
|
|
33
|
+
self.tool_box = tool_box or XGAMcpToolBox()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def __async_init__(self, general_tools:List[str], custom_tools: List[str]) -> None:
|
|
37
|
+
await self.tool_box.load_mcp_tools_schema()
|
|
38
|
+
await self.tool_box.creat_task_tool_box(self.task_id, general_tools, custom_tools)
|
|
39
|
+
general_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
|
|
40
|
+
custom_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "custom_tool")
|
|
41
|
+
|
|
42
|
+
self.task_prompt = self.prompt_builder.build_task_prompt(self.model_name, general_tool_schemas, custom_tool_schemas)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
async def create(cls,
|
|
46
|
+
session_id: Optional[str] = None,
|
|
47
|
+
task_id: Optional[str] = None,
|
|
48
|
+
agent_id: Optional[str] = None,
|
|
49
|
+
trace_id: Optional[str] = None,
|
|
50
|
+
system_prompt: Optional[str] = None,
|
|
51
|
+
general_tools: Optional[List[str]] = None,
|
|
52
|
+
custom_tools: Optional[List[str]] = None,
|
|
53
|
+
llm_config: Optional[Dict[str, Any]] = None,
|
|
54
|
+
prompt_builder: Optional[XGAPromptBuilder] = None,
|
|
55
|
+
tool_box: Optional[XGAToolBox] = None) -> 'XGATaskEngine':
|
|
56
|
+
engine: XGATaskEngine = cls(session_id=session_id,
|
|
57
|
+
task_id=task_id,
|
|
58
|
+
agent_id=agent_id,
|
|
59
|
+
trace_id=trace_id,
|
|
60
|
+
system_prompt=system_prompt,
|
|
61
|
+
llm_config=llm_config,
|
|
62
|
+
prompt_builder=prompt_builder,
|
|
63
|
+
tool_box=tool_box)
|
|
64
|
+
general_tools = general_tools or ["*"]
|
|
65
|
+
custom_tools = custom_tools or []
|
|
66
|
+
await engine.__async_init__(general_tools, custom_tools)
|
|
67
|
+
return engine
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def run_task(self, task_messages: List[Dict[str, Any]]) -> AsyncGenerator:
|
|
71
|
+
try:
|
|
72
|
+
yield self.task_prompt
|
|
73
|
+
|
|
74
|
+
finally:
|
|
75
|
+
await self.tool_box.destroy_task_tool_box(self.task_id)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _run_task_once(self):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def add_message(self, message: XGAMessage):
|
|
83
|
+
message.message_id = f"xga_msg_{uuid4()}"
|
|
84
|
+
message.session_id = self.session_id
|
|
85
|
+
message.agent_id = self.agent_id
|
|
86
|
+
self.messages.append(message)
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
import asyncio
|
|
90
|
+
|
|
91
|
+
async def main():
|
|
92
|
+
tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
93
|
+
engine = await XGATaskEngine.create(tool_box=tool_box, custom_tools=["bomc_fault.*"])
|
|
94
|
+
# engine = await XGATaskEngine.create()
|
|
95
|
+
|
|
96
|
+
async for chunk in engine.run_task(task_messages=[{}]):
|
|
97
|
+
print(chunk)
|
|
98
|
+
|
|
99
|
+
asyncio.run(main())
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
|
|
4
5
|
from typing import List, Any, Dict, Optional, Literal, override
|
|
5
6
|
|
|
6
7
|
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
7
8
|
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
8
9
|
|
|
9
|
-
from xgae.engine.xga_base import XGAToolSchema, XGAToolBox, XGAToolResult
|
|
10
|
-
from xgae.utils.setup_env import
|
|
11
|
-
|
|
10
|
+
from xgae.engine.xga_base import XGAError, XGAToolSchema, XGAToolBox, XGAToolResult
|
|
11
|
+
from xgae.utils.setup_env import langfuse
|
|
12
12
|
|
|
13
13
|
class XGAMcpToolBox(XGAToolBox):
|
|
14
14
|
GENERAL_MCP_SERVER_NAME = "xga_general"
|
|
15
15
|
|
|
16
16
|
def __init__(self,
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
custom_mcp_server_file: Optional[str] = None,
|
|
18
|
+
custom_mcp_server_config: Optional[Dict[str, Any]] = None
|
|
19
|
+
):
|
|
19
20
|
general_mcp_server_config = self._load_mcp_servers_config("mcpservers/xga_server.json")
|
|
20
21
|
tool_box_mcp_server_config = general_mcp_server_config.get("mcpServers", {})
|
|
21
22
|
|
|
@@ -31,6 +32,8 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
31
32
|
self.mcp_tool_schemas: Dict[str, List[XGAToolSchema]] = {}
|
|
32
33
|
self.task_tool_schemas: Dict[str, Dict[str,XGAToolSchema]] = {}
|
|
33
34
|
|
|
35
|
+
self.is_loaded_tool_schemas = False
|
|
36
|
+
|
|
34
37
|
@override
|
|
35
38
|
async def creat_task_tool_box(self, task_id: str, general_tools: List[str], custom_tools: List[str]):
|
|
36
39
|
task_tool_schemas = {}
|
|
@@ -41,7 +44,7 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
41
44
|
for tool_schema in general_tool_schemas:
|
|
42
45
|
if tool_schema.tool_name in general_tools:
|
|
43
46
|
task_tool_schemas[tool_schema.tool_name] = tool_schema
|
|
44
|
-
task_tool_schemas.pop("end_task")
|
|
47
|
+
task_tool_schemas.pop("end_task", None)
|
|
45
48
|
|
|
46
49
|
for server_tool_name in custom_tools:
|
|
47
50
|
parts = server_tool_name.split(".")
|
|
@@ -122,20 +125,35 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
122
125
|
|
|
123
126
|
return result
|
|
124
127
|
|
|
128
|
+
|
|
125
129
|
async def load_mcp_tools_schema(self)-> None:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
input_schema =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
if not self.is_loaded_tool_schemas:
|
|
131
|
+
for server_name in self.mcp_server_names:
|
|
132
|
+
self.mcp_tool_schemas[server_name] = []
|
|
133
|
+
mcp_tools = await self._mcp_client.get_tools(server_name=server_name)
|
|
134
|
+
|
|
135
|
+
for tool in mcp_tools:
|
|
136
|
+
input_schema = tool.args_schema
|
|
137
|
+
if server_name == self.GENERAL_MCP_SERVER_NAME:
|
|
138
|
+
input_schema['properties'].pop("task_id", None)
|
|
139
|
+
if 'task_id' in input_schema['required']:
|
|
140
|
+
input_schema['required'].remove('task_id')
|
|
141
|
+
params_properties = input_schema.get("properties", {})
|
|
142
|
+
for param_properties in params_properties.values():
|
|
143
|
+
param_properties.pop("title", None)
|
|
144
|
+
|
|
145
|
+
metadata = tool.metadata or {}
|
|
146
|
+
tool_schema = XGAToolSchema(tool_name=tool.name,
|
|
147
|
+
server_name=server_name,
|
|
148
|
+
description=tool.description,
|
|
149
|
+
input_schema=input_schema,
|
|
150
|
+
metadata=metadata)
|
|
151
|
+
self.mcp_tool_schemas[server_name].append(tool_schema)
|
|
152
|
+
self.is_loaded_tool_schemas = True
|
|
153
|
+
|
|
154
|
+
async def reload_mcp_tools_schema(self) -> None:
|
|
155
|
+
self.is_loaded_tool_schemas = False
|
|
156
|
+
await self.load_mcp_tools_schema()
|
|
139
157
|
|
|
140
158
|
@staticmethod
|
|
141
159
|
def _load_mcp_servers_config(mcp_config_path: str) -> Dict[str, Any]:
|
|
@@ -164,10 +182,11 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
164
182
|
if __name__ == "__main__":
|
|
165
183
|
import asyncio
|
|
166
184
|
from dataclasses import asdict
|
|
185
|
+
|
|
167
186
|
async def main():
|
|
168
187
|
task_id = "task1"
|
|
169
|
-
mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
170
|
-
|
|
188
|
+
#mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
189
|
+
mcp_tool_box = XGAMcpToolBox()
|
|
171
190
|
await mcp_tool_box.load_mcp_tools_schema()
|
|
172
191
|
await mcp_tool_box.creat_task_tool_box(task_id=task_id, general_tools=["*"], custom_tools=["bomc_fault.*"])
|
|
173
192
|
tool_schemas = mcp_tool_box.get_task_tool_schemas(task_id, "general_tool")
|
|
@@ -189,4 +208,5 @@ if __name__ == "__main__":
|
|
|
189
208
|
print(f"call complete result: {result}")
|
|
190
209
|
|
|
191
210
|
await mcp_tool_box.destroy_task_tool_box(task_id)
|
|
211
|
+
|
|
192
212
|
asyncio.run(main())
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import datetime
|
|
3
|
+
from typing import Optional, List
|
|
4
|
+
|
|
5
|
+
from xga_base import XGAToolSchema, XGAError
|
|
6
|
+
from xgae.utils.utils import read_file, format_file_with_args
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class XGAPromptBuilder():
|
|
10
|
+
def __init__(self, system_prompt: Optional[str] = None):
|
|
11
|
+
self.system_prompt = system_prompt
|
|
12
|
+
|
|
13
|
+
def build_task_prompt(self, model_name: str, general_tool_schemas: List[XGAToolSchema], custom_tool_schemas: List[XGAToolSchema])-> str:
|
|
14
|
+
if self.system_prompt is None:
|
|
15
|
+
self.system_prompt = self._load_default_system_prompt(model_name)
|
|
16
|
+
|
|
17
|
+
task_prompt = self.system_prompt
|
|
18
|
+
|
|
19
|
+
tool_prompt = self.build_general_tool_prompt(general_tool_schemas)
|
|
20
|
+
task_prompt = task_prompt + "\n" + tool_prompt
|
|
21
|
+
|
|
22
|
+
tool_prompt = self.build_custom_tool_prompt(custom_tool_schemas)
|
|
23
|
+
task_prompt = task_prompt + "\n" + tool_prompt
|
|
24
|
+
|
|
25
|
+
return task_prompt
|
|
26
|
+
|
|
27
|
+
def build_general_tool_prompt(self, tool_schemas:List[XGAToolSchema])-> str:
|
|
28
|
+
tool_prompt = ""
|
|
29
|
+
tool_schemas = tool_schemas or []
|
|
30
|
+
if len(tool_schemas) > 0:
|
|
31
|
+
tool_prompt = read_file("templates/general_tool_prompt_template.txt")
|
|
32
|
+
example_prompt = ""
|
|
33
|
+
openai_schemas = []
|
|
34
|
+
for tool_schema in tool_schemas:
|
|
35
|
+
openai_schema = {}
|
|
36
|
+
openai_schema["type"] = "function"
|
|
37
|
+
openai_function = {}
|
|
38
|
+
openai_schema["function"] = openai_function
|
|
39
|
+
|
|
40
|
+
openai_function["name"] = tool_schema.tool_name
|
|
41
|
+
openai_function["description"] = tool_schema.description if tool_schema.description else 'No description available'
|
|
42
|
+
|
|
43
|
+
openai_parameters = {}
|
|
44
|
+
input_schema = tool_schema.input_schema
|
|
45
|
+
openai_function["parameters"] = openai_parameters
|
|
46
|
+
openai_parameters["type"] = input_schema["type"]
|
|
47
|
+
openai_parameters["properties"] = input_schema.get("properties", {})
|
|
48
|
+
openai_parameters["required"] = input_schema["required"]
|
|
49
|
+
|
|
50
|
+
openai_schemas.append(openai_schema)
|
|
51
|
+
|
|
52
|
+
metadata = tool_schema.metadata or {}
|
|
53
|
+
example = metadata.get("example", None)
|
|
54
|
+
if example:
|
|
55
|
+
example_prompt += f"\n{example}\n"
|
|
56
|
+
|
|
57
|
+
schema_prompt = json.dumps(openai_schemas, ensure_ascii=False, indent=2)
|
|
58
|
+
tool_prompt = tool_prompt.format(tool_schemas=schema_prompt, tool_examples=example_prompt)
|
|
59
|
+
return tool_prompt
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def build_custom_tool_prompt(self, tool_schemas:List[XGAToolSchema])-> str:
|
|
63
|
+
tool_prompt = ""
|
|
64
|
+
tool_schemas = tool_schemas or []
|
|
65
|
+
if len(tool_schemas) > 0:
|
|
66
|
+
tool_prompt = read_file("templates/custom_tool_prompt_template.txt")
|
|
67
|
+
tool_info = ""
|
|
68
|
+
for tool_schema in tool_schemas:
|
|
69
|
+
description = tool_schema.description if tool_schema.description else 'No description available'
|
|
70
|
+
tool_info += f"- **{tool_schema.tool_name}**: {description}\n"
|
|
71
|
+
parameters = tool_schema.input_schema.get("properties", {})
|
|
72
|
+
tool_info += f" Parameters: {parameters}\n"
|
|
73
|
+
tool_prompt = tool_prompt.replace("{tool_schemas}", tool_info)
|
|
74
|
+
|
|
75
|
+
return tool_prompt
|
|
76
|
+
|
|
77
|
+
def _load_default_system_prompt(self, model_name) -> Optional[str]:
|
|
78
|
+
if "gemini-2.5-flash" in model_name.lower() and "gemini-2.5-pro" not in model_name.lower():
|
|
79
|
+
system_prompt_template = read_file("templates/gemini_system_prompt_template.txt")
|
|
80
|
+
else:
|
|
81
|
+
system_prompt_template = read_file("templates/system_prompt_template.txt")
|
|
82
|
+
|
|
83
|
+
system_prompt = format_file_with_args(system_prompt_template, {"datetime": datetime})
|
|
84
|
+
|
|
85
|
+
system_prompt = system_prompt.format(
|
|
86
|
+
current_date=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d'),
|
|
87
|
+
current_time=datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S'),
|
|
88
|
+
current_year=datetime.datetime.now(datetime.timezone.utc).strftime('%Y')
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if "anthropic" not in model_name.lower():
|
|
92
|
+
sample_response = read_file("templates/system_prompt_response_sample.txt")
|
|
93
|
+
system_prompt = system_prompt + "\n\n <sample_assistant_response>" + sample_response + "</sample_assistant_response>"
|
|
94
|
+
|
|
95
|
+
return system_prompt
|
|
96
|
+
|
|
97
|
+
|
|
@@ -9,8 +9,6 @@ from typing import Union, Dict, Any, Optional, List
|
|
|
9
9
|
from litellm.utils import ModelResponse, CustomStreamWrapper
|
|
10
10
|
from openai import OpenAIError
|
|
11
11
|
|
|
12
|
-
from setup_env import setup_xga_env
|
|
13
|
-
|
|
14
12
|
|
|
15
13
|
class LLMError(Exception):
|
|
16
14
|
"""Base exception for LLM-related errors."""
|
|
@@ -20,7 +18,7 @@ class LLMClient:
|
|
|
20
18
|
RATE_LIMIT_DELAY = 30
|
|
21
19
|
RETRY_DELAY = 0.1
|
|
22
20
|
|
|
23
|
-
def __init__(self, llm_config: Optional[Dict[str, Any]]=
|
|
21
|
+
def __init__(self, llm_config: Optional[Dict[str, Any]]=None) -> None:
|
|
24
22
|
"""
|
|
25
23
|
Arg: llm_config (Optional[Dict[str, Any]], optional)
|
|
26
24
|
model: Override default model to use, default set by .env LLM_MODEL
|
|
@@ -36,6 +34,7 @@ class LLMClient:
|
|
|
36
34
|
reasoning_effort: Optional level of reasoning effort, default is ‘low’
|
|
37
35
|
top_p: Optional Top-p sampling parameter, default is None
|
|
38
36
|
"""
|
|
37
|
+
llm_config = llm_config or {}
|
|
39
38
|
litellm.modify_params = True
|
|
40
39
|
litellm.drop_params = True
|
|
41
40
|
|
|
@@ -214,8 +213,6 @@ class LLMClient:
|
|
|
214
213
|
raise LLMError(f"LLM completion failed after {self.max_retries} attempts !")
|
|
215
214
|
|
|
216
215
|
if __name__ == "__main__":
|
|
217
|
-
setup_xga_env()
|
|
218
|
-
|
|
219
216
|
async def llm_completion():
|
|
220
217
|
llm_client = LLMClient({
|
|
221
218
|
"stream": False #default is True
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from langfuse import Langfuse
|
|
5
|
+
|
|
6
|
+
_log_initialized = False
|
|
7
|
+
|
|
8
|
+
def setup_logging() -> None:
|
|
9
|
+
global _log_initialized
|
|
10
|
+
if not _log_initialized:
|
|
11
|
+
import colorlog
|
|
12
|
+
from dotenv import load_dotenv
|
|
13
|
+
load_dotenv()
|
|
14
|
+
|
|
15
|
+
env_log_level = os.getenv("LOG_LEVEL", "INFO")
|
|
16
|
+
env_log_file = os.getenv("LOG_FILE", "log/xga.log")
|
|
17
|
+
log_level = getattr(logging, env_log_level.upper(), logging.INFO)
|
|
18
|
+
|
|
19
|
+
log_dir = os.path.dirname(env_log_file)
|
|
20
|
+
if log_dir and not os.path.exists(log_dir):
|
|
21
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
22
|
+
else:
|
|
23
|
+
os.remove(env_log_file)
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger()
|
|
26
|
+
for handler in logger.handlers[:]:
|
|
27
|
+
logger.removeHandler(handler)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
log_colors = {
|
|
32
|
+
'DEBUG': 'cyan',
|
|
33
|
+
'INFO': 'green',
|
|
34
|
+
'WARNING': 'yellow',
|
|
35
|
+
'ERROR': 'red',
|
|
36
|
+
'CRITICAL': 'red,bg_white'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console_formatter = colorlog.ColoredFormatter('%(log_color)s%(asctime)s - %(levelname)-8s%(reset)s %(white)s%(message)s',
|
|
40
|
+
log_colors=log_colors,
|
|
41
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
file_formatter = logging.Formatter(
|
|
45
|
+
'%(asctime)s -%(levelname)-8s %(message)s',
|
|
46
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
console_handler = logging.StreamHandler()
|
|
50
|
+
console_handler.setFormatter(console_formatter)
|
|
51
|
+
|
|
52
|
+
file_handler = logging.FileHandler(env_log_file, encoding='utf-8')
|
|
53
|
+
file_handler.setFormatter(file_formatter)
|
|
54
|
+
|
|
55
|
+
logger.addHandler(console_handler)
|
|
56
|
+
logger.addHandler(file_handler)
|
|
57
|
+
|
|
58
|
+
logger.setLevel(log_level)
|
|
59
|
+
|
|
60
|
+
logging.info(f"Logger is initialized, log_level={env_log_level}, log_file={env_log_file}")
|
|
61
|
+
|
|
62
|
+
_log_initialized = True
|
|
63
|
+
|
|
64
|
+
setup_logging()
|
|
65
|
+
|
|
66
|
+
_langfuse_initialized = False
|
|
67
|
+
|
|
68
|
+
def setup_langfuse() -> Langfuse:
|
|
69
|
+
global _langfuse_initialized
|
|
70
|
+
_langfuse = None
|
|
71
|
+
if not _langfuse_initialized:
|
|
72
|
+
env_public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
|
|
73
|
+
env_secret_key = os.getenv("LANGFUSE_SECRET_KEY")
|
|
74
|
+
env_host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
|
75
|
+
if env_public_key and env_secret_key:
|
|
76
|
+
_langfuse = Langfuse(tracing_enabled=True,
|
|
77
|
+
public_key=env_public_key,
|
|
78
|
+
secret_key=env_secret_key,
|
|
79
|
+
host=env_host)
|
|
80
|
+
logging.info("Langfuse initialized Successfully by Key !")
|
|
81
|
+
else:
|
|
82
|
+
_langfuse = Langfuse(tracing_enabled=False)
|
|
83
|
+
logging.warning("Not set key, Langfuse is disabled!")
|
|
84
|
+
|
|
85
|
+
_langfuse_initialized = True
|
|
86
|
+
return _langfuse
|
|
87
|
+
|
|
88
|
+
langfuse: Langfuse = Langfuse if _langfuse_initialized else setup_langfuse()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
try:
|
|
93
|
+
trace_id = langfuse.create_trace_id()
|
|
94
|
+
print(f"trace_id={trace_id}")
|
|
95
|
+
except Exception as e:
|
|
96
|
+
handle_error(e)
|