jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.209__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 (42) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +63 -103
  3. jarvis/jarvis_agent/edit_file_handler.py +43 -47
  4. jarvis/jarvis_agent/jarvis.py +33 -39
  5. jarvis/jarvis_code_agent/code_agent.py +74 -30
  6. jarvis/jarvis_code_agent/lint.py +6 -6
  7. jarvis/jarvis_code_analysis/code_review.py +164 -175
  8. jarvis/jarvis_data/config_schema.json +0 -25
  9. jarvis/jarvis_git_utils/git_commiter.py +148 -153
  10. jarvis/jarvis_methodology/main.py +70 -81
  11. jarvis/jarvis_platform/base.py +21 -17
  12. jarvis/jarvis_platform/kimi.py +59 -64
  13. jarvis/jarvis_platform/tongyi.py +118 -131
  14. jarvis/jarvis_platform/yuanbao.py +117 -122
  15. jarvis/jarvis_platform_manager/main.py +102 -502
  16. jarvis/jarvis_platform_manager/service.py +432 -0
  17. jarvis/jarvis_smart_shell/main.py +99 -33
  18. jarvis/jarvis_tools/ask_user.py +0 -1
  19. jarvis/jarvis_tools/edit_file.py +64 -55
  20. jarvis/jarvis_tools/file_analyzer.py +17 -28
  21. jarvis/jarvis_tools/read_code.py +80 -81
  22. jarvis/jarvis_utils/builtin_replace_map.py +1 -36
  23. jarvis/jarvis_utils/config.py +13 -48
  24. jarvis/jarvis_utils/embedding.py +6 -51
  25. jarvis/jarvis_utils/git_utils.py +93 -43
  26. jarvis/jarvis_utils/http.py +104 -0
  27. jarvis/jarvis_utils/methodology.py +12 -17
  28. jarvis/jarvis_utils/utils.py +186 -63
  29. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
  30. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
  31. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
  32. jarvis/jarvis_data/huggingface.tar.gz +0 -0
  33. jarvis/jarvis_dev/main.py +0 -1247
  34. jarvis/jarvis_tools/chdir.py +0 -72
  35. jarvis/jarvis_tools/code_plan.py +0 -218
  36. jarvis/jarvis_tools/create_code_agent.py +0 -95
  37. jarvis/jarvis_tools/create_sub_agent.py +0 -82
  38. jarvis/jarvis_tools/file_operation.py +0 -238
  39. jarvis/jarvis_utils/jarvis_history.py +0 -98
  40. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
  41. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
  42. {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
@@ -7,9 +7,8 @@ import os
7
7
  import time
8
8
  from typing import Dict, Generator, List, Tuple
9
9
 
10
- import requests # type: ignore
11
-
12
10
  from jarvis.jarvis_platform.base import BasePlatform
11
+ from jarvis.jarvis_utils import http
13
12
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
13
  from jarvis.jarvis_utils.utils import while_success
15
14
 
@@ -67,7 +66,7 @@ class KimiModel(BasePlatform):
67
66
  }
68
67
  try:
69
68
  response = while_success(
70
- lambda: requests.request("POST", url, headers=headers, data=payload),
69
+ lambda: http.post(url, headers=headers, data=payload),
71
70
  sleep_time=5,
72
71
  )
73
72
  if response.status_code != 200:
@@ -95,7 +94,7 @@ class KimiModel(BasePlatform):
95
94
  }
96
95
 
97
96
  response = while_success(
98
- lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
97
+ lambda: http.post(url, headers=headers, data=payload), sleep_time=5
99
98
  )
100
99
  return response.json()
101
100
 
@@ -109,7 +108,7 @@ class KimiModel(BasePlatform):
109
108
  with open(file_path, "rb") as f:
110
109
  content = f.read()
111
110
  response = while_success(
112
- lambda: requests.put(presigned_url, data=content), sleep_time=5
111
+ lambda: http.put(presigned_url, data=content), sleep_time=5
113
112
  )
114
113
  return response.status_code == 200
115
114
  except Exception as e:
@@ -136,7 +135,7 @@ class KimiModel(BasePlatform):
136
135
  }
137
136
 
138
137
  response = while_success(
139
- lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
138
+ lambda: http.post(url, headers=headers, data=payload), sleep_time=5
140
139
  )
141
140
  return response.json()
142
141
 
@@ -154,7 +153,7 @@ class KimiModel(BasePlatform):
154
153
  while retry_count < max_retries:
