jarvis-ai-assistant 0.3.26__py3-none-any.whl → 0.3.28__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 (62) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +303 -177
  3. jarvis/jarvis_agent/agent_manager.py +6 -0
  4. jarvis/jarvis_agent/config.py +92 -0
  5. jarvis/jarvis_agent/config_editor.py +1 -1
  6. jarvis/jarvis_agent/event_bus.py +48 -0
  7. jarvis/jarvis_agent/file_methodology_manager.py +1 -3
  8. jarvis/jarvis_agent/jarvis.py +77 -36
  9. jarvis/jarvis_agent/memory_manager.py +70 -3
  10. jarvis/jarvis_agent/prompt_manager.py +82 -0
  11. jarvis/jarvis_agent/run_loop.py +130 -0
  12. jarvis/jarvis_agent/shell_input_handler.py +1 -1
  13. jarvis/jarvis_agent/task_analyzer.py +89 -11
  14. jarvis/jarvis_agent/task_manager.py +26 -0
  15. jarvis/jarvis_agent/user_interaction.py +42 -0
  16. jarvis/jarvis_code_agent/code_agent.py +18 -3
  17. jarvis/jarvis_code_agent/lint.py +5 -5
  18. jarvis/jarvis_code_analysis/code_review.py +0 -1
  19. jarvis/jarvis_data/config_schema.json +7 -6
  20. jarvis/jarvis_git_squash/main.py +6 -1
  21. jarvis/jarvis_git_utils/git_commiter.py +51 -16
  22. jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
  23. jarvis/jarvis_memory_organizer/memory_organizer.py +2 -5
  24. jarvis/jarvis_methodology/main.py +0 -2
  25. jarvis/jarvis_multi_agent/__init__.py +3 -3
  26. jarvis/jarvis_platform/base.py +5 -6
  27. jarvis/jarvis_platform/registry.py +1 -1
  28. jarvis/jarvis_platform/yuanbao.py +0 -1
  29. jarvis/jarvis_platform_manager/main.py +28 -11
  30. jarvis/jarvis_platform_manager/service.py +1 -1
  31. jarvis/jarvis_rag/cli.py +1 -1
  32. jarvis/jarvis_rag/embedding_manager.py +0 -1
  33. jarvis/jarvis_rag/llm_interface.py +0 -3
  34. jarvis/jarvis_smart_shell/main.py +0 -1
  35. jarvis/jarvis_stats/cli.py +15 -35
  36. jarvis/jarvis_stats/stats.py +178 -51
  37. jarvis/jarvis_tools/clear_memory.py +1 -3
  38. jarvis/jarvis_tools/cli/main.py +0 -1
  39. jarvis/jarvis_tools/edit_file.py +0 -1
  40. jarvis/jarvis_tools/generate_new_tool.py +3 -5
  41. jarvis/jarvis_tools/registry.py +17 -3
  42. jarvis/jarvis_tools/retrieve_memory.py +2 -3
  43. jarvis/jarvis_tools/save_memory.py +3 -3
  44. jarvis/jarvis_tools/search_web.py +2 -2
  45. jarvis/jarvis_tools/sub_agent.py +114 -85
  46. jarvis/jarvis_tools/sub_code_agent.py +29 -7
  47. jarvis/jarvis_tools/virtual_tty.py +3 -14
  48. jarvis/jarvis_utils/builtin_replace_map.py +4 -4
  49. jarvis/jarvis_utils/config.py +44 -15
  50. jarvis/jarvis_utils/fzf.py +56 -0
  51. jarvis/jarvis_utils/git_utils.py +1 -1
  52. jarvis/jarvis_utils/globals.py +1 -2
  53. jarvis/jarvis_utils/input.py +0 -3
  54. jarvis/jarvis_utils/methodology.py +3 -5
  55. jarvis/jarvis_utils/output.py +1 -1
  56. jarvis/jarvis_utils/utils.py +117 -27
  57. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/METADATA +2 -3
  58. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/RECORD +62 -56
  59. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/WHEEL +0 -0
  60. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/entry_points.txt +0 -0
  61. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/licenses/LICENSE +0 -0
  62. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/top_level.txt +0 -0
@@ -3,10 +3,9 @@
3
3
  任务分析器模块
4
4
  负责处理任务分析和方法论生成功能
