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
@@ -25,7 +25,12 @@ class YuanbaoPlatform(BasePlatform):
25
25
 
26
26
  def get_model_list(self) -> List[Tuple[str, str]]:
27
27
  """获取支持的模型列表"""
28
- return [("deep_seek", "DeepSeek-R1"), ("deep_seek_v3", "DeepSeek-v3"), ("hunyuan_gpt_175B_0404", "Tencent Hunyuan"), ("hunyuan_t1", "Tencent Hunyuan-T1")]
28
+ return [
29
+ ("deep_seek", "DeepSeek-R1"),
30
+ ("deep_seek_v3", "DeepSeek-v3"),
31
+ ("hunyuan_gpt_175B_0404", "Tencent Hunyuan"),
32
+ ("hunyuan_t1", "Tencent Hunyuan-T1"),
33
+ ]
29
34
 
30
35
  def __init__(self):
31
36
  """
@@ -61,26 +66,26 @@ class YuanbaoPlatform(BasePlatform):
61
66
  def _get_base_headers(self):
62
67
  """获取API请求的基础头部信息"""
63
68
  return {
64
- 'Host': 'yuanbao.tencent.com',
65
- 'X-Language': 'zh-CN',
66
- 'X-Requested-With': 'XMLHttpRequest',
67
- 'chat_version': 'v1',
68
- 'X-Instance-ID': '5',
69
- 'X-Requested-With': 'XMLHttpRequest',
70
- 'Accept': 'application/json, text/plain, */*',
71
- 'Content-Type': 'application/json',
72
- 'sec-ch-ua-mobile': '?0',
73
- 'Origin': 'https://yuanbao.tencent.com',
74
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0',
75
- 'Referer': f'https://yuanbao.tencent.com/chat/{self.agent_id}',
76
- 'X-Source': 'web',
77
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
78
- 'Accept': '*/*',
79
- 'Sec-Fetch-Site': 'same-origin',
80
- 'Sec-Fetch-Mode': 'cors',
81
- 'Sec-Fetch-Dest': 'empty',
82
- 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
83
- 'Cookie': self.cookies
69
+ "Host": "yuanbao.tencent.com",
70
+ "X-Language": "zh-CN",
71
+ "X-Requested-With": "XMLHttpRequest",
72
+ "chat_version": "v1",
73
+ "X-Instance-ID": "5",
74
+ "X-Requested-With": "XMLHttpRequest",
75
+ "Accept": "application/json, text/plain, */*",
76
+ "Content-Type": "application/json",
77
+ "sec-ch-ua-mobile": "?0",
78
+ "Origin": "https://yuanbao.tencent.com",
79
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0",
80
+ "Referer": f"https://yuanbao.tencent.com/chat/{self.agent_id}",
81
+ "X-Source": "web",
82
+ "Accept-Encoding": "gzip, deflate, br, zstd",
83
+ "Accept": "*/*",
84
+ "Sec-Fetch-Site": "same-origin",
85
+ "Sec-Fetch-Mode": "cors",
86
+ "Sec-Fetch-Dest": "empty",
87
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
88
+ "Cookie": self.cookies,
84
89
  }
85
90
 
86
91
  def _create_conversation(self) -> bool:
@@ -89,19 +94,21 @@ class YuanbaoPlatform(BasePlatform):
89
94
 
90
95
  headers = self._get_base_headers()
91
96
 
92
- payload = json.dumps({
93
- "agentId": self.agent_id
94
- })
97
+ payload = json.dumps({"agentId": self.agent_id})
95
98
 
96
99
  try:
97
- response = while_success(lambda: requests.post(url, headers=headers, data=payload), sleep_time=5)
100
+ response = while_success(
101
+ lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
102
+ )
98
103
  response_json = response.json()
99
104
 
100
105
  if "id" in response_json:
101
106
  self.conversation_id = response_json["id"]
102
107
  return True
103
108
  else:
104
- PrettyOutput.print(f"错误:创建会话失败,响应: {response_json}", OutputType.ERROR)
109
+ PrettyOutput.print(
110
+ f"错误:创建会话失败,响应: {response_json}", OutputType.ERROR
111
+ )
105
112
  return False
106
113
  except Exception as e:
107
114
  PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
@@ -113,53 +120,79 @@ class YuanbaoPlatform(BasePlatform):
113
120
 
114
121
  def upload_files(self, file_list: List[str]) -> bool:
115
122
  """上传文件到元宝平台
