jarvis-ai-assistant 0.3.23__py3-none-any.whl → 0.3.25__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +96 -13
- jarvis/jarvis_agent/agent_manager.py +0 -3
- jarvis/jarvis_agent/jarvis.py +19 -34
- jarvis/jarvis_agent/main.py +2 -8
- jarvis/jarvis_code_agent/code_agent.py +5 -11
- jarvis/jarvis_code_analysis/code_review.py +12 -40
- jarvis/jarvis_data/config_schema.json +11 -18
- jarvis/jarvis_git_utils/git_commiter.py +11 -25
- jarvis/jarvis_mcp/sse_mcp_client.py +4 -3
- jarvis/jarvis_mcp/streamable_mcp_client.py +9 -8
- jarvis/jarvis_memory_organizer/memory_organizer.py +46 -53
- jarvis/jarvis_methodology/main.py +4 -2
- jarvis/jarvis_platform/base.py +90 -21
- jarvis/jarvis_platform/kimi.py +16 -22
- jarvis/jarvis_platform/registry.py +7 -14
- jarvis/jarvis_platform/tongyi.py +21 -32
- jarvis/jarvis_platform/yuanbao.py +15 -17
- jarvis/jarvis_platform_manager/main.py +14 -51
- jarvis/jarvis_rag/cli.py +21 -13
- jarvis/jarvis_rag/embedding_manager.py +138 -6
- jarvis/jarvis_rag/llm_interface.py +0 -2
- jarvis/jarvis_rag/rag_pipeline.py +41 -17
- jarvis/jarvis_rag/reranker.py +24 -2
- jarvis/jarvis_rag/retriever.py +21 -23
- jarvis/jarvis_smart_shell/main.py +1 -10
- jarvis/jarvis_tools/cli/main.py +22 -15
- jarvis/jarvis_tools/edit_file.py +6 -6
- jarvis/jarvis_tools/execute_script.py +1 -2
- jarvis/jarvis_tools/file_analyzer.py +12 -6
- jarvis/jarvis_tools/registry.py +13 -10
- jarvis/jarvis_tools/sub_agent.py +5 -8
- jarvis/jarvis_tools/sub_code_agent.py +5 -5
- jarvis/jarvis_utils/config.py +24 -10
- jarvis/jarvis_utils/input.py +8 -5
- jarvis/jarvis_utils/methodology.py +11 -6
- jarvis/jarvis_utils/utils.py +29 -12
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/METADATA +10 -3
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/RECORD +43 -43
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.25.dist-info}/top_level.txt +0 -0
jarvis/jarvis_rag/retriever.py
CHANGED
@@ -184,32 +184,26 @@ class ChromaRetriever:
|
|
184
184
|
deleted = result["deleted"]
|
185
185
|
if not changed and not deleted:
|
186
186
|
return
|
187
|
+
# 为避免在循环中逐条打印,先拼接后统一打印
|
188
|
+
lines: list[str] = []
|
187
189
|
if changed:
|
188
|
-
|
189
|
-
f"检测到 {len(changed)} 个已索引文件发生变化,建议重新索引以保证检索准确性。"
|
190
|
-
OutputType.WARNING,
|
190
|
+
lines.append(
|
191
|
+
f"检测到 {len(changed)} 个已索引文件发生变化,建议重新索引以保证检索准确性。"
|
191
192
|
)
|
192
|
-
for p in changed[:5]
|
193
|
-
PrettyOutput.print(f" 变更: {p}", OutputType.WARNING)
|
193
|
+
lines.extend([f" 变更: {p}" for p in changed[:5]])
|
194
194
|
if len(changed) > 5:
|
195
|
-
|
196
|
-
f" ... 以及另外 {len(changed) - 5} 个文件", OutputType.WARNING
|
197
|
-
)
|
195
|
+
lines.append(f" ... 以及另外 {len(changed) - 5} 个文件")
|
198
196
|
if deleted:
|
199
|
-
|
200
|
-
f"检测到 {len(deleted)} 个已索引文件已被删除,建议清理并重新索引。"
|
201
|
-
OutputType.WARNING,
|
197
|
+
lines.append(
|
198
|
+
f"检测到 {len(deleted)} 个已索引文件已被删除,建议清理并重新索引。"
|
202
199
|
)
|
203
|
-
for p in deleted[:5]
|
204
|
-
PrettyOutput.print(f" 删除: {p}", OutputType.WARNING)
|
200
|
+
lines.extend([f" 删除: {p}" for p in deleted[:5]])
|
205
201
|
if len(deleted) > 5:
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
PrettyOutput.print(
|
210
|
-
"提示:请使用 'jarvis-rag add <路径>' 重新索引相关文件,以更新向量库与BM25索引。",
|
211
|
-
OutputType.INFO,
|
202
|
+
lines.append(f" ... 以及另外 {len(deleted) - 5} 个文件")
|
203
|
+
lines.append(
|
204
|
+
"提示:请使用 'jarvis-rag add <路径>' 重新索引相关文件,以更新向量库与BM25索引。"
|
212
205
|
)
|
206
|
+
PrettyOutput.print("\n".join(lines), OutputType.WARNING)
|
213
207
|
|
214
208
|
def detect_index_changes(self) -> Dict[str, List[str]]:
|
215
209
|
"""
|
@@ -253,14 +247,18 @@ class ChromaRetriever:
|
|
253
247
|
return
|
254
248
|
|
255
249
|
# 先处理删除
|
250
|
+
delete_errors: list[str] = []
|
256
251
|
for src in deleted:
|
257
252
|
try:
|
258
253
|
self.collection.delete(where={"source": src}) # type: ignore[arg-type]
|
259
254
|
except Exception as e:
|
260
|
-
|
255
|
+
delete_errors.append(f"删除源 '{src}' 时出错: {e}")
|
256
|
+
if delete_errors:
|
257
|
+
PrettyOutput.print("\n".join(delete_errors), OutputType.WARNING)
|
261
258
|
|
262
259
|
# 再处理变更(重建)
|
263
260
|
docs_to_add: List[Document] = []
|
261
|
+
rebuild_errors: list[str] = []
|
264
262
|
for src in changed:
|
265
263
|
try:
|
266
264
|
# 删除旧条目
|
@@ -275,9 +273,9 @@ class ChromaRetriever:
|
|
275
273
|
Document(page_content=content, metadata={"source": src})
|
276
274
|
)
|
277
275
|
except Exception as e:
|
278
|
-
|
279
|
-
|
280
|
-
|
276
|
+
rebuild_errors.append(f"重建源 '{src}' 内容时出错: {e}")
|
277
|
+
if rebuild_errors:
|
278
|
+
PrettyOutput.print("\n".join(rebuild_errors), OutputType.WARNING)
|
281
279
|
|
282
280
|
if docs_to_add:
|
283
281
|
try:
|
@@ -24,20 +24,11 @@ Example:
|
|
24
24
|
|
25
25
|
def execute_command(command: str, should_run: bool) -> None:
|
26
26
|
"""Print command without execution"""
|
27
|
-
|
27
|
+
print(command)
|
28
28
|
if should_run:
|
29
29
|
os.system(command)
|
30
30
|
|
31
31
|
|
32
|
-
def _check_fish_shell() -> bool:
|
33
|
-
"""Check if current shell is fish
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
bool: True if fish shell, False otherwise
|
37
|
-
"""
|
38
|
-
return get_shell_name() == "fish"
|
39
|
-
|
40
|
-
|
41
32
|
def _get_config_file() -> str:
|
42
33
|
"""Get fish config file path
|
43
34
|
|
jarvis/jarvis_tools/cli/main.py
CHANGED
@@ -39,18 +39,22 @@ def list_tools(
|
|
39
39
|
)
|
40
40
|
else:
|
41
41
|
PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
|
42
|
+
# 为避免 PrettyOutput 对每行加框造成信息稀疏,先拼接字符串再统一打印
|
43
|
+
lines = []
|
44
|
+
import json as _json # local import to ensure available
|
42
45
|
for tool in tools:
|
43
|
-
|
44
|
-
|
46
|
+
lines.append(f"\n{tool['name']}")
|
47
|
+
lines.append(f" 描述: {tool['description']}")
|
45
48
|
if detailed:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
_json.dumps(tool["parameters"], ensure_ascii=False, indent=2)
|
51
|
-
|
52
|
-
|
53
|
-
)
|
49
|
+
lines.append(" 参数:")
|
50
|
+
# 使用 Markdown 代码块统一展示参数
|
51
|
+
lines.append("```json")
|
52
|
+
try:
|
53
|
+
lines.append(_json.dumps(tool["parameters"], ensure_ascii=False, indent=2))
|
54
|
+
except Exception:
|
55
|
+
lines.append(str(tool.get("parameters")))
|
56
|
+
lines.append("```")
|
57
|
+
PrettyOutput.print("\n".join(lines), OutputType.INFO, lang="markdown")
|
54
58
|
|
55
59
|
|
56
60
|
@app.command("stat")
|
@@ -202,15 +206,18 @@ def call_tool(
|
|
202
206
|
missing_params = [p for p in required_params if p not in tool_args]
|
203
207
|
|
204
208
|
if missing_params:
|
205
|
-
|
206
|
-
f"错误: 缺少必需参数: {', '.join(missing_params)}", OutputType.ERROR
|
207
|
-
)
|
208
|
-
PrettyOutput.print("\n参数说明:", OutputType.INFO)
|
209
|
+
# 先拼接提示与参数说明,再统一打印,避免循环中逐条打印
|
209
210
|
params = tool_obj.parameters.get("properties", {})
|
211
|
+
lines = [
|
212
|
+
f"错误: 缺少必需参数: {', '.join(missing_params)}",
|
213
|
+
"",
|
214
|
+
"参数说明:",
|
215
|
+
]
|
210
216
|
for param_name in required_params:
|
211
217
|
param_info = params.get(param_name, {})
|
212
218
|
desc = param_info.get("description", "无描述")
|
213
|
-
|
219
|
+
lines.append(f" - {param_name}: {desc}")
|
220
|
+
PrettyOutput.print("\n".join(lines), OutputType.ERROR)
|
214
221
|
raise typer.Exit(code=1)
|
215
222
|
|
216
223
|
result = registry.execute_tool(tool_name, tool_args)
|
jarvis/jarvis_tools/edit_file.py
CHANGED
@@ -122,8 +122,8 @@ class FileSearchReplaceTool:
|
|
122
122
|
|
123
123
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
124
124
|
|
125
|
-
stdout_messages = []
|
126
|
-
stderr_messages = []
|
125
|
+
stdout_messages: list[str] = []
|
126
|
+
stderr_messages: list[str] = []
|
127
127
|
overall_success = False
|
128
128
|
file_results = []
|
129
129
|
|
@@ -168,10 +168,10 @@ class FileSearchReplaceTool:
|
|
168
168
|
)
|
169
169
|
|
170
170
|
# 整合所有错误信息到stderr
|
171
|
-
all_stderr = []
|
172
|
-
for
|
173
|
-
if not
|
174
|
-
all_stderr.append(f"文件 {
|
171
|
+
all_stderr: list[str] = []
|
172
|
+
for file_result in file_results:
|
173
|
+
if not file_result["success"]:
|
174
|
+
all_stderr.append(f"文件 {file_result['file']} 处理失败: {file_result['stderr']}")
|
175
175
|
|
176
176
|
return {
|
177
177
|
"success": overall_success,
|
@@ -30,7 +30,7 @@ class FileAnalyzerTool:
|
|
30
30
|
|
31
31
|
@staticmethod
|
32
32
|
def check() -> bool:
|
33
|
-
return PlatformRegistry().
|
33
|
+
return PlatformRegistry().get_normal_platform().support_upload_files()
|
34
34
|
|
35
35
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
36
36
|
"""执行文件分析操作
|
@@ -45,25 +45,31 @@ class FileAnalyzerTool:
|
|
45
45
|
file_paths = args["file_paths"]
|
46
46
|
prompt = args["prompt"]
|
47
47
|
|
48
|
-
#
|
48
|
+
# 验证文件路径(先收集不存在的文件,统一打印一次)
|
49
49
|
valid_files = []
|
50
|
+
missing_files = []
|
50
51
|
for file_path in file_paths:
|
51
52
|
if os.path.exists(file_path):
|
52
53
|
valid_files.append(file_path)
|
53
54
|
else:
|
54
|
-
|
55
|
+
missing_files.append(file_path)
|
56
|
+
if missing_files:
|
57
|
+
PrettyOutput.print(
|
58
|
+
"以下文件不存在:\n" + "\n".join(f" - {p}" for p in missing_files),
|
59
|
+
OutputType.WARNING,
|
60
|
+
)
|
55
61
|
|
56
62
|
if not valid_files:
|
57
63
|
return {"success": False, "stdout": "", "stderr": "没有找到有效的文件"}
|
58
64
|
|
59
|
-
#
|
60
|
-
platform = PlatformRegistry().
|
65
|
+
# 创建平台实例
|
66
|
+
platform = PlatformRegistry().get_normal_platform()
|
61
67
|
|
62
68
|
if not platform:
|
63
69
|
return {
|
64
70
|
"success": False,
|
65
71
|
"stdout": "",
|
66
|
-
"stderr": "
|
72
|
+
"stderr": "无法创建平台实例",
|
67
73
|
}
|
68
74
|
|
69
75
|
# 设置系统消息
|
jarvis/jarvis_tools/registry.py
CHANGED
@@ -73,7 +73,7 @@ arguments:
|
|
73
73
|
|
74
74
|
<string_format>
|
75
75
|
# 📝 字符串参数格式
|
76
|
-
|
76
|
+
使用 |2 语法表示字符串参数,防止多行字符串行首空格引起歧义。
|
77
77
|
|
78
78
|
{ot("TOOL_CALL")}
|
79
79
|
want: 当前的git状态,期望获取xxx的提交记录
|
@@ -81,7 +81,7 @@ name: execute_script
|
|
81
81
|
|
82
82
|
arguments:
|
83
83
|
interpreter: bash
|
84
|
-
script_content: |
|
84
|
+
script_content: |
|
85
85
|
git status --porcelain
|
86
86
|
{ct("TOOL_CALL")}
|
87
87
|
</string_format>
|
@@ -98,7 +98,6 @@ arguments:
|
|
98
98
|
<common_errors>
|
99
99
|
# ⚠️ 常见错误
|
100
100
|
- 同时调用多个工具
|
101
|
-
- 字符串参数缺少 |2
|
102
101
|
- 假设工具结果
|
103
102
|
- 创建虚构对话
|
104
103
|
- 在没有所需信息的情况下继续
|
@@ -276,14 +275,17 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
276
275
|
# 如果配置了 use 列表,只保留列表中的工具
|
277
276
|
if use_list:
|
278
277
|
filtered_tools = {}
|
278
|
+
missing = []
|
279
279
|
for tool_name in use_list:
|
280
280
|
if tool_name in self.tools:
|
281
281
|
filtered_tools[tool_name] = self.tools[tool_name]
|
282
282
|
else:
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
)
|
283
|
+
missing.append(tool_name)
|
284
|
+
if missing:
|
285
|
+
PrettyOutput.print(
|
286
|
+
"警告: 配置的工具不存在: " + ", ".join(f"'{name}'" for name in missing),
|
287
|
+
OutputType.WARNING,
|
288
|
+
)
|
287
289
|
self.tools = filtered_tools
|
288
290
|
|
289
291
|
# 如果配置了 dont_use 列表,排除列表中的工具
|
@@ -315,14 +317,15 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
315
317
|
)
|
316
318
|
|
317
319
|
# 遍历目录中的所有.yaml文件
|
320
|
+
error_lines = []
|
318
321
|
for file_path in mcp_tools_dir.glob("*.yaml"):
|
319
322
|
try:
|
320
323
|
config = yaml.safe_load(open(file_path, "r", encoding="utf-8"))
|
321
324
|
self.register_mcp_tool_by_config(config)
|
322
325
|
except Exception as e:
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
+
error_lines.append(f"文件 {file_path} 加载失败: {str(e)}")
|
327
|
+
if error_lines:
|
328
|
+
PrettyOutput.print("\n".join(error_lines), OutputType.WARNING)
|
326
329
|
|
327
330
|
def _load_builtin_tools(self) -> None:
|
328
331
|
"""从内置工具目录加载工具"""
|
jarvis/jarvis_tools/sub_agent.py
CHANGED
@@ -64,12 +64,6 @@ class SubAgentTool:
|
|
64
64
|
f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
|
65
65
|
)
|
66
66
|
|
67
|
-
# 读取背景信息并组合任务
|
68
|
-
background: str = str(args.get("background", "")).strip()
|
69
|
-
enhanced_task = (
|
70
|
-
f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
|
71
|
-
)
|
72
|
-
|
73
67
|
# 继承父Agent的运行参数(用于覆盖默认值);若无父Agent则使用默认/全局配置
|
74
68
|
parent_agent = args.get("agent")
|
75
69
|
# 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
|
@@ -133,7 +127,7 @@ class SubAgentTool:
|
|
133
127
|
system_prompt=system_prompt,
|
134
128
|
name="SubAgent",
|
135
129
|
description="Temporary sub agent for executing a subtask",
|
136
|
-
|
130
|
+
|
137
131
|
model_group=model_group, # 继承父Agent模型组(如可用)
|
138
132
|
summary_prompt=summary_prompt, # 继承父Agent总结提示词(如可用)
|
139
133
|
auto_complete=auto_complete,
|
@@ -160,7 +154,10 @@ class SubAgentTool:
|
|
160
154
|
try:
|
161
155
|
model_name = parent_agent.model.name() # type: ignore[attr-defined]
|
162
156
|
if model_name:
|
163
|
-
|
157
|
+
from typing import Any
|
158
|
+
model_obj: Any = getattr(agent, "model", None)
|
159
|
+
if model_obj is not None:
|
160
|
+
model_obj.set_model_name(model_name)
|
164
161
|
except Exception:
|
165
162
|
pass
|
166
163
|
if use_tools:
|
@@ -103,12 +103,10 @@ class SubCodeAgentTool:
|
|
103
103
|
pass
|
104
104
|
|
105
105
|
# 创建 CodeAgent:参数优先使用父Agent的配置(若可获取),否则使用默认
|
106
|
-
# 推断/继承
|
107
|
-
llm_type = "normal"
|
106
|
+
# 推断/继承 tool_group
|
108
107
|
tool_group = None
|
109
108
|
try:
|
110
109
|
if parent_agent is not None:
|
111
|
-
llm_type = getattr(parent_agent, "llm_type", llm_type)
|
112
110
|
tool_group = getattr(parent_agent, "tool_group", tool_group)
|
113
111
|
except Exception:
|
114
112
|
pass
|
@@ -135,7 +133,6 @@ class SubCodeAgentTool:
|
|
135
133
|
|
136
134
|
try:
|
137
135
|
code_agent = CodeAgent(
|
138
|
-
llm_type=llm_type,
|
139
136
|
model_group=model_group,
|
140
137
|
need_summary=True,
|
141
138
|
append_tools=append_tools,
|
@@ -164,7 +161,10 @@ class SubCodeAgentTool:
|
|
164
161
|
try:
|
165
162
|
parent_model_name = parent_agent.model.name() # type: ignore[attr-defined]
|
166
163
|
if parent_model_name:
|
167
|
-
|
164
|
+
from typing import Any
|
165
|
+
model_obj: Any = getattr(code_agent.agent, "model", None)
|
166
|
+
if model_obj is not None:
|
167
|
+
model_obj.set_model_name(parent_model_name)
|
168
168
|
except Exception:
|
169
169
|
pass
|
170
170
|
except Exception:
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -115,6 +115,14 @@ def get_shell_name() -> str:
|
|
115
115
|
return os.path.basename(shell_path).lower()
|
116
116
|
|
117
117
|
|
118
|
+
def _apply_llm_group_env_override(group_config: Dict[str, Any]) -> None:
|
119
|
+
"""如果模型组配置中包含ENV,则应用环境变量覆盖"""
|
120
|
+
if "ENV" in group_config and isinstance(group_config["ENV"], dict):
|
121
|
+
os.environ.update(
|
122
|
+
{str(k): str(v) for k, v in group_config["ENV"].items() if v is not None}
|
123
|
+
)
|
124
|
+
|
125
|
+
|
118
126
|
def _get_resolved_model_config(
|
119
127
|
model_group_override: Optional[str] = None,
|
120
128
|
) -> Dict[str, Any]:
|
@@ -141,6 +149,8 @@ def _get_resolved_model_config(
|
|
141
149
|
if isinstance(group_item, dict) and model_group_name in group_item:
|
142
150
|
group_config = group_item[model_group_name]
|
143
151
|
break
|
152
|
+
|
153
|
+
_apply_llm_group_env_override(group_config)
|
144
154
|
|
145
155
|
# Start with group config
|
146
156
|
resolved_config = group_config.copy()
|
@@ -149,8 +159,6 @@ def _get_resolved_model_config(
|
|
149
159
|
for key in [
|
150
160
|
"JARVIS_PLATFORM",
|
151
161
|
"JARVIS_MODEL",
|
152
|
-
"JARVIS_THINKING_PLATFORM",
|
153
|
-
"JARVIS_THINKING_MODEL",
|
154
162
|
"JARVIS_MAX_INPUT_TOKEN_COUNT",
|
155
163
|
]:
|
156
164
|
if key in GLOBAL_CONFIG_DATA:
|
@@ -181,7 +189,7 @@ def get_normal_model_name(model_group_override: Optional[str] = None) -> str:
|
|
181
189
|
return config.get("JARVIS_MODEL", "deep_seek_v3")
|
182
190
|
|
183
191
|
|
184
|
-
def
|
192
|
+
def _deprecated_platform_name_v1(model_group_override: Optional[str] = None) -> str:
|
185
193
|
"""
|
186
194
|
获取思考操作的平台名称。
|
187
195
|
|
@@ -190,12 +198,10 @@ def get_thinking_platform_name(model_group_override: Optional[str] = None) -> st
|
|
190
198
|
"""
|
191
199
|
config = _get_resolved_model_config(model_group_override)
|
192
200
|
# Fallback to normal platform if thinking platform is not specified
|
193
|
-
return
|
194
|
-
"JARVIS_THINKING_PLATFORM", get_normal_platform_name(model_group_override)
|
195
|
-
)
|
201
|
+
return get_normal_platform_name(model_group_override)
|
196
202
|
|
197
203
|
|
198
|
-
def
|
204
|
+
def _deprecated_model_name_v1(model_group_override: Optional[str] = None) -> str:
|
199
205
|
"""
|
200
206
|
获取思考操作的模型名称。
|
201
207
|
|
@@ -204,9 +210,7 @@ def get_thinking_model_name(model_group_override: Optional[str] = None) -> str:
|
|
204
210
|
"""
|
205
211
|
config = _get_resolved_model_config(model_group_override)
|
206
212
|
# Fallback to normal model if thinking model is not specified
|
207
|
-
return
|
208
|
-
"JARVIS_THINKING_MODEL", get_normal_model_name(model_group_override)
|
209
|
-
)
|
213
|
+
return get_normal_model_name(model_group_override)
|
210
214
|
|
211
215
|
|
212
216
|
def is_execute_tool_confirm() -> bool:
|
@@ -638,6 +642,16 @@ def get_tool_dont_use_list() -> List[str]:
|
|
638
642
|
return config.get("dont_use", [])
|
639
643
|
|
640
644
|
|
645
|
+
def get_tool_filter_threshold() -> int:
|
646
|
+
"""
|
647
|
+
获取AI工具筛选的阈值。
|
648
|
+
|
649
|
+
返回:
|
650
|
+
int: 当工具数量超过此阈值时,触发AI筛选。默认为30
|
651
|
+
"""
|
652
|
+
return int(GLOBAL_CONFIG_DATA.get("JARVIS_TOOL_FILTER_THRESHOLD", 30))
|
653
|
+
|
654
|
+
|
641
655
|
def is_enable_git_repo_jca_switch() -> bool:
|
642
656
|
"""
|
643
657
|
是否启用:在初始化环境前检测Git仓库并提示可切换到代码开发模式(jca)
|
jarvis/jarvis_utils/input.py
CHANGED
@@ -384,14 +384,17 @@ def _show_history_and_copy():
|
|
384
384
|
PrettyOutput.print("没有可复制的消息", OutputType.INFO)
|
385
385
|
return
|
386
386
|
|
387
|
-
|
387
|
+
# 为避免 PrettyOutput 在循环中为每行加框,先拼接后统一打印
|
388
|
+
lines = []
|
389
|
+
lines.append("\n" + "=" * 20 + " 消息历史记录 " + "=" * 20)
|
388
390
|
for i, msg in enumerate(history):
|
389
391
|
cleaned_msg = msg.replace("\n", r"\n")
|
390
392
|
display_msg = (
|
391
393
|
(cleaned_msg[:70] + "...") if len(cleaned_msg) > 70 else cleaned_msg
|
392
394
|
)
|
393
|
-
|
394
|
-
|
395
|
+
lines.append(f" {i + 1}: {display_msg.strip()}")
|
396
|
+
lines.append("=" * 58 + "\n")
|
397
|
+
PrettyOutput.print("\n".join(lines), OutputType.INFO)
|
395
398
|
|
396
399
|
while True:
|
397
400
|
try:
|
@@ -699,7 +702,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
|
|
699
702
|
"未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
|
700
703
|
)
|
701
704
|
else:
|
702
|
-
files
|
705
|
+
files = []
|
703
706
|
try:
|
704
707
|
r = subprocess.run(
|
705
708
|
["git", "ls-files"],
|
@@ -832,7 +835,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
|
|
832
835
|
"未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
|
833
836
|
)
|
834
837
|
else:
|
835
|
-
files
|
838
|
+
files = []
|
836
839
|
try:
|
837
840
|
import os as _os
|
838
841
|
|
@@ -81,11 +81,13 @@ def _load_all_methodologies() -> Dict[str, str]:
|
|
81
81
|
|
82
82
|
import glob
|
83
83
|
|
84
|
+
# 收集循环中的提示,统一打印,避免逐条加框
|
85
|
+
warn_dirs: List[str] = []
|
86
|
+
error_lines: List[str] = []
|
87
|
+
|
84
88
|
for directory in set(methodology_dirs): # Use set to avoid duplicates
|
85
89
|
if not os.path.isdir(directory):
|
86
|
-
|
87
|
-
f"警告: 方法论目录不存在或不是一个目录: {directory}", OutputType.WARNING
|
88
|
-
)
|
90
|
+
warn_dirs.append(f"警告: 方法论目录不存在或不是一个目录: {directory}")
|
89
91
|
continue
|
90
92
|
|
91
93
|
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
@@ -100,10 +102,13 @@ def _load_all_methodologies() -> Dict[str, str]:
|
|
100
102
|
all_methodologies[problem_type] = content
|
101
103
|
except Exception as e:
|
102
104
|
filename = os.path.basename(filepath)
|
103
|
-
|
104
|
-
f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING
|
105
|
-
)
|
105
|
+
error_lines.append(f"加载方法论文件 {filename} 失败: {str(e)}")
|
106
106
|
|
107
|
+
# 统一打印目录警告与文件加载失败信息
|
108
|
+
if warn_dirs:
|
109
|
+
PrettyOutput.print("\n".join(warn_dirs), OutputType.WARNING)
|
110
|
+
if error_lines:
|
111
|
+
PrettyOutput.print("\n".join(error_lines), OutputType.WARNING)
|
107
112
|
return all_methodologies
|
108
113
|
|
109
114
|
|
jarvis/jarvis_utils/utils.py
CHANGED
@@ -673,13 +673,20 @@ def _interactive_config_setup(config_file_path: Path):
|
|
673
673
|
|
674
674
|
# 如果有配置指导,先显示总体说明
|
675
675
|
if config_guide:
|
676
|
-
PrettyOutput
|
676
|
+
# 为避免 PrettyOutput 在循环中为每行加框,先拼接后统一打印
|
677
|
+
guide_lines = ["", "配置获取方法:"]
|
678
|
+
for key in required_keys:
|
679
|
+
if key in config_guide and config_guide[key]:
|
680
|
+
guide_lines.append(f"")
|
681
|
+
guide_lines.append(f"{key} 获取方法:")
|
682
|
+
guide_lines.append(str(config_guide[key]))
|
683
|
+
PrettyOutput.print("\n".join(guide_lines), OutputType.INFO)
|
684
|
+
else:
|
685
|
+
# 若无指导,仍需遍历以保持后续逻辑一致
|
686
|
+
pass
|
677
687
|
|
678
688
|
for key in required_keys:
|
679
|
-
#
|
680
|
-
if key in config_guide and config_guide[key]:
|
681
|
-
PrettyOutput.print(f"\n{key} 获取方法:", OutputType.INFO)
|
682
|
-
PrettyOutput.print(config_guide[key], OutputType.INFO)
|
689
|
+
# 显示该环境变量的配置指导(上文已统一打印,此处不再逐条打印)
|
683
690
|
|
684
691
|
default_value = defaults.get(key, "")
|
685
692
|
prompt_text = f" - {key}"
|
@@ -737,9 +744,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
737
744
|
config_data = {
|
738
745
|
"ENV": env_vars,
|
739
746
|
"JARVIS_PLATFORM": platform_name,
|
740
|
-
"JARVIS_THINKING_PLATFORM": platform_name,
|
741
747
|
"JARVIS_MODEL": model_name,
|
742
|
-
"JARVIS_THINKING_MODEL": model_name,
|
743
748
|
}
|
744
749
|
|
745
750
|
if not test_passed:
|
@@ -1080,6 +1085,14 @@ def _collect_optional_config_interactively(
|
|
1080
1085
|
)
|
1081
1086
|
or changed
|
1082
1087
|
)
|
1088
|
+
changed = (
|
1089
|
+
_ask_and_set_int(
|
1090
|
+
"JARVIS_TOOL_FILTER_THRESHOLD",
|
1091
|
+
"设置AI工具筛选阈值 (当可用工具数超过此值时触发AI筛选, 默认30)",
|
1092
|
+
30,
|
1093
|
+
)
|
1094
|
+
or changed
|
1095
|
+
)
|
1083
1096
|
|
1084
1097
|
# 目录类配置(逗号分隔)
|
1085
1098
|
changed = (
|
@@ -1159,7 +1172,7 @@ def _collect_optional_config_interactively(
|
|
1159
1172
|
try:
|
1160
1173
|
if "JARVIS_RAG" not in config_data:
|
1161
1174
|
if get_yes_no("是否配置 RAG 检索增强参数?", default=False):
|
1162
|
-
rag_conf = {}
|
1175
|
+
rag_conf: Dict[str, Any] = {}
|
1163
1176
|
emb = get_single_line_input(
|
1164
1177
|
f"RAG 嵌入模型(留空使用默认: {rag_default_embed}):",
|
1165
1178
|
default="",
|
@@ -1454,7 +1467,7 @@ def _read_old_config_file(config_file):
|
|
1454
1467
|
|
1455
1468
|
|
1456
1469
|
def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
1457
|
-
"""
|
1470
|
+
"""循环执行函数直到成功(累计日志后统一打印,避免逐次加框)
|
1458
1471
|
|
1459
1472
|
参数:
|
1460
1473
|
func -- 要执行的函数
|
@@ -1463,17 +1476,20 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
1463
1476
|
返回:
|
1464
1477
|
函数执行结果
|
1465
1478
|
"""
|
1479
|
+
result: Any = None
|
1466
1480
|
while True:
|
1467
1481
|
try:
|
1468
|
-
|
1469
|
-
|
1482
|
+
result = func()
|
1483
|
+
break
|
1484
|
+
except Exception:
|
1470
1485
|
PrettyOutput.print(f"重试中,等待 {sleep_time}s...", OutputType.WARNING)
|
1471
1486
|
time.sleep(sleep_time)
|
1472
1487
|
continue
|
1488
|
+
return result
|
1473
1489
|
|
1474
1490
|
|
1475
1491
|
def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
1476
|
-
"""循环执行函数直到返回True
|
1492
|
+
"""循环执行函数直到返回True(累计日志后统一打印,避免逐次加框)
|
1477
1493
|
|
1478
1494
|
参数:
|
1479
1495
|
func: 要执行的函数,必须返回布尔值
|
@@ -1486,6 +1502,7 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
|
1486
1502
|
与while_success不同,此函数只检查返回是否为True,
|
1487
1503
|
不捕获异常,异常会直接抛出
|
1488
1504
|
"""
|
1505
|
+
ret: bool = False
|
1489
1506
|
while True:
|
1490
1507
|
ret = func()
|
1491
1508
|
if ret:
|