155
154
  payload = json.dumps({"ids": [file_id]}, ensure_ascii=False)
156
155
  response = while_success(
157
- lambda: requests.post(url, headers=headers, data=payload, stream=True),
156
+ lambda: http.post(url, headers=headers, data=payload, stream=True),
158
157
  sleep_time=5,
159
158
  )
160
159
 
@@ -162,7 +161,12 @@ class KimiModel(BasePlatform):
162
161
  if not line:
163
162
  continue
164
163
 
165
- line = line.decode("utf-8")
164
+ # httpx 返回字符串,requests 返回字节,需要兼容处理
165
+ if isinstance(line, bytes):
166
+ line = line.decode("utf-8")
167
+ else:
168
+ line = str(line)
169
+
166
170
  if not line.startswith("data: "):
167
171
  continue
168
172
 
@@ -187,68 +191,54 @@ class KimiModel(BasePlatform):
187
191
  if not file_list:
188
192
  return True
189
193
 
190
- from yaspin import yaspin # type: ignore
191
-
192
194
  if not self.chat_id:
193
- with yaspin(text="创建聊天会话...", color="yellow") as spinner:
194
- if not self._create_chat():
195
- yaspin.text = "创建聊天会话失败"
196
- spinner.fail("❌")
197
- return False
198
- spinner.text = "创建聊天会话成功"
199
- spinner.ok("✅")
195
+ print("🚀 正在创建聊天会话...")
196
+ if not self._create_chat():
197
+ print("❌ 创建聊天会话失败")
198
+ return False
199
+ print("✅ 创建聊天会话成功")
200
200
 
201
201
  uploaded_files = []
202
202
  for index, file_path in enumerate(file_list, 1):
203
203
  file_name = os.path.basename(file_path)
204
- with yaspin(
205
- text=f"处理文件 [{index}/{len(file_list)}]: {file_name}", color="yellow"
206
- ) as spinner:
207
- try:
208
- mime_type, _ = mimetypes.guess_type(file_path)
209
- action = (
210
- "image"
211
- if mime_type and mime_type.startswith("image/")
212
- else "file"
213
- )
214
-
215
- # 获取预签名URL
216
- spinner.text = f"获取上传URL: {file_name}"
217
- presigned_data = self._get_presigned_url(file_path, action)
218
-
219
- # 上传文件
220
- spinner.text = f"上传文件: {file_name}"
221
- if self._upload_file(file_path, presigned_data["url"]):
222
- # 获取文件信息
223
- spinner.text = f"获取文件信息: {file_name}"
224
- file_info = self._get_file_info(
225
- presigned_data, file_name, action
226
- )
227
-
228
- # 只有文件需要解析
229
- if action == "file":
230
- spinner.text = f"等待文件解析: {file_name}"
231
- if self._wait_for_parse(file_info["id"]):
232
- uploaded_files.append(file_info)
233
- spinner.text = f"文件处理完成: {file_name}"
234
- spinner.ok("✅")
235
- else:
236
- spinner.text = f"❌文件解析失败: {file_name}"
237
- spinner.fail("")
238
- return False
239
- else:
204
+ print(f"🔍 处理文件 [{index}/{len(file_list)}]: {file_name}")
205
+ try:
206
+ mime_type, _ = mimetypes.guess_type(file_path)
207
+ action = (
208
+ "image" if mime_type and mime_type.startswith("image/") else "file"
209
+ )
210
+
211
+ # 获取预签名URL
212
+ print(f"🔍 获取上传URL: {file_name}")
213
+ presigned_data = self._get_presigned_url(file_path, action)
214
+
215
+ # 上传文件
216
+ print(f"🔍 上传文件: {file_name}")
217
+ if self._upload_file(file_path, presigned_data["url"]):
218
+ # 获取文件信息
219
+ print(f"🔍 获取文件信息: {file_name}")
220
+ file_info = self._get_file_info(presigned_data, file_name, action)
221
+
222
+ # 只有文件需要解析
223
+ if action == "file":
224
+ print(f"🔍 等待文件解析: {file_name}")
225
+ if self._wait_for_parse(file_info["id"]):
240
226
  uploaded_files.append(file_info)
241
- spinner.write(f"✅图片处理完成: {file_name}")
227
+ print(f" 文件处理完成: {file_name}")
228
+ else:
229
+ print(f"❌ 文件解析失败: {file_name}")
230
+ return False
242
231
  else:
243
- spinner.text = f"文件上传失败: {file_name}"
244
- spinner.fail("")
245
- return False
246
-
247
- except Exception as e:
248
- spinner.text = f"处理文件出错 {file_path}: {str(e)}"
249
- spinner.fail("❌")
232
+ uploaded_files.append(file_info)
233
+ print(f"✅ 图片处理完成: {file_name}")
234
+ else:
235
+ print(f"❌ 文件上传失败: {file_name}")
250
236
  return False
251
237
 
238
+ except Exception as e:
239
+ print(f"❌ 处理文件出错 {file_path}: {str(e)}")
240
+ return False
241
+
252
242
  self.uploaded_files = uploaded_files
253
243
  return True
254
244
 
@@ -295,7 +285,7 @@ class KimiModel(BasePlatform):
295
285
 
296
286
  try:
297
287
  response = while_success(
298
- lambda: requests.post(url, headers=headers, json=payload, stream=True),
288
+ lambda: http.post(url, headers=headers, json=payload, stream=True),
299
289
  sleep_time=5,
300
290
  )
301
291
  # 如果禁止输出,则静默处理
@@ -303,7 +293,12 @@ class KimiModel(BasePlatform):
303
293
  if not line:
304
294
  continue
305
295
 
306
- line = line.decode("utf-8")
296
+ # httpx 返回字符串,requests 返回字节,需要兼容处理
297
+ if isinstance(line, bytes):
298
+ line = line.decode("utf-8")
299
+ else:
300
+ line = str(line)
301
+
307
302
  if not line.startswith("data: "):
308
303
  continue
309
304
 
@@ -337,7 +332,7 @@ class KimiModel(BasePlatform):
337
332
 
338
333
  try:
339
334
  response = while_success(
340
- lambda: requests.delete(url, headers=headers), sleep_time=5
335
+ lambda: http.delete(url, headers=headers), sleep_time=5
341
336
  )
342
337
  if response.status_code == 200:
343
338
  self.chat_id = ""
@@ -5,11 +5,8 @@ import time
5
5
  import uuid
6
6
  from typing import Any, Dict, Generator, List, Tuple
7
7
 
8
- import requests
9
- from yaspin import yaspin
10
- from yaspin.spinners import Spinners
11
-
12
8
  from jarvis.jarvis_platform.base import BasePlatform
9
+ from jarvis.jarvis_utils import http
13
10
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
11
  from jarvis.jarvis_utils.utils import while_success
15
12
 
@@ -163,7 +160,7 @@ class TongyiPlatform(BasePlatform):
163
160
 
164
161
  try:
165
162
  response = while_success(
166
- lambda: requests.post(url, headers=headers, json=payload, stream=True),
163
+ lambda: http.post(url, headers=headers, json=payload, stream=True),
167
164
  sleep_time=5,
168
165
  )
169
166
  if response.status_code != 200:
@@ -176,7 +173,13 @@ class TongyiPlatform(BasePlatform):
176
173
  for line in response.iter_lines():
177
174
  if not line:
178
175
  continue
179
- line_str = line.decode("utf-8")
176
+
177
+ # httpx 返回字符串,requests 返回字节,需要兼容处理
178
+ if isinstance(line, bytes):
179
+ line_str = line.decode("utf-8")
180
+ else:
181
+ line_str = str(line)
182
+
180
183
  if not line_str.startswith("data: "):
181
184
  continue
182
185
 
@@ -260,7 +263,7 @@ class TongyiPlatform(BasePlatform):
260
263
 
261
264
  try:
262
265
  response = while_success(
263
- lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
266
+ lambda: http.post(url, headers=headers, json=payload), sleep_time=5
264
267
  )
265
268
  if response.status_code != 200:
266
269
  raise Exception(f"HTTP {response.status_code}: {response.text}")
@@ -289,140 +292,124 @@ class TongyiPlatform(BasePlatform):
289
292
 
290
293
  for file_path in file_list:
291
294
  file_name = os.path.basename(file_path)