116
-
123
+
117
124
  参数:
118
125
  file_list: 要上传的文件路径列表
119
-
126
+
120
127
  返回:
121
128
  用于聊天消息的文件元数据字典列表
122
129
  """
123
130
  if not self.cookies:
124
131
  PrettyOutput.print("未设置YUANBAO_COOKIES,无法上传文件", OutputType.ERROR)
125
132
  return False
126
-
133
+
127
134
  uploaded_files = []
128
-
135
+
129
136
  for file_path in file_list:
130
137
  file_name = os.path.basename(file_path)
131
138
  with yaspin(Spinners.dots, text=f"上传文件 {file_name}") as spinner:
132
139
  try:
133
-
140
+
134
141
  # 1. Prepare the file information
135
142
  spinner.text = f"准备文件信息: {file_name}"
136
143
  file_size = os.path.getsize(file_path)
137
- file_extension = os.path.splitext(file_path)[1].lower().lstrip('.')
138
-
144
+ file_extension = os.path.splitext(file_path)[1].lower().lstrip(".")
145
+
139
146
  # Determine file_type using file extension
140
147
  file_type = "txt" # Default type
141
-
148
+
142
149
  # Image types
143
- if file_extension in ['jpg', 'jpeg', 'png', 'webp', 'bmp', 'gif']:
150
+ if file_extension in ["jpg", "jpeg", "png", "webp", "bmp", "gif"]:
144
151
  file_type = "image"
145
152
  # PDF type
146
- elif file_extension == 'pdf':
153
+ elif file_extension == "pdf":
147
154
  file_type = "pdf"
148
155
  # Spreadsheet types
149
- elif file_extension in ['xls', 'xlsx']:
156
+ elif file_extension in ["xls", "xlsx"]:
150
157
  file_type = "excel"
151
- # Presentation types
152
- elif file_extension in ['ppt', 'pptx']:
158
+ # Presentation types
159
+ elif file_extension in ["ppt", "pptx"]:
153
160
  file_type = "ppt"
154
161
  # Document types
155
- elif file_extension in ['doc', 'docx']:
162
+ elif file_extension in ["doc", "docx"]:
156
163
  file_type = "doc"
157
164
  # Code file types
158
- elif file_extension in ['bat', 'c', 'cpp', 'cs', 'css', 'go', 'h', 'hpp', 'ini',
159
- 'java', 'js', 'json', 'log', 'lua', 'php', 'pl', 'py', 'rb',
160
- 'sh', 'sql', 'swift', 'tex', 'toml', 'vue', 'yaml', 'yml', 'rs']:
165
+ elif file_extension in [
166
+ "bat",
167
+ "c",
168
+ "cpp",
169
+ "cs",
170
+ "css",
171
+ "go",
172
+ "h",
173
+ "hpp",
174
+ "ini",
175
+ "java",
176
+ "js",
177
+ "json",
178
+ "log",
179
+ "lua",
180
+ "php",
181
+ "pl",
182
+ "py",
183
+ "rb",
184
+ "sh",
185
+ "sql",
186
+ "swift",
187
+ "tex",
188
+ "toml",
189
+ "vue",
190
+ "yaml",
191
+ "yml",
192
+ "rs",
193
+ ]:
161
194
  file_type = "code"
162
-
195
+
163
196
  # 2. Generate upload information
164
197
  spinner.text = f"获取上传信息: {file_name}"
165
198
  upload_info = self._generate_upload_info(file_name)
@@ -167,15 +200,17 @@ class YuanbaoPlatform(BasePlatform):
167
200
  spinner.text = f"无法获取文件 {file_name} 的上传信息"
168
201
  spinner.fail("❌")
169
202
  return False
170
-
203
+
171
204
  # 3. Upload the file to COS
172
205
  spinner.text = f"上传文件到云存储: {file_name}"
173
- upload_success = self._upload_file_to_cos(file_path, upload_info, spinner)
206
+ upload_success = self._upload_file_to_cos(
207
+ file_path, upload_info, spinner
208
+ )
174
209
  if not upload_success:
175
210
  spinner.text = f"上传文件 {file_name} 失败"
176
211
  spinner.fail("❌")
177
212
  return False
178
-
213
+
179
214
  # 4. Create file metadata for chat
180
215
  spinner.text = f"生成文件元数据: {file_name}"
181
216
  file_metadata = {
@@ -185,9 +220,9 @@ class YuanbaoPlatform(BasePlatform):
185
220
  "fileName": file_name,
186
221
  "size": file_size,
187
222
  "width": 0,
188
- "height": 0
223
+ "height": 0,
189
224
  }
190
-
225
+
191
226
  # Get image dimensions if it's an image file
192
227
  if file_type == "image":
193
228
  try:
@@ -195,135 +230,146 @@ class YuanbaoPlatform(BasePlatform):
195
230
  file_metadata["width"] = img.width
196
231
  file_metadata["height"] = img.height
197
232
  except Exception as e:
198
- spinner.write(f"⚠️ 无法获取图片 {file_name} 的尺寸: {str(e)}")
199
-
233
+ spinner.write(
234
+ f"⚠️ 无法获取图片 {file_name} 的尺寸: {str(e)}"
235
+ )
236
+
200
237
  uploaded_files.append(file_metadata)
201
238
  spinner.text = f"文件 {file_name} 上传成功"
202
239
  spinner.ok("✅")
203
- time.sleep(3) # 上传成功后等待3秒
204
-
240
+ time.sleep(3) # 上传成功后等待3秒
241
+
205
242
  except Exception as e:
206
243
  spinner.text = f"上传文件 {file_path} 时出错: {str(e)}"
207
244
  spinner.fail("❌")
208
245
  return False
209
-
246
+
210
247
  self.multimedia = uploaded_files
211
248
  return True
212
249
 
213
-
214
250
  def _generate_upload_info(self, file_name: str) -> Dict:
215
251
  """从元宝API生成上传信息
