jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.8__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 (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.8.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,6 @@ from typing import Generator, List, Tuple
12
12
  from jarvis.jarvis_platform.base import BasePlatform
13
13
  from jarvis.jarvis_utils.clipboard import copy_to_clipboard
14
14
  from jarvis.jarvis_utils.input import get_multiline_input
15
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
16
15
 
17
16
 
18
17
  class HumanPlatform(BasePlatform):
@@ -39,7 +38,7 @@ class HumanPlatform(BasePlatform):
39
38
  if model_name == "human":
40
39
  self.model_name = model_name
41
40
  else:
42
- PrettyOutput.print(f"错误:不支持的模型: {model_name}", OutputType.ERROR)
41
+ print(f"错误:不支持的模型: {model_name}")
43
42
 
44
43
  def chat(self, message: str) -> Generator[str, None, None]:
45
44
  """发送消息并获取人类响应"""
@@ -66,7 +65,7 @@ class HumanPlatform(BasePlatform):
66
65
 
67
66
  def upload_files(self, file_list: List[str]) -> bool:
68
67
  """文件上传功能,人类平台不需要实际处理"""
69
- PrettyOutput.print("人类交互平台不支持文件上传", OutputType.WARNING)
68
+ print("⚠️ 人类交互平台不支持文件上传")
70
69
  return False
71
70
 
72
71
  def delete_chat(self) -> bool:
@@ -88,10 +87,10 @@ class HumanPlatform(BasePlatform):
88
87
  with open(file_path, "w", encoding="utf-8") as f:
89
88
  json.dump(state, f, ensure_ascii=False, indent=4)
90
89
  self._saved = True
91
- PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
90
+ print(f"会话已成功保存到 {file_path}")
92
91
  return True
93
92
  except Exception as e:
94
- PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
93
+ print(f"保存会话失败: {str(e)}")
95
94
  return False
96
95
 
97
96
  def restore(self, file_path: str) -> bool:
@@ -106,13 +105,13 @@ class HumanPlatform(BasePlatform):
106
105
  self.first_message = state.get("first_message", True)
107
106
  self._saved = True
108
107
 
109
- PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
108
+ print(f"从 {file_path} 成功恢复会话")
110
109
  return True
111
110
  except FileNotFoundError:
112
- PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
111
+ print(f"会话文件未找到: {file_path}")
113
112
  return False
114
113
  except Exception as e:
115
- PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
114
+ print(f"恢复会话失败: {str(e)}")
116
115
  return False
117
116
 
118
117
  def name(self) -> str:
@@ -9,7 +9,6 @@ from typing import Dict, Generator, List, Tuple
9
9
 
10
10
  from jarvis.jarvis_platform.base import BasePlatform
11
11
  from jarvis.jarvis_utils import http
12
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
13
12
  from jarvis.jarvis_utils.utils import while_success
14
13
 
15
14
 
@@ -37,7 +36,7 @@ class KimiModel(BasePlatform):
37
36
  self.chat_id = "" # 当前会话ID
38
37
  self.api_key = os.getenv("KIMI_API_KEY") # 从环境变量获取API密钥
39
38
  if not self.api_key:
40
- PrettyOutput.print("KIMI_API_KEY 未设置", OutputType.WARNING)
39
+ print("⚠️ KIMI_API_KEY 未设置")
41
40
  self.auth_header = f"Bearer {self.api_key}" # 认证头信息
42
41
  self.uploaded_files = [] # 存储已上传文件的信息
43
42
  self.chat_id = "" # 当前会话ID
@@ -65,18 +64,15 @@ class KimiModel(BasePlatform):
65
64
  }
66
65
  try:
67
66
  response = while_success(
68
- lambda: http.post(url, headers=headers, data=payload),
69
- sleep_time=5,
67
+ lambda: http.post(url, headers=headers, data=payload)
70
68
  )
71
69
  if response.status_code != 200:
