jarvis-ai-assistant 0.1.192__py3-none-any.whl → 0.1.194__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +69 -37
- jarvis/jarvis_agent/builtin_input_handler.py +26 -4
- jarvis/jarvis_agent/jarvis.py +38 -22
- jarvis/jarvis_agent/main.py +20 -12
- jarvis/jarvis_agent/output_handler.py +7 -7
- jarvis/jarvis_agent/shell_input_handler.py +14 -11
- jarvis/jarvis_code_agent/code_agent.py +93 -90
- jarvis/jarvis_code_agent/lint.py +92 -105
- jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
- jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
- jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
- jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
- jarvis/jarvis_code_analysis/checklists/go.py +1 -1
- jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
- jarvis/jarvis_code_analysis/checklists/java.py +1 -1
- jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
- jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
- jarvis/jarvis_code_analysis/checklists/loader.py +51 -35
- jarvis/jarvis_code_analysis/checklists/php.py +1 -1
- jarvis/jarvis_code_analysis/checklists/python.py +1 -1
- jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
- jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
- jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
- jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
- jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
- jarvis/jarvis_code_analysis/checklists/web.py +1 -1
- jarvis/jarvis_code_analysis/code_review.py +293 -192
- jarvis/jarvis_dev/main.py +73 -56
- jarvis/jarvis_git_details/main.py +29 -33
- jarvis/jarvis_git_squash/main.py +13 -11
- jarvis/jarvis_git_utils/git_commiter.py +12 -3
- jarvis/jarvis_mcp/__init__.py +8 -10
- jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
- jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
- jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
- jarvis/jarvis_methodology/main.py +81 -47
- jarvis/jarvis_multi_agent/__init__.py +24 -16
- jarvis/jarvis_multi_agent/main.py +10 -4
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/base.py +49 -21
- jarvis/jarvis_platform/human.py +5 -3
- jarvis/jarvis_platform/kimi.py +96 -72
- jarvis/jarvis_platform/openai.py +23 -28
- jarvis/jarvis_platform/registry.py +50 -33
- jarvis/jarvis_platform/tongyi.py +16 -10
- jarvis/jarvis_platform/yuanbao.py +205 -147
- jarvis/jarvis_platform_manager/main.py +4 -2
- jarvis/jarvis_smart_shell/main.py +35 -29
- jarvis/jarvis_tools/ask_user.py +8 -16
- jarvis/jarvis_tools/base.py +3 -2
- jarvis/jarvis_tools/chdir.py +7 -19
- jarvis/jarvis_tools/cli/main.py +14 -10
- jarvis/jarvis_tools/code_plan.py +10 -31
- jarvis/jarvis_tools/create_code_agent.py +10 -13
- jarvis/jarvis_tools/create_sub_agent.py +10 -22
- jarvis/jarvis_tools/edit_file.py +98 -76
- jarvis/jarvis_tools/execute_script.py +46 -46
- jarvis/jarvis_tools/file_analyzer.py +22 -34
- jarvis/jarvis_tools/file_operation.py +69 -62
- jarvis/jarvis_tools/generate_new_tool.py +0 -2
- jarvis/jarvis_tools/methodology.py +19 -23
- jarvis/jarvis_tools/read_code.py +42 -33
- jarvis/jarvis_tools/read_webpage.py +7 -16
- jarvis/jarvis_tools/registry.py +65 -32
- jarvis/jarvis_tools/rewrite_file.py +26 -29
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +133 -122
- jarvis/jarvis_utils/__init__.py +0 -1
- jarvis/jarvis_utils/builtin_replace_map.py +96 -8
- jarvis/jarvis_utils/config.py +59 -32
- jarvis/jarvis_utils/embedding.py +17 -14
- jarvis/jarvis_utils/file_processors.py +16 -9
- jarvis/jarvis_utils/git_utils.py +140 -99
- jarvis/jarvis_utils/globals.py +1 -1
- jarvis/jarvis_utils/input.py +84 -52
- jarvis/jarvis_utils/methodology.py +28 -21
- jarvis/jarvis_utils/output.py +159 -78
- jarvis/jarvis_utils/tag.py +2 -1
- jarvis/jarvis_utils/utils.py +85 -51
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
- jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
- jarvis/jarvis_agent/file_input_handler.py +0 -112
- jarvis/jarvis_event/__init__.py +0 -0
- jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/top_level.txt +0 -0
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -8,7 +8,6 @@ from typing import Dict, Generator, List, Tuple
|
|
8
8
|
import requests # type: ignore
|
9
9
|
|
10
10
|
from jarvis.jarvis_platform.base import BasePlatform
|
11
|
-
from jarvis.jarvis_utils.config import get_data_dir
|
12
11
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
13
12
|
from jarvis.jarvis_utils.utils import while_success
|
14
13
|
|
@@ -22,8 +21,8 @@ class KimiModel(BasePlatform):
|
|
22
21
|
"""Get model list"""
|
23
22
|
return [
|
24
23
|
("kimi", "基于网页的 Kimi,免费接口"),
|
25
|
-
("k1", "基于网页的 Kimi,深度思考模型")
|
26
|
-
|
24
|
+
("k1", "基于网页的 Kimi,深度思考模型"),
|
25
|
+
]
|
27
26
|
|
28
27
|
def __init__(self):
|
29
28
|
"""
|
@@ -52,45 +51,47 @@ class KimiModel(BasePlatform):
|
|
52
51
|
def _create_chat(self) -> bool:
|
53
52
|
"""Create a new chat session"""
|
54
53
|
url = "https://kimi.moonshot.cn/api/chat"
|
55
|
-
payload = json.dumps(
|
56
|
-
"name": "Unnamed session",
|
57
|
-
|
58
|
-
"kimiplus_id": "kimi"
|
59
|
-
})
|
54
|
+
payload = json.dumps(
|
55
|
+
{"name": "Unnamed session", "is_example": False, "kimiplus_id": "kimi"}
|
56
|
+
)
|
60
57
|
headers = {
|
61
|
-
|
62
|
-
|
58
|
+
"Authorization": self.auth_header,
|
59
|
+
"Content-Type": "application/json",
|
63
60
|
}
|
64
61
|
try:
|
65
|
-
response = while_success(
|
62
|
+
response = while_success(
|
63
|
+
lambda: requests.request("POST", url, headers=headers, data=payload),
|
64
|
+
sleep_time=5,
|
65
|
+
)
|
66
66
|
if response.status_code != 200:
|
67
|
-
PrettyOutput.print(
|
67
|
+
PrettyOutput.print(
|
68
|
+
f"错误:创建会话失败:{response.json()}", OutputType.ERROR
|
69
|
+
)
|
68
70
|
return False
|
69
71
|
self.chat_id = response.json()["id"]
|
70
72
|
return True
|
71
73
|
except Exception as e:
|
72
74
|
PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
|
73
75
|
return False
|
74
|
-
|
76
|
+
|
75
77
|
def _get_presigned_url(self, filename: str, action: str) -> Dict:
|
76
78
|
"""Get presigned upload URL"""
|
77
79
|
url = "https://kimi.moonshot.cn/api/pre-sign-url"
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
"name": os.path.basename(filename)
|
84
|
-
}, ensure_ascii=False)
|
85
|
-
|
80
|
+
|
81
|
+
payload = json.dumps(
|
82
|
+
{"action": action, "name": os.path.basename(filename)}, ensure_ascii=False
|
83
|
+
)
|
84
|
+
|
86
85
|
headers = {
|
87
|
-
|
88
|
-
|
86
|
+
"Authorization": self.auth_header,
|
87
|
+
"Content-Type": "application/json",
|
89
88
|
}
|
90
|
-
|
91
|
-
response = while_success(
|
89
|
+
|
90
|
+
response = while_success(
|
91
|
+
lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
|
92
|
+
)
|
92
93
|
return response.json()
|
93
|
-
|
94
|
+
|
94
95
|
def support_upload_files(self) -> bool:
|
95
96
|
"""Check if platform supports upload files"""
|
96
97
|
return True
|
@@ -98,9 +99,11 @@ class KimiModel(BasePlatform):
|
|
98
99
|
def _upload_file(self, file_path: str, presigned_url: str) -> bool:
|
99
100
|
"""Upload file to presigned URL"""
|
100
101
|
try:
|
101
|
-
with open(file_path,
|
102
|
+
with open(file_path, "rb") as f:
|
102
103
|
content = f.read()
|
103
|
-
response = while_success(
|
104
|
+
response = while_success(
|
105
|
+
lambda: requests.put(presigned_url, data=content), sleep_time=5
|
106
|
+
)
|
104
107
|
return response.status_code == 200
|
105
108
|
except Exception as e:
|
106
109
|
PrettyOutput.print(f"错误:上传文件失败:{e}", OutputType.ERROR)
|
@@ -109,45 +112,53 @@ class KimiModel(BasePlatform):
|
|
109
112
|
def _get_file_info(self, file_data: Dict, name: str, file_type: str) -> Dict:
|
110
113
|
"""Get file information"""
|
111
114
|
url = "https://kimi.moonshot.cn/api/file"
|
112
|
-
payload = json.dumps(
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
115
|
+
payload = json.dumps(
|
116
|
+
{
|
117
|
+
"type": file_type,
|
118
|
+
"name": name,
|
119
|
+
"object_name": file_data["object_name"],
|
120
|
+
"chat_id": self.chat_id,
|
121
|
+
"file_id": file_data.get("file_id", ""),
|
122
|
+
},
|
123
|
+
ensure_ascii=False,
|
124
|
+
)
|
125
|
+
|
120
126
|
headers = {
|
121
|
-
|
122
|
-
|
127
|
+
"Authorization": self.auth_header,
|
128
|
+
"Content-Type": "application/json",
|
123
129
|
}
|
124
|
-
|
125
|
-
response = while_success(
|
130
|
+
|
131
|
+
response = while_success(
|
132
|
+
lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
|
133
|
+
)
|
126
134
|
return response.json()
|
127
135
|
|
128
136
|
def _wait_for_parse(self, file_id: str) -> bool:
|
129
137
|
"""Wait for file parsing to complete"""
|
130
138
|
url = "https://kimi.moonshot.cn/api/file/parse_process"
|
131
139
|
headers = {
|
132
|
-
|
133
|
-
|
140
|
+
"Authorization": self.auth_header,
|
141
|
+
"Content-Type": "application/json",
|
134
142
|
}
|
135
|
-
|
143
|
+
|
136
144
|
max_retries = 30
|
137
145
|
retry_count = 0
|
138
|
-
|
146
|
+
|
139
147
|
while retry_count < max_retries:
|
140
148
|
payload = json.dumps({"ids": [file_id]}, ensure_ascii=False)
|
141
|
-
response = while_success(
|
142
|
-
|
149
|
+
response = while_success(
|
150
|
+
lambda: requests.post(url, headers=headers, data=payload, stream=True),
|
151
|
+
sleep_time=5,
|
152
|
+
)
|
153
|
+
|
143
154
|
for line in response.iter_lines():
|
144
155
|
if not line:
|
145
156
|
continue
|
146
|
-
|
147
|
-
line = line.decode(
|
157
|
+
|
158
|
+
line = line.decode("utf-8")
|
148
159
|
if not line.startswith("data: "):
|
149
160
|
continue
|
150
|
-
|
161
|
+
|
151
162
|
try:
|
152
163
|
data = json.loads(line[6:])
|
153
164
|
if data.get("event") == "resp":
|
@@ -158,18 +169,19 @@ class KimiModel(BasePlatform):
|
|
158
169
|
return False
|
159
170
|
except json.JSONDecodeError:
|
160
171
|
continue
|
161
|
-
|
172
|
+
|
162
173
|
retry_count += 1
|
163
174
|
time.sleep(1)
|
164
|
-
|
175
|
+
|
165
176
|
return False
|
177
|
+
|
166
178
|
def upload_files(self, file_list: List[str]) -> bool:
|
167
179
|
"""Upload file list and return file information"""
|
168
180
|
if not file_list:
|
169
181
|
return True
|
170
182
|
|
171
183
|
from yaspin import yaspin # type: ignore
|
172
|
-
|
184
|
+
|
173
185
|
if not self.chat_id:
|
174
186
|
with yaspin(text="创建聊天会话...", color="yellow") as spinner:
|
175
187
|
if not self._create_chat():
|
@@ -182,22 +194,30 @@ class KimiModel(BasePlatform):
|
|
182
194
|
uploaded_files = []
|
183
195
|
for index, file_path in enumerate(file_list, 1):
|
184
196
|
file_name = os.path.basename(file_path)
|
185
|
-
with yaspin(
|
197
|
+
with yaspin(
|
198
|
+
text=f"处理文件 [{index}/{len(file_list)}]: {file_name}", color="yellow"
|
199
|
+
) as spinner:
|
186
200
|
try:
|
187
201
|
mime_type, _ = mimetypes.guess_type(file_path)
|
188
|
-
action =
|
189
|
-
|
202
|
+
action = (
|
203
|
+
"image"
|
204
|
+
if mime_type and mime_type.startswith("image/")
|
205
|
+
else "file"
|
206
|
+
)
|
207
|
+
|
190
208
|
# 获取预签名URL
|
191
209
|
spinner.text = f"获取上传URL: {file_name}"
|
192
210
|
presigned_data = self._get_presigned_url(file_path, action)
|
193
|
-
|
211
|
+
|
194
212
|
# 上传文件
|
195
213
|
spinner.text = f"上传文件: {file_name}"
|
196
214
|
if self._upload_file(file_path, presigned_data["url"]):
|
197
215
|
# 获取文件信息
|
198
216
|
spinner.text = f"获取文件信息: {file_name}"
|
199
|
-
file_info = self._get_file_info(
|
200
|
-
|
217
|
+
file_info = self._get_file_info(
|
218
|
+
presigned_data, file_name, action
|
219
|
+
)
|
220
|
+
|
201
221
|
# 只有文件需要解析
|
202
222
|
if action == "file":
|
203
223
|
spinner.text = f"等待文件解析: {file_name}"
|
@@ -211,21 +231,20 @@ class KimiModel(BasePlatform):
|
|
211
231
|
return False
|
212
232
|
else:
|
213
233
|
uploaded_files.append(file_info)
|
214
|
-
spinner.write(
|
234
|
+
spinner.write(f"✅图片处理完成: {file_name}")
|
215
235
|
else:
|
216
236
|
spinner.text = f"文件上传失败: {file_name}"
|
217
237
|
spinner.fail("❌")
|
218
238
|
return False
|
219
|
-
|
239
|
+
|
220
240
|
except Exception as e:
|
221
241
|
spinner.text = f"处理文件出错 {file_path}: {str(e)}"
|
222
242
|
spinner.fail("❌")
|
223
243
|
return False
|
224
|
-
|
244
|
+
|
225
245
|
self.uploaded_files = uploaded_files
|
226
246
|
return True
|
227
247
|
|
228
|
-
|
229
248
|
def chat(self, message: str) -> Generator[str, None, None]:
|
230
249
|
"""Send message and get response"""
|
231
250
|
if not self.chat_id:
|
@@ -258,18 +277,21 @@ class KimiModel(BasePlatform):
|
|
258
277
|
}
|
259
278
|
|
260
279
|
headers = {
|
261
|
-
|
262
|
-
|
280
|
+
"Authorization": self.auth_header,
|
281
|
+
"Content-Type": "application/json",
|
263
282
|
}
|
264
283
|
|
265
284
|
try:
|
266
|
-
response = while_success(
|
285
|
+
response = while_success(
|
286
|
+
lambda: requests.post(url, headers=headers, json=payload, stream=True),
|
287
|
+
sleep_time=5,
|
288
|
+
)
|
267
289
|
# 如果禁止输出,则静默处理
|
268
290
|
for line in response.iter_lines():
|
269
291
|
if not line:
|
270
292
|
continue
|
271
293
|
|
272
|
-
line = line.decode(
|
294
|
+
line = line.decode("utf-8")
|
273
295
|
if not line.startswith("data: "):
|
274
296
|
continue
|
275
297
|
|
@@ -285,7 +307,6 @@ class KimiModel(BasePlatform):
|
|
285
307
|
except json.JSONDecodeError:
|
286
308
|
continue
|
287
309
|
|
288
|
-
|
289
310
|
return None
|
290
311
|
|
291
312
|
except Exception as e:
|
@@ -298,25 +319,28 @@ class KimiModel(BasePlatform):
|
|
298
319
|
|
299
320
|
url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}"
|
300
321
|
headers = {
|
301
|
-
|
302
|
-
|
322
|
+
"Authorization": self.auth_header,
|
323
|
+
"Content-Type": "application/json",
|
303
324
|
}
|
304
325
|
|
305
326
|
try:
|
306
|
-
response = while_success(
|
327
|
+
response = while_success(
|
328
|
+
lambda: requests.delete(url, headers=headers), sleep_time=5
|
329
|
+
)
|
307
330
|
if response.status_code == 200:
|
308
331
|
self.chat_id = ""
|
309
332
|
self.uploaded_files = []
|
310
333
|
self.first_chat = True # 重置first_chat标记
|
311
334
|
return True
|
312
335
|
else:
|
313
|
-
PrettyOutput.print(
|
336
|
+
PrettyOutput.print(
|
337
|
+
f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
|
338
|
+
)
|
314
339
|
return False
|
315
340
|
except Exception as e:
|
316
341
|
PrettyOutput.print(f"删除会话时发生错误: {str(e)}", OutputType.ERROR)
|
317
342
|
return False
|
318
343
|
|
319
|
-
|
320
344
|
def name(self) -> str:
|
321
345
|
"""Model name"""
|
322
346
|
return self.model_name
|
jarvis/jarvis_platform/openai.py
CHANGED
@@ -22,23 +22,19 @@ class OpenAIModel(BasePlatform):
|
|
22
22
|
PrettyOutput.print("OPENAI_API_KEY 未设置", OutputType.WARNING)
|
23
23
|
|
24
24
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
25
|
-
self.model_name =
|
25
|
+
self.model_name = os.getenv("JARVIS_MODEL") or "gpt-4o"
|
26
26
|
|
27
|
-
|
28
|
-
self.client = OpenAI(
|
29
|
-
api_key=self.api_key,
|
30
|
-
base_url=self.base_url
|
31
|
-
)
|
27
|
+
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
32
28
|
self.messages: List[Dict[str, str]] = []
|
33
29
|
self.system_message = ""
|
34
30
|
|
35
31
|
def upload_files(self, file_list: List[str]) -> bool:
|
36
32
|
"""
|
37
33
|
上传文件到OpenAI平台
|
38
|
-
|
34
|
+
|
39
35
|
参数:
|
40
36
|
file_list: 需要上传的文件路径列表
|
41
|
-
|
37
|
+
|
42
38
|
返回:
|
43
39
|
bool: 上传是否成功 (当前实现始终返回False)
|
44
40
|
"""
|
@@ -47,10 +43,10 @@ class OpenAIModel(BasePlatform):
|
|
47
43
|
def get_model_list(self) -> List[Tuple[str, str]]:
|
48
44
|
"""
|
49
45
|
获取可用的OpenAI模型列表
|
50
|
-
|
46
|
+
|
51
47
|
返回:
|
52
48
|
List[Tuple[str, str]]: 模型ID和名称的元组列表
|
53
|
-
|
49
|
+
|
54
50
|
异常:
|
55
51
|
当API调用失败时会打印错误信息并返回空列表
|
56
52
|
"""
|
@@ -67,7 +63,7 @@ class OpenAIModel(BasePlatform):
|
|
67
63
|
def set_model_name(self, model_name: str):
|
68
64
|
"""
|
69
65
|
设置当前使用的模型名称
|
70
|
-
|
66
|
+
|
71
67
|
参数:
|
72
68
|
model_name: 要设置的模型名称
|
73
69
|
"""
|
@@ -77,10 +73,10 @@ class OpenAIModel(BasePlatform):
|
|
77
73
|
def set_system_prompt(self, message: str):
|
78
74
|
"""
|
79
75
|
设置系统消息(角色设定)
|
80
|
-
|
76
|
+
|
81
77
|
参数:
|
82
78
|
message: 系统消息内容
|
83
|
-
|
79
|
+
|
84
80
|
说明:
|
85
81
|
设置后会立即添加到消息历史中
|
86
82
|
"""
|
@@ -90,13 +86,13 @@ class OpenAIModel(BasePlatform):
|
|
90
86
|
def chat(self, message: str) -> Generator[str, None, None]:
|
91
87
|
"""
|
92
88
|
执行对话并返回生成器
|
93
|
-
|
89
|
+
|
94
90
|
参数:
|
95
91
|
message: 用户输入的消息内容
|
96
|
-
|
92
|
+
|
97
93
|
返回:
|
98
94
|
Generator[str, None, None]: 生成器,逐块返回AI响应内容
|
99
|
-
|
95
|
+
|
100
96
|
异常:
|
101
97
|
当API调用失败时会抛出异常并打印错误信息
|
102
98
|
"""
|
@@ -107,9 +103,9 @@ class OpenAIModel(BasePlatform):
|
|
107
103
|
|
108
104
|
response = self.client.chat.completions.create(
|
109
105
|
model=self.model_name, # Use the configured model name
|
110
|
-
messages=self.messages,
|
111
|
-
stream=True
|
112
|
-
)
|
106
|
+
messages=self.messages, # type: ignore
|
107
|
+
stream=True,
|
108
|
+
) # type: ignore
|
113
109
|
|
114
110
|
full_response = ""
|
115
111
|
for chunk in response:
|
@@ -130,20 +126,19 @@ class OpenAIModel(BasePlatform):
|
|
130
126
|
def name(self) -> str:
|
131
127
|
"""
|
132
128
|
获取当前使用的模型名称
|
133
|
-
|
129
|
+
|
134
130
|
返回:
|
135
131
|
str: 当前配置的模型名称
|
136
132
|
"""
|
137
133
|
return self.model_name
|
138
134
|
|
139
|
-
|
140
|
-
def delete_chat(self)->bool:
|
135
|
+
def delete_chat(self) -> bool:
|
141
136
|
"""
|
142
137
|
删除当前对话历史
|
143
|
-
|
138
|
+
|
144
139
|
返回:
|
145
140
|
bool: 操作是否成功
|
146
|
-
|
141
|
+
|
147
142
|
说明:
|
148
143
|
如果设置了系统消息,会保留系统消息
|
149
144
|
"""
|
@@ -156,17 +151,17 @@ class OpenAIModel(BasePlatform):
|
|
156
151
|
def support_web(self) -> bool:
|
157
152
|
"""
|
158
153
|
检查是否支持网页访问功能
|
159
|
-
|
154
|
+
|
160
155
|
返回:
|
161
156
|
bool: 当前是否支持网页访问 (OpenAI平台始终返回False)
|
162
157
|
"""
|
163
158
|
return False
|
164
|
-
|
159
|
+
|
165
160
|
def support_upload_files(self) -> bool:
|
166
161
|
"""
|
167
162
|
检查是否支持上传文件功能
|
168
|
-
|
163
|
+
|
169
164
|
返回:
|
170
165
|
bool: 当前是否支持上传文件 (OpenAI平台始终返回False)
|
171
166
|
"""
|
172
|
-
return False
|
167
|
+
return False
|
@@ -6,27 +6,31 @@ import sys
|
|
6
6
|
from typing import Dict, List, Optional, Type
|
7
7
|
|
8
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
9
|
-
from jarvis.jarvis_utils.config import (
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
from jarvis.jarvis_utils.config import (
|
10
|
+
get_data_dir,
|
11
|
+
get_normal_model_name,
|
12
|
+
get_normal_platform_name,
|
13
|
+
get_thinking_model_name,
|
14
|
+
get_thinking_platform_name,
|
15
|
+
)
|
13
16
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
14
17
|
|
15
18
|
REQUIRED_METHODS = [
|
16
|
-
(
|
17
|
-
(
|
18
|
-
(
|
19
|
-
(
|
20
|
-
(
|
21
|
-
(
|
22
|
-
(
|
19
|
+
("chat", ["message"]), # 方法名和参数列表
|
20
|
+
("name", []),
|
21
|
+
("delete_chat", []),
|
22
|
+
("set_system_prompt", ["message"]),
|
23
|
+
("set_model_name", ["model_name"]),
|
24
|
+
("get_model_list", []),
|
25
|
+
("upload_files", ["file_list"]),
|
23
26
|
]
|
24
27
|
|
28
|
+
|
25
29
|
class PlatformRegistry:
|
26
30
|
"""Platform registry"""
|
27
31
|
|
28
32
|
global_platform_name: str = "yuanbao"
|
29
|
-
global_platform_registry: Optional[
|
33
|
+
global_platform_registry: Optional["PlatformRegistry"] = None
|
30
34
|
|
31
35
|
@staticmethod
|
32
36
|
def get_platform_dir() -> str:
|
@@ -35,7 +39,9 @@ class PlatformRegistry:
|
|
35
39
|
try:
|
36
40
|
os.makedirs(user_platform_dir)
|
37
41
|
# 创建 __init__.py 使其成为 Python 包
|
38
|
-
with open(
|
42
|
+
with open(
|
43
|
+
os.path.join(user_platform_dir, "__init__.py"), "w", errors="ignore"
|
44
|
+
) as f:
|
39
45
|
pass
|
40
46
|
except Exception as e:
|
41
47
|
PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
|
@@ -66,15 +72,16 @@ class PlatformRegistry:
|
|
66
72
|
|
67
73
|
# 检查方法参数
|
68
74
|
import inspect
|
75
|
+
|
69
76
|
sig = inspect.signature(method)
|
70
|
-
method_params = [p for p in sig.parameters if p !=
|
77
|
+
method_params = [p for p in sig.parameters if p != "self"]
|
71
78
|
if len(method_params) != len(params):
|
72
79
|
missing_methods.append(f"{method_name}(parameter mismatch)")
|
73
80
|
|
74
81
|
if missing_methods:
|
75
82
|
PrettyOutput.print(
|
76
83
|
f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
77
|
-
OutputType.WARNING
|
84
|
+
OutputType.WARNING,
|
78
85
|
)
|
79
86
|
return False
|
80
87
|
|
@@ -108,35 +115,40 @@ class PlatformRegistry:
|
|
108
115
|
|
109
116
|
# 遍历目录下的所有.py文件
|
110
117
|
for filename in os.listdir(directory):
|
111
|
-
if filename.endswith(
|
118
|
+
if filename.endswith(".py") and not filename.startswith("__"):
|
112
119
|
module_name = filename[:-3] # 移除.py后缀
|
113
120
|
try:
|
114
121
|
# 导入模块
|
115
122
|
if package_name:
|
116
|
-
module = importlib.import_module(
|
123
|
+
module = importlib.import_module(
|
124
|
+
f"{package_name}.{module_name}"
|
125
|
+
)
|
117
126
|
else:
|
118
127
|
module = importlib.import_module(module_name)
|
119
128
|
|
120
129
|
# 遍历模块中的所有类
|
121
130
|
for _, obj in inspect.getmembers(module):
|
122
131
|
# 检查是否是BasePlatform的子类,但不是BasePlatform本身
|
123
|
-
if (
|
124
|
-
|
125
|
-
obj
|
126
|
-
|
132
|
+
if (
|
133
|
+
inspect.isclass(obj)
|
134
|
+
and issubclass(obj, BasePlatform)
|
135
|
+
and obj != BasePlatform
|
136
|
+
and hasattr(obj, "platform_name")
|
137
|
+
):
|
127
138
|
# 检查平台实现
|
128
139
|
if not PlatformRegistry.check_platform_implementation(obj):
|
129
140
|
continue
|
130
|
-
platforms[obj.platform_name] = obj
|
141
|
+
platforms[obj.platform_name] = obj # type: ignore
|
131
142
|
break
|
132
143
|
except Exception as e:
|
133
|
-
PrettyOutput.print(
|
144
|
+
PrettyOutput.print(
|
145
|
+
f"加载平台 {module_name} 失败: {str(e)}", OutputType.ERROR
|
146
|
+
)
|
134
147
|
|
135
148
|
return platforms
|
136
149
|
|
137
|
-
|
138
150
|
@staticmethod
|
139
|
-
def get_global_platform_registry() ->
|
151
|
+
def get_global_platform_registry() -> "PlatformRegistry":
|
140
152
|
"""Get global platform registry"""
|
141
153
|
if PlatformRegistry.global_platform_registry is None:
|
142
154
|
PlatformRegistry.global_platform_registry = PlatformRegistry()
|
@@ -148,27 +160,32 @@ class PlatformRegistry:
|
|
148
160
|
# 从用户平台目录加载额外平台
|
149
161
|
platform_dir = PlatformRegistry.get_platform_dir()
|
150
162
|
if platform_dir and os.path.exists(platform_dir):
|
151
|
-
for
|
163
|
+
for (
|
164
|
+
platform_name,
|
165
|
+
platform_class,
|
166
|
+
) in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
152
167
|
self.register_platform(platform_name, platform_class)
|
153
168
|
platform_dir = os.path.dirname(__file__)
|
154
169
|
if platform_dir and os.path.exists(platform_dir):
|
155
|
-
for
|
170
|
+
for (
|
171
|
+
platform_name,
|
172
|
+
platform_class,
|
173
|
+
) in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
156
174
|
self.register_platform(platform_name, platform_class)
|
157
175
|
|
158
|
-
|
159
176
|
def get_normal_platform(self) -> BasePlatform:
|
160
177
|
platform_name = get_normal_platform_name()
|
161
178
|
model_name = get_normal_model_name()
|
162
179
|
platform = self.create_platform(platform_name)
|
163
|
-
platform.set_model_name(model_name)
|
164
|
-
return platform
|
180
|
+
platform.set_model_name(model_name) # type: ignore
|
181
|
+
return platform # type: ignore
|
165
182
|
|
166
183
|
def get_thinking_platform(self) -> BasePlatform:
|
167
184
|
platform_name = get_thinking_platform_name()
|
168
185
|
model_name = get_thinking_model_name()
|
169
186
|
platform = self.create_platform(platform_name)
|
170
|
-
platform.set_model_name(model_name)
|
171
|
-
return platform
|
187
|
+
platform.set_model_name(model_name) # type: ignore
|
188
|
+
return platform # type: ignore
|
172
189
|
|
173
190
|
def register_platform(self, name: str, platform_class: Type[BasePlatform]) -> None:
|
174
191
|
"""Register platform class
|
@@ -203,4 +220,4 @@ class PlatformRegistry:
|
|
203
220
|
|
204
221
|
def get_available_platforms(self) -> List[str]:
|
205
222
|
"""Get available platform list"""
|
206
|
-
return list(self.platforms.keys())
|
223
|
+
return list(self.platforms.keys())
|