292
- with yaspin(Spinners.dots, text=f"上传文件 {file_name}") as spinner:
293
- try:
294
- if not os.path.exists(file_path):
295
- spinner.text = f"文件不存在: {file_path}"
296
- spinner.fail("❌")
297
- return False
298
-
299
- # Get file name and content type
300
- content_type = self._get_content_type(file_path)
301
-
302
- spinner.text = f"准备上传文件: {file_name}"
303
-
304
- # Prepare form data
305
- form_data = {
306
- "OSSAccessKeyId": upload_token["accessId"],
307
- "policy": upload_token["policy"],
308
- "signature": upload_token["signature"],
309
- "key": f"{upload_token['dir']}{file_name}",
310
- "dir": upload_token["dir"],
311
- "success_action_status": "200",
312
- }
295
+ print(f"🔍 上传文件 {file_name}")
296
+ try:
297
+ if not os.path.exists(file_path):
298
+ print(f"文件不存在: {file_path}")
299
+ return False
313
300
 
314
- # Prepare files
315
- files = {
316
- "file": (file_name, open(file_path, "rb"), content_type)
317
- }
301
+ # Get file name and content type
302
+ content_type = self._get_content_type(file_path)
318
303
 
319
- spinner.text = f"正在上传文件: {file_name}"
304
+ print(f"🔍 准备上传文件: {file_name}")
320
305
 
321
- # Upload file
322
- response = requests.post(
323
- upload_token["host"], data=form_data, files=files
324
- )
306
+ # Prepare form data
307
+ form_data = {
308
+ "OSSAccessKeyId": upload_token["accessId"],
309
+ "policy": upload_token["policy"],
310
+ "signature": upload_token["signature"],
311
+ "key": f"{upload_token['dir']}{file_name}",
312
+ "dir": upload_token["dir"],
313
+ "success_action_status": "200",
314
+ }
325
315
 
326
- if response.status_code != 200:
327
- spinner.text = (
328
- f"上传失败 {file_name}: HTTP {response.status_code}"
329
- )
330
- spinner.fail("❌")
331
- return False
332
-
333
- # Determine file type based on extension
334
- file_ext = os.path.splitext(file_path)[1].lower()
335
- is_image = file_ext in self.IMAGE_EXTENSIONS
336
-
337
- uploaded_files.append(
338
- {
339
- "fileKey": file_name,
340
- "fileType": "image" if is_image else "file",
341
- "dir": upload_token["dir"],
342
- }
343
- )
316
+ # Prepare files
317
+ files = {"file": (file_name, open(file_path, "rb"), content_type)}
318
+
319
+ print(f"📤 正在上传文件: {file_name}")
320
+
321
+ # Upload file
322
+ response = http.post(
323
+ upload_token["host"], data=form_data, files=files
324
+ )
325
+
326
+ if response.status_code != 200:
327
+ print(f"❌ 上传失败 {file_name}: HTTP {response.status_code}")
328
+ return False
329
+
330
+ # Determine file type based on extension
331
+ file_ext = os.path.splitext(file_path)[1].lower()
332
+ is_image = file_ext in self.IMAGE_EXTENSIONS
344
333
 