72
- PrettyOutput.print(
73
- f"错误:创建会话失败:{response.json()}", OutputType.ERROR
74
- )
70
+ print(f"❌ 错误:创建会话失败:{response.json()}")
75
71
  return False
76
72
  self.chat_id = response.json()["id"]
77
73
  return True
78
74
  except Exception as e:
79
- PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
75
+ print(f"错误:创建会话失败:{e}")
80
76
  return False
81
77
 
82
78
  def _get_presigned_url(self, filename: str, action: str) -> Dict:
@@ -93,8 +89,7 @@ class KimiModel(BasePlatform):
93
89
  }
94
90
 
95
91
  response = while_success(
96
- lambda: http.post(url, headers=headers, data=payload), sleep_time=5
97
- )
92
+ lambda: http.post(url, headers=headers, data=payload) )
98
93
  return response.json()
99
94
 
100
95
  def support_upload_files(self) -> bool:
@@ -107,11 +102,11 @@ class KimiModel(BasePlatform):
107
102
  with open(file_path, "rb") as f:
108
103
  content = f.read()
109
104
  response = while_success(
110
- lambda: http.put(presigned_url, data=content), sleep_time=5
105
+ lambda: http.put(presigned_url, data=content)
111
106
  )
112
107
  return response.status_code == 200
113
108
  except Exception as e:
114
- PrettyOutput.print(f"错误:上传文件失败:{e}", OutputType.ERROR)
109
+ print(f"错误:上传文件失败:{e}")
115
110
  return False
116
111
 
117
112
  def _get_file_info(self, file_data: Dict, name: str, file_type: str) -> Dict:
@@ -134,8 +129,7 @@ class KimiModel(BasePlatform):
134
129
  }
135
130
 
136
131
  response = while_success(
137
- lambda: http.post(url, headers=headers, data=payload), sleep_time=5
138
- )
132
+ lambda: http.post(url, headers=headers, data=payload) )
139
133
  return response.json()
140
134
 
141
135
  def _wait_for_parse(self, file_id: str) -> bool:
@@ -153,7 +147,6 @@ class KimiModel(BasePlatform):
153
147
  payload = {"ids": [file_id]}
154
148
  response_stream = while_success(
155
149
  lambda: http.stream_post(url, headers=headers, json=payload),
156
- sleep_time=5,
157
150
  )
158
151
 
159
152
  # 处理流式响应
@@ -171,7 +164,7 @@ class KimiModel(BasePlatform):
171
164
  return True
172
165
  elif status == "failed":
173
166
  return False
174
- except json.JSONDecodeError:
167
+ except Exception:
175
168
  continue
176
169
 
177
170
  retry_count += 1
@@ -185,11 +178,11 @@ class KimiModel(BasePlatform):
185
178
  return True
186
179
 
187
180
  if not self.chat_id:
188
- PrettyOutput.print("正在创建聊天会话...", OutputType.INFO)
181
+ print("ℹ️ 正在创建聊天会话...")
189
182
  if not self._create_chat():
190
- PrettyOutput.print("创建聊天会话失败", OutputType.ERROR)
183
+ print("创建聊天会话失败")
191
184
  return False
192
- PrettyOutput.print("创建聊天会话成功", OutputType.SUCCESS)
185
+ print("创建聊天会话成功")
193
186
 
194
187
  uploaded_files = []
195
188
  for index, file_path in enumerate(file_list, 1):
@@ -220,22 +213,26 @@ class KimiModel(BasePlatform):
220
213
  log_lines.append(f"文件处理完成: {file_name}")
221
214
  else:
222
215
  log_lines.append(f"文件解析失败: {file_name}")
223
- PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
216
+ joined_logs = '\n'.join(log_lines)
217
+ print(f"❌ {joined_logs}")
224
218
  return False
225
219
  else:
226
220
  uploaded_files.append(file_info)
227
221
  log_lines.append(f"图片处理完成: {file_name}")
228
222
  else:
229
223
  log_lines.append(f"文件上传失败: {file_name}")
230
- PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
224
+ joined_logs = '\n'.join(log_lines)
225
+ print(f"❌ {joined_logs}")
231
226
  return False
232
227
 
233
228
  # 成功路径统一输出本文件的处理日志
234
- PrettyOutput.print("\n".join(log_lines), OutputType.INFO)
229
+ joined_logs = '\n'.join(log_lines)
230
+ print(f"ℹ️ {joined_logs}")
235
231
 
236
232
  except Exception as e:
237
233
  log_lines.append(f"处理文件出错 {file_path}: {str(e)}")
238
- PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
234
+ joined_logs = '\n'.join(log_lines)
235
+ print(f"❌ {joined_logs}")
239
236
  return False
240
237
 
241
238
  self.uploaded_files = uploaded_files
@@ -287,7 +284,6 @@ class KimiModel(BasePlatform):
287
284
  # 使用新的stream_post接口发送消息请求,获取流式响应
288
285
  response_stream = while_success(
289
286
  lambda: http.stream_post(url, headers=headers, json=payload),
290
- sleep_time=5,
291
287
  )
292
288
 
293
289
  # 处理流式响应
@@ -306,7 +302,7 @@ class KimiModel(BasePlatform):
306
302
  text = data.get("text", "")
307
303
  if text:
308
304
  yield text
309
- except json.JSONDecodeError:
305
+ except Exception:
310
306
  continue
311
307
 
312
308
  return None
@@ -327,7 +323,7 @@ class KimiModel(BasePlatform):
327
323
 
328
324
  try:
329
325
  response = while_success(
330
- lambda: http.delete(url, headers=headers), sleep_time=5
326
+ lambda: http.delete(url, headers=headers)
331
327
  )
332
328
  if response.status_code == 200:
333
329
  self.chat_id = ""
@@ -335,18 +331,16 @@ class KimiModel(BasePlatform):
335
331
  self.first_chat = True # 重置first_chat标记
336
332
  return True
337
333
  else:
338
- PrettyOutput.print(
339
- f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
340
- )
334
+ print(f"⚠️ 删除会话失败: HTTP {response.status_code}")
341
335
  return False
342
336
  except Exception as e:
343
- PrettyOutput.print(f"删除会话时发生错误: {str(e)}", OutputType.ERROR)
337
+ print(f"删除会话时发生错误: {str(e)}")
344
338
  return False
345
339
 
346
340
  def save(self, file_path: str) -> bool:
347
341
  """Save chat session to a file."""
348
342
  if not self.chat_id:
349
- PrettyOutput.print("没有活动的会话可供保存", OutputType.WARNING)
343
+ print("⚠️ 没有活动的会话可供保存")
350
344
  return False
351
345
 
