jarvis-ai-assistant 0.1.178__py3-none-any.whl → 0.1.180__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/__init__.py +130 -79
- jarvis/jarvis_agent/builtin_input_handler.py +1 -1
- jarvis/jarvis_agent/jarvis.py +9 -13
- jarvis/jarvis_agent/main.py +4 -2
- jarvis/jarvis_code_agent/code_agent.py +34 -23
- jarvis/jarvis_code_agent/lint.py +164 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +6 -20
- jarvis/jarvis_code_analysis/code_review.py +8 -6
- jarvis/jarvis_data/config_schema.json +260 -0
- jarvis/jarvis_dev/main.py +1 -8
- jarvis/jarvis_git_details/main.py +1 -1
- jarvis/jarvis_git_squash/main.py +5 -3
- jarvis/jarvis_git_utils/git_commiter.py +25 -24
- jarvis/jarvis_mcp/sse_mcp_client.py +6 -4
- jarvis/jarvis_mcp/stdio_mcp_client.py +5 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +404 -0
- jarvis/jarvis_methodology/main.py +10 -9
- jarvis/jarvis_multi_agent/main.py +3 -1
- jarvis/jarvis_platform/base.py +14 -8
- jarvis/jarvis_platform/human.py +3 -1
- jarvis/jarvis_platform/kimi.py +8 -27
- jarvis/jarvis_platform/openai.py +4 -16
- jarvis/jarvis_platform/registry.py +6 -2
- jarvis/jarvis_platform/yuanbao.py +9 -29
- jarvis/jarvis_platform_manager/main.py +11 -9
- jarvis/jarvis_smart_shell/main.py +7 -3
- jarvis/jarvis_tools/ask_codebase.py +4 -3
- jarvis/jarvis_tools/ask_user.py +2 -1
- jarvis/jarvis_tools/base.py +3 -1
- jarvis/jarvis_tools/chdir.py +2 -1
- jarvis/jarvis_tools/cli/main.py +1 -0
- jarvis/jarvis_tools/code_plan.py +5 -3
- jarvis/jarvis_tools/create_code_agent.py +5 -2
- jarvis/jarvis_tools/create_sub_agent.py +1 -3
- jarvis/jarvis_tools/edit_file.py +4 -4
- jarvis/jarvis_tools/execute_script.py +1 -1
- jarvis/jarvis_tools/file_analyzer.py +5 -3
- jarvis/jarvis_tools/file_operation.py +4 -7
- jarvis/jarvis_tools/find_methodology.py +4 -2
- jarvis/jarvis_tools/generate_new_tool.py +2 -1
- jarvis/jarvis_tools/methodology.py +3 -4
- jarvis/jarvis_tools/read_code.py +2 -1
- jarvis/jarvis_tools/read_webpage.py +3 -1
- jarvis/jarvis_tools/registry.py +60 -45
- jarvis/jarvis_tools/rewrite_file.py +2 -1
- jarvis/jarvis_tools/search_web.py +1 -0
- jarvis/jarvis_tools/virtual_tty.py +5 -4
- jarvis/jarvis_utils/__init__.py +2 -0
- jarvis/jarvis_utils/builtin_replace_map.py +1 -1
- jarvis/jarvis_utils/config.py +88 -17
- jarvis/jarvis_utils/embedding.py +4 -3
- jarvis/jarvis_utils/file_processors.py +1 -0
- jarvis/jarvis_utils/git_utils.py +83 -40
- jarvis/jarvis_utils/globals.py +4 -2
- jarvis/jarvis_utils/input.py +14 -7
- jarvis/jarvis_utils/methodology.py +6 -4
- jarvis/jarvis_utils/output.py +10 -6
- jarvis/jarvis_utils/utils.py +140 -24
- {jarvis_ai_assistant-0.1.178.dist-info → jarvis_ai_assistant-0.1.180.dist-info}/METADATA +66 -59
- jarvis_ai_assistant-0.1.180.dist-info/RECORD +99 -0
- jarvis_ai_assistant-0.1.178.dist-info/RECORD +0 -96
- {jarvis_ai_assistant-0.1.178.dist-info → jarvis_ai_assistant-0.1.180.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.178.dist-info → jarvis_ai_assistant-0.1.180.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.178.dist-info → jarvis_ai_assistant-0.1.180.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.178.dist-info → jarvis_ai_assistant-0.1.180.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Any, Dict, List, Callable
|
|
3
|
-
import requests
|
|
4
2
|
import json
|
|
5
3
|
import threading
|
|
6
4
|
import time
|
|
7
|
-
from
|
|
8
|
-
from
|
|
5
|
+
from typing import Any, Callable, Dict, List
|
|
6
|
+
from urllib.parse import parse_qs, urlencode, urljoin
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
9
10
|
from jarvis.jarvis_mcp import McpClient
|
|
11
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class SSEMcpClient(McpClient):
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Any, Dict, List
|
|
3
|
-
import subprocess
|
|
4
|
-
import os
|
|
5
2
|
import json
|
|
6
|
-
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
|
+
|
|
7
7
|
from jarvis.jarvis_mcp import McpClient
|
|
8
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class StdioMcpClient(McpClient):
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Any, Callable, Dict, List
|
|
5
|
+
from urllib.parse import urljoin
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from jarvis.jarvis_mcp import McpClient
|
|
10
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StreamableMcpClient(McpClient):
|
|
14
|
+
"""Streamable HTTP MCP客户端实现
|
|
15
|
+
|
|
16
|
+
参数:
|
|
17
|
+
config: 配置字典,包含以下字段:
|
|
18
|
+
- base_url: str - MCP服务器的基础URL
|
|
19
|
+
- auth_token: str - 认证令牌(可选)
|
|
20
|
+
- headers: Dict[str, str] - 额外的HTTP头(可选)
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, config: Dict[str, Any]):
|
|
23
|
+
self.config = config
|
|
24
|
+
self.base_url = config.get('base_url', '')
|
|
25
|
+
if not self.base_url:
|
|
26
|
+
raise ValueError('No base_url specified in config')
|
|
27
|
+
|
|
28
|
+
# 设置HTTP客户端
|
|
29
|
+
self.session = requests.Session()
|
|
30
|
+
self.session.headers.update({
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
'Accept': 'application/json',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
# 添加认证令牌(如果提供)
|
|
36
|
+
auth_token = config.get('auth_token')
|
|
37
|
+
if auth_token:
|
|
38
|
+
self.session.headers['Authorization'] = f'Bearer {auth_token}'
|
|
39
|
+
|
|
40
|
+
# 添加额外的HTTP头
|
|
41
|
+
extra_headers = config.get('headers', {})
|
|
42
|
+
self.session.headers.update(extra_headers)
|
|
43
|
+
|
|
44
|
+
# 请求相关属性
|
|
45
|
+
self.pending_requests = {} # 存储等待响应的请求 {id: Event}
|
|
46
|
+
self.request_results = {} # 存储请求结果 {id: result}
|
|
47
|
+
self.notification_handlers = {}
|
|
48
|
+
self.event_lock = threading.Lock()
|
|
49
|
+
self.request_id_counter = 0
|
|
50
|
+
|
|
51
|
+
# 初始化连接
|
|
52
|
+
self._initialize()
|
|
53
|
+
|
|
54
|
+
def _initialize(self) -> None:
|
|
55
|
+
"""初始化MCP连接"""
|
|
56
|
+
try:
|
|
57
|
+
# 发送初始化请求
|
|
58
|
+
response = self._send_request('initialize', {
|
|
59
|
+
'processId': None, # 远程客户端不需要进程ID
|
|
60
|
+
'clientInfo': {
|
|
61
|
+
'name': 'jarvis',
|
|
62
|
+
'version': '1.0.0'
|
|
63
|
+
},
|
|
64
|
+
'capabilities': {},
|
|
65
|
+
'protocolVersion': "2025-03-26"
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
# 验证服务器响应
|
|
69
|
+
if 'result' not in response:
|
|
70
|
+
raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
|
|
71
|
+
|
|
72
|
+
# 发送initialized通知
|
|
73
|
+
self._send_notification('notifications/initialized', {})
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
|
|
77
|
+
raise
|
|
78
|
+
|
|
79
|
+
def register_notification_handler(self, method: str, handler: Callable) -> None:
|
|
80
|
+
"""注册通知处理器
|
|
81
|
+
|
|
82
|
+
参数:
|
|
83
|
+
method: 通知方法名
|
|
84
|
+
handler: 处理通知的回调函数,接收params参数
|
|
85
|
+
"""
|
|
86
|
+
with self.event_lock:
|
|
87
|
+
if method not in self.notification_handlers:
|
|
88
|
+
self.notification_handlers[method] = []
|
|
89
|
+
self.notification_handlers[method].append(handler)
|
|
90
|
+
|
|
91
|
+
def unregister_notification_handler(self, method: str, handler: Callable) -> None:
|
|
92
|
+
"""注销通知处理器
|
|
93
|
+
|
|
94
|
+
参数:
|
|
95
|
+
method: 通知方法名
|
|
96
|
+
handler: 要注销的处理器函数
|
|
97
|
+
"""
|
|
98
|
+
with self.event_lock:
|
|
99
|
+
if method in self.notification_handlers:
|
|
100
|
+
if handler in self.notification_handlers[method]:
|
|
101
|
+
self.notification_handlers[method].remove(handler)
|
|
102
|
+
if not self.notification_handlers[method]:
|
|
103
|
+
del self.notification_handlers[method]
|
|
104
|
+
|
|
105
|
+
def _get_next_request_id(self) -> str:
|
|
106
|
+
"""获取下一个请求ID"""
|
|
107
|
+
with self.event_lock:
|
|
108
|
+
self.request_id_counter += 1
|
|
109
|
+
return str(self.request_id_counter)
|
|
110
|
+
|
|
111
|
+
def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
112
|
+
"""发送请求到MCP服务器
|
|
113
|
+
|
|
114
|
+
参数:
|
|
115
|
+
method: 请求方法
|
|
116
|
+
params: 请求参数
|
|
117
|
+
|
|
118
|
+
返回:
|
|
119
|
+
Dict[str, Any]: 响应结果
|
|
120
|
+
"""
|
|
121
|
+
# 生成唯一请求ID
|
|
122
|
+
req_id = self._get_next_request_id()
|
|
123
|
+
|
|
124
|
+
# 创建事件标志,用于等待响应
|
|
125
|
+
event = threading.Event()
|
|
126
|
+
|
|
127
|
+
with self.event_lock:
|
|
128
|
+
self.pending_requests[req_id] = event
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
# 构建请求
|
|
132
|
+
request = {
|
|
133
|
+
'jsonrpc': '2.0',
|
|
134
|
+
'method': method,
|
|
135
|
+
'params': params,
|
|
136
|
+
'id': req_id
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# 发送请求到Streamable HTTP端点
|
|
140
|
+
mcp_url = urljoin(self.base_url, 'mcp')
|
|
141
|
+
response = self.session.post(
|
|
142
|
+
mcp_url,
|
|
143
|
+
json=request,
|
|
144
|
+
stream=True # 启用流式传输
|
|
145
|
+
)
|
|
146
|
+
response.raise_for_status()
|
|
147
|
+
|
|
148
|
+
# 处理流式响应
|
|
149
|
+
result = None
|
|
150
|
+
for line in response.iter_lines(decode_unicode=True):
|
|
151
|
+
if line:
|
|
152
|
+
try:
|
|
153
|
+
data = json.loads(line)
|
|
154
|
+
if 'id' in data and data['id'] == req_id:
|
|
155
|
+
# 这是我们的请求响应
|
|
156
|
+
result = data
|
|
157
|
+
break
|
|
158
|
+
elif 'method' in data:
|
|
159
|
+
# 这是一个通知
|
|
160
|
+
method = data.get('method', '')
|
|
161
|
+
params = data.get('params', {})
|
|
162
|
+
if method in self.notification_handlers:
|
|
163
|
+
for handler in self.notification_handlers[method]:
|
|
164
|
+
try:
|
|
165
|
+
handler(params)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
PrettyOutput.print(
|
|
168
|
+
f"处理通知时出错 ({method}): {e}",
|
|
169
|
+
OutputType.ERROR
|
|
170
|
+
)
|
|
171
|
+
except json.JSONDecodeError:
|
|
172
|
+
PrettyOutput.print(f"无法解析响应: {line}", OutputType.WARNING)
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
if result is None:
|
|
176
|
+
raise RuntimeError(f"未收到响应: {method}")
|
|
177
|
+
|
|
178
|
+
return result
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
|
|
182
|
+
raise
|
|
183
|
+
finally:
|
|
184
|
+
# 清理请求状态
|
|
185
|
+
with self.event_lock:
|
|
186
|
+
self.pending_requests.pop(req_id, None)
|
|
187
|
+
self.request_results.pop(req_id, None)
|
|
188
|
+
|
|
189
|
+
def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
|
|
190
|
+
"""发送通知到MCP服务器(不需要响应)
|
|
191
|
+
|
|
192
|
+
参数:
|
|
193
|
+
method: 通知方法
|
|
194
|
+
params: 通知参数
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
# 构建通知
|
|
198
|
+
notification = {
|
|
199
|
+
'jsonrpc': '2.0',
|
|
200
|
+
'method': method,
|
|
201
|
+
'params': params
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# 发送通知到Streamable HTTP端点
|
|
205
|
+
mcp_url = urljoin(self.base_url, 'mcp')
|
|
206
|
+
response = self.session.post(
|
|
207
|
+
mcp_url,
|
|
208
|
+
json=notification
|
|
209
|
+
)
|
|
210
|
+
response.raise_for_status()
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
PrettyOutput.print(f"发送通知失败: {str(e)}", OutputType.ERROR)
|
|
214
|
+
raise
|
|
215
|
+
|
|
216
|
+
def get_tool_list(self) -> List[Dict[str, Any]]:
|
|
217
|
+
"""获取工具列表
|
|
218
|
+
|
|
219
|
+
返回:
|
|
220
|
+
List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
|
|
221
|
+
- name: str - 工具名称
|
|
222
|
+
- description: str - 工具描述
|
|
223
|
+
- parameters: Dict - 工具参数
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
response = self._send_request('tools/list', {})
|
|
227
|
+
if 'result' in response and 'tools' in response['result']:
|
|
228
|
+
# 注意这里: 响应结构是 response['result']['tools']
|
|
229
|
+
tools = response['result']['tools']
|
|
230
|
+
# 将MCP协议字段转换为内部格式
|
|
231
|
+
formatted_tools = []
|
|
232
|
+
for tool in tools:
|
|
233
|
+
# 从inputSchema中提取参数定义
|
|
234
|
+
input_schema = tool.get('inputSchema', {})
|
|
235
|
+
parameters = {}
|
|
236
|
+
if 'properties' in input_schema:
|
|
237
|
+
parameters = input_schema['properties']
|
|
238
|
+
|
|
239
|
+
formatted_tools.append({
|
|
240
|
+
'name': tool.get('name', ''),
|
|
241
|
+
'description': tool.get('description', ''),
|
|
242
|
+
'parameters': parameters
|
|
243
|
+
})
|
|
244
|
+
return formatted_tools
|
|
245
|
+
else:
|
|
246
|
+
error_msg = "获取工具列表失败"
|
|
247
|
+
if 'error' in response:
|
|
248
|
+
error_msg += f": {response['error']}"
|
|
249
|
+
elif 'result' in response:
|
|
250
|
+
error_msg += f": 响应格式不正确 - {response['result']}"
|
|
251
|
+
else:
|
|
252
|
+
error_msg += ": 未知错误"
|
|
253
|
+
|
|
254
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
255
|
+
return []
|
|
256
|
+
except Exception as e:
|
|
257
|
+
PrettyOutput.print(f"获取工具列表失败: {str(e)}", OutputType.ERROR)
|
|
258
|
+
return []
|
|
259
|
+
|
|
260
|
+
def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
261
|
+
"""执行工具
|
|
262
|
+
|
|
263
|
+
参数:
|
|
264
|
+
tool_name: 工具名称
|
|
265
|
+
arguments: 参数字典,包含工具执行所需的参数
|
|
266
|
+
|
|
267
|
+
返回:
|
|
268
|
+
Dict[str, Any]: 执行结果,包含以下字段:
|
|
269
|
+
- success: bool - 是否执行成功
|
|
270
|
+
- stdout: str - 标准输出
|
|
271
|
+
- stderr: str - 标准错误
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
response = self._send_request('tools/call', {
|
|
275
|
+
'name': tool_name,
|
|
276
|
+
'arguments': arguments
|
|
277
|
+
})
|
|
278
|
+
if 'result' in response:
|
|
279
|
+
result = response['result']
|
|
280
|
+
# 从content中提取输出信息
|
|
281
|
+
stdout = ''
|
|
282
|
+
stderr = ''
|
|
283
|
+
for content in result.get('content', []):
|
|
284
|
+
if content.get('type') == 'text':
|
|
285
|
+
stdout += content.get('text', '')
|
|
286
|
+
elif content.get('type') == 'error':
|
|
287
|
+
stderr += content.get('text', '')
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
'success': True,
|
|
291
|
+
'stdout': stdout,
|
|
292
|
+
'stderr': stderr
|
|
293
|
+
}
|
|
294
|
+
else:
|
|
295
|
+
return {
|
|
296
|
+
'success': False,
|
|
297
|
+
'stdout': '',
|
|
298
|
+
'stderr': response.get('error', 'Unknown error')
|
|
299
|
+
}
|
|
300
|
+
except Exception as e:
|
|
301
|
+
PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
|
|
302
|
+
return {
|
|
303
|
+
'success': False,
|
|
304
|
+
'stdout': '',
|
|
305
|
+
'stderr': str(e)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
def get_resource_list(self) -> List[Dict[str, Any]]:
|
|
309
|
+
"""获取资源列表
|
|
310
|
+
|
|
311
|
+
返回:
|
|
312
|
+
List[Dict[str, Any]]: 资源列表,每个资源包含以下字段:
|
|
313
|
+
- uri: str - 资源的唯一标识符
|
|
314
|
+
- name: str - 资源的名称
|
|
315
|
+
- description: str - 资源的描述(可选)
|
|
316
|
+
- mimeType: str - 资源的MIME类型(可选)
|
|
317
|
+
"""
|
|
318
|
+
try:
|
|
319
|
+
response = self._send_request('resources/list', {})
|
|
320
|
+
if 'result' in response and 'resources' in response['result']:
|
|
321
|
+
return response['result']['resources']
|
|
322
|
+
else:
|
|
323
|
+
error_msg = "获取资源列表失败"
|
|
324
|
+
if 'error' in response:
|
|
325
|
+
error_msg += f": {response['error']}"
|
|
326
|
+
else:
|
|
327
|
+
error_msg += ": 未知错误"
|
|
328
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
329
|
+
return []
|
|
330
|
+
except Exception as e:
|
|
331
|
+
PrettyOutput.print(f"获取资源列表失败: {str(e)}", OutputType.ERROR)
|
|
332
|
+
return []
|
|
333
|
+
|
|
334
|
+
def get_resource(self, uri: str) -> Dict[str, Any]:
|
|
335
|
+
"""获取指定资源的内容
|
|
336
|
+
|
|
337
|
+
参数:
|
|
338
|
+
uri: str - 资源的URI标识符
|
|
339
|
+
|
|
340
|
+
返回:
|
|
341
|
+
Dict[str, Any]: 执行结果,包含以下字段:
|
|
342
|
+
- success: bool - 是否执行成功
|
|
343
|
+
- stdout: str - 资源内容(文本或base64编码的二进制内容)
|
|
344
|
+
- stderr: str - 错误信息
|
|
345
|
+
"""
|
|
346
|
+
try:
|
|
347
|
+
response = self._send_request('resources/read', {
|
|
348
|
+
'uri': uri
|
|
349
|
+
})
|
|
350
|
+
if 'result' in response and 'contents' in response['result']:
|
|
351
|
+
contents = response['result']['contents']
|
|
352
|
+
if contents:
|
|
353
|
+
content = contents[0] # 获取第一个资源内容
|
|
354
|
+
# 根据资源类型返回内容
|
|
355
|
+
if 'text' in content:
|
|
356
|
+
return {
|
|
357
|
+
'success': True,
|
|
358
|
+
'stdout': content['text'],
|
|
359
|
+
'stderr': ''
|
|
360
|
+
}
|
|
361
|
+
elif 'blob' in content:
|
|
362
|
+
return {
|
|
363
|
+
'success': True,
|
|
364
|
+
'stdout': content['blob'],
|
|
365
|
+
'stderr': ''
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
'success': False,
|
|
369
|
+
'stdout': '',
|
|
370
|
+
'stderr': '资源内容为空'
|
|
371
|
+
}
|
|
372
|
+
else:
|
|
373
|
+
error_msg = "获取资源内容失败"
|
|
374
|
+
if 'error' in response:
|
|
375
|
+
error_msg += f": {response['error']}"
|
|
376
|
+
else:
|
|
377
|
+
error_msg += ": 未知错误"
|
|
378
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
379
|
+
return {
|
|
380
|
+
'success': False,
|
|
381
|
+
'stdout': '',
|
|
382
|
+
'stderr': error_msg
|
|
383
|
+
}
|
|
384
|
+
except Exception as e:
|
|
385
|
+
error_msg = f"获取资源内容失败: {str(e)}"
|
|
386
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
387
|
+
return {
|
|
388
|
+
'success': False,
|
|
389
|
+
'stdout': '',
|
|
390
|
+
'stderr': error_msg
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
def __del__(self):
|
|
394
|
+
"""清理资源"""
|
|
395
|
+
# 清理请求状态
|
|
396
|
+
with self.event_lock:
|
|
397
|
+
for event in self.pending_requests.values():
|
|
398
|
+
event.set() # 释放所有等待的请求
|
|
399
|
+
self.pending_requests.clear()
|
|
400
|
+
self.request_results.clear()
|
|
401
|
+
|
|
402
|
+
# 关闭HTTP会话
|
|
403
|
+
if self.session:
|
|
404
|
+
self.session.close()
|
|
@@ -8,18 +8,19 @@
|
|
|
8
8
|
- 列出所有方法论
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
+
import argparse
|
|
11
12
|
import hashlib
|
|
12
|
-
import os
|
|
13
13
|
import json
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
import yaml # type: ignore
|
|
17
|
+
from yaspin import yaspin # type: ignore
|
|
18
|
+
|
|
20
19
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
21
|
-
from jarvis.jarvis_utils.
|
|
22
|
-
|
|
20
|
+
from jarvis.jarvis_utils.methodology import (_get_methodology_directory,
|
|
21
|
+
_load_all_methodologies)
|
|
22
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
23
|
+
|
|
23
24
|
|
|
24
25
|
def import_methodology(input_file):
|
|
25
26
|
"""导入方法论文件(合并策略)"""
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import yaml
|
|
3
|
+
|
|
3
4
|
from jarvis.jarvis_multi_agent import MultiAgent
|
|
4
|
-
from jarvis.jarvis_utils.utils import init_env
|
|
5
5
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
6
|
+
from jarvis.jarvis_utils.utils import init_env
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def main():
|
|
8
10
|
"""从YAML配置文件初始化并运行多智能体系统
|
jarvis/jarvis_platform/base.py
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
2
|
import re
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
4
|
from typing import Generator, List, Tuple
|
|
5
5
|
|
|
6
|
+
from rich import box
|
|
7
|
+
from rich.live import Live
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.text import Text
|
|
6
10
|
from yaspin import yaspin
|
|
7
11
|
|
|
8
|
-
from jarvis.jarvis_utils.config import get_max_input_token_count,
|
|
12
|
+
from jarvis.jarvis_utils.config import (get_max_input_token_count,
|
|
13
|
+
get_pretty_output, is_print_prompt)
|
|
9
14
|
from jarvis.jarvis_utils.embedding import split_text_into_chunks
|
|
10
15
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
11
|
-
from jarvis.jarvis_utils.
|
|
12
|
-
from jarvis.jarvis_utils.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from rich import box
|
|
16
|
+
from jarvis.jarvis_utils.tag import ct, ot
|
|
17
|
+
from jarvis.jarvis_utils.utils import (get_context_token_count,
|
|
18
|
+
is_context_overflow, while_success,
|
|
19
|
+
while_true)
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class BasePlatform(ABC):
|
|
19
23
|
"""Base class for large language models"""
|
|
@@ -122,6 +126,8 @@ class BasePlatform(ABC):
|
|
|
122
126
|
return response
|
|
123
127
|
|
|
124
128
|
def chat_until_success(self, message: str) -> str:
|
|
129
|
+
if is_print_prompt():
|
|
130
|
+
PrettyOutput.print(f"{message}", OutputType.USER)
|
|
125
131
|
return while_true(lambda: while_success(lambda: self._chat(message), 5), 5)
|
|
126
132
|
|
|
127
133
|
@abstractmethod
|
jarvis/jarvis_platform/human.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Generator, List, Tuple
|
|
3
2
|
import random
|
|
4
3
|
import string
|
|
4
|
+
from typing import Generator, List, Tuple
|
|
5
|
+
|
|
5
6
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
6
7
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
7
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
8
9
|
|
|
10
|
+
|
|
9
11
|
class HumanPlatform(BasePlatform):
|
|
10
12
|
"""人类交互平台实现,模拟大模型但实际上与人交互"""
|
|
11
13
|
|
jarvis/jarvis_platform/kimi.py
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Dict, Generator, List, Tuple
|
|
3
|
-
import requests # type: ignore
|
|
4
2
|
import json
|
|
5
|
-
import os
|
|
6
3
|
import mimetypes
|
|
4
|
+
import os
|
|
7
5
|
import time
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
from typing import Dict, Generator, List, Tuple
|
|
7
|
+
|
|
8
|
+
import requests # type: ignore
|
|
9
|
+
|
|
12
10
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
11
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
13
12
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
14
13
|
from jarvis.jarvis_utils.utils import while_success
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
|
|
17
16
|
class KimiModel(BasePlatform):
|
|
18
17
|
"""Kimi model implementation"""
|
|
@@ -34,24 +33,6 @@ class KimiModel(BasePlatform):
|
|
|
34
33
|
self.chat_id = ""
|
|
35
34
|
self.api_key = os.getenv("KIMI_API_KEY")
|
|
36
35
|
if not self.api_key:
|
|
37
|
-
message = (
|
|
38
|
-
"需要设置 KIMI_API_KEY 才能使用 Jarvis。请按照以下步骤操作:\n"
|
|
39
|
-
"1. 获取 Kimi API Key:\n"
|
|
40
|
-
" • 访问 Kimi AI 平台: https://kimi.moonshot.cn\n"
|
|
41
|
-
" • 登录您的账户\n"
|
|
42
|
-
" • 打开浏览器开发者工具 (F12 或右键 -> 检查)\n"
|
|
43
|
-
" • 切换到网络标签\n"
|
|
44
|
-
" • 发送任意消息\n"
|
|
45
|
-
" • 在请求中找到 Authorization 头\n"
|
|
46
|
-
" • 复制 token 值(去掉 'Bearer ' 前缀)\n"
|
|
47
|
-
"2. 设置环境变量:\n"
|
|
48
|
-
" • 方法 1: 创建或编辑配置文件:\n"
|
|
49
|
-
f" echo 'KIMI_API_KEY=your_key_here' > {get_data_dir()}/env\n"
|
|
50
|
-
" • 方法 2: 直接设置环境变量:\n"
|
|
51
|
-
" export KIMI_API_KEY=your_key_here\n"
|
|
52
|
-
"设置后,重新运行 Jarvis。"
|
|
53
|
-
)
|
|
54
|
-
PrettyOutput.print(message, OutputType.INFO)
|
|
55
36
|
PrettyOutput.print("KIMI_API_KEY 未设置", OutputType.WARNING)
|
|
56
37
|
self.auth_header = f"Bearer {self.api_key}"
|
|
57
38
|
self.uploaded_files = [] # 存储已上传文件的信息
|
|
@@ -183,7 +164,7 @@ class KimiModel(BasePlatform):
|
|
|
183
164
|
if not file_list:
|
|
184
165
|
return True
|
|
185
166
|
|
|
186
|
-
from yaspin import yaspin
|
|
167
|
+
from yaspin import yaspin # type: ignore
|
|
187
168
|
|
|
188
169
|
if not self.chat_id:
|
|
189
170
|
with yaspin(text="创建聊天会话...", color="yellow") as spinner:
|
jarvis/jarvis_platform/openai.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Dict, Generator, List, Tuple
|
|
3
2
|
import os
|
|
3
|
+
from typing import Dict, Generator, List, Tuple
|
|
4
|
+
|
|
4
5
|
from openai import OpenAI
|
|
6
|
+
|
|
5
7
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
6
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
9
|
|
|
10
|
+
|
|
8
11
|
class OpenAIModel(BasePlatform):
|
|
9
12
|
platform_name = "openai"
|
|
10
13
|
|
|
@@ -16,21 +19,6 @@ class OpenAIModel(BasePlatform):
|
|
|
16
19
|
self.system_message = ""
|
|
17
20
|
self.api_key = os.getenv("OPENAI_API_KEY")
|
|
18
21
|
if not self.api_key:
|
|
19
|
-
message = (
|
|
20
|
-
"需要设置以下环境变量才能使用 OpenAI 模型:\n"
|
|
21
|
-
" • OPENAI_API_KEY: API 密钥\n"
|
|
22
|
-
" • OPENAI_API_BASE: (可选) API 基础地址, 默认使用 https://api.openai.com/v1\n"
|
|
23
|
-
"您可以通过以下方式设置它们:\n"
|
|
24
|
-
"1. 创建或编辑 ~/.jarvis/env 文件:\n"
|
|
25
|
-
" OPENAI_API_KEY=your_api_key\n"
|
|
26
|
-
" OPENAI_API_BASE=your_api_base\n"
|
|
27
|
-
" OPENAI_MODEL_NAME=your_model_name\n"
|
|
28
|
-
"2. 直接设置环境变量:\n"
|
|
29
|
-
" export OPENAI_API_KEY=your_api_key\n"
|
|
30
|
-
" export OPENAI_API_BASE=your_api_base\n"
|
|
31
|
-
" export OPENAI_MODEL_NAME=your_model_name"
|
|
32
|
-
)
|
|
33
|
-
PrettyOutput.print(message, OutputType.INFO)
|
|
34
22
|
PrettyOutput.print("OPENAI_API_KEY 未设置", OutputType.WARNING)
|
|
35
23
|
|
|
36
24
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
@@ -3,9 +3,13 @@ import importlib
|
|
|
3
3
|
import inspect
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
-
from typing import Dict,
|
|
6
|
+
from typing import Dict, List, Optional, Type
|
|
7
|
+
|
|
7
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
8
|
-
from jarvis.jarvis_utils.config import
|
|
9
|
+
from jarvis.jarvis_utils.config import (get_data_dir, get_normal_model_name,
|
|
10
|
+
get_normal_platform_name,
|
|
11
|
+
get_thinking_model_name,
|
|
12
|
+
get_thinking_platform_name)
|
|
9
13
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
10
14
|
|
|
11
15
|
REQUIRED_METHODS = [
|