5
5
  """
6
- from typing import Optional
7
6
 
8
7
  from jarvis.jarvis_utils.globals import get_interrupt, set_interrupt
9
- from jarvis.jarvis_utils.input import user_confirm
8
+
10
9
  from jarvis.jarvis_agent.prompts import TASK_ANALYSIS_PROMPT
11
10
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
12
11
 
@@ -22,6 +21,12 @@ class TaskAnalyzer:
22
21
  agent: Agent实例
23
22
  """
24
23
  self.agent = agent
24
+ self._analysis_done = False
25
+ # 旁路集成事件订阅,失败不影响主流程
26
+ try:
27
+ self._subscribe_events()
28
+ except Exception:
29
+ pass
25
30
 
26
31
  def analysis_task(self, satisfaction_feedback: str = ""):
27
32
  """分析任务并生成方法论"""
@@ -38,8 +43,15 @@ class TaskAnalyzer:
38
43
  # 循环处理工具调用,直到没有工具调用为止
39
44
  self._process_analysis_loop()
40
45
 
41
- except Exception as e:
46
+ except Exception:
42
47
  PrettyOutput.print("分析失败", OutputType.ERROR)
48
+ finally:
49
+ # 标记已完成一次分析,避免事件回调重复执行
50
+ self._analysis_done = True
51
+ try:
52
+ self.agent.set_user_data("__task_analysis_done__", True)
53
+ except Exception:
54
+ pass
43
55
 
44
56
  def _prepare_analysis_prompt(self, satisfaction_feedback: str) -> str:
45
57
  """准备分析提示"""
@@ -59,8 +71,27 @@ class TaskAnalyzer:
59
71
  if not self._handle_analysis_interrupt(response):
60
72
  break
61
73
 
62
- # 执行工具调用
63
- need_return, self.agent.session.prompt = self.agent._call_tools(response)
74
+ # 执行工具调用(补充事件:before_tool_call/after_tool_call)
75
+ try:
76
+ self.agent.event_bus.emit(
77
+ "before_tool_call",
78
+ agent=self.agent,
79
+ current_response=response,
80
+ )
81
+ except Exception:
82
+ pass
83
+ need_return, tool_prompt = self.agent._call_tools(response)
84
+ self.agent.session.prompt = tool_prompt
85
+ try:
86
+ self.agent.event_bus.emit(
87
+ "after_tool_call",
88
+ agent=self.agent,
89
+ current_response=response,
90
+ need_return=need_return,
91
+ tool_prompt=tool_prompt,
92
+ )
93
+ except Exception:
94
+ pass
64
95
 
65
96
  # 如果没有工具调用或者没有新的提示,退出循环
66
97
  if not self.agent.session.prompt:
@@ -73,8 +104,8 @@ class TaskAnalyzer:
73
104
  bool: True 继续分析,False 退出分析
