jarvis-ai-assistant 0.1.149__py3-none-any.whl → 0.1.150__py3-none-any.whl
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 jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/jarvis.py +7 -5
- jarvis/jarvis_lsp/registry.py +2 -1
- jarvis/jarvis_mcp/__init__.py +36 -0
- jarvis/jarvis_mcp/local_mcp_client.py +241 -0
- jarvis/jarvis_mcp/remote_mcp_client.py +230 -0
- jarvis/jarvis_platform/kimi.py +3 -2
- jarvis/jarvis_platform/registry.py +2 -4
- jarvis/jarvis_platform/yuanbao.py +4 -4
- jarvis/jarvis_tools/file_operation.py +7 -26
- jarvis/jarvis_tools/methodology.py +2 -1
- jarvis/jarvis_tools/registry.py +108 -3
- jarvis/jarvis_utils/config.py +14 -8
- jarvis/jarvis_utils/embedding.py +2 -2
- jarvis/jarvis_utils/file_processors.py +0 -262
- jarvis/jarvis_utils/input.py +7 -1
- jarvis/jarvis_utils/methodology.py +2 -2
- jarvis/jarvis_utils/utils.py +5 -5
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/METADATA +12 -16
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/RECORD +24 -21
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.149.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/jarvis.py
CHANGED
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
|
|
5
5
|
from typing import Dict # 仅保留实际使用的类型导入
|
|
6
6
|
|
|
7
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
7
8
|
from prompt_toolkit import prompt
|
|
8
9
|
import yaml
|
|
9
10
|
from yaspin import yaspin
|
|
@@ -25,13 +26,14 @@ def _load_tasks() -> Dict[str, str]:
|
|
|
25
26
|
"""Load tasks from .jarvis files in user home and current directory."""
|
|
26
27
|
tasks: Dict[str, str] = {}
|
|
27
28
|
|
|
28
|
-
# Check
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# Check pre-command in data directory
|
|
30
|
+
data_dir = get_data_dir()
|
|
31
|
+
pre_command_path = os.path.join(data_dir, "pre-command")
|
|
32
|
+
if os.path.exists(pre_command_path):
|
|
33
|
+
spinner_text = f"从{pre_command_path}加载预定义任务..."
|
|
32
34
|
with yaspin(text=spinner_text, color="cyan") as spinner:
|
|
33
35
|
try:
|
|
34
|
-
with open(
|
|
36
|
+
with open(pre_command_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
35
37
|
user_tasks = yaml.safe_load(f)
|
|
36
38
|
if isinstance(user_tasks, dict):
|
|
37
39
|
for name, desc in user_tasks.items():
|
jarvis/jarvis_lsp/registry.py
CHANGED
|
@@ -6,6 +6,7 @@ import sys
|
|
|
6
6
|
from typing import Dict, Type, Optional, List
|
|
7
7
|
from jarvis.jarvis_lsp.base import BaseLSP
|
|
8
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
9
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
9
10
|
|
|
10
11
|
REQUIRED_METHODS = [
|
|
11
12
|
('initialize', ['workspace_path']),
|
|
@@ -21,7 +22,7 @@ class LSPRegistry:
|
|
|
21
22
|
@staticmethod
|
|
22
23
|
def get_lsp_dir() -> str:
|
|
23
24
|
"""Get LSP implementation directory."""
|
|
24
|
-
user_lsp_dir = os.path.
|
|
25
|
+
user_lsp_dir = os.path.join(get_data_dir(), "lsp")
|
|
25
26
|
if not os.path.exists(user_lsp_dir):
|
|
26
27
|
try:
|
|
27
28
|
os.makedirs(user_lsp_dir)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any, Dict, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class McpClient(ABC):
|
|
6
|
+
"""MCP客户端抽象基类"""
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def get_tool_list(self) -> List[Dict[str, Any]]:
|
|
10
|
+
"""获取工具列表
|
|
11
|
+
|
|
12
|
+
返回:
|
|
13
|
+
List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
|
|
14
|
+
- name: str - 工具名称
|
|
15
|
+
- description: str - 工具描述
|
|
16
|
+
- parameters: Dict - 工具参数
|
|
17
|
+
"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
22
|
+
"""执行工具
|
|
23
|
+
|
|
24
|
+
参数:
|
|
25
|
+
tool_name: 工具名称
|
|
26
|
+
arguments: 参数字典,包含工具执行所需的参数
|
|
27
|
+
|
|
28
|
+
返回:
|
|
29
|
+
Dict[str, Any]: 执行结果,包含以下字段:
|
|
30
|
+
- success: bool - 是否执行成功
|
|
31
|
+
- stdout: str - 标准输出
|
|
32
|
+
- stderr: str - 标准错误
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
6
|
+
from . import McpClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LocalMcpClient(McpClient):
|
|
10
|
+
"""本地MCP客户端实现
|
|
11
|
+
|
|
12
|
+
参数:
|
|
13
|
+
config: 配置字典(command、args、env)
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, config: Dict[str, Any]):
|
|
16
|
+
self.config = config
|
|
17
|
+
self.process = None
|
|
18
|
+
self.protocol_version = "2025-03-26" # MCP协议版本
|
|
19
|
+
self._start_process()
|
|
20
|
+
self._initialize()
|
|
21
|
+
|
|
22
|
+
def _start_process(self) -> None:
|
|
23
|
+
"""启动MCP进程"""
|
|
24
|
+
try:
|
|
25
|
+
# 构建命令和参数
|
|
26
|
+
command = self.config.get('command', '')
|
|
27
|
+
if not command:
|
|
28
|
+
raise ValueError('No command specified in config')
|
|
29
|
+
|
|
30
|
+
# 获取参数列表
|
|
31
|
+
args = self.config.get('args', [])
|
|
32
|
+
if not isinstance(args, list):
|
|
33
|
+
args = [str(args)]
|
|
34
|
+
|
|
35
|
+
# 获取环境变量
|
|
36
|
+
env = os.environ.copy()
|
|
37
|
+
env.update(self.config.get('env', {}))
|
|
38
|
+
|
|
39
|
+
# 启动进程
|
|
40
|
+
self.process = subprocess.Popen(
|
|
41
|
+
[command] + args,
|
|
42
|
+
stdin=subprocess.PIPE,
|
|
43
|
+
stdout=subprocess.PIPE,
|
|
44
|
+
stderr=subprocess.PIPE,
|
|
45
|
+
env=env,
|
|
46
|
+
text=True
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
PrettyOutput.print(f"启动MCP进程失败: {str(e)}", OutputType.ERROR)
|
|
51
|
+
raise
|
|
52
|
+
|
|
53
|
+
def _initialize(self) -> None:
|
|
54
|
+
"""初始化MCP连接"""
|
|
55
|
+
try:
|
|
56
|
+
# 发送初始化请求
|
|
57
|
+
response = self._send_request('initialize', {
|
|
58
|
+
'processId': os.getpid(),
|
|
59
|
+
'clientInfo': {
|
|
60
|
+
'name': 'jarvis',
|
|
61
|
+
'version': '1.0.0'
|
|
62
|
+
},
|
|
63
|
+
'capabilities': {},
|
|
64
|
+
'protocolVersion': self.protocol_version
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
# 验证服务器响应
|
|
68
|
+
if 'result' not in response:
|
|
69
|
+
raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
|
|
70
|
+
|
|
71
|
+
result = response['result']
|
|
72
|
+
|
|
73
|
+
# 发送initialized通知 - 使用正确的方法名格式
|
|
74
|
+
self._send_notification('notifications/initialized', {})
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
|
|
78
|
+
raise
|
|
79
|
+
|
|
80
|
+
def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
81
|
+
"""发送请求到MCP进程
|
|
82
|
+
|
|
83
|
+
参数:
|
|
84
|
+
method: 请求方法
|
|
85
|
+
params: 请求参数
|
|
86
|
+
|
|
87
|
+
返回:
|
|
88
|
+
Dict[str, Any]: 响应结果
|
|
89
|
+
"""
|
|
90
|
+
if not self.process:
|
|
91
|
+
raise RuntimeError('MCP process not started')
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
# 构建请求
|
|
95
|
+
request = {
|
|
96
|
+
'jsonrpc': '2.0',
|
|
97
|
+
'method': method,
|
|
98
|
+
'params': params,
|
|
99
|
+
'id': 1
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# 发送请求
|
|
103
|
+
self.process.stdin.write(json.dumps(request) + '\n') # type: ignore
|
|
104
|
+
self.process.stdin.flush() # type: ignore
|
|
105
|
+
|
|
106
|
+
# 读取响应
|
|
107
|
+
response = self.process.stdout.readline() # type: ignore
|
|
108
|
+
return json.loads(response)
|
|
109
|
+
|
|
110
|
+
except Exception as e:
|
|
111
|
+
PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
|
|
112
|
+
raise
|
|
113
|
+
|
|
114
|
+
def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
|
|
115
|
+
"""发送通知到MCP进程(不需要响应)
|
|
116
|
+
|
|
117
|
+
参数:
|
|
118
|
+
method: 通知方法
|
|
119
|
+
params: 通知参数
|
|
120
|
+
"""
|
|
121
|
+
if not self.process:
|
|
122
|
+
raise RuntimeError('MCP process not started')
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
# 构建通知
|
|
126
|
+
notification = {
|
|
127
|
+
'jsonrpc': '2.0',
|
|
128
|
+
'method': method,
|
|
129
|
+
'params': params
|
|
130
|
+
}
|
|
131
|
+
# 发送通知
|
|
132
|
+
self.process.stdin.write(json.dumps(notification) + '\n') # type: ignore
|
|
133
|
+
self.process.stdin.flush() # type: ignore
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
PrettyOutput.print(f"发送通知失败: {str(e)}", OutputType.ERROR)
|
|
137
|
+
raise
|
|
138
|
+
|
|
139
|
+
def get_tool_list(self) -> List[Dict[str, Any]]:
|
|
140
|
+
"""获取工具列表
|
|
141
|
+
|
|
142
|
+
返回:
|
|
143
|
+
List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
|
|
144
|
+
- name: str - 工具名称
|
|
145
|
+
- description: str - 工具描述
|
|
146
|
+
- parameters: Dict - 工具参数
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
response = self._send_request('tools/list', {})
|
|
150
|
+
if 'result' in response and 'tools' in response['result']:
|
|
151
|
+
# 注意这里: 响应结构是 response['result']['tools']
|
|
152
|
+
tools = response['result']['tools']
|
|
153
|
+
# 将MCP协议字段转换为内部格式
|
|
154
|
+
formatted_tools = []
|
|
155
|
+
for tool in tools:
|
|
156
|
+
# 从inputSchema中提取参数定义
|
|
157
|
+
input_schema = tool.get('inputSchema', {})
|
|
158
|
+
parameters = {}
|
|
159
|
+
if 'properties' in input_schema:
|
|
160
|
+
parameters = input_schema['properties']
|
|
161
|
+
|
|
162
|
+
formatted_tools.append({
|
|
163
|
+
'name': tool.get('name', ''),
|
|
164
|
+
'description': tool.get('description', ''),
|
|
165
|
+
'parameters': parameters
|
|
166
|
+
})
|
|
167
|
+
return formatted_tools
|
|
168
|
+
else:
|
|
169
|
+
error_msg = "获取工具列表失败"
|
|
170
|
+
if 'error' in response:
|
|
171
|
+
error_msg += f": {response['error']}"
|
|
172
|
+
elif 'result' in response:
|
|
173
|
+
error_msg += f": 响应格式不正确 - {response['result']}"
|
|
174
|
+
else:
|
|
175
|
+
error_msg += ": 未知错误"
|
|
176
|
+
|
|
177
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
178
|
+
return []
|
|
179
|
+
except Exception as e:
|
|
180
|
+
PrettyOutput.print(f"获取工具列表失败: {str(e)}", OutputType.ERROR)
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
184
|
+
"""执行工具
|
|
185
|
+
|
|
186
|
+
参数:
|
|
187
|
+
tool_name: 工具名称
|
|
188
|
+
arguments: 参数字典,包含工具执行所需的参数
|
|
189
|
+
|
|
190
|
+
返回:
|
|
191
|
+
Dict[str, Any]: 执行结果,包含以下字段:
|
|
192
|
+
- success: bool - 是否执行成功
|
|
193
|
+
- stdout: str - 标准输出
|
|
194
|
+
- stderr: str - 标准错误
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
response = self._send_request('tools/call', {
|
|
198
|
+
'name': tool_name,
|
|
199
|
+
'arguments': arguments
|
|
200
|
+
})
|
|
201
|
+
if 'result' in response:
|
|
202
|
+
result = response['result']
|
|
203
|
+
# 从content中提取输出信息
|
|
204
|
+
stdout = ''
|
|
205
|
+
stderr = ''
|
|
206
|
+
for content in result.get('content', []):
|
|
207
|
+
if content.get('type') == 'text':
|
|
208
|
+
stdout += content.get('text', '')
|
|
209
|
+
elif content.get('type') == 'error':
|
|
210
|
+
stderr += content.get('text', '')
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
'success': True,
|
|
214
|
+
'stdout': stdout,
|
|
215
|
+
'stderr': stderr
|
|
216
|
+
}
|
|
217
|
+
else:
|
|
218
|
+
return {
|
|
219
|
+
'success': False,
|
|
220
|
+
'stdout': '',
|
|
221
|
+
'stderr': response.get('error', 'Unknown error')
|
|
222
|
+
}
|
|
223
|
+
except Exception as e:
|
|
224
|
+
PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
|
|
225
|
+
return {
|
|
226
|
+
'success': False,
|
|
227
|
+
'stdout': '',
|
|
228
|
+
'stderr': str(e)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
def __del__(self):
|
|
232
|
+
"""清理资源"""
|
|
233
|
+
if self.process:
|
|
234
|
+
try:
|
|
235
|
+
# 发送退出通知 - 使用通知而非请求
|
|
236
|
+
self._send_notification('notifications/exit', {})
|
|
237
|
+
# 等待进程结束
|
|
238
|
+
self.process.wait(timeout=1)
|
|
239
|
+
except:
|
|
240
|
+
# 如果进程没有正常退出,强制终止
|
|
241
|
+
self.process.kill()
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
import requests
|
|
3
|
+
import sseclient
|
|
4
|
+
from urllib.parse import urljoin
|
|
5
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
6
|
+
from . import McpClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RemoteMcpClient(McpClient):
|
|
10
|
+
"""远程MCP客户端实现
|
|
11
|
+
|
|
12
|
+
参数:
|
|
13
|
+
config: 配置字典,包含以下字段:
|
|
14
|
+
- base_url: str - MCP服务器的基础URL
|
|
15
|
+
- auth_token: str - 认证令牌(可选)
|
|
16
|
+
- headers: Dict[str, str] - 额外的HTTP头(可选)
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, config: Dict[str, Any]):
|
|
19
|
+
self.config = config
|
|
20
|
+
self.base_url = config.get('base_url', '')
|
|
21
|
+
if not self.base_url:
|
|
22
|
+
raise ValueError('No base_url specified in config')
|
|
23
|
+
|
|
24
|
+
# 设置HTTP客户端
|
|
25
|
+
self.session = requests.Session()
|
|
26
|
+
self.session.headers.update({
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'Accept': 'application/json',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
# 添加认证令牌(如果提供)
|
|
32
|
+
auth_token = config.get('auth_token')
|
|
33
|
+
if auth_token:
|
|
34
|
+
self.session.headers['Authorization'] = f'Bearer {auth_token}'
|
|
35
|
+
|
|
36
|
+
# 添加额外的HTTP头
|
|
37
|
+
extra_headers = config.get('headers', {})
|
|
38
|
+
self.session.headers.update(extra_headers)
|
|
39
|
+
|
|
40
|
+
# 初始化SSE连接
|
|
41
|
+
self.sse_client = None
|
|
42
|
+
self._initialize()
|
|
43
|
+
|
|
44
|
+
def _initialize(self) -> None:
|
|
45
|
+
"""初始化MCP连接"""
|
|
46
|
+
try:
|
|
47
|
+
# 发送初始化请求
|
|
48
|
+
response = self._send_request('initialize', {
|
|
49
|
+
'processId': None, # 远程客户端不需要进程ID
|
|
50
|
+
'clientInfo': {
|
|
51
|
+
'name': 'jarvis',
|
|
52
|
+
'version': '1.0.0'
|
|
53
|
+
},
|
|
54
|
+
'capabilities': {},
|
|
55
|
+
'protocolVersion': "2025-03-26"
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
# 验证服务器响应
|
|
59
|
+
if 'result' not in response:
|
|
60
|
+
raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
|
|
61
|
+
|
|
62
|
+
result = response['result']
|
|
63
|
+
|
|
64
|
+
# 发送initialized通知 - 使用正确的方法名格式
|
|
65
|
+
self._send_notification('notifications/initialized', {})
|
|
66
|
+
|
|
67
|
+
# 建立SSE连接
|
|
68
|
+
sse_url = urljoin(self.base_url, 'events')
|
|
69
|
+
response = self.session.get(sse_url, stream=True)
|
|
70
|
+
self.sse_client = sseclient.SSEClient(response)
|
|
71
|
+
|
|
72
|
+
except Exception as e:
|
|
73
|
+
PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
77
|
+
"""发送请求到MCP服务器
|
|
78
|
+
|
|
79
|
+
参数:
|
|
80
|
+
method: 请求方法
|
|
81
|
+
params: 请求参数
|
|
82
|
+
|
|
83
|
+
返回:
|
|
84
|
+
Dict[str, Any]: 响应结果
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
# 构建请求
|
|
88
|
+
request = {
|
|
89
|
+
'jsonrpc': '2.0',
|
|
90
|
+
'method': method,
|
|
91
|
+
'params': params,
|
|
92
|
+
'id': 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# 发送请求
|
|
96
|
+
response = self.session.post(
|
|
97
|
+
urljoin(self.base_url, 'rpc'),
|
|
98
|
+
json=request
|
|
99
|
+
)
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
return response.json()
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
|
|
108
|
+
"""发送通知到MCP服务器(不需要响应)
|
|
109
|
+
|
|
110
|
+
参数:
|
|
111
|
+
method: 通知方法
|
|
112
|
+
params: 通知参数
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
# 构建通知
|
|
116
|
+
notification = {
|
|
117
|
+
'jsonrpc': '2.0',
|
|
118
|
+
'method': method,
|
|
119
|
+
'params': params
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# 发送通知
|
|
123
|
+
response = self.session.post(
|
|
124
|
+
urljoin(self.base_url, 'rpc'),
|
|
125
|
+
json=notification
|
|
126
|
+
)
|
|
127
|
+
response.raise_for_status()
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
PrettyOutput.print(f"发送通知失败: {str(e)}", OutputType.ERROR)
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
def get_tool_list(self) -> List[Dict[str, Any]]:
|
|
134
|
+
"""获取工具列表
|
|
135
|
+
|
|
136
|
+
返回:
|
|
137
|
+
List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
|
|
138
|
+
- name: str - 工具名称
|
|
139
|
+
- description: str - 工具描述
|
|
140
|
+
- parameters: Dict - 工具参数
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
response = self._send_request('tools/list', {})
|
|
144
|
+
if 'result' in response and 'tools' in response['result']:
|
|
145
|
+
# 注意这里: 响应结构是 response['result']['tools']
|
|
146
|
+
tools = response['result']['tools']
|
|
147
|
+
# 将MCP协议字段转换为内部格式
|
|
148
|
+
formatted_tools = []
|
|
149
|
+
for tool in tools:
|
|
150
|
+
# 从inputSchema中提取参数定义
|
|
151
|
+
input_schema = tool.get('inputSchema', {})
|
|
152
|
+
parameters = {}
|
|
153
|
+
if 'properties' in input_schema:
|
|
154
|
+
parameters = input_schema['properties']
|
|
155
|
+
|
|
156
|
+
formatted_tools.append({
|
|
157
|
+
'name': tool.get('name', ''),
|
|
158
|
+
'description': tool.get('description', ''),
|
|
159
|
+
'parameters': parameters
|
|
160
|
+
})
|
|
161
|
+
return formatted_tools
|
|
162
|
+
else:
|
|
163
|
+
error_msg = "获取工具列表失败"
|
|
164
|
+
if 'error' in response:
|
|
165
|
+
error_msg += f": {response['error']}"
|
|
166
|
+
elif 'result' in response:
|
|
167
|
+
error_msg += f": 响应格式不正确 - {response['result']}"
|
|
168
|
+
else:
|
|
169
|
+
error_msg += ": 未知错误"
|
|
170
|
+
|
|
171
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
172
|
+
return []
|
|
173
|
+
except Exception as e:
|
|
174
|
+
PrettyOutput.print(f"获取工具列表失败: {str(e)}", OutputType.ERROR)
|
|
175
|
+
return []
|
|
176
|
+
|
|
177
|
+
def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
178
|
+
"""执行工具
|
|
179
|
+
|
|
180
|
+
参数:
|
|
181
|
+
tool_name: 工具名称
|
|
182
|
+
arguments: 参数字典,包含工具执行所需的参数
|
|
183
|
+
|
|
184
|
+
返回:
|
|
185
|
+
Dict[str, Any]: 执行结果,包含以下字段:
|
|
186
|
+
- success: bool - 是否执行成功
|
|
187
|
+
- stdout: str - 标准输出
|
|
188
|
+
- stderr: str - 标准错误
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
response = self._send_request('tools/call', {
|
|
192
|
+
'name': tool_name,
|
|
193
|
+
'arguments': arguments
|
|
194
|
+
})
|
|
195
|
+
if 'result' in response:
|
|
196
|
+
result = response['result']
|
|
197
|
+
# 从content中提取输出信息
|
|
198
|
+
stdout = ''
|
|
199
|
+
stderr = ''
|
|
200
|
+
for content in result.get('content', []):
|
|
201
|
+
if content.get('type') == 'text':
|
|
202
|
+
stdout += content.get('text', '')
|
|
203
|
+
elif content.get('type') == 'error':
|
|
204
|
+
stderr += content.get('text', '')
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
'success': True,
|
|
208
|
+
'stdout': stdout,
|
|
209
|
+
'stderr': stderr
|
|
210
|
+
}
|
|
211
|
+
else:
|
|
212
|
+
return {
|
|
213
|
+
'success': False,
|
|
214
|
+
'stdout': '',
|
|
215
|
+
'stderr': response.get('error', 'Unknown error')
|
|
216
|
+
}
|
|
217
|
+
except Exception as e:
|
|
218
|
+
PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
|
|
219
|
+
return {
|
|
220
|
+
'success': False,
|
|
221
|
+
'stdout': '',
|
|
222
|
+
'stderr': str(e)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
def __del__(self):
|
|
226
|
+
"""清理资源"""
|
|
227
|
+
if self.sse_client and hasattr(self.sse_client, 'resp'):
|
|
228
|
+
self.sse_client.resp.close()
|
|
229
|
+
if self.session:
|
|
230
|
+
self.session.close()
|
jarvis/jarvis_platform/kimi.py
CHANGED
|
@@ -7,6 +7,7 @@ import time
|
|
|
7
7
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
8
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
9
9
|
from jarvis.jarvis_utils.utils import while_success
|
|
10
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
10
11
|
|
|
11
12
|
class KimiModel(BasePlatform):
|
|
12
13
|
"""Kimi model implementation"""
|
|
@@ -39,8 +40,8 @@ class KimiModel(BasePlatform):
|
|
|
39
40
|
" • 在请求中找到 Authorization 头\n"
|
|
40
41
|
" • 复制 token 值(去掉 'Bearer ' 前缀)\n"
|
|
41
42
|
"2. 设置环境变量:\n"
|
|
42
|
-
" • 方法 1:
|
|
43
|
-
" echo 'KIMI_API_KEY=your_key_here' >
|
|
43
|
+
" • 方法 1: 创建或编辑配置文件:\n"
|
|
44
|
+
f" echo 'KIMI_API_KEY=your_key_here' > {get_data_dir()}/env\n"
|
|
44
45
|
" • 方法 2: 直接设置环境变量:\n"
|
|
45
46
|
" export KIMI_API_KEY=your_key_here\n"
|
|
46
47
|
"设置后,重新运行 Jarvis。"
|
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Dict, Type, Optional, List
|
|
6
6
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
7
|
-
from jarvis.jarvis_utils.config import get_normal_model_name, get_normal_platform_name, get_thinking_model_name, get_thinking_platform_name
|
|
7
|
+
from jarvis.jarvis_utils.config import get_normal_model_name, get_normal_platform_name, get_thinking_model_name, get_thinking_platform_name, get_data_dir
|
|
8
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
9
9
|
|
|
10
10
|
REQUIRED_METHODS = [
|
|
@@ -26,15 +26,13 @@ class PlatformRegistry:
|
|
|
26
26
|
|
|
27
27
|
@staticmethod
|
|
28
28
|
def get_platform_dir() -> str:
|
|
29
|
-
user_platform_dir = os.path.
|
|
29
|
+
user_platform_dir = os.path.join(get_data_dir(), "models")
|
|
30
30
|
if not os.path.exists(user_platform_dir):
|
|
31
31
|
try:
|
|
32
32
|
os.makedirs(user_platform_dir)
|
|
33
33
|
# 创建 __init__.py 使其成为 Python 包
|
|
34
34
|
with open(os.path.join(user_platform_dir, "__init__.py"), "w", errors="ignore") as f:
|
|
35
35
|
pass
|
|
36
|
-
|
|
37
|
-
pass
|
|
38
36
|
except Exception as e:
|
|
39
37
|
PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
|
|
40
38
|
return ""
|
|
@@ -6,7 +6,6 @@ import hmac
|
|
|
6
6
|
import hashlib
|
|
7
7
|
import time
|
|
8
8
|
import urllib.parse
|
|
9
|
-
from pathlib import Path
|
|
10
9
|
from PIL import Image
|
|
11
10
|
from yaspin import yaspin
|
|
12
11
|
from yaspin.spinners import Spinners
|
|
@@ -14,6 +13,7 @@ from yaspin.api import Yaspin
|
|
|
14
13
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
15
14
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
16
15
|
from jarvis.jarvis_utils.utils import while_success
|
|
16
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
17
17
|
|
|
18
18
|
class YuanbaoPlatform(BasePlatform):
|
|
19
19
|
"""Hunyuan模型实现"""
|
|
@@ -46,9 +46,9 @@ class YuanbaoPlatform(BasePlatform):
|
|
|
46
46
|
" • 发送任意消息\n"
|
|
47
47
|
" • 在请求中找到 X-Uskey 和 T-UserID 头部值\n"
|
|
48
48
|
"2. 设置环境变量:\n"
|
|
49
|
-
" • 方法 1:
|
|
50
|
-
" echo 'YUANBAO_COOKIES=your_cookies_here' >>
|
|
51
|
-
" echo 'YUANBAO_AGENT_ID=your_agent_id_here' >>
|
|
49
|
+
" • 方法 1: 创建或编辑配置文件:\n"
|
|
50
|
+
f" echo 'YUANBAO_COOKIES=your_cookies_here' >> {get_data_dir()}/env\n"
|
|
51
|
+
f" echo 'YUANBAO_AGENT_ID=your_agent_id_here' >> {get_data_dir()}/env\n"
|
|
52
52
|
" • 方法 2: 直接设置环境变量:\n"
|
|
53
53
|
" export YUANBAO_COOKIES=your_cookies_here\n"
|
|
54
54
|
" export YUANBAO_AGENT_ID=your_agent_id_here\n"
|