352
346
  state = {
@@ -361,10 +355,10 @@ class KimiModel(BasePlatform):
361
355
  with open(file_path, "w", encoding="utf-8") as f:
362
356
  json.dump(state, f, ensure_ascii=False, indent=4)
363
357
  self._saved = True
364
- PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
358
+ print(f"会话已成功保存到 {file_path}")
365
359
  return True
366
360
  except Exception as e:
367
- PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
361
+ print(f"保存会话失败: {str(e)}")
368
362
  return False
369
363
 
370
364
  def restore(self, file_path: str) -> bool:
@@ -380,13 +374,13 @@ class KimiModel(BasePlatform):
380
374
  self.uploaded_files = state.get("uploaded_files", [])
381
375
  self._saved = True
382
376
 
383
- PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
377
+ print(f"从 {file_path} 成功恢复会话")
384
378
  return True
385
379
  except FileNotFoundError:
386
- PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
380
+ print(f"会话文件未找到: {file_path}")
387
381
  return False
388
382
  except Exception as e:
389
- PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
383
+ print(f"恢复会话失败: {str(e)}")
390
384
  return False
391
385
 
392
386
  def name(self) -> str:
@@ -6,7 +6,6 @@ from typing import Dict, Generator, List, Tuple
6
6
  from openai import OpenAI
7
7
 
8
8
  from jarvis.jarvis_platform.base import BasePlatform
9
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
10
9
 
11
10
 
12
11
  class OpenAIModel(BasePlatform):
@@ -19,7 +18,7 @@ class OpenAIModel(BasePlatform):
19
18
  self.system_message = ""
20
19
  self.api_key = os.getenv("OPENAI_API_KEY")
21
20
  if not self.api_key:
22
- PrettyOutput.print("OPENAI_API_KEY 未设置", OutputType.WARNING)
21
+ print("⚠️ OPENAI_API_KEY 未设置")
23
22
 
24
23
  self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
25
24
  self.model_name = os.getenv("JARVIS_MODEL") or "gpt-4o"
@@ -35,9 +34,9 @@ class OpenAIModel(BasePlatform):
35
34
  # Ensure all header keys/values are strings
36
35
  self.extra_headers = {str(k): str(v) for k, v in parsed.items()}
37
36
  else:
38
- PrettyOutput.print("OPENAI_EXTRA_HEADERS 应为 JSON 对象,如 {'X-Source':'jarvis'}", OutputType.WARNING)
37
+ print("⚠️ OPENAI_EXTRA_HEADERS 应为 JSON 对象,如 {'X-Source':'jarvis'}")
39
38
  except Exception as e:
40
- PrettyOutput.print(f"解析 OPENAI_EXTRA_HEADERS 失败: {e}", OutputType.WARNING)
39
+ print(f"⚠️ 解析 OPENAI_EXTRA_HEADERS 失败: {e}")
41
40
 
42
41
  # Initialize OpenAI client, try to pass default headers if SDK supports it
43
42
  try:
@@ -49,7 +48,7 @@ class OpenAIModel(BasePlatform):
49
48
  # Fallback: SDK version may not support default_headers
50
49
  self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
51
50
  if self.extra_headers:
52
- PrettyOutput.print("当前 OpenAI SDK 不支持 default_headers,未能注入额外 HTTP 头", OutputType.WARNING)
51
+ print("⚠️ 当前 OpenAI SDK 不支持 default_headers,未能注入额外 HTTP 头")
53
52
  self.messages: List[Dict[str, str]] = []
54
53
  self.system_message = ""
55
54
 
@@ -82,7 +81,7 @@ class OpenAIModel(BasePlatform):
82
81
  model_list.append((model.id, model.id))
83
82
  return model_list
84
83
  except Exception as e:
85
- PrettyOutput.print(f"获取模型列表失败:{str(e)}", OutputType.ERROR)
84
+ print(f"获取模型列表失败:{str(e)}")
86
85
  return []
87
86
 
88
87
  def set_model_name(self, model_name: str):
@@ -125,26 +124,65 @@ class OpenAIModel(BasePlatform):
125
124
  # Add user message to history
126
125
  self.messages.append({"role": "user", "content": message})
127
126
 
128
- response = self.client.chat.completions.create(
129
- model=self.model_name, # Use the configured model name
130
- messages=self.messages, # type: ignore
131
- stream=True,
132
- ) # type: ignore
133
-
134
- full_response = ""
135
- for chunk in response:
136
- if chunk.choices and chunk.choices[0].delta.content:
137
- text = chunk.choices[0].delta.content
138
- full_response += text
139
- yield text
140
-
141
- # Add assistant reply to history
142
- self.messages.append({"role": "assistant", "content": full_response})
127
+ # 累计完整响应
128
+ accumulated_response = ""
129
+
130
+ # 循环处理,直到不是因为长度限制而结束
131
+ while True:
132
+ response = self.client.chat.completions.create(
133
+ model=self.model_name, # Use the configured model name
134
+ messages=self.messages, # type: ignore
135
+ stream=True,
136
+ ) # type: ignore
137
+
138
+ full_response = ""
139
+ finish_reason = None
140
+
141
+ for chunk in response:
142
+ if chunk.choices and len(chunk.choices) > 0:
143
+ choice = chunk.choices[0]
144
+
145
+ # 检查 finish_reason(通常在最后一个 chunk 中)
146
+ if choice.finish_reason:
147
+ finish_reason = choice.finish_reason
148
+
149
+ # 获取内容增量
150
+ if choice.delta and choice.delta.content:
151
+ text = choice.delta.content
152
+ full_response += text
153
+ accumulated_response += text
154
+ yield text
155
+
156
+ # 如果是因为长度限制而结束,继续获取剩余内容
157
+ if finish_reason == "length":
158
+ # 将已获取的内容追加到消息历史中,以便下次请求时模型知道已生成的内容
159
+ if self.messages and self.messages[-1].get("role") == "assistant":
160
+ # 追加到现有的 assistant 消息
161
+ self.messages[-1]["content"] += full_response
162
+ else:
163
+ # 创建新的 assistant 消息
164
+ self.messages.append({"role": "assistant", "content": full_response})
165
+
166
+ # 添加一个继续请求的用户消息,让模型继续生成
167
+ self.messages.append({"role": "user", "content": "请继续。"})
168
+ # 继续循环,获取剩余内容
169
+ continue
170
+ else:
171
+ # 正常结束(stop、null 或其他原因)
172
+ # 将完整响应添加到消息历史
173
+ if accumulated_response:
174
+ if self.messages and self.messages[-1].get("role") == "assistant":
175
+ # 如果最后一条是 assistant 消息,追加本次的内容
176
+ self.messages[-1]["content"] += full_response
177
+ else:
178
+ # 创建新的 assistant 消息,使用累计的完整响应
179
+ self.messages.append({"role": "assistant", "content": accumulated_response})
180
+ break
143
181
 
144
182
  return None
145
183
 
146
184
  except Exception as e:
147
- PrettyOutput.print(f"对话失败:{str(e)}", OutputType.ERROR)
185
+ print(f"对话失败:{str(e)}")
148
186
  raise Exception(f"Chat failed: {str(e)}")
149
187
 
150
188
  def name(self) -> str:
@@ -193,10 +231,10 @@ class OpenAIModel(BasePlatform):
193
231
  with open(file_path, "w", encoding="utf-8") as f:
194
232
  json.dump(state, f, ensure_ascii=False, indent=4)
195
233
  self._saved = True
196
- PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
234
+ print(f"会话已成功保存到 {file_path}")
197
235
  return True
198
236
  except Exception as e:
199
- PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
237
+ print(f"保存会话失败: {str(e)}")
200
238
  return False
201
239
 
202
240
  def restore(self, file_path: str) -> bool:
@@ -210,13 +248,13 @@ class OpenAIModel(BasePlatform):
210
248
  # atexit.register(self.delete_chat)
211
249
  self._saved = True
212
250
 
213
- PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
251
+ print(f"从 {file_path} 成功恢复会话")
214
252
  return True
215
253
  except FileNotFoundError:
216
- PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
254
+ print(f"会话文件未找到: {file_path}")
217
255
  return False
218
256
  except Exception as e:
219
- PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
257
+ print(f"恢复会话失败: {str(e)}")
220
258
  return False
221
259
 
222
260
  def support_web(self) -> bool:
@@ -7,11 +7,14 @@ from typing import Dict, List, Optional, Type
7
7
 
8
8
  from jarvis.jarvis_platform.base import BasePlatform
9
9
  from jarvis.jarvis_utils.config import (
10
+ get_cheap_model_name,
11
+ get_cheap_platform_name,
10
12
  get_data_dir,
11
13
  get_normal_model_name,
12
14
  get_normal_platform_name,
15
+ get_smart_model_name,
16
+ get_smart_platform_name,
13
17
  )
14
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
15
18
 
16
19
  REQUIRED_METHODS = [
17
20
  ("chat", ["message"]), # 方法名和参数列表
@@ -42,7 +45,7 @@ class PlatformRegistry:
42
45
  ):
43
46
  pass
44
47
  except Exception as e:
45
- PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
48
+ print(f"创建平台目录失败: {str(e)}")
46
49
  return ""
47
50
  return user_platform_dir
48
51
 
@@ -77,10 +80,7 @@ class PlatformRegistry:
77
80
  missing_methods.append(f"{method_name}(parameter mismatch)")
78
81
 
79
82
  if missing_methods:
80
- PrettyOutput.print(
81
- f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
82
- OutputType.WARNING,
83
- )
83
+ print(f"⚠️ 平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}")
84
84
  return False
85
85
 
86
86
  return True
@@ -99,7 +99,7 @@ class PlatformRegistry:
99
99
 
100
100
  # 确保目录存在
101
101
  if not os.path.exists(directory):
102
- PrettyOutput.print(f"平台目录不存在: {directory}", OutputType.WARNING)
102
+ print(f"⚠️ 平台目录不存在: {directory}")
103
103
  return platforms
104
104
 
105
105
  # 获取目录的包名
@@ -149,7 +149,8 @@ class PlatformRegistry:
149
149
  error_lines.append(f"加载平台 {module_name} 失败: {str(e)}")
150
150
 
151
151
  if error_lines:
152
- PrettyOutput.print("\n".join(error_lines), OutputType.ERROR)
152
+ joined_errors = '\n'.join(error_lines)
153
+ print(f"❌ {joined_errors}")
153
154
  return platforms
154
155
 
155
156
  @staticmethod
@@ -179,13 +180,28 @@ class PlatformRegistry:
179
180
  self.register_platform(platform_name, platform_class)
180
181
 
181
182
  def get_normal_platform(self) -> BasePlatform:
183
+ """获取正常操作的平台实例"""
182
184
  platform_name = get_normal_platform_name()
183
185
  model_name = get_normal_model_name()
184
186
  platform = self.create_platform(platform_name)
185
187
  platform.set_model_name(model_name) # type: ignore
186
188
  return platform # type: ignore
187
189
 
190
+ def get_cheap_platform(self) -> BasePlatform:
191
+ """获取廉价操作的平台实例"""
192
+ platform_name = get_cheap_platform_name()
193
+ model_name = get_cheap_model_name()
194
+ platform = self.create_platform(platform_name)
195
+ platform.set_model_name(model_name) # type: ignore
196
+ return platform # type: ignore
188
197
 
198
+ def get_smart_platform(self) -> BasePlatform:
199
+ """获取智能操作的平台实例"""
200
+ platform_name = get_smart_platform_name()
201
+ model_name = get_smart_model_name()
202
+ platform = self.create_platform(platform_name)
203
+ platform.set_model_name(model_name) # type: ignore
204
+ return platform # type: ignore
189
205
 
190
206
  def register_platform(self, name: str, platform_class: Type[BasePlatform]) -> None:
191
207
  """Register platform class
@@ -207,7 +223,7 @@ class PlatformRegistry:
207
223
  BasePlatform: Platform instance
208
224
  """
209
225
  if name not in self.platforms:
210
- PrettyOutput.print(f"未找到平台: {name}", OutputType.WARNING)
226
+ print(f"⚠️ 未找到平台: {name}")
211
227
  return None
212
228
 
213
229
  try:
@@ -215,7 +231,7 @@ class PlatformRegistry:
215
231
  platform = self.platforms[name]()
216
232
  return platform
217
233
  except Exception as e:
218
- PrettyOutput.print(f"创建平台失败: {str(e)}", OutputType.ERROR)
234
+ print(f"创建平台失败: {str(e)}")
219
235
  return None
220
236
 
221
237
  def get_available_platforms(self) -> List[str]: