jarvis-ai-assistant 0.1.224__py3-none-any.whl → 0.2.0__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 (32) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +33 -11
  3. jarvis/jarvis_agent/jarvis.py +87 -32
  4. jarvis/jarvis_agent/main.py +13 -2
  5. jarvis/jarvis_code_agent/code_agent.py +17 -4
  6. jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
  7. jarvis/jarvis_code_analysis/code_review.py +14 -3
  8. jarvis/jarvis_data/config_schema.json +41 -0
  9. jarvis/jarvis_git_utils/git_commiter.py +9 -2
  10. jarvis/jarvis_mcp/sse_mcp_client.py +9 -7
  11. jarvis/jarvis_mcp/stdio_mcp_client.py +2 -2
  12. jarvis/jarvis_platform/base.py +28 -13
  13. jarvis/jarvis_platform_manager/main.py +18 -6
  14. jarvis/jarvis_rag/llm_interface.py +1 -3
  15. jarvis/jarvis_smart_shell/main.py +18 -12
  16. jarvis/jarvis_tools/ask_user.py +1 -0
  17. jarvis/jarvis_tools/generate_new_tool.py +1 -0
  18. jarvis/jarvis_tools/registry.py +17 -9
  19. jarvis/jarvis_tools/search_web.py +19 -9
  20. jarvis/jarvis_utils/builtin_replace_map.py +0 -1
  21. jarvis/jarvis_utils/config.py +80 -21
  22. jarvis/jarvis_utils/git_utils.py +2 -2
  23. jarvis/jarvis_utils/globals.py +17 -11
  24. jarvis/jarvis_utils/methodology.py +37 -23
  25. jarvis/jarvis_utils/output.py +2 -2
  26. jarvis/jarvis_utils/utils.py +138 -3
  27. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/METADATA +49 -12
  28. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/RECORD +32 -32
  29. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/WHEEL +0 -0
  30. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/entry_points.txt +0 -0
  31. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/licenses/LICENSE +0 -0
  32. {jarvis_ai_assistant-0.1.224.dist-info → jarvis_ai_assistant-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import re
3
3
  from abc import ABC, abstractmethod
4
- from typing import Generator, List, Tuple
4
+ from types import TracebackType
5
+ from typing import Generator, List, Optional, Tuple, Type
6
+
7
+ from typing_extensions import Self
5
8
 
6
9
  from rich import box # type: ignore
7
10
  from rich.live import Live # type: ignore
@@ -28,9 +31,19 @@ class BasePlatform(ABC):
28
31
  self.suppress_output = True # 添加输出控制标志
29
32
  self.web = False # 添加web属性,默认false
30
33
  self._saved = False
31
-
32
- def __del__(self):
33
- """Destroy model"""
34
+ self.model_group: Optional[str] = None
35
+
36
+ def __enter__(self) -> Self:
37
+ """Enter context manager"""
38
+ return self
39
+
40
+ def __exit__(
41
+ self,
42
+ exc_type: Optional[Type[BaseException]],
43
+ exc_val: Optional[BaseException],
44
+ exc_tb: Optional[TracebackType],
45
+ ) -> None:
46
+ """Exit context manager"""
34
47
  if not self._saved:
35
48
  self.delete_chat()
36
49
 
@@ -64,9 +77,11 @@ class BasePlatform(ABC):
64
77
 
65
78
  input_token_count = get_context_token_count(message)
66
79
 
67
- if input_token_count > get_max_input_token_count():
68
- max_chunk_size = get_max_input_token_count() - 1024 # 留出一些余量
69
- min_chunk_size = get_max_input_token_count() - 2048
80
+ if input_token_count > get_max_input_token_count(self.model_group):
81
+ max_chunk_size = (
82
+ get_max_input_token_count(self.model_group) - 1024
83
+ ) # 留出一些余量
84
+ min_chunk_size = get_max_input_token_count(self.model_group) - 2048
70
85
  inputs = split_text_into_chunks(message, max_chunk_size, min_chunk_size)
71
86
  print("📤 正在提交长上下文...")
72
87
  prefix_prompt = f"""
@@ -95,14 +110,10 @@ class BasePlatform(ABC):
95
110
  ):
96
111
  response += trunk
97
112
 
98
- print(
99
- f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}"
100
- )
113
+ print(f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}")
101
114
  print("✅ 提交完成")
102
115
  response += "\n" + while_true(
103
- lambda: while_success(
104
- lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5
105
- ),
116
+ lambda: while_success(lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5),
106
117
  5,
107
118
  )
108
119
  else:
@@ -233,6 +244,10 @@ class BasePlatform(ABC):
233
244
  """Set whether to suppress output"""
234
245
  self.suppress_output = suppress
235
246
 
247
+ def set_model_group(self, model_group: Optional[str]):
248
+ """Set model group"""
249
+ self.model_group = model_group
250
+
236
251
  def set_web(self, web: bool):
237
252
  """Set web flag"""
238
253
  self.web = web
@@ -188,9 +188,13 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
188
188
  for entry in conversation_history:
189
189
  file_obj.write(f"{entry['role']}: {entry['content']}\n\n")
190
190
 
191
- PrettyOutput.print(f"所有对话已保存到 {file_path}", OutputType.SUCCESS)
191
+ PrettyOutput.print(
192
+ f"所有对话已保存到 {file_path}", OutputType.SUCCESS
193
+ )
192
194
  except Exception as exc:
193
- PrettyOutput.print(f"保存所有对话失败: {str(exc)}", OutputType.ERROR)
195
+ PrettyOutput.print(
196
+ f"保存所有对话失败: {str(exc)}", OutputType.ERROR
197
+ )
194
198
  continue
195
199
 
196
200
  # Check if it is a save_session command
@@ -211,7 +215,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
211
215
  file_path = file_path[1:-1]
212
216
 
213
217
  if platform.save(file_path):
214
- PrettyOutput.print(f"会话已保存到 {file_path}", OutputType.SUCCESS)
218
+ PrettyOutput.print(
219
+ f"会话已保存到 {file_path}", OutputType.SUCCESS
220
+ )
215
221
  else:
216
222
  PrettyOutput.print("保存会话失败", OutputType.ERROR)
217
223
  except Exception as exc:
@@ -237,7 +243,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
237
243
 
238
244
  if platform.restore(file_path):
239
245
  conversation_history = [] # Clear local history after loading
240
- PrettyOutput.print(f"会话已从 {file_path} 加载", OutputType.SUCCESS)
246
+ PrettyOutput.print(
247
+ f"会话已从 {file_path} 加载", OutputType.SUCCESS
248
+ )
241
249
  else:
242
250
  PrettyOutput.print("加载会话失败", OutputType.ERROR)
243
251
  except Exception as exc:
@@ -433,8 +441,12 @@ def main() -> None:
433
441
  service_parser.add_argument(
434
442
  "--port", type=int, default=8000, help="服务端口 (默认: 8000)"
435
443
  )
436
- service_parser.add_argument("--platform", "-p", help="指定默认平台,当客户端未指定平台时使用")
437
- service_parser.add_argument("--model", "-m", help="指定默认模型,当客户端未指定平台时使用")
444
+ service_parser.add_argument(
445
+ "--platform", "-p", help="指定默认平台,当客户端未指定平台时使用"
446
+ )
447
+ service_parser.add_argument(
448
+ "--model", "-m", help="指定默认模型,当客户端未指定平台时使用"
449
+ )
438
450
  service_parser.set_defaults(func=service_command)
439
451
 
440
452
  # role subcommand
@@ -101,9 +101,7 @@ class JarvisPlatform_LLM(LLMInterface):
101
101
  try:
102
102
  self.registry = PlatformRegistry.get_global_platform_registry()
103
103
  self.platform: BasePlatform = self.registry.get_normal_platform()
104
- self.platform.set_suppress_output(
105
- False
106
- ) # 确保模型没有控制台输出
104
+ self.platform.set_suppress_output(False) # 确保模型没有控制台输出
107
105
  print(f"🚀 已初始化 Jarvis 平台 LLM,模型: {self.platform.name()}")
108
106
  except Exception as e:
109
107
  print(f"❌ 初始化 Jarvis 平台 LLM 失败: {e}")
@@ -20,34 +20,37 @@ def execute_command(command: str, should_run: bool) -> None:
20
20
 
21
21
  def _check_fish_shell() -> bool:
22
22
  """Check if current shell is fish
23
-
23
+
24
24
  Returns:
25
25
  bool: True if fish shell, False otherwise
26
26
  """
27
27
  return get_shell_name() == "fish"
28
28
 
29
+
29
30
  def _get_config_file() -> str:
30
31
  """Get fish config file path
31
-
32
+
32
33
  Returns:
33
34
  str: Path to fish config file
34
35
  """
35
36
  return os.path.expanduser("~/.config/fish/config.fish")
36
37
 
38
+
37
39
  def _get_markers() -> Tuple[str, str]:
38
40
  """Get start and end markers for JSS completion
39
-
41
+
40
42
  Returns:
41
43
  Tuple[str, str]: (start_marker, end_marker)
42
44
  """
43
45
  return (
44
46
  "# ===== JARVIS JSS FISH COMPLETION START =====",
45
- "# ===== JARVIS JSS FISH COMPLETION END ====="
47
+ "# ===== JARVIS JSS FISH COMPLETION END =====",
46
48
  )
47
49
 
50
+
48
51
  def install_jss_completion() -> int:
49
52
  """Install JSS fish shell command completion
50
-
53
+
51
54
  Returns:
52
55
  int: 0 if success, 1 if failed
53
56
  """
@@ -70,7 +73,8 @@ def install_jss_completion() -> int:
70
73
  return 0
71
74
 
72
75
  with open(config_file, "a") as f:
73
- f.write(f"""
76
+ f.write(
77
+ f"""
74
78
  {start_marker}
75
79
  function fish_command_not_found
76
80
  if test (string length "$argv") -lt 10
@@ -83,13 +87,15 @@ function __fish_command_not_found_handler --on-event fish_command_not_found
83
87
  fish_command_not_found "$argv"
84
88
  end
85
89
  {end_marker}
86
- """)
90
+ """
91
+ )
87
92
  print("JSS fish completion已安装,请执行: source ~/.config/fish/config.fish")
88
93
  return 0
89
94
 
95
+
90
96
  def uninstall_jss_completion() -> int:
91
97
  """Uninstall JSS fish shell command completion
92
-
98
+
93
99
  Returns:
94
100
  int: 0 if success, 1 if failed
95
101
  """
@@ -112,10 +118,10 @@ def uninstall_jss_completion() -> int:
112
118
  return 0
113
119
 
114
120
  new_content = content.split(start_marker)[0] + content.split(end_marker)[-1]
115
-
121
+
116
122
  with open(config_file, "w") as f:
117
123
  f.write(new_content)
118
-
124
+
119
125
  print("JSS fish completion已卸载,请执行: source ~/.config/fish/config.fish")
120
126
  return 0
121
127
 
@@ -205,7 +211,7 @@ Example:
205
211
  install_parser.add_argument(
206
212
  "--shell", choices=["fish"], default="fish", help="指定shell类型(仅支持fish)"
207
213
  )
208
-
214
+
209
215
  # 添加uninstall子命令
210
216
  uninstall_parser = subparsers.add_parser(
211
217
  "uninstall", help="卸载JSS fish shell命令补全功能"
@@ -232,7 +238,7 @@ Example:
232
238
  print(f"错误: 不支持的shell类型: {args.shell}, 仅支持fish")
233
239
  return 1
234
240
  return uninstall_jss_completion()
235
-
241
+
236
242
  # 处理request命令
237
243
  if not args.request:
238
244
  # 检查是否在交互式终端中运行
@@ -34,6 +34,7 @@ class AskUserTool:
34
34
 
35
35
  # 获取agent对象并重置工具调用计数
36
36
  agent = args["agent"]
37
+ agent.set_run_input_handlers_next_turn(True)
37
38
 
38
39
  # 显示问题给用户
39
40
  PrettyOutput.print(f"问题: {question}", OutputType.SYSTEM)
@@ -12,6 +12,7 @@ class generate_new_tool:
12
12
  生成并注册新的Jarvis工具。该工具会在用户数据目录下创建新的工具文件,
13
13
  并自动注册到当前的工具注册表中。适用场景:1. 需要创建新的自定义工具;
14
14
  2. 扩展Jarvis功能;3. 自动化重复性操作;4. 封装特定领域的功能。
15
+ 重要提示:在编写工具代码时,应尽量将工具执行的过程和结果打印出来,方便追踪工具的执行状态。
15
16
  """
16
17
 
17
18
  parameters = {
@@ -17,7 +17,7 @@ from jarvis.jarvis_tools.base import Tool
17
17
  from jarvis.jarvis_utils.config import get_data_dir, get_tool_load_dirs
18
18
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
19
19
  from jarvis.jarvis_utils.tag import ct, ot
20
- from jarvis.jarvis_utils.utils import is_context_overflow
20
+ from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
21
21
 
22
22
  tool_call_help = f"""
23
23
  <tool_system_guide>
@@ -124,7 +124,7 @@ class ToolRegistry(OutputHandlerProtocol):
124
124
  """加载工具"""
125
125
  tools = self.get_all_tools()
126
126
  if tools:
127
- tools_prompt = "<tools_section>\n"
127
+ tools_prompt = f"{tool_call_help}\n<tools_section>\n"
128
128
  tools_prompt += " <header>## 可用工具:</header>\n"
129
129
  tools_prompt += " <tools_list>\n"
130
130
  for tool in tools:
@@ -287,16 +287,18 @@ class ToolRegistry(OutputHandlerProtocol):
287
287
 
288
288
  def _load_external_tools(self) -> None:
289
289
  """从jarvis_data/tools和配置的目录加载外部工具"""
290
- tool_dirs = [Path(get_data_dir()) / "tools"] + [
291
- Path(p) for p in get_tool_load_dirs()
292
- ]
290
+ tool_dirs = [str(Path(get_data_dir()) / "tools")] + get_tool_load_dirs()
291
+
292
+ # --- 全局每日更新检查 ---
293
+ daily_check_git_updates(tool_dirs, "tools")
293
294
 
294
295
  for tool_dir in tool_dirs:
295
- if not tool_dir.exists():
296
+ p_tool_dir = Path(tool_dir)
297
+ if not p_tool_dir.exists() or not p_tool_dir.is_dir():
296
298
  continue
297
299
 
298
300
  # 遍历目录中的所有.py文件
299
- for file_path in tool_dir.glob("*.py"):
301
+ for file_path in p_tool_dir.glob("*.py"):
300
302
  # 跳过__init__.py
301
303
  if file_path.name == "__init__.py":
302
304
  continue
@@ -565,7 +567,10 @@ class ToolRegistry(OutputHandlerProtocol):
565
567
  ot("TOOL_CALL") + r"(.*?)" + ct("TOOL_CALL"), content, re.DOTALL
566
568
  )
567
569
  if not data:
568
- return {}, f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}"
570
+ return (
571
+ {},
572
+ f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}",
573
+ )
569
574
  ret = []
570
575
  for item in data:
571
576
  try:
@@ -714,7 +719,10 @@ class ToolRegistry(OutputHandlerProtocol):
714
719
  )
715
720
 
716
721
  # 检查内容是否过大
717
- is_large_content = is_context_overflow(output)
722
+ model_group = None
723
+ if agent_instance.model:
724
+ model_group = agent_instance.model.model_group
725
+ is_large_content = is_context_overflow(output, model_group)
718
726
 
719
727
  if is_large_content:
720
728
  # 创建临时文件
@@ -3,7 +3,7 @@
3
3
  from typing import Any, Dict
4
4
 
5
5
  import httpx
6
- from bs4 import BeautifulSoup
6
+ from markdownify import markdownify as md
7
7
  from ddgs import DDGS
8
8
 
9
9
  from jarvis.jarvis_agent import Agent
@@ -27,7 +27,7 @@ class SearchWebTool:
27
27
  """Performs a web search, scrapes content, and summarizes the results."""
28
28
  try:
29
29
  PrettyOutput.print("▶️ 使用 DuckDuckGo 开始网页搜索...", OutputType.INFO)
30
- results = list(DDGS().text(query, max_results=5))
30
+ results = list(DDGS().text(query, max_results=50))
31
31
 
32
32
  if not results:
33
33
  return {
@@ -36,19 +36,29 @@ class SearchWebTool:
36
36
  "success": False,
37
37
  }
38
38
 
39
- urls = [r["href"] for r in results]
40
39
  full_content = ""
41
40
  visited_urls = []
41
+ visited_count = 0
42
+
43
+ for r in results:
44
+ if visited_count >= 10:
45
+ PrettyOutput.print("ℹ️ 已成功获取10个网页,停止抓取。", OutputType.INFO)
46
+ break
47
+
48
+ url = r["href"]
49
+ title = r.get("title", url)
42
50
 
43
- for url in urls:
44
51
  try:
45
- PrettyOutput.print(f"📄 正在抓取内容: {url}", OutputType.INFO)
52
+ PrettyOutput.print(
53
+ f"📄 ({visited_count + 1}/10) 正在抓取: {title} ({url})",
54
+ OutputType.INFO,
55
+ )
46
56
  response = http_get(url, timeout=10.0, follow_redirects=True)
47
- soup = BeautifulSoup(response.text, "lxml")
48
- body = soup.find("body")
49
- if body:
50
- full_content += body.get_text(" ", strip=True) + "\n\n"
57
+ content = md(response.text, strip=['script', 'style'])
58
+ if content:
59
+ full_content += content + "\n\n"
51
60
  visited_urls.append(url)
61
+ visited_count += 1
52
62
  except httpx.HTTPStatusError as e:
53
63
  PrettyOutput.print(
54
64
  f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}",
@@ -29,7 +29,6 @@ arguments:
29
29
  """,
30
30
  "description": "网页搜索",
31
31
  },
32
-
33
32
  "FindRelatedFiles": {
34
33
  "append": False,
35
34
  "template": f"""
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import os
3
3
  from functools import lru_cache
4
- from typing import Any, Dict, List
4
+ from typing import Any, Dict, List, Optional
5
5
 
6
6
  import yaml # type: ignore
7
7
 
@@ -76,24 +76,26 @@ def get_replace_map() -> dict:
76
76
  return {**BUILTIN_REPLACE_MAP, **file_map}
77
77
 
78
78
 
79
- def get_max_token_count() -> int:
79
+ def get_max_token_count(model_group_override: Optional[str] = None) -> int:
80
80
  """
81
81
  获取模型允许的最大token数量。
82
82
 
83
83
  返回:
84
84
  int: 模型能处理的最大token数量。
85
85
  """
86
- return int(GLOBAL_CONFIG_DATA.get("JARVIS_MAX_TOKEN_COUNT", "960000"))
86
+ config = _get_resolved_model_config(model_group_override)
87
+ return int(config.get("JARVIS_MAX_TOKEN_COUNT", "960000"))
87
88
 
88
89
 
89
- def get_max_input_token_count() -> int:
90
+ def get_max_input_token_count(model_group_override: Optional[str] = None) -> int:
90
91
  """
91
92
  获取模型允许的最大输入token数量。
92
93
 
93
94
  返回:
94
95
  int: 模型能处理的最大输入token数量。
95
96
  """
96
- return int(GLOBAL_CONFIG_DATA.get("JARVIS_MAX_INPUT_TOKEN_COUNT", "32000"))
97
+ config = _get_resolved_model_config(model_group_override)
98
+ return int(config.get("JARVIS_MAX_INPUT_TOKEN_COUNT", "32000"))
97
99
 
98
100
 
99
101
  def get_shell_name() -> str:
@@ -113,48 +115,94 @@ def get_shell_name() -> str:
113
115
  return os.path.basename(shell_path).lower()
114
116
 
115
117
 
116
- def get_normal_platform_name() -> str:
118
+ def _get_resolved_model_config(model_group_override: Optional[str] = None) -> Dict[str, Any]:
119
+ """
120
+ 解析并合并模型配置,处理模型组。
121
+
122
+ 优先级顺序:
123
+ 1. 单独的环境变量 (JARVIS_PLATFORM, JARVIS_MODEL, etc.)
124
+ 2. JARVIS_MODEL_GROUP 中定义的组配置
125
+ 3. 代码中的默认值
126
+
127
+ 返回:
128
+ Dict[str, Any]: 解析后的模型配置字典
129
+ """
130
+ group_config = {}
131
+ model_group_name = model_group_override or GLOBAL_CONFIG_DATA.get("JARVIS_MODEL_GROUP")
132
+ # The format is a list of single-key dicts: [{'group_name': {...}}, ...]
133
+ model_groups = GLOBAL_CONFIG_DATA.get("JARVIS_MODEL_GROUPS", [])
134
+
135
+ if model_group_name and isinstance(model_groups, list):
136
+ for group_item in model_groups:
137
+ if isinstance(group_item, dict) and model_group_name in group_item:
138
+ group_config = group_item[model_group_name]
139
+ break
140
+
141
+ # Start with group config
142
+ resolved_config = group_config.copy()
143
+
144
+ # Override with specific settings from GLOBAL_CONFIG_DATA
145
+ for key in [
146
+ "JARVIS_PLATFORM",
147
+ "JARVIS_MODEL",
148
+ "JARVIS_THINKING_PLATFORM",
149
+ "JARVIS_THINKING_MODEL",
150
+ "JARVIS_MAX_TOKEN_COUNT",
151
+ "JARVIS_MAX_INPUT_TOKEN_COUNT",
152
+ "JARVIS_MAX_BIG_CONTENT_SIZE",
153
+ ]:
154
+ if key in GLOBAL_CONFIG_DATA:
155
+ resolved_config[key] = GLOBAL_CONFIG_DATA[key]
156
+
157
+ return resolved_config
158
+
159
+
160
+ def get_normal_platform_name(model_group_override: Optional[str] = None) -> str:
117
161
  """
118
162
  获取正常操作的平台名称。
119
163
 
120
164
  返回:
121
165
  str: 平台名称,默认为'yuanbao'
122
166
  """
123
- return GLOBAL_CONFIG_DATA.get("JARVIS_PLATFORM", "yuanbao")
167
+ config = _get_resolved_model_config(model_group_override)
168
+ return config.get("JARVIS_PLATFORM", "yuanbao")
124
169
 
125
170
 
126
- def get_normal_model_name() -> str:
171
+ def get_normal_model_name(model_group_override: Optional[str] = None) -> str:
127
172
  """
128
173
  获取正常操作的模型名称。
129
174
 
130
175
  返回:
131
- str: 模型名称,默认为'deep_seek'
176
+ str: 模型名称,默认为'deep_seek_v3'
132
177
  """
133
- return GLOBAL_CONFIG_DATA.get("JARVIS_MODEL", "deep_seek_v3")
178
+ config = _get_resolved_model_config(model_group_override)
179
+ return config.get("JARVIS_MODEL", "deep_seek_v3")
134
180
 
135
181
 
136
- def get_thinking_platform_name() -> str:
182
+ def get_thinking_platform_name(model_group_override: Optional[str] = None) -> str:
137
183
  """
138
184
  获取思考操作的平台名称。
139
185
 
140
186
  返回:
141
- str: 平台名称,默认为'yuanbao'
187
+ str: 平台名称,默认为正常操作平台
142
188
  """
143
- return GLOBAL_CONFIG_DATA.get(
144
- "JARVIS_THINKING_PLATFORM", GLOBAL_CONFIG_DATA.get("JARVIS_PLATFORM", "yuanbao")
189
+ config = _get_resolved_model_config(model_group_override)
190
+ # Fallback to normal platform if thinking platform is not specified
191
+ return config.get(
192
+ "JARVIS_THINKING_PLATFORM", get_normal_platform_name(model_group_override)
145
193
  )
146
194
 
147
195
 
148
- def get_thinking_model_name() -> str:
196
+ def get_thinking_model_name(model_group_override: Optional[str] = None) -> str:
149
197
  """
150
198
  获取思考操作的模型名称。
151
199
 
152
200
  返回:
153
- str: 模型名称,默认为'deep_seek'
201
+ str: 模型名称,默认为正常操作模型
154
202
  """
155
- return GLOBAL_CONFIG_DATA.get(
156
- "JARVIS_THINKING_MODEL", GLOBAL_CONFIG_DATA.get("JARVIS_MODEL", "deep_seek")
157
- )
203
+ config = _get_resolved_model_config(model_group_override)
204
+ # Fallback to normal model if thinking model is not specified
205
+ return config.get("JARVIS_THINKING_MODEL", get_normal_model_name(model_group_override))
158
206
 
159
207
 
160
208
  def is_execute_tool_confirm() -> bool:
@@ -190,14 +238,15 @@ def get_data_dir() -> str:
190
238
  )
191
239
 
192
240
 
193
- def get_max_big_content_size() -> int:
241
+ def get_max_big_content_size(model_group_override: Optional[str] = None) -> int:
194
242
  """
195
243
  获取最大大内容大小。
196
244
 
197
245
  返回:
198
246
  int: 最大大内容大小
199
247
  """
200
- return int(GLOBAL_CONFIG_DATA.get("JARVIS_MAX_BIG_CONTENT_SIZE", "160000"))
248
+ config = _get_resolved_model_config(model_group_override)
249
+ return int(config.get("JARVIS_MAX_BIG_CONTENT_SIZE", "160000"))
201
250
 
202
251
 
203
252
  def get_pretty_output() -> bool:
@@ -240,6 +289,16 @@ def get_tool_load_dirs() -> List[str]:
240
289
  return GLOBAL_CONFIG_DATA.get("JARVIS_TOOL_LOAD_DIRS", [])
241
290
 
242
291
 
292
+ def get_methodology_dirs() -> List[str]:
293
+ """
294
+ 获取方法论加载目录。
295
+
296
+ 返回:
297
+ List[str]: 方法论加载目录列表
298
+ """
299
+ return GLOBAL_CONFIG_DATA.get("JARVIS_METHODOLOGY_DIRS", [])
300
+
301
+
243
302
  def is_print_prompt() -> bool:
244
303
  """
245
304
  获取是否打印提示。
@@ -538,12 +538,12 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
538
538
  return []
539
539
 
540
540
  # 解析提交信息
541
- commits = []
541
+ commits: List[Dict[str, Any]] = []
542
542
  lines = result.stdout.splitlines()
543
543
  for i in range(0, len(lines), 4):
544
544
  if i + 3 >= len(lines):
545
545
  break
546
- commit = {
546
+ commit: Dict[str, Any] = {
547
547
  "hash": lines[i],
548
548
  "message": lines[i + 1],
549
549
  "author": lines[i + 2],
@@ -11,7 +11,7 @@ import os
11
11
 
12
12
  # 全局变量:保存最后一条消息
13
13
  last_message: str = ""
14
- from typing import Any, Set
14
+ from typing import Any, Dict, Set
15
15
 
16
16
  import colorama
17
17
  from rich.console import Console
@@ -22,7 +22,7 @@ colorama.init()
22
22
  # 禁用tokenizers并行以避免多进程问题
23
23
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
24
24
  # 全局代理管理
25
- global_agents: Set[str] = set()
25
+ global_agents: Dict[str, Any] = {}
26
26
  current_agent_name: str = ""
27
27
  # 表示与大模型交互的深度(>0表示正在交互)
28
28
  g_in_chat: int = 0
@@ -66,6 +66,19 @@ def make_agent_name(agent_name: str) -> str:
66
66
  return agent_name
67
67
 
68
68
 
69
+ def get_agent(agent_name: str) -> Any:
70
+ """
71
+ 获取指定名称的代理实例。
72
+
73
+ 参数:
74
+ agent_name: 代理名称
75
+
76
+ 返回:
77
+ Any: 代理实例,如果不存在则返回None
78
+ """
79
+ return global_agents.get(agent_name)
80
+
81
+
69
82
  def set_agent(agent_name: str, agent: Any) -> None:
70
83
  """
71
84
  设置当前代理并将其添加到全局代理集合中。
@@ -74,7 +87,7 @@ def set_agent(agent_name: str, agent: Any) -> None:
74
87
  agent_name: 代理名称
75
88
  agent: 代理对象
76
89
  """
77
- global_agents.add(agent_name)
90
+ global_agents[agent_name] = agent
78
91
  global current_agent_name
79
92
  current_agent_name = agent_name
80
93
 
@@ -101,7 +114,7 @@ def delete_agent(agent_name: str) -> None:
101
114
  agent_name: 要删除的代理名称
102
115
  """
103
116
  if agent_name in global_agents:
104
- global_agents.remove(agent_name)
117
+ del global_agents[agent_name]
105
118
  global current_agent_name
106
119
  current_agent_name = ""
107
120
 
@@ -173,10 +186,3 @@ def get_last_message() -> str:
173
186
  str: 最后一条消息
174
187
  """
175
188
  return last_message
176
- """
177
- 获取当前中断信号状态。
178
-
179
- 返回:
180
- int: 当前中断计数
181
- """
182
- return g_interrupt