jarvis-ai-assistant 0.2.3__py3-none-any.whl → 0.2.5__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 (33) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +13 -7
  3. jarvis/jarvis_agent/edit_file_handler.py +4 -0
  4. jarvis/jarvis_agent/jarvis.py +22 -25
  5. jarvis/jarvis_agent/main.py +6 -6
  6. jarvis/jarvis_code_agent/code_agent.py +273 -11
  7. jarvis/jarvis_code_analysis/code_review.py +21 -19
  8. jarvis/jarvis_data/config_schema.json +25 -29
  9. jarvis/jarvis_git_squash/main.py +3 -3
  10. jarvis/jarvis_git_utils/git_commiter.py +32 -11
  11. jarvis/jarvis_mcp/sse_mcp_client.py +4 -6
  12. jarvis/jarvis_mcp/streamable_mcp_client.py +5 -9
  13. jarvis/jarvis_rag/retriever.py +1 -1
  14. jarvis/jarvis_smart_shell/main.py +2 -2
  15. jarvis/jarvis_stats/__init__.py +13 -0
  16. jarvis/jarvis_stats/cli.py +404 -0
  17. jarvis/jarvis_stats/stats.py +538 -0
  18. jarvis/jarvis_stats/storage.py +381 -0
  19. jarvis/jarvis_stats/visualizer.py +282 -0
  20. jarvis/jarvis_tools/cli/main.py +82 -15
  21. jarvis/jarvis_tools/registry.py +32 -16
  22. jarvis/jarvis_tools/search_web.py +3 -3
  23. jarvis/jarvis_tools/virtual_tty.py +315 -26
  24. jarvis/jarvis_utils/config.py +12 -8
  25. jarvis/jarvis_utils/git_utils.py +8 -16
  26. jarvis/jarvis_utils/methodology.py +74 -67
  27. jarvis/jarvis_utils/utils.py +468 -72
  28. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/METADATA +29 -3
  29. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/RECORD +33 -28
  30. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/entry_points.txt +2 -0
  31. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/WHEEL +0 -0
  32. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/licenses/LICENSE +0 -0
  33. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/top_level.txt +0 -0
@@ -81,10 +81,10 @@ def get_max_token_count(model_group_override: Optional[str] = None) -> int:
81
81
  获取模型允许的最大token数量。
82
82
 
83
83
  返回:
84
- int: 模型能处理的最大token数量。
84
+ int: 模型能处理的最大token数量,为最大输入token数量的100倍。
85
85
  """
86
- config = _get_resolved_model_config(model_group_override)
87
- return int(config.get("JARVIS_MAX_TOKEN_COUNT", "960000"))
86
+ max_input_tokens = get_max_input_token_count(model_group_override)
87
+ return max_input_tokens * 100
88
88
 
89
89
 
90
90
  def get_max_input_token_count(model_group_override: Optional[str] = None) -> int:
@@ -151,9 +151,7 @@ def _get_resolved_model_config(
151
151
  "JARVIS_MODEL",
152
152
  "JARVIS_THINKING_PLATFORM",
153
153
  "JARVIS_THINKING_MODEL",
154
- "JARVIS_MAX_TOKEN_COUNT",
155
154
  "JARVIS_MAX_INPUT_TOKEN_COUNT",
156
- "JARVIS_MAX_BIG_CONTENT_SIZE",
157
155
  ]:
158
156
  if key in GLOBAL_CONFIG_DATA:
159
157
  resolved_config[key] = GLOBAL_CONFIG_DATA[key]
@@ -249,10 +247,10 @@ def get_max_big_content_size(model_group_override: Optional[str] = None) -> int:
249
247
  获取最大大内容大小。
250
248
 
251
249
  返回:
252
- int: 最大大内容大小
250
+ int: 最大大内容大小,为最大输入token数量的5倍
253
251
  """
254
- config = _get_resolved_model_config(model_group_override)
255
- return int(config.get("JARVIS_MAX_BIG_CONTENT_SIZE", "160000"))
252
+ max_input_tokens = get_max_input_token_count(model_group_override)
253
+ return max_input_tokens * 5
256
254
 
257
255
 
258
256
  def get_pretty_output() -> bool:
@@ -262,6 +260,12 @@ def get_pretty_output() -> bool:
262
260
  返回:
263
261
  bool: 如果启用PrettyOutput则返回True,默认为True
264
262
  """
263
+ import platform
264
+
265
+ # Windows系统强制设置为False
266
+ if platform.system() == "Windows":
267
+ return False
268
+
265
269
  return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT", False) == True
266
270
 
267
271
 
@@ -214,9 +214,7 @@ def handle_commit_workflow() -> bool:
214
214
  Returns:
215
215
  bool: 提交是否成功
216
216
  """