74
105
  """
75
106
  set_interrupt(False)
76
- user_input = self.agent.multiline_inputer(
77
- f"分析任务期间被中断,请输入用户干预信息:"
107
+ user_input = self.agent._multiline_input(
108
+ "分析任务期间被中断,请输入用户干预信息:", False
78
109
  )
79
110
 
80
111
  if not user_input:
@@ -98,7 +129,7 @@ class TaskAnalyzer:
98
129
 
99
130
  def _handle_interrupt_with_tool_calls(self, user_input: str) -> str:
100
131
  """处理有工具调用时的中断"""
101
- if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
132
+ if self.agent.user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
102
133
  return f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
103
134
  else:
104
135
  return f"被用户中断,用户补充信息为:{user_input}\n\n检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
@@ -108,11 +139,11 @@ class TaskAnalyzer:
108
139
  satisfaction_feedback = ""
109
140
 
110
141
  if not auto_completed and self.agent.use_analysis:
111
- if user_confirm("您对本次任务的完成是否满意?", True):
142
+ if self.agent.user_confirm("您对本次任务的完成是否满意?", True):
112
143
  satisfaction_feedback = "\n\n用户对本次任务的完成表示满意。"
113
144
  else:
114
- feedback = self.agent.multiline_inputer(
115
- "请提供您的反馈意见(可留空直接回车):"
145
+ feedback = self.agent._multiline_input(
146
+ "请提供您的反馈意见(可留空直接回车):", False
116
147
  )
117
148
  if feedback:
118
149
  satisfaction_feedback = (
@@ -124,3 +155,50 @@ class TaskAnalyzer:
124
155
  )
125
156
 
126
157
  return satisfaction_feedback
158
+
159
+ # -----------------------
160
+ # 事件订阅与处理(旁路)
161
+ # -----------------------
162
+ def _subscribe_events(self) -> None:
163
+ bus = self.agent.get_event_bus() # type: ignore[attr-defined]
164
+ # 在生成总结前触发(保持与原顺序一致)
165
+ bus.subscribe("before_summary", self._on_before_summary)
166
+ # 当无需总结时,作为兜底触发分析
167
+ bus.subscribe("task_completed", self._on_task_completed)
168
+
169
+ def _on_before_summary(self, **payload) -> None:
170
+ if self._analysis_done:
171
+ return
172
+ # 避免与直接调用重复
173
+ try:
174
+ if bool(self.agent.get_user_data("__task_analysis_done__")):
175
+ self._analysis_done = True
176
+ return
177
+ except Exception:
178
+ pass
179
+ auto_completed = bool(payload.get("auto_completed", False))
180
+ try:
181
+ feedback = self.collect_satisfaction_feedback(auto_completed)
182
+ if getattr(self.agent, "use_analysis", False):
183
+ self.analysis_task(feedback)
184
+ except Exception:
185
+ # 忽略事件处理异常,保证主流程
186
+ self._analysis_done = True
187
+
188
+ def _on_task_completed(self, **payload) -> None:
189
+ # 当未在 before_summary 阶段执行过时,作为兜底
190
+ if self._analysis_done:
191
+ return
192
+ try:
193
+ if bool(self.agent.get_user_data("__task_analysis_done__")):
194
+ self._analysis_done = True
195
+ return
196
+ except Exception:
197
+ pass
198
+ auto_completed = bool(payload.get("auto_completed", False))
199
+ try:
200
+ feedback = self.collect_satisfaction_feedback(auto_completed)
201
+ if getattr(self.agent, "use_analysis", False):
202
+ self.analysis_task(feedback)
203
+ except Exception:
204
+ self._analysis_done = True
@@ -15,6 +15,7 @@ from jarvis.jarvis_agent import (
15
15
  user_confirm,
16
16
  )
17
17
  from jarvis.jarvis_utils.config import get_data_dir
18
+ from jarvis.jarvis_utils.fzf import fzf_select
18
19
 
19
20
 
20
21
  class TaskManager:
@@ -89,6 +90,31 @@ class TaskManager:
89
90
  Console().print(table)
90
91
  PrettyOutput.print("[0] 跳过预定义任务", OutputType.INFO)
91
92
 
93
+ # Try fzf selection first (with numbered options and a skip option)
94
+ fzf_list = [f"{0:>3} | 跳过预定义任务"] + [
95
+ f"{i:>3} | {name}" for i, name in enumerate(task_names, 1)
96
+ ]
97
+ selected_str = fzf_select(fzf_list, prompt="选择一个任务编号 (ESC跳过) > ")
98
+ if selected_str:
99
+ try:
100
+ num_part = selected_str.split("|", 1)[0].strip()
101
+ idx = int(num_part)
102
+ if idx == 0:
103
+ return ""
104
+ if 1 <= idx <= len(task_names):
105
+ selected_task = tasks[task_names[idx - 1]]
106
+ PrettyOutput.print(f"将要执行任务:\n {selected_task}", OutputType.INFO)
107
+ # 询问是否需要补充信息
108
+ need_additional = user_confirm("需要为此任务添加补充信息吗?", default=False)
109
+ if need_additional:
110
+ additional_input = get_multiline_input("请输入补充信息:")
111
+ if additional_input:
112
+ selected_task = f"{selected_task}\n\n补充信息:\n{additional_input}"
113
+ return selected_task
114
+ except Exception:
115
+ # 如果解析失败,则回退到手动输入
116
+ pass
117
+
92
118
  while True:
93
119
  try:
94
120
  choice_str = prompt(
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ UserInteractionHandler: 抽象用户交互(多行输入与确认)逻辑,便于将来替换为 TUI/WebUI。
4
+
5
+ 阶段一(最小变更):
6
+ - 仅提供封装,不直接修改 Agent 的现有调用
7
+ - 后续步骤在 Agent 中以旁路方式接入,保持向后兼容
8
+ """
9
+ from typing import Callable
10
+
11
+
12
+
13
+ class UserInteractionHandler:
14
+ def __init__(
15
+ self,
16
+ multiline_inputer: Callable[..., str],
17
+ confirm_func: Callable[[str, bool], bool],
18
+ ) -> None:
19
+ """
20
+ 参数:
21
+ - multiline_inputer: 提供多行输入的函数,优先支持 (tip, print_on_empty=bool),兼容仅接受 (tip) 的实现
22
+ - confirm_func: 用户确认函数 (tip: str, default: bool) -> bool
23
+ """
24
+ self._multiline_inputer = multiline_inputer
25
+ self._confirm = confirm_func
26
+
27
+ def multiline_input(self, tip: str, print_on_empty: bool) -> str:
28
+ """
29
+ 多行输入封装:兼容两类签名
30
+ 1) func(tip, print_on_empty=True/False)
31
+ 2) func(tip)
32
+ """
33
+ try:
34
+ return self._multiline_inputer(tip, print_on_empty=print_on_empty) # type: ignore[call-arg]
35
+ except TypeError:
36
+ return self._multiline_inputer(tip) # type: ignore[misc]
37
+
38
+ def confirm(self, tip: str, default: bool = True) -> bool:
39
+ """
40
+ 用户确认封装,直接委派
41
+ """
42
+ return self._confirm(tip, default)
@@ -21,6 +21,7 @@ from jarvis.jarvis_tools.registry import ToolRegistry
21
21
  from jarvis.jarvis_utils.config import (
22
22
  is_confirm_before_apply_patch,
23
23
  is_enable_static_analysis,
24
+ get_git_check_mode,
24
25
  )
25
26
  from jarvis.jarvis_utils.git_utils import (
26
27
  confirm_add_new_files,
@@ -197,6 +198,17 @@ class CodeAgent:
197
198
  missing_configs
198
199
  )
199
200
  PrettyOutput.print(message, OutputType.WARNING)
201
+ # 通过配置控制严格校验模式(JARVIS_GIT_CHECK_MODE):
202
+ # - warn: 仅告警并继续,后续提交可能失败
203
+ # - strict: 严格模式(默认),直接退出
204
+ mode = get_git_check_mode().lower()
205
+ if mode == "warn":
206
+ PrettyOutput.print(
207
+ "已启用 Git 校验警告模式(JARVIS_GIT_CHECK_MODE=warn),将继续运行。"
208
+ "注意:后续提交可能失败,请尽快配置 git user.name 与 user.email。",
209
+ OutputType.INFO,
210
+ )
211
+ return
200
212
  sys.exit(1)
201
213
 
202
214
  except FileNotFoundError:
@@ -250,7 +262,7 @@ class CodeAgent:
250
262
  if has_uncommitted_changes():
251
263
 
252
264
  git_commiter = GitCommitTool()
253
- git_commiter.execute({"prefix": prefix, "suffix": suffix})
265
+ git_commiter.execute({"prefix": prefix, "suffix": suffix, "agent": self.agent, "model_group": getattr(self.agent.model, "model_group", None)})
254
266
 
255
267
  def _init_env(self, prefix: str, suffix: str) -> None:
256
268
  """初始化环境,组合以下功能:
@@ -510,7 +522,7 @@ class CodeAgent:
510
522
  check=True,
511
523
  )
512
524
  git_commiter = GitCommitTool()
513
- git_commiter.execute({"prefix": prefix, "suffix": suffix})
525
+ git_commiter.execute({"prefix": prefix, "suffix": suffix, "agent": self.agent, "model_group": getattr(self.agent.model, "model_group", None)})
514
526
 
515
527
  # 在用户接受commit后,根据配置决定是否保存记忆
516
528
  if self.agent.force_save_memory:
@@ -544,7 +556,7 @@ class CodeAgent:
544
556
  f"提交 {i+1}: {commit['hash'][:7]} - {commit['message']} ({len(commit['files'])}个文件)\n"
545
557
  + "\n".join(f" - {file}" for file in commit["files"][:5])
546
558
  + ("\n ..." if len(commit["files"]) > 5 else "")
547
- for i, commit in enumerate(commits_info)
559
+ for i, commit in enumerate(commits_info[:5])
548
560
  )
549
561
  project_info.append(f"最近提交:\n{commits_str}")
550
562
 
@@ -575,6 +587,8 @@ class CodeAgent:
575
587
  PrettyOutput.print(f"执行失败: {str(e)}", OutputType.WARNING)
576
588
  return str(e)
577
589
 
590
+
591
+
578
592
  self._handle_uncommitted_changes()
579
593
  end_commit = get_latest_commit_hash()
580
594
  commits = self._show_commit_history(start_commit, end_commit)
@@ -615,6 +629,7 @@ class CodeAgent:
615
629
 
616
630
  StatsManager.increment("code_modifications", group="code_agent")
617
631
 
632
+
618
633
  # 获取提交信息
619
634
  end_hash = get_latest_commit_hash()
620
635
  commits = get_commits_between(start_hash, end_hash)
@@ -27,11 +27,11 @@ LINT_TOOLS = {
27
27
  # Go
28
28
  ".go": ["go vet"],
29
29
  # Python
30
- ".py": ["black", "pylint", "mypy"],
31
- ".pyw": ["black", "pylint", "mypy"],
32
- ".pyi": ["black", "pylint", "mypy"],
33
- ".pyx": ["black", "pylint", "mypy"],
34
- ".pxd": ["black", "pylint", "mypy"],
30
+ ".py": ["ruff", "mypy"],
31
+ ".pyw": ["ruff", "mypy"],
32
+ ".pyi": ["ruff", "mypy"],
33
+ ".pyx": ["ruff", "mypy"],
34
+ ".pxd": ["ruff", "mypy"],
35
35
  # Rust
36
36
  ".rs": ["cargo clippy", "rustfmt"],
37
37
  ".rlib": ["cargo clippy", "rustfmt"],
@@ -3,7 +3,6 @@ import os
3
3
  import re
4
4
  import subprocess
5
5
  import tempfile
6
- import sys
7
6
  from typing import Any, Dict, List, Optional
8
7
 
9
8
  import typer
@@ -126,8 +126,6 @@
126
126
  "description": "常规操作模型名称",
127
127
  "default": "deep_seek_v3"
128
128
  },
129
-
130
-
131
129
  "JARVIS_WEB_SEARCH_PLATFORM": {
132
130
  "type": "string",
133
131
  "description": "Web搜索使用的平台名称",
@@ -160,8 +158,6 @@
160
158
  "type": "string",
161
159
  "default": "deep_seek_v3"
162
160
  },
163
-
164
-
165
161
  "JARVIS_MAX_INPUT_TOKEN_COUNT": {
166
162
  "type": "number",
167
163
  "default": 32000
@@ -196,7 +192,6 @@
196
192
  "description": "Jarvis数据存储目录路径",
197
193
  "default": "~/.jarvis"
198
194
  },
199
-
200
195
  "JARVIS_PRETTY_OUTPUT": {
201
196
  "type": "boolean",
202
197
  "description": "是否启用美化输出",
@@ -292,6 +287,12 @@
292
287
  "description": "是否启用立即中断:在对话迭代中检测到中断信号时立即返回",
293
288
  "default": false
294
289
  },
290
+ "JARVIS_GIT_CHECK_MODE": {
291
+ "type": "string",
292
+ "enum": ["strict", "warn"],
293
+ "description": "Git 配置校验模式:strict 表示严格模式(默认),缺失配置时直接退出;warn 表示警告模式,仅提示并继续运行",
294
+ "default": "strict"
295
+ },
295
296
  "JARVIS_TOOL_GROUP": {
296
297
  "type": "string",
297
298
  "description": "选择一个预定义的工具配置组",
@@ -445,4 +446,4 @@
445
446
  }
446
447
  },
447
448
  "additionalProperties": true
448
- }
449
+ }
@@ -8,6 +8,7 @@ from jarvis.jarvis_git_utils.git_commiter import GitCommitTool
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  from jarvis.jarvis_utils.utils import init_env
10
10
  from jarvis.jarvis_utils.input import user_confirm
11
+ from jarvis.jarvis_utils.globals import get_agent, current_agent_name
11
12
 
12
13
  app = typer.Typer(help="Git压缩工具")
13
14
 
@@ -46,7 +47,11 @@ class GitSquashTool:
46
47
 
47
48
  # Use existing GitCommitTool for new commit
48
49
  commit_tool = GitCommitTool()
49
- commit_tool.execute({"lang": args.get("lang", "Chinese")})
50
+ agent = get_agent(current_agent_name)
51
+ exec_args = {"lang": args.get("lang", "Chinese")}
52
+ if agent:
53
+ exec_args["agent"] = agent
54
+ commit_tool.execute(exec_args)
50
55
  except Exception as e:
51
56
  PrettyOutput.print(f"压缩提交失败: {str(e)}", OutputType.WARNING)
52
57
 
@@ -2,7 +2,6 @@
2
2
  import os
3
3
  import re
4
4
  import subprocess
5
- import sys
6
5
  import tempfile
7
6
  from typing import Any, Dict, Optional
8
7
 
@@ -138,7 +137,7 @@ class GitCommitTool:
138
137
  base_prompt = (
139
138
  custom_prompt
140
139
  if custom_prompt
141
- else f"""根据代码差异生成提交信息:
140
+ else """根据代码差异生成提交信息:
142
141
  提交信息应使用中文书写
143
142
  # 格式模板
144
143
  必须使用以下格式:
@@ -163,6 +162,9 @@ commit信息
163
162
  {ct("COMMIT_MESSAGE")}
164
163
  """
165
164
 
165
+ # 优先从调用方传入的 agent 获取平台与模型
166
+ agent_from_args = args.get("agent")
167
+
166
168
  # Get model_group from args
167
169
  model_group = args.get("model_group")
168
170
 
@@ -172,32 +174,65 @@ commit信息
172
174
  get_normal_model_name,
173
175
  )
174
176
 
175
- platform_name = get_normal_platform_name(model_group)
176
- model_name = get_normal_model_name(model_group)
177
+ platform_name = None
178
+ model_name = None
177
179
 
178
- # If no explicit parameters, try to get from existing agent
179
- agent = get_agent(current_agent_name)
180
180
  if (
181
- not platform_name
182
- and agent
183
- and hasattr(agent, "model")
184
- and agent.model
181
+ agent_from_args
182
+ and hasattr(agent_from_args, "model")
183
+ and getattr(agent_from_args, "model", None)
185
184
  ):
186
- platform_name = agent.model.platform_name()
187
- model_name = agent.model.name()
188
- if not model_group:
189
- model_group = agent.model.model_group
185
+ try:
186
+ platform_name = agent_from_args.model.platform_name()
187
+ model_name = agent_from_args.model.name()
188
+ if not model_group and hasattr(
189
+ agent_from_args.model, "model_group"
190
+ ):
191
+ model_group = agent_from_args.model.model_group
192
+ except Exception:
193
+ # 安全回退到后续逻辑
194
+ platform_name = None
195
+ model_name = None
196
+
197
+ # 如果未能从agent获取到,再根据 model_group 获取
198
+ if not platform_name:
199
+ platform_name = get_normal_platform_name(model_group)
200
+ if not model_name:
201
+ model_name = get_normal_model_name(model_group)
202
+
203
+ # If no explicit parameters, try to get from existing global agent
204
+ if not platform_name:
205
+ agent = get_agent(current_agent_name)
206
+ if (
207
+ agent
208
+ and hasattr(agent, "model")
209
+ and getattr(agent, "model", None)
210
+ ):
211
+ platform_name = agent.model.platform_name()
212
+ model_name = agent.model.name()
213
+ if not model_group and hasattr(agent.model, "model_group"):
214
+ model_group = agent.model.model_group
190
215
 
191
216
  # Create a new platform instance
192
217
  if platform_name:
193
218
  platform = PlatformRegistry().create_platform(platform_name)
194
219
  if platform and model_name:
195
220
  platform.set_model_name(model_name)
196
- if model_group:
197
- platform.model_group = model_group
221
+ if platform and model_group:
222
+ try:
223
+ platform.set_model_group(model_group)
224
+ except Exception:
225
+ # 兼容早期实现
226
+ platform.model_group = model_group # type: ignore
198
227
  else:
199
228
  platform = PlatformRegistry().get_normal_platform()
200
229
 
230
+ # 跳过模型可用性校验:
231
+ # 为避免某些平台/代理不支持 get_model_list 接口导致的噪音日志(如 404),
232
+ # 这里默认不调用 platform.get_model_list() 进行模型可用性校验。
233
+ # 如果未来需要恢复校验,可参考被移除的逻辑。
234
+ # no-op
235
+
201
236
  # Ensure platform is not None
202
237
  if not platform:
203
238
  return {
@@ -73,7 +73,7 @@ class StdioMcpClient(McpClient):
73
73
  f"初始化失败: {response.get('error', 'Unknown error')}"
74
74
  )
75
75
 
76
- result = response["result"]
76
+ response["result"]
77
77
 
78
78
  # 发送initialized通知 - 使用正确的方法名格式
79
79
  self._send_notification("notifications/initialized", {})
@@ -7,14 +7,11 @@
7
7
  """
8
8
 
9
9
  import json
10
- import sys
11
10
  from collections import defaultdict
12
- from itertools import combinations
13
11
  from pathlib import Path
14
- from typing import Dict, List, Set, Tuple, Any, Optional
12
+ from typing import Dict, List, Set, Any, Optional
15
13
 
16
14
  import typer
17
- import yaml
18
15
 
19
16
  from jarvis.jarvis_utils.config import (
20
17
  get_data_dir,
@@ -311,7 +308,7 @@ tags:
311
308
  group_memories = [memories[i] for i in original_indices]
312
309
 
313
310
  # 显示将要合并的记忆(先拼接后统一打印,避免循环逐条输出)
314
- lines = [f"", f"准备合并 {len(group_memories)} 个记忆:"]
311
+ lines = ["", f"准备合并 {len(group_memories)} 个记忆:"]
315
312
  for mem in group_memories:
316
313
  lines.append(
317
314
  f" - ID: {mem.get('id', '未知')}, "
@@ -11,8 +11,6 @@
11
11
  import hashlib
12
12
  import json
13
13
  import os
14
- import sys
15
- from typing import Optional
16
14
 
17
15
  import typer
18
16
  import yaml # type: ignore
@@ -73,7 +73,7 @@ content: |2
73
73
  if len(send_messages) > 1:
74
74
  return (
75
75
  False,
76
- f"Send multiple messages, please only send one message at a time.",
76
+ "Send multiple messages, please only send one message at a time.",
77
77
  )
78
78
  if len(send_messages) == 0:
79
79
  return False, ""
@@ -100,7 +100,7 @@ content: |2
100
100
  msg = yaml.safe_load(item)
101
101
  if "to" in msg and "content" in msg:
102
102
  ret.append(msg)
103
- except Exception as e:
103
+ except Exception:
104
104
  continue
105
105
  return ret
106
106
 
@@ -161,7 +161,7 @@ content: {msg['content']}
161
161
  """
162
162
  to_agent_name = msg.get("to")
163
163
  if not to_agent_name:
164
- return f"消息中未指定 `to` 字段"
164
+ return "消息中未指定 `to` 字段"
165
165
 
166
166
  if to_agent_name not in self.agents_config_map:
167
167
  PrettyOutput.print(
@@ -88,7 +88,7 @@ class BasePlatform(ABC):
88
88
  PrettyOutput.print(
89
89
  f"长上下文,分批提交,共{len(inputs)}部分...", OutputType.INFO
90
90
  )
91
- prefix_prompt = f"""
91
+ prefix_prompt = """
92
92
  我将分多次提供大量内容,在我明确告诉你内容已经全部提供完毕之前,每次仅需要输出"已收到",明白请输出"开始接收输入"。
93
93
  """
94
94
  while_true(lambda: while_success(lambda: self._chat(prefix_prompt), 5), 5)
@@ -188,12 +188,11 @@ class BasePlatform(ABC):
188
188
 
189
189
  # If content overflows, truncate to show only the last few lines
190
190
  if len(lines) > max_text_height:
191
- new_text = "\n".join(
192
- text_content.plain.splitlines()[
193
- -max_text_height:
194
- ]
191
+ # Rebuild the text from the wrapped lines to ensure visual consistency
192
+ # This correctly handles both wrapped long lines and explicit newlines
193
+ text_content.plain = "\n".join(
194
+ [line.plain for line in lines[-max_text_height:]]
195
195
  )
196
- text_content.plain = new_text
197
196
 
198
197
  panel.subtitle = (
199
198
  "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
@@ -39,7 +39,7 @@ class PlatformRegistry:
39
39
  # 创建 __init__.py 使其成为 Python 包
40
40
  with open(
41
41
  os.path.join(user_platform_dir, "__init__.py"), "w", errors="ignore"
42
- ) as f:
42
+ ):
43
43
  pass
44
44
  except Exception as e:
45
45
  PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
@@ -69,7 +69,6 @@ class YuanbaoPlatform(BasePlatform):
69
69
  "X-Requested-With": "XMLHttpRequest",
70
70
  "chat_version": "v1",
71
71
  "X-Instance-ID": "5",
72
- "X-Requested-With": "XMLHttpRequest",
73
72
  "Accept": "application/json, text/plain, */*",
74
73
  "Content-Type": "application/json",
75
74
  "sec-ch-ua-mobile": "?0",