216
-
252
+
217
253
  参数:
218
254
  file_name: 要上传的文件名
219
-
255
+
220
256
  返回:
221
257
  包含上传信息的字典,如果失败则返回空字典
222
258
  """
223
259
  url = "https://yuanbao.tencent.com/api/resource/genUploadInfo"
224
-
260
+
225
261
  headers = self._get_base_headers()
226
-
227
- payload = {
228
- "fileName": file_name,
229
- "docFrom": "localDoc",
230
- "docOpenId": ""
231
- }
232
-
262
+
263
+ payload = {"fileName": file_name, "docFrom": "localDoc", "docOpenId": ""}
264
+
233
265
  try:
234
266
  response = while_success(
235
- lambda: requests.post(url, headers=headers, json=payload),
236
- sleep_time=5
267
+ lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
237
268
  )
238
-
269
+
239
270
  if response.status_code != 200:
240
- PrettyOutput.print(f"获取上传信息失败,状态码: {response.status_code}", OutputType.ERROR)
241
- if hasattr(response, 'text'):
271
+ PrettyOutput.print(
272
+ f"获取上传信息失败,状态码: {response.status_code}",
273
+ OutputType.ERROR,
274
+ )
275
+ if hasattr(response, "text"):
242
276
  PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
243
277
  return {}
244
-
278
+
245
279
  upload_info = response.json()
246
280
  return upload_info
247
-
281
+
248
282
  except Exception as e:
249
283
  PrettyOutput.print(f"获取上传信息时出错: {str(e)}", OutputType.ERROR)
250
284
  return {}
251
-
252
- def _upload_file_to_cos(self, file_path: str, upload_info: Dict, spinner: Yaspin) -> bool:
285
+
286
+ def _upload_file_to_cos(
287
+ self, file_path: str, upload_info: Dict, spinner: Yaspin
288
+ ) -> bool:
253
289
  """使用提供的上传信息将文件上传到腾讯COS
254
-
290
+
255
291
  参数:
256
292
  file_path: 要上传的文件路径
257
293
  upload_info: 从generate_upload_info获取的上传信息
258
-
294
+
259
295
  返回:
260
296
  布尔值表示成功或失败
261
297
  """
262
298
  try:
263
299
  # Extract required information from upload_info
264
300
  bucket_url = f"https://{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
265
- object_path = upload_info.get('location', '')
301
+ object_path = upload_info.get("location", "")
266
302
  url = f"{bucket_url}{object_path}"
267
-
303
+
268
304
  # Security credentials
269
- tmp_secret_id = upload_info.get('encryptTmpSecretId', '')
270
- tmp_secret_key = upload_info.get('encryptTmpSecretKey', '')
271
- token = upload_info.get('encryptToken', '')
272
- start_time = upload_info.get('startTime', int(time.time()))
273
- expired_time = upload_info.get('expiredTime', start_time + 600)
305
+ tmp_secret_id = upload_info.get("encryptTmpSecretId", "")
306
+ tmp_secret_key = upload_info.get("encryptTmpSecretKey", "")
307
+ token = upload_info.get("encryptToken", "")
308
+ start_time = upload_info.get("startTime", int(time.time()))
309
+ expired_time = upload_info.get("expiredTime", start_time + 600)
274
310
  key_time = f"{start_time};{expired_time}"
275
-
311
+
276
312
  # Read file content
277
- with open(file_path, 'rb') as file:
313
+ with open(file_path, "rb") as file:
278
314
  file_content = file.read()
279
315
 
280
316
  spinner.write(f"ℹ️ 上传文件大小: {len(file_content)}")
281
-
317
+
282
318
  # Prepare headers for PUT request
283
319
  host = f"{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
284
320
  headers = {
285
- 'Host': host,
286
- 'Content-Length': str(len(file_content)),
287
- 'Content-Type': 'application/octet-stream',
288
- 'x-cos-security-token': token
321
+ "Host": host,
322
+ "Content-Length": str(len(file_content)),
323
+ "Content-Type": "application/octet-stream",
324
+ "x-cos-security-token": token,
289
325
  }
290
-
326
+
291
327
  # Generate signature for COS request (per Tencent Cloud documentation)
292
328
  signature = self._generate_cos_signature(
293
329
  secret_key=tmp_secret_key,
294
330
  method="PUT",
295
331
  path=urllib.parse.quote(object_path),
296
332
  params={},
297
- headers={'host': host, 'content-length': headers['Content-Length']},
298
- key_time=key_time
333
+ headers={"host": host, "content-length": headers["Content-Length"]},
334
+ key_time=key_time,
299
335
  )
300
-
336
+
301
337
  # Add Authorization header with signature
302
- headers['Authorization'] = (
338
+ headers["Authorization"] = (
303
339
  f"q-sign-algorithm=sha1&q-ak={tmp_secret_id}&q-sign-time={key_time}&"
304
340
  f"q-key-time={key_time}&q-header-list=content-length;host&"
305
341
  f"q-url-param-list=&q-signature={signature}"
306
342
  )
307
-
343
+
308
344
  # Upload the file
309
345
  response = requests.put(url, headers=headers, data=file_content)
310
-
346
+
311
347
  if response.status_code not in [200, 204]:
312
- PrettyOutput.print(f"文件上传到COS失败,状态码: {response.status_code}", OutputType.ERROR)
313
- if hasattr(response, 'text'):
348
+ PrettyOutput.print(
349
+ f"文件上传到COS失败,状态码: {response.status_code}",
350
+ OutputType.ERROR,
351
+ )
352
+ if hasattr(response, "text"):
314
353
  PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
315
354
  return False
316
-
355
+
317
356
  return True
318
-
357
+
319
358
  except Exception as e:
320
359
  PrettyOutput.print(f"上传文件到COS时出错: {str(e)}", OutputType.ERROR)
321
360
  return False
322
-
323
- def _generate_cos_signature(self, secret_key: str, method: str, path: str,
324
- params: Dict, headers: Dict, key_time: str) -> str:
361
+
362
+ def _generate_cos_signature(
363
+ self,
364
+ secret_key: str,
365
+ method: str,
366
+ path: str,
367
+ params: Dict,
368
+ headers: Dict,
369
+ key_time: str,
370
+ ) -> str:
325
371
  """根据腾讯云COS文档生成COS签名
326
-
372
+
327
373
  参数:
328
374
  secret_key: 临时密钥
329
375
  method: HTTP方法(GET, PUT等)
@@ -331,50 +377,56 @@ class YuanbaoPlatform(BasePlatform):
331
377
  params: URL参数
332
378
  headers: HTTP头部
333
379
  key_time: 签名时间范围
334
-
380
+
335
381
  返回:
336
382
  签名字符串
337
383
  """
338
384
  try:
339
385
  # 1. Generate SignKey
340
386
  sign_key = hmac.new(
341
- secret_key.encode('utf-8'),
342
- key_time.encode('utf-8'),
343
- hashlib.sha1
387
+ secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1
344
388
  ).hexdigest()
345
-
389
+
346
390
  # 2. Format parameters and headers
347
- formatted_params = '&'.join([f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
348
- for k, v in sorted(params.items())])
349
-
350
- formatted_headers = '&'.join([f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
351
- for k, v in sorted(headers.items())])
352
-
391
+ formatted_params = "&".join(
392
+ [
393
+ f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
394
+ for k, v in sorted(params.items())
395
+ ]
396
+ )
397
+
398
+ formatted_headers = "&".join(
399
+ [
400
+ f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
401
+ for k, v in sorted(headers.items())
402
+ ]
403
+ )
404
+
353
405
  # 3. Generate HttpString
354
- http_string = f"{method.lower()}\n{path}\n{formatted_params}\n{formatted_headers}\n"
355
-
406
+ http_string = (
407
+ f"{method.lower()}\n{path}\n{formatted_params}\n{formatted_headers}\n"
408
+ )
409
+
356
410
  # 4. Generate StringToSign
357
411
  string_to_sign = f"sha1\n{key_time}\n{hashlib.sha1(http_string.encode('utf-8')).hexdigest()}\n"
358
-
412
+
359
413
  # 5. Generate Signature
360
414
  signature = hmac.new(
361
- sign_key.encode('utf-8'),
362
- string_to_sign.encode('utf-8'),
363
- hashlib.sha1
415
+ sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1
364
416
  ).hexdigest()
365
-
417
+
366
418
  return signature
367
-
419
+
368
420
  except Exception as e:
369
421
  PrettyOutput.print(f"生成签名时出错: {str(e)}", OutputType.ERROR)
370
422
  raise e
371
423
 
372
424
  def chat(self, message: str) -> Generator[str, None, None]:
373
425
  """发送消息并获取响应,可选文件附件
374
-
426
+
375
427
  参数:
376
428
  message: 要发送的消息文本
377
-
429
+
378
430
  返回:
379
431
  模型的响应
380
432
  """
@@ -389,7 +441,9 @@ class YuanbaoPlatform(BasePlatform):
389
441
  chat_model_ext_info = {
390
442
  "modelId": self.model_name,
391
443
  "subModelId": "",
392
- "supportFunctions": ["openInternetSearch"] if self.web else ["autoInternetSearch"],
444
+ "supportFunctions": (
445
+ ["openInternetSearch"] if self.web else ["autoInternetSearch"]
446
+ ),
393
447
  }
394
448
 
395
449
  # 准备消息内容
@@ -403,7 +457,7 @@ class YuanbaoPlatform(BasePlatform):
403
457
  "imageIntention": {
404
458
  "needIntentionModel": True,
405
459
  "backendUpdateFlag": 2,
406
- "intentionStatus": True
460
+ "intentionStatus": True,
407
461
  }
408
462
  },
409
463
  "multimedia": self.multimedia,
@@ -415,11 +469,10 @@ class YuanbaoPlatform(BasePlatform):
415
469
  }
416
470
 
417
471
  if self.first_chat:
418
- payload["chatModelExtInfo"] = json.dumps(chat_model_ext_info),
472
+ payload["chatModelExtInfo"] = (json.dumps(chat_model_ext_info),)
419
473
 
420
474
  self.multimedia = []
421
475
 
422
-
423
476
  # 添加系统消息(如果是第一次对话)
424
477
  if self.first_chat and self.system_message:
425
478
  payload["prompt"] = f"{self.system_message}\n\n{message}"
@@ -430,16 +483,16 @@ class YuanbaoPlatform(BasePlatform):
430
483
  # 发送消息请求,获取流式响应
431
484
  response = while_success(
432
485
  lambda: requests.post(url, headers=headers, json=payload, stream=True),
433
- sleep_time=5
486
+ sleep_time=5,
434
487
  )
435
488
 
436
489
  # 检查响应状态
437
490
  if response.status_code != 200:
438
491
  error_msg = f"发送消息失败,状态码: {response.status_code}"
439
- if hasattr(response, 'text'):
492
+ if hasattr(response, "text"):
440
493
  error_msg += f", 响应: {response.text}"
441
494
  raise Exception(error_msg)
442
-
495
+
443
496
  in_thinking = False
444
497
 
445
498
  # 处理SSE流响应
@@ -447,7 +500,7 @@ class YuanbaoPlatform(BasePlatform):
447
500
  if not line:
448
501
  continue
449
502
 
450
- line_str = line.decode('utf-8')
503
+ line_str = line.decode("utf-8")
451
504
 
452
505
  # SSE格式的行通常以"data: "开头
453
506
  if line_str.startswith("data: "):
@@ -496,25 +549,25 @@ class YuanbaoPlatform(BasePlatform):
496
549
  headers = self._get_base_headers()
497
550
 
498
551
  # 更新X-AgentID头部,需要包含会话ID
499
- headers.update({
500
- 'X-AgentID': f"{self.agent_id}/{self.conversation_id}"
501
- })
552
+ headers.update({"X-AgentID": f"{self.agent_id}/{self.conversation_id}"})
502
553
 
503
554
  # 创建请求体,包含要删除的会话ID
504
- payload = {
505
- "conversationIds": [self.conversation_id]
506
- }
555
+ payload = {"conversationIds": [self.conversation_id]}
507
556
 
508
557
  try:
509
- response = while_success(lambda: requests.post(url, headers=headers, json=payload), sleep_time=5)
558
+ response = while_success(
559
+ lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
560
+ )
510
561
 
511
562
  if response.status_code == 200:
512
563
  self.conversation_id = ""
513
564
  self.first_chat = True
514
565
  return True
515
566
  else:
516
- PrettyOutput.print(f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING)
517
- if hasattr(response, 'text'):
567
+ PrettyOutput.print(
568
+ f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
569
+ )
570
+ if hasattr(response, "text"):
518
571
  PrettyOutput.print(f"响应: {response.text}", OutputType.WARNING)
519
572
  return False
520
573
  except Exception as e:
@@ -524,7 +577,7 @@ class YuanbaoPlatform(BasePlatform):
524
577
  def name(self) -> str:
525
578
  """模型名称"""
526
579
  return self.model_name
527
-
580
+
528
581
  def support_web(self) -> bool:
529
582
  """Yuanbao平台支持web功能"""
530
583
  return True
@@ -374,7 +374,6 @@ def role_command(args):
374
374
 
375
375
  def service_command(args):
376
376
  """Process service subcommand - start OpenAI-compatible API server"""
377
- import json
378
377
  import os
379
378
  import time
380
379
  import uuid
@@ -784,7 +783,10 @@ def main():
784
783
  # role subcommand
785
784
  role_parser = subparsers.add_parser("role", help="加载角色配置文件并开始对话")
786
785
  role_parser.add_argument(
787
- "--config", "-c", default="~/.jarvis/roles.yaml", help="角色配置文件路径(YAML格式,默认: ~/.jarvis/roles.yaml)"
786
+ "--config",
787
+ "-c",
788
+ default="~/.jarvis/roles.yaml",
789
+ help="角色配置文件路径(YAML格式,默认: ~/.jarvis/roles.yaml)",
788
790
  )
789
791
 
790
792
  args = parser.parse_args()