217
- if is_confirm_before_apply_patch() and not user_confirm(
218
- "是否要提交代码?", default=True
219
- ):
217
+ if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
220
218
  revert_change()
221
219
  return False
222
220
 
@@ -429,9 +427,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
429
427
  if not in_venv and (
430
428
  "Permission denied" in error_msg or "not writeable" in error_msg
431
429
  ):
432
- if user_confirm(
433
- "检测到权限问题,是否尝试用户级安装(--user)?", True
434
- ):
430
+ if user_confirm("检测到权限问题,是否尝试用户级安装(--user)?", True):
435
431
  user_result = subprocess.run(
436
432
  install_cmd + ["--user"],
437
433
  cwd=git_root,
@@ -446,9 +442,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
446
442
  PrettyOutput.print(f"代码安装失败: {error_msg}", OutputType.ERROR)
447
443
  return False
448
444
  except Exception as e:
449
- PrettyOutput.print(
450
- f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR
451
- )
445
+ PrettyOutput.print(f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR)
452
446
  return False
453
447
  # 更新检查日期文件
454
448
  with open(last_check_file, "w") as f:
@@ -482,9 +476,7 @@ def get_diff_file_list() -> List[str]:
482
476
  subprocess.run(["git", "reset"], check=True)
483
477
 
484
478
  if result.returncode != 0:
485
- PrettyOutput.print(
486
- f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR
487
- )
479
+ PrettyOutput.print(f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR)
488
480
  return []
489
481
 
490
482
  return [f for f in result.stdout.splitlines() if f]
@@ -533,8 +525,10 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
533
525
  ],
534
526
  capture_output=True,
535
527
  text=True,
528
+ encoding="utf-8",
529
+ errors="replace",
536
530
  )
537
- if result.returncode != 0:
531
+ if result.returncode != 0 or result.stdout is None:
538
532
  return []
539
533
 
540
534
  # 解析提交信息
@@ -632,9 +626,7 @@ def confirm_add_new_files() -> None:
632
626
  need_confirm = True
633
627
 
634
628
  if binary_files:
635
- output_lines.append(
636
- f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
637
- )
629
+ output_lines.append(f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)")
638
630
  output_lines.append("二进制文件列表:")
639
631
  output_lines.extend(f" - {file}" for file in binary_files)
640
632
  need_confirm = True
@@ -149,6 +149,7 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
149
149
 
150
150
  参数:
151
151
  user_input: 用户输入文本,用于提示大模型
152
+ tool_registery: 工具注册表,用于获取工具列表
152
153
 
153
154
  返回:
154
155
  str: 相关的方法论提示,如果未找到方法论则返回空字符串
@@ -170,70 +171,87 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
170
171
  return ""
171
172
  print(f"✅ 加载方法论文件完成 (共 {len(methodologies)} 个)")
172
173
 
173
- # 获取当前平台
174
- agent = get_agent(current_agent_name)
175
- if agent:
176
- platform = agent.model
177
- model_group = agent.model.model_group
178
- else:
179
- platform = PlatformRegistry().get_normal_platform()
180
- model_group = None
181
- platform.set_suppress_output(False)
182
- if not platform:
183
- return ""
174
+ platform = PlatformRegistry().get_normal_platform()
175
+ platform.set_suppress_output(True)
176
+
177
+ # 步骤1:获取所有方法论的标题
178
+ methodology_titles = list(methodologies.keys())
184
179
 
185
- # 构建基础提示信息
186
- base_prompt = f"""以下是所有可用的方法论内容:
180
+ # 步骤2:让大模型选择相关性高的方法论
181
+ selection_prompt = f"""以下是所有可用的方法论标题:
187
182
 
188
183
  """
189
- # 构建完整内容
190
- full_content = base_prompt
191
- for problem_type, content in methodologies.items():
192
- full_content += f"## {problem_type}\n\n{content}\n\n---\n\n"
184
+ for i, title in enumerate(methodology_titles, 1):
185
+ selection_prompt += f"{i}. {title}\n"
193
186
 
194
- full_content += f"以下是所有可用的工具内容:\n\n"
195
- full_content += prompt
187
+ selection_prompt += f"""
188
+ 以下是可用的工具列表:
189
+ {prompt}
196
190
 
197
- # 添加用户输入和输出要求
198
- full_content += f"""
199
- 请根据以上方法论和可调用的工具内容,规划/总结出以下用户需求的执行步骤: {user_input}
191
+ 用户需求:{user_input}
200
192
 
201
- 请按以下格式回复:
202
- ### 与该任务/需求相关的方法论
203
- 1. [方法论名字]
204
- 2. [方法论名字]
205
- ### 根据以上方法论,规划/总结出执行步骤
206
- 1. [步骤1]
207
- 2. [步骤2]
208
- 3. [步骤3]
193
+ 请分析用户需求,从上述方法论中选择出与需求相关性较高的方法论(可以选择多个)。
209
194
 
210
- 如果没有匹配的方法论,请输出:没有历史方法论可参考
211
- 除以上要求外,不要输出任何内容
195
+ 请严格按照以下格式返回序号:
196
+ <NUM>序号1,序号2,序号3</NUM>
197
+
198
+ 例如:<NUM>1,3,5</NUM>
199
+
200
+ 如果没有相关的方法论,请返回:<NUM>none</NUM>
201
+
202
+ 注意:只返回<NUM>标签内的内容,不要有其他任何输出。
212
203
  """
213
204
 
214
- # 检查内容是否过大
215
- is_large_content = is_context_overflow(full_content, model_group)
216
- temp_file_path = None
205
+ # 获取大模型选择的方法论序号
206
+ response = platform.chat_until_success(selection_prompt).strip()
207
+
208
+ # 重置平台,恢复输出
209
+ platform.reset()
210
+ platform.set_suppress_output(False)
211
+
212
+ # 从响应中提取<NUM>标签内的内容
213
+ import re
214
+ num_match = re.search(r'<NUM>(.*?)</NUM>', response, re.DOTALL)
215
+
216
+ if not num_match:
217
+ # 如果没有找到<NUM>标签,尝试直接解析响应
218
+ selected_indices_str = response
219
+ else:
220
+ selected_indices_str = num_match.group(1).strip()
221
+
222
+ if selected_indices_str.lower() == "none":
223
+ return "没有历史方法论可参考"
217
224
 
225
+ # 解析选择的序号
226
+ selected_methodologies = {}
218
227
  try:
219
- if is_large_content:
220
- # 创建临时文件
221
- print(f"📝 创建方法论临时文件...")
222
- temp_file_path = _create_methodology_temp_file(methodologies)
223
- if not temp_file_path:
224
- print(f"❌ 创建方法论临时文件失败")
225
- return ""
226
- print(f"✅ 创建方法论临时文件完成")
227
-
228
- # 尝试上传文件
229
- upload_success = platform.upload_files([temp_file_path])
230
-
231
- if upload_success:
232
- # 使用上传的文件生成摘要
233
- return platform.chat_until_success(
234
- base_prompt
235
- + f"""
236
- 请根据已上传的方法论和可调用的工具文件内容,规划/总结出以下用户需求的执行步骤: {user_input}
228
+ if selected_indices_str:
229
+ indices = [int(idx.strip()) for idx in selected_indices_str.split(",") if idx.strip().isdigit()]
230
+ for idx in indices:
231
+ if 1 <= idx <= len(methodology_titles):
232
+ title = methodology_titles[idx - 1]
233
+ selected_methodologies[title] = methodologies[title]
234
+ except Exception:
235
+ # 如果解析失败,返回空结果
236
+ return "没有历史方法论可参考"
237
+
238
+ if not selected_methodologies:
239
+ return "没有历史方法论可参考"
240
+
241
+ # 步骤3:将选择出来的方法论内容提供给大模型生成步骤
242
+ final_prompt = f"""以下是与用户需求相关的方法论内容:
243
+
244
+ """
245
+ for problem_type, content in selected_methodologies.items():
246
+ final_prompt += f"## {problem_type}\n\n{content}\n\n---\n\n"
247
+
248
+ final_prompt += f"""以下是所有可用的工具内容:
249
+
250
+ {prompt}
251
+
252
+ 用户需求:{user_input}
253
+
254
+ 请根据以上方法论和可调用的工具内容,规划/总结出执行步骤。
237
255
 
238
256
  请按以下格式回复:
239
257
  ### 与该任务/需求相关的方法论
@@ -244,22 +262,11 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
244
262
  2. [步骤2]
245
263
  3. [步骤3]
246
264
 
247
- 如果没有匹配的方法论,请输出:没有历史方法论可参考
248
265
  除以上要求外,不要输出任何内容
249
266
  """
250
- )
251
- else:
252
- return "没有历史方法论可参考"
253
- # 如果内容不大或上传失败,直接使用chat_until_success
254
- return platform.chat_until_success(full_content)
255
-
256
- finally:
257
- # 清理临时文件
258
- if temp_file_path and os.path.exists(temp_file_path):
259
- try:
260
- os.remove(temp_file_path)
261
- except Exception:
262
- pass
267
+
268
+ # 如果内容不大,直接使用chat_until_success
269
+ return platform.chat_until_success(final_prompt)
263
270
 
264
271
  except Exception as e:
265
272
  PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)