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