345
- spinner.text = f"获取下载链接: {file_name}"
346
-
347
- # Get download links for uploaded files
348
- url = "https://api.tongyi.com/dialog/downloadLink/batch"
349
- headers = self._get_base_headers()
350
- payload = {
351
- "fileKeys": [f["fileKey"] for f in uploaded_files],
352
- "fileType": (
353
- "image"
354
- if any(f["fileType"] == "image" for f in uploaded_files)
355
- else "file"
356
- ),
334
+ uploaded_files.append(
335
+ {
336
+ "fileKey": file_name,
337
+ "fileType": "image" if is_image else "file",
357
338
  "dir": upload_token["dir"],
358
339
  }
340
+ )
341
+
342
+ print(f"🔍 获取下载链接: {file_name}")
343
+
344
+ # Get download links for uploaded files
345
+ url = "https://api.tongyi.com/dialog/downloadLink/batch"
346
+ headers = self._get_base_headers()
347
+ payload = {
348
+ "fileKeys": [f["fileKey"] for f in uploaded_files],
349
+ "fileType": (
350
+ "image"
351
+ if any(f["fileType"] == "image" for f in uploaded_files)
352
+ else "file"
353
+ ),
354
+ "dir": upload_token["dir"],
355
+ }
359
356
 
360
- response = requests.post(url, headers=headers, json=payload)
361
- if response.status_code != 200:
362
- spinner.text = (
363
- f"获取下载链接失败: HTTP {response.status_code}"
364
- )
365
- spinner.fail("❌")
366
- return False
367
-
368
- result = response.json()
369
- if not result.get("success"):
370
- spinner.text = f"获取下载链接失败: {result.get('errorMsg')}"
371
- spinner.fail("")
372
- return False
373
-
374
- # Add files to chat
375
- self.uploaded_file_info = result.get("data", {}).get(
376
- "results", []
357
+ response = http.post(url, headers=headers, json=payload)
358
+ if response.status_code != 200:
359
+ print(f"❌ 获取下载链接失败: HTTP {response.status_code}")
360
+ return False
361
+
362
+ result = response.json()
363
+ if not result.get("success"):
364
+ print(f"❌ 获取下载链接失败: {result.get('errorMsg')}")
365
+ return False
366
+
367
+ # Add files to chat
368
+ self.uploaded_file_info = result.get("data", {}).get("results", [])
369
+ for file_info in self.uploaded_file_info:
370
+ print(f"🔍 添加文件到对话: {file_name}")
371
+ add_url = "https://api.tongyi.com/assistant/api/chat/file/add"
372
+ add_payload = {
373
+ "workSource": "chat",
374
+ "terminal": "web",
375
+ "workCode": "0",
376
+ "channel": "home",
377
+ "workType": "file",
378
+ "module": "uploadhistory",
379
+ "workName": file_info["fileKey"],
380
+ "workId": file_info["docId"],
381
+ "workResourcePath": file_info["url"],
382
+ "sessionId": "",
383
+ "batchId": str(uuid.uuid4()).replace("-", "")[
384
+ :32
385
+ ], # Generate random batchId
386
+ "fileSize": os.path.getsize(file_path),
387
+ }
388
+
389
+ add_response = http.post(
390
+ add_url, headers=headers, json=add_payload
377
391
  )
378
- for file_info in self.uploaded_file_info:
379
- spinner.text = f"添加文件到对话: {file_name}"
380
- add_url = (
381
- "https://api.tongyi.com/assistant/api/chat/file/add"
392
+ if add_response.status_code != 200:
393
+ print(
394
+ f"❌ 添加文件到对话失败: HTTP {add_response.status_code}"
382
395
  )
383
- add_payload = {
384
- "workSource": "chat",
385
- "terminal": "web",
386
- "workCode": "0",
387
- "channel": "home",
388
- "workType": "file",
389
- "module": "uploadhistory",
390
- "workName": file_info["fileKey"],
391
- "workId": file_info["docId"],
392
- "workResourcePath": file_info["url"],
393
- "sessionId": "",
394
- "batchId": str(uuid.uuid4()).replace("-", "")[
395
- :32
396
- ], # Generate random batchId
397
- "fileSize": os.path.getsize(file_path),
398
- }
399
-
400
- add_response = requests.post(
401
- add_url, headers=headers, json=add_payload
396
+ continue
397
+
398
+ add_result = add_response.json()
399
+ if not add_result.get("success"):
400
+ print(
401
+ f" 添加文件到对话失败: {add_result.get('errorMsg')}"
402
402
  )
403
- if add_response.status_code != 200:
404
- spinner.text = f"添加文件到对话失败: HTTP {add_response.status_code}"
405
- spinner.fail("")
406
- continue
407
-
408
- add_result = add_response.json()
409
- if not add_result.get("success"):
410
- spinner.text = (
411
- f"添加文件到对话失败: {add_result.get('errorMsg')}"
412
- )
413
- spinner.fail("❌")
414
- continue
415
-
416
- file_info.update(add_result.get("data", {}))
417
-
418
- spinner.text = f"文件 {file_name} 上传成功"
419
- spinner.ok("✅")
420
- time.sleep(1) # 短暂暂停以便用户看到成功状态
421
-
422
- except Exception as e:
423
- spinner.text = f"上传文件 {file_name} 时出错: {str(e)}"
424
- spinner.fail("❌")
425
- return False
403
+ continue
404
+
405
+ file_info.update(add_result.get("data", {}))
406
+
407
+ print(f"✅ 文件 {file_name} 上传成功")
408
+ time.sleep(1) # 短暂暂停以便用户看到成功状态
409
+
410
+ except Exception as e:
411
+ print(f" 上传文件 {file_name} 时出错: {str(e)}")
412
+ return False
426
413
  return True
427
414
 
428
415
  except Exception as e:
@@ -482,7 +469,7 @@ class TongyiPlatform(BasePlatform):
482
469
 
483
470
  try:
484
471
  response = while_success(
485
- lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
472
+ lambda: http.post(url, headers=headers, json=payload), sleep_time=5
486
473
  )
487
474
  if response.status_code != 200:
488
475
  PrettyOutput.print(