jarvis-ai-assistant 0.3.30__py3-none-any.whl → 0.7.6__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 +458 -152
- jarvis/jarvis_agent/agent_manager.py +17 -13
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +329 -0
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +628 -55
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +34 -10
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +105 -9
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +20 -22
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +31 -6
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/utils.py +5 -1
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +786 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +575 -0
- jarvis/jarvis_c2rust/collector.py +250 -0
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +1254 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +2157 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2983 -0
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +132 -0
- jarvis/jarvis_code_agent/code_agent.py +1371 -220
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
- jarvis/jarvis_code_agent/lint.py +501 -8
- jarvis/jarvis_code_agent/utils.py +141 -0
- jarvis/jarvis_code_analysis/code_review.py +493 -584
- jarvis/jarvis_data/config_schema.json +128 -12
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +82 -75
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
- jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +287 -55
- jarvis/jarvis_multi_agent/main.py +36 -4
- jarvis/jarvis_platform/base.py +524 -202
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +88 -25
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +32 -43
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +53 -55
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +305 -0
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +139 -0
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +336 -0
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +226 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +2 -2
- jarvis/jarvis_stats/stats.py +8 -8
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +21 -23
- jarvis/jarvis_tools/edit_file.py +1019 -132
- jarvis/jarvis_tools/execute_script.py +83 -25
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +14 -21
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1736 -35
- jarvis/jarvis_tools/read_symbols.py +140 -0
- jarvis/jarvis_tools/read_webpage.py +12 -13
- jarvis/jarvis_tools/registry.py +427 -200
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +72 -158
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +18 -18
- jarvis/jarvis_tools/sub_agent.py +36 -43
- jarvis/jarvis_tools/sub_code_agent.py +25 -26
- jarvis/jarvis_tools/virtual_tty.py +55 -33
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +232 -45
- jarvis/jarvis_utils/embedding.py +8 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +225 -36
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +99 -48
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +52 -48
- jarvis/jarvis_utils/utils.py +819 -491
- jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_agent/edit_file_handler.py +0 -296
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -6,6 +6,9 @@ import signal
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
import time
|
|
9
|
+
import atexit
|
|
10
|
+
import errno
|
|
11
|
+
import threading
|
|
9
12
|
from pathlib import Path
|
|
10
13
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
11
14
|
from datetime import datetime, date
|
|
@@ -23,7 +26,6 @@ from jarvis.jarvis_utils.config import (
|
|
|
23
26
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
|
24
27
|
from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
|
|
25
28
|
from jarvis.jarvis_utils.input import user_confirm
|
|
26
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
27
29
|
|
|
28
30
|
# 向后兼容:导出 get_yes_no 供外部模块引用
|
|
29
31
|
get_yes_no = user_confirm
|
|
@@ -59,6 +61,10 @@ COMMAND_MAPPING = {
|
|
|
59
61
|
"jst": "jarvis-stats",
|
|
60
62
|
# 记忆整理
|
|
61
63
|
"jmo": "jarvis-memory-organizer",
|
|
64
|
+
# 安全分析
|
|
65
|
+
"jsec": "jarvis-sec",
|
|
66
|
+
# C2Rust迁移
|
|
67
|
+
"jc2r": "jarvis-c2rust",
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
# RAG 依赖检测工具函数(更精确)
|
|
@@ -205,6 +211,117 @@ def _setup_signal_handler() -> None:
|
|
|
205
211
|
signal.signal(signal.SIGINT, sigint_handler)
|
|
206
212
|
|
|
207
213
|
|
|
214
|
+
# ----------------------------
|
|
215
|
+
# 单实例文件锁(放置于初始化早期使用)
|
|
216
|
+
# ----------------------------
|
|
217
|
+
_INSTANCE_LOCK_PATH: Optional[Path] = None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _get_instance_lock_path(lock_name: str = "instance.lock") -> Path:
|
|
221
|
+
try:
|
|
222
|
+
data_dir = Path(str(get_data_dir()))
|
|
223
|
+
except Exception:
|
|
224
|
+
data_dir = Path(os.path.expanduser("~/.jarvis"))
|
|
225
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
226
|
+
return data_dir / lock_name
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _read_lock_owner_pid(lock_path: Path) -> Optional[int]:
|
|
230
|
+
try:
|
|
231
|
+
txt = lock_path.read_text(encoding="utf-8", errors="ignore").strip()
|
|
232
|
+
if not txt:
|
|
233
|
+
return None
|
|
234
|
+
try:
|
|
235
|
+
info = json.loads(txt)
|
|
236
|
+
pid = info.get("pid")
|
|
237
|
+
return int(pid) if pid is not None else None
|
|
238
|
+
except Exception:
|
|
239
|
+
# 兼容纯数字PID
|
|
240
|
+
return int(txt)
|
|
241
|
+
except Exception:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _is_process_alive(pid: int) -> bool:
|
|
246
|
+
if pid is None or pid <= 0:
|
|
247
|
+
return False
|
|
248
|
+
try:
|
|
249
|
+
os.kill(pid, 0)
|
|
250
|
+
except ProcessLookupError:
|
|
251
|
+
return False
|
|
252
|
+
except PermissionError:
|
|
253
|
+
# 无权限但进程存在
|
|
254
|
+
return True
|
|
255
|
+
except OSError as e:
|
|
256
|
+
# 某些平台上,EPERM 表示进程存在但无权限
|
|
257
|
+
if getattr(e, "errno", None) == errno.EPERM:
|
|
258
|
+
return True
|
|
259
|
+
return False
|
|
260
|
+
else:
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _release_instance_lock() -> None:
|
|
265
|
+
global _INSTANCE_LOCK_PATH
|
|
266
|
+
try:
|
|
267
|
+
if _INSTANCE_LOCK_PATH and _INSTANCE_LOCK_PATH.exists():
|
|
268
|
+
_INSTANCE_LOCK_PATH.unlink()
|
|
269
|
+
except Exception:
|
|
270
|
+
# 清理失败不影响退出
|
|
271
|
+
pass
|
|
272
|
+
_INSTANCE_LOCK_PATH = None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _acquire_single_instance_lock(lock_name: str = "instance.lock") -> None:
|
|
276
|
+
"""
|
|
277
|
+
在数据目录(~/.jarvis 或配置的数据目录)下创建实例锁,防止重复启动。
|
|
278
|
+
如果检测到已有存活实例,提示后退出。
|
|
279
|
+
"""
|
|
280
|
+
global _INSTANCE_LOCK_PATH
|
|
281
|
+
lock_path = _get_instance_lock_path(lock_name)
|
|
282
|
+
|
|
283
|
+
# 已存在锁:检查是否为有效存活实例
|
|
284
|
+
if lock_path.exists():
|
|
285
|
+
pid = _read_lock_owner_pid(lock_path)
|
|
286
|
+
if pid and _is_process_alive(pid):
|
|
287
|
+
print(f"⚠️ 检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。\n如果确认不存在正在运行的实例,请删除锁文件后重试:{lock_path}")
|
|
288
|
+
sys.exit(0)
|
|
289
|
+
# 尝试移除陈旧锁
|
|
290
|
+
try:
|
|
291
|
+
lock_path.unlink()
|
|
292
|
+
except Exception:
|
|
293
|
+
print(f"❌ 无法删除旧锁文件:{lock_path},请手动清理后重试。")
|
|
294
|
+
sys.exit(1)
|
|
295
|
+
|
|
296
|
+
# 原子创建锁文件,避免并发竞争
|
|
297
|
+
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
|
|
298
|
+
try:
|
|
299
|
+
fd = os.open(str(lock_path), flags)
|
|
300
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fp:
|
|
301
|
+
payload = {
|
|
302
|
+
"pid": os.getpid(),
|
|
303
|
+
"time": int(time.time()),
|
|
304
|
+
"argv": sys.argv[:10],
|
|
305
|
+
}
|
|
306
|
+
try:
|
|
307
|
+
fp.write(json.dumps(payload, ensure_ascii=False))
|
|
308
|
+
except Exception:
|
|
309
|
+
fp.write(str(os.getpid()))
|
|
310
|
+
_INSTANCE_LOCK_PATH = lock_path
|
|
311
|
+
atexit.register(_release_instance_lock)
|
|
312
|
+
except FileExistsError:
|
|
313
|
+
# 极端并发下再次校验
|
|
314
|
+
pid = _read_lock_owner_pid(lock_path)
|
|
315
|
+
if pid and _is_process_alive(pid):
|
|
316
|
+
print(f"⚠️ 检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。")
|
|
317
|
+
sys.exit(0)
|
|
318
|
+
print(f"❌ 锁文件已存在但可能为陈旧状态:{lock_path},请手动删除后重试。")
|
|
319
|
+
sys.exit(1)
|
|
320
|
+
except Exception as e:
|
|
321
|
+
print(f"❌ 创建实例锁失败: {e}")
|
|
322
|
+
sys.exit(1)
|
|
323
|
+
|
|
324
|
+
|
|
208
325
|
def _check_pip_updates() -> bool:
|
|
209
326
|
"""检查pip安装的Jarvis是否有更新
|
|
210
327
|
|
|
@@ -234,7 +351,7 @@ def _check_pip_updates() -> bool:
|
|
|
234
351
|
with urllib.request.urlopen(url, timeout=5) as response:
|
|
235
352
|
data = json.loads(response.read().decode())
|
|
236
353
|
latest_version = data["info"]["version"]
|
|
237
|
-
except (urllib.error.URLError, KeyError,
|
|
354
|
+
except (urllib.error.URLError, KeyError, ValueError):
|
|
238
355
|
return False
|
|
239
356
|
|
|
240
357
|
# 比较版本
|
|
@@ -242,13 +359,10 @@ def _check_pip_updates() -> bool:
|
|
|
242
359
|
latest_ver = version.parse(latest_version)
|
|
243
360
|
|
|
244
361
|
if latest_ver > current_ver:
|
|
245
|
-
|
|
246
|
-
f"检测到新版本 v{latest_version} (当前版本: v{__version__})",
|
|
247
|
-
OutputType.INFO,
|
|
248
|
-
)
|
|
362
|
+
print(f"ℹ️ 检测到新版本 v{latest_version} (当前版本: v{__version__})")
|
|
249
363
|
|
|
250
364
|
# 检测是否在虚拟环境中
|
|
251
|
-
|
|
365
|
+
hasattr(sys, "real_prefix") or (
|
|
252
366
|
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
|
|
253
367
|
)
|
|
254
368
|
|
|
@@ -292,7 +406,7 @@ def _check_pip_updates() -> bool:
|
|
|
292
406
|
|
|
293
407
|
# 自动尝试升级(失败时提供手动命令)
|
|
294
408
|
try:
|
|
295
|
-
|
|
409
|
+
print("ℹ️ 正在自动更新 Jarvis,请稍候...")
|
|
296
410
|
result = subprocess.run(
|
|
297
411
|
cmd_list,
|
|
298
412
|
capture_output=True,
|
|
@@ -302,25 +416,18 @@ def _check_pip_updates() -> bool:
|
|
|
302
416
|
timeout=600,
|
|
303
417
|
)
|
|
304
418
|
if result.returncode == 0:
|
|
305
|
-
|
|
419
|
+
print("✅ 更新成功,正在重启以应用新版本...")
|
|
306
420
|
# 更新检查日期,避免重复触发
|
|
307
421
|
last_check_file.write_text(today_str)
|
|
308
422
|
return True
|
|
309
423
|
else:
|
|
310
424
|
err = (result.stderr or result.stdout or "").strip()
|
|
311
425
|
if err:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
OutputType.WARNING,
|
|
315
|
-
)
|
|
316
|
-
PrettyOutput.print(
|
|
317
|
-
f"请手动执行以下命令更新: {update_cmd}", OutputType.INFO
|
|
318
|
-
)
|
|
426
|
+
print(f"⚠️ 自动更新失败,错误信息(已截断): {err[:500]}")
|
|
427
|
+
print(f"ℹ️ 请手动执行以下命令更新: {update_cmd}")
|
|
319
428
|
except Exception:
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
f"请手动执行以下命令更新: {update_cmd}", OutputType.INFO
|
|
323
|
-
)
|
|
429
|
+
print("⚠️ 自动更新出现异常,已切换为手动更新方式。")
|
|
430
|
+
print(f"ℹ️ 请手动执行以下命令更新: {update_cmd}")
|
|
324
431
|
|
|
325
432
|
# 更新检查日期
|
|
326
433
|
last_check_file.write_text(today_str)
|
|
@@ -338,14 +445,22 @@ def _check_jarvis_updates() -> bool:
|
|
|
338
445
|
返回:
|
|
339
446
|
bool: 是否需要重启进程
|
|
340
447
|
"""
|
|
341
|
-
|
|
448
|
+
# 从当前文件目录向上查找包含 .git 的仓库根目录,修复原先只检查 src/jarvis 的问题
|
|
449
|
+
try:
|
|
450
|
+
script_path = Path(__file__).resolve()
|
|
451
|
+
repo_root: Optional[Path] = None
|
|
452
|
+
for d in [script_path.parent] + list(script_path.parents):
|
|
453
|
+
if (d / ".git").exists():
|
|
454
|
+
repo_root = d
|
|
455
|
+
break
|
|
456
|
+
except Exception:
|
|
457
|
+
repo_root = None
|
|
342
458
|
|
|
343
|
-
# 先检查是否是git
|
|
344
|
-
|
|
345
|
-
if git_dir.exists():
|
|
459
|
+
# 先检查是否是git源码安装(找到仓库根目录即认为是源码安装)
|
|
460
|
+
if repo_root and (repo_root / ".git").exists():
|
|
346
461
|
from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
|
|
347
462
|
|
|
348
|
-
return check_and_update_git_repo(str(
|
|
463
|
+
return check_and_update_git_repo(str(repo_root))
|
|
349
464
|
|
|
350
465
|
# 检查是否是pip/uv pip安装的版本
|
|
351
466
|
return _check_pip_updates()
|
|
@@ -353,8 +468,6 @@ def _check_jarvis_updates() -> bool:
|
|
|
353
468
|
|
|
354
469
|
def _show_usage_stats(welcome_str: str) -> None:
|
|
355
470
|
"""显示Jarvis使用统计信息"""
|
|
356
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
357
|
-
|
|
358
471
|
try:
|
|
359
472
|
|
|
360
473
|
from rich.console import Console, Group
|
|
@@ -365,6 +478,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
365
478
|
console = Console()
|
|
366
479
|
|
|
367
480
|
from jarvis.jarvis_stats.stats import StatsManager
|
|
481
|
+
from jarvis.jarvis_stats.storage import StatsStorage
|
|
368
482
|
|
|
369
483
|
# 获取所有可用的指标
|
|
370
484
|
all_metrics = StatsManager.list_metrics()
|
|
@@ -380,19 +494,47 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
380
494
|
"other": {"title": "📦 其他指标", "metrics": {}, "suffix": ""},
|
|
381
495
|
}
|
|
382
496
|
|
|
383
|
-
#
|
|
384
|
-
|
|
497
|
+
# 复用存储实例,避免重复创建
|
|
498
|
+
storage = StatsStorage()
|
|
499
|
+
|
|
500
|
+
# 一次性读取元数据,避免重复读取
|
|
501
|
+
try:
|
|
502
|
+
meta = storage._load_json(storage.meta_file)
|
|
503
|
+
metrics_info = meta.get("metrics", {})
|
|
504
|
+
except Exception:
|
|
505
|
+
metrics_info = {}
|
|
506
|
+
|
|
507
|
+
# 批量读取所有总量文件,避免逐个文件操作
|
|
508
|
+
metric_totals: Dict[str, float] = {}
|
|
509
|
+
totals_dir = storage.totals_dir
|
|
510
|
+
if totals_dir.exists():
|
|
385
511
|
try:
|
|
386
|
-
|
|
512
|
+
for total_file in totals_dir.glob("*"):
|
|
513
|
+
if total_file.is_file():
|
|
514
|
+
try:
|
|
515
|
+
with open(total_file, "r", encoding="utf-8") as f:
|
|
516
|
+
total = float((f.read() or "0").strip() or "0")
|
|
517
|
+
if total > 0:
|
|
518
|
+
metric_totals[total_file.name] = total
|
|
519
|
+
except Exception:
|
|
520
|
+
pass
|
|
387
521
|
except Exception:
|
|
388
|
-
|
|
522
|
+
pass
|
|
389
523
|
|
|
524
|
+
# 遍历所有指标,使用批量读取的数据
|
|
525
|
+
for metric in all_metrics:
|
|
526
|
+
# 从批量读取的数据中获取总量
|
|
527
|
+
total = metric_totals.get(metric, 0.0)
|
|
528
|
+
|
|
390
529
|
if not total or total <= 0:
|
|
391
530
|
continue
|
|
392
531
|
|
|
393
|
-
#
|
|
394
|
-
|
|
395
|
-
|
|
532
|
+
# 从已加载的元数据中获取分组信息,避免重复读取
|
|
533
|
+
try:
|
|
534
|
+
info = metrics_info.get(metric, {})
|
|
535
|
+
group = info.get("group", "other")
|
|
536
|
+
except Exception:
|
|
537
|
+
group = "other"
|
|
396
538
|
|
|
397
539
|
if group == "tool":
|
|
398
540
|
categorized_stats["tool"]["metrics"][metric] = int(total)
|
|
@@ -679,7 +821,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
679
821
|
|
|
680
822
|
# 愿景 Panel
|
|
681
823
|
vision_text = Text(
|
|
682
|
-
"
|
|
824
|
+
"让开发者与AI成为共生伙伴",
|
|
683
825
|
justify="center",
|
|
684
826
|
style="italic",
|
|
685
827
|
)
|
|
@@ -694,7 +836,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
694
836
|
|
|
695
837
|
# 使命 Panel
|
|
696
838
|
mission_text = Text(
|
|
697
|
-
"
|
|
839
|
+
"让灵感高效落地为代码与行动",
|
|
698
840
|
justify="center",
|
|
699
841
|
style="italic",
|
|
700
842
|
)
|
|
@@ -755,11 +897,11 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
755
897
|
# 输出错误信息以便调试
|
|
756
898
|
import traceback
|
|
757
899
|
|
|
758
|
-
|
|
759
|
-
|
|
900
|
+
print(f"❌ 统计显示出错: {str(e)}")
|
|
901
|
+
print(f"❌ {traceback.format_exc()}")
|
|
760
902
|
|
|
761
903
|
|
|
762
|
-
def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
904
|
+
def init_env(welcome_str: str = "", config_file: Optional[str] = None) -> None:
|
|
763
905
|
"""初始化Jarvis环境
|
|
764
906
|
|
|
765
907
|
参数:
|
|
@@ -769,35 +911,60 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
|
769
911
|
# 0. 检查是否处于Jarvis打开的终端环境,避免嵌套
|
|
770
912
|
try:
|
|
771
913
|
if os.environ.get("JARVIS_TERMINAL") == "1":
|
|
772
|
-
|
|
773
|
-
"检测到当前终端由 Jarvis 打开。再次启动可能导致嵌套。",
|
|
774
|
-
OutputType.WARNING,
|
|
775
|
-
)
|
|
914
|
+
print("⚠️ 检测到当前终端由 Jarvis 打开。再次启动可能导致嵌套。")
|
|
776
915
|
if not user_confirm("是否仍要继续启动 Jarvis?", default=False):
|
|
777
|
-
|
|
916
|
+
print("ℹ️ 已取消启动以避免终端嵌套。")
|
|
778
917
|
sys.exit(0)
|
|
779
918
|
except Exception:
|
|
780
919
|
pass
|
|
781
920
|
|
|
782
921
|
# 1. 设置信号处理
|
|
783
|
-
|
|
922
|
+
try:
|
|
923
|
+
_setup_signal_handler()
|
|
924
|
+
except Exception:
|
|
925
|
+
pass
|
|
784
926
|
|
|
785
|
-
# 2.
|
|
786
|
-
|
|
927
|
+
# 2. 统计命令使用(异步执行,避免阻塞初始化)
|
|
928
|
+
try:
|
|
929
|
+
count_cmd_usage()
|
|
930
|
+
except Exception:
|
|
931
|
+
# 静默失败,不影响正常使用
|
|
932
|
+
pass
|
|
787
933
|
|
|
788
934
|
# 3. 设置配置文件
|
|
789
935
|
global g_config_file
|
|
790
936
|
g_config_file = config_file
|
|
791
|
-
|
|
937
|
+
try:
|
|
938
|
+
load_config()
|
|
939
|
+
except Exception:
|
|
940
|
+
# 静默失败,不影响正常使用
|
|
941
|
+
pass
|
|
792
942
|
|
|
793
943
|
# 4. 显示历史统计数据(仅在显示欢迎信息时显示)
|
|
944
|
+
# 使用延迟加载,避免阻塞初始化
|
|
794
945
|
if welcome_str:
|
|
795
|
-
|
|
946
|
+
try:
|
|
947
|
+
# 在后台线程中显示统计,避免阻塞主流程
|
|
948
|
+
import threading
|
|
949
|
+
def show_stats_async():
|
|
950
|
+
try:
|
|
951
|
+
_show_usage_stats(welcome_str)
|
|
952
|
+
except Exception:
|
|
953
|
+
pass
|
|
954
|
+
stats_thread = threading.Thread(target=show_stats_async, daemon=True)
|
|
955
|
+
stats_thread.start()
|
|
956
|
+
except Exception:
|
|
957
|
+
# 静默失败,不影响正常使用
|
|
958
|
+
pass
|
|
796
959
|
|
|
797
|
-
# 5. 检查Jarvis
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
960
|
+
# 5. 检查Jarvis更新(异步执行,避免阻塞)
|
|
961
|
+
try:
|
|
962
|
+
if _check_jarvis_updates():
|
|
963
|
+
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
964
|
+
sys.exit(0)
|
|
965
|
+
except Exception:
|
|
966
|
+
# 静默失败,不影响正常使用
|
|
967
|
+
pass
|
|
801
968
|
|
|
802
969
|
|
|
803
970
|
def _interactive_config_setup(config_file_path: Path):
|
|
@@ -809,9 +976,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
809
976
|
user_confirm as get_yes_no,
|
|
810
977
|
)
|
|
811
978
|
|
|
812
|
-
|
|
813
|
-
"欢迎使用 Jarvis!未找到配置文件,现在开始引导配置。", OutputType.INFO
|
|
814
|
-
)
|
|
979
|
+
print("ℹ️ 欢迎使用 Jarvis!未找到配置文件,现在开始引导配置。")
|
|
815
980
|
|
|
816
981
|
# 1. 选择平台
|
|
817
982
|
registry = PlatformRegistry.get_global_platform_registry()
|
|
@@ -821,7 +986,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
821
986
|
# 2. 配置环境变量
|
|
822
987
|
platform_class = registry.platforms.get(platform_name)
|
|
823
988
|
if not platform_class:
|
|
824
|
-
|
|
989
|
+
print(f"❌ 平台 '{platform_name}' 加载失败。")
|
|
825
990
|
sys.exit(1)
|
|
826
991
|
|
|
827
992
|
env_vars = {}
|
|
@@ -829,9 +994,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
829
994
|
defaults = platform_class.get_env_defaults()
|
|
830
995
|
config_guide = platform_class.get_env_config_guide()
|
|
831
996
|
if required_keys:
|
|
832
|
-
|
|
833
|
-
f"请输入 {platform_name} 平台所需的配置信息:", OutputType.INFO
|
|
834
|
-
)
|
|
997
|
+
print(f"ℹ️ 请输入 {platform_name} 平台所需的配置信息:")
|
|
835
998
|
|
|
836
999
|
# 如果有配置指导,先显示总体说明
|
|
837
1000
|
if config_guide:
|
|
@@ -842,7 +1005,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
842
1005
|
guide_lines.append("")
|
|
843
1006
|
guide_lines.append(f"{key} 获取方法:")
|
|
844
1007
|
guide_lines.append(str(config_guide[key]))
|
|
845
|
-
|
|
1008
|
+
print("ℹ️ " + "\n".join(guide_lines))
|
|
846
1009
|
else:
|
|
847
1010
|
# 若无指导,仍需遍历以保持后续逻辑一致
|
|
848
1011
|
pass
|
|
@@ -864,7 +1027,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
864
1027
|
try:
|
|
865
1028
|
platform_instance = registry.create_platform(platform_name)
|
|
866
1029
|
if not platform_instance:
|
|
867
|
-
|
|
1030
|
+
print(f"❌ 无法创建平台 '{platform_name}'。")
|
|
868
1031
|
sys.exit(1)
|
|
869
1032
|
|
|
870
1033
|
model_list_tuples = platform_instance.get_model_list()
|
|
@@ -876,13 +1039,13 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
876
1039
|
model_name, _ = model_list_tuples[selected_index]
|
|
877
1040
|
|
|
878
1041
|
except Exception:
|
|
879
|
-
|
|
1042
|
+
print("❌ 获取模型列表失败")
|
|
880
1043
|
if not get_yes_no("无法获取模型列表,是否继续配置?"):
|
|
881
1044
|
sys.exit(1)
|
|
882
1045
|
model_name = get_input("请输入模型名称:")
|
|
883
1046
|
|
|
884
1047
|
# 4. 测试配置
|
|
885
|
-
|
|
1048
|
+
print("ℹ️ 正在测试配置...")
|
|
886
1049
|
test_passed = False
|
|
887
1050
|
try:
|
|
888
1051
|
platform_instance = registry.create_platform(platform_name)
|
|
@@ -891,16 +1054,14 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
891
1054
|
response_generator = platform_instance.chat("hello")
|
|
892
1055
|
response = "".join(response_generator)
|
|
893
1056
|
if response:
|
|
894
|
-
|
|
895
|
-
f"测试成功,模型响应: {response}", OutputType.SUCCESS
|
|
896
|
-
)
|
|
1057
|
+
print(f"✅ 测试成功,模型响应: {response}")
|
|
897
1058
|
test_passed = True
|
|
898
1059
|
else:
|
|
899
|
-
|
|
1060
|
+
print("❌ 测试失败,模型没有响应。")
|
|
900
1061
|
else:
|
|
901
|
-
|
|
1062
|
+
print("❌ 测试失败,无法创建平台实例。")
|
|
902
1063
|
except Exception:
|
|
903
|
-
|
|
1064
|
+
print("❌ 测试失败")
|
|
904
1065
|
|
|
905
1066
|
# 5. 交互式确认并应用配置(不直接生成配置文件)
|
|
906
1067
|
config_data = {
|
|
@@ -911,7 +1072,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
911
1072
|
|
|
912
1073
|
if not test_passed:
|
|
913
1074
|
if not get_yes_no("配置测试失败,是否仍要应用该配置并继续?", default=False):
|
|
914
|
-
|
|
1075
|
+
print("ℹ️ 已取消配置。")
|
|
915
1076
|
sys.exit(0)
|
|
916
1077
|
|
|
917
1078
|
# 6. 选择其他功能开关与可选项(复用统一逻辑)
|
|
@@ -934,11 +1095,11 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
934
1095
|
if header:
|
|
935
1096
|
f.write(header)
|
|
936
1097
|
f.write(yaml_str)
|
|
937
|
-
|
|
938
|
-
|
|
1098
|
+
print(f"✅ 配置文件已生成: {config_file_path}")
|
|
1099
|
+
print("ℹ️ 配置完成,请重新启动Jarvis。")
|
|
939
1100
|
sys.exit(0)
|
|
940
1101
|
except Exception:
|
|
941
|
-
|
|
1102
|
+
print("❌ 写入配置文件失败")
|
|
942
1103
|
sys.exit(1)
|
|
943
1104
|
|
|
944
1105
|
|
|
@@ -1016,359 +1177,420 @@ def _process_env_variables(config_data: dict) -> None:
|
|
|
1016
1177
|
)
|
|
1017
1178
|
|
|
1018
1179
|
|
|
1019
|
-
def
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
bool
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1180
|
+
def _ask_config_bool(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: bool) -> bool:
|
|
1181
|
+
"""询问并设置布尔类型配置项"""
|
|
1182
|
+
try:
|
|
1183
|
+
if not ask_all and _key in config_data:
|
|
1184
|
+
return False
|
|
1185
|
+
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
|
1186
|
+
cur = bool(config_data.get(_key, _default))
|
|
1187
|
+
val = get_yes_no(_tip, default=cur)
|
|
1188
|
+
if bool(val) == cur:
|
|
1189
|
+
return False
|
|
1190
|
+
config_data[_key] = bool(val)
|
|
1191
|
+
return True
|
|
1192
|
+
except Exception:
|
|
1193
|
+
return False
|
|
1033
1194
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
cur = bool(config_data.get(_key, _default))
|
|
1040
|
-
val = get_yes_no(_tip, default=cur)
|
|
1041
|
-
# 与当前值相同则不写入,避免冗余
|
|
1042
|
-
if bool(val) == cur:
|
|
1043
|
-
return False
|
|
1044
|
-
config_data[_key] = bool(val)
|
|
1045
|
-
else:
|
|
1046
|
-
cur = str(config_data.get(_key, _default or ""))
|
|
1047
|
-
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1048
|
-
v = ("" if val is None else str(val)).strip()
|
|
1049
|
-
# 输入与当前值相同则不写入
|
|
1050
|
-
if v == cur:
|
|
1051
|
-
return False
|
|
1052
|
-
config_data[_key] = v
|
|
1053
|
-
return True
|
|
1054
|
-
except Exception:
|
|
1055
|
-
# 异常时不写入,保持精简
|
|
1195
|
+
|
|
1196
|
+
def _ask_config_str(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: str = "") -> bool:
|
|
1197
|
+
"""询问并设置字符串类型配置项"""
|
|
1198
|
+
try:
|
|
1199
|
+
if not ask_all and _key in config_data:
|
|
1056
1200
|
return False
|
|
1201
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1202
|
+
cur = str(config_data.get(_key, _default or ""))
|
|
1203
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1204
|
+
v = ("" if val is None else str(val)).strip()
|
|
1205
|
+
if v == cur:
|
|
1206
|
+
return False
|
|
1207
|
+
config_data[_key] = v
|
|
1208
|
+
return True
|
|
1209
|
+
except Exception:
|
|
1210
|
+
return False
|
|
1057
1211
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
return False
|
|
1072
|
-
config_data[_key] = s
|
|
1073
|
-
return True
|
|
1074
|
-
except Exception:
|
|
1212
|
+
|
|
1213
|
+
def _ask_config_optional_str(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: str = "") -> bool:
|
|
1214
|
+
"""询问并设置可选字符串类型配置项(空输入表示不改变)"""
|
|
1215
|
+
try:
|
|
1216
|
+
if not ask_all and _key in config_data:
|
|
1217
|
+
return False
|
|
1218
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1219
|
+
cur = str(config_data.get(_key, _default or ""))
|
|
1220
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1221
|
+
if val is None:
|
|
1222
|
+
return False
|
|
1223
|
+
s = str(val).strip()
|
|
1224
|
+
if s == "" or s == cur:
|
|
1075
1225
|
return False
|
|
1226
|
+
config_data[_key] = s
|
|
1227
|
+
return True
|
|
1228
|
+
except Exception:
|
|
1229
|
+
return False
|
|
1230
|
+
|
|
1076
1231
|
|
|
1077
|
-
|
|
1232
|
+
def _ask_config_int(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: int) -> bool:
|
|
1233
|
+
"""询问并设置整数类型配置项"""
|
|
1234
|
+
try:
|
|
1235
|
+
if not ask_all and _key in config_data:
|
|
1236
|
+
return False
|
|
1237
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1238
|
+
cur = str(config_data.get(_key, _default))
|
|
1239
|
+
val_str = get_single_line_input(f"{_tip}", default=cur)
|
|
1240
|
+
s = "" if val_str is None else str(val_str).strip()
|
|
1241
|
+
if s == "" or s == cur:
|
|
1242
|
+
return False
|
|
1078
1243
|
try:
|
|
1079
|
-
|
|
1080
|
-
return False
|
|
1081
|
-
cur = str(config_data.get(_key, _default))
|
|
1082
|
-
val_str = get_single_line_input(f"{_tip}", default=cur)
|
|
1083
|
-
s = "" if val_str is None else str(val_str).strip()
|
|
1084
|
-
if s == "" or s == cur:
|
|
1085
|
-
return False
|
|
1086
|
-
try:
|
|
1087
|
-
v = int(s)
|
|
1088
|
-
except Exception:
|
|
1089
|
-
return False
|
|
1090
|
-
if str(v) == cur:
|
|
1091
|
-
return False
|
|
1092
|
-
config_data[_key] = v
|
|
1093
|
-
return True
|
|
1244
|
+
v = int(s)
|
|
1094
1245
|
except Exception:
|
|
1095
1246
|
return False
|
|
1247
|
+
if str(v) == cur:
|
|
1248
|
+
return False
|
|
1249
|
+
config_data[_key] = v
|
|
1250
|
+
return True
|
|
1251
|
+
except Exception:
|
|
1252
|
+
return False
|
|
1096
1253
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
return False
|
|
1115
|
-
items = [x.strip() for x in s.split(",") if x.strip()]
|
|
1116
|
-
if isinstance(cur_val, list) and items == cur_val:
|
|
1117
|
-
return False
|
|
1118
|
-
config_data[_key] = items
|
|
1119
|
-
return True
|
|
1120
|
-
except Exception:
|
|
1254
|
+
|
|
1255
|
+
def _ask_config_list(config_data: dict, ask_all: bool, _key: str, _tip: str) -> bool:
|
|
1256
|
+
"""询问并设置列表类型配置项(逗号分隔)"""
|
|
1257
|
+
try:
|
|
1258
|
+
if not ask_all and _key in config_data:
|
|
1259
|
+
return False
|
|
1260
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1261
|
+
cur_val = config_data.get(_key, [])
|
|
1262
|
+
if isinstance(cur_val, list):
|
|
1263
|
+
cur_display = ", ".join([str(x) for x in cur_val])
|
|
1264
|
+
else:
|
|
1265
|
+
cur_display = str(cur_val or "")
|
|
1266
|
+
val = get_single_line_input(f"{_tip}", default=cur_display)
|
|
1267
|
+
if val is None:
|
|
1268
|
+
return False
|
|
1269
|
+
s = str(val).strip()
|
|
1270
|
+
if s == cur_display.strip():
|
|
1121
1271
|
return False
|
|
1272
|
+
if not s:
|
|
1273
|
+
return False
|
|
1274
|
+
items = [x.strip() for x in s.split(",") if x.strip()]
|
|
1275
|
+
if isinstance(cur_val, list) and items == cur_val:
|
|
1276
|
+
return False
|
|
1277
|
+
config_data[_key] = items
|
|
1278
|
+
return True
|
|
1279
|
+
except Exception:
|
|
1280
|
+
return False
|
|
1281
|
+
|
|
1122
1282
|
|
|
1283
|
+
def _collect_basic_switches(config_data: dict, ask_all: bool) -> bool:
|
|
1284
|
+
"""收集基础开关配置"""
|
|
1123
1285
|
changed = False
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
"在进入默认通用代理前,是否先列出可用配置(agent/multi_agent/roles)供选择?",
|
|
1138
|
-
False,
|
|
1139
|
-
"bool",
|
|
1140
|
-
)
|
|
1141
|
-
or changed
|
|
1142
|
-
)
|
|
1286
|
+
changed = _ask_config_bool(
|
|
1287
|
+
config_data, ask_all,
|
|
1288
|
+
"JARVIS_ENABLE_GIT_JCA_SWITCH",
|
|
1289
|
+
"是否在检测到Git仓库时,提示并可自动切换到代码开发模式(jca)?",
|
|
1290
|
+
False,
|
|
1291
|
+
) or changed
|
|
1292
|
+
changed = _ask_config_bool(
|
|
1293
|
+
config_data, ask_all,
|
|
1294
|
+
"JARVIS_ENABLE_STARTUP_CONFIG_SELECTOR",
|
|
1295
|
+
"在进入默认通用代理前,是否先列出可用配置(agent/multi_agent/roles)供选择?",
|
|
1296
|
+
False,
|
|
1297
|
+
) or changed
|
|
1298
|
+
return changed
|
|
1143
1299
|
|
|
1144
|
-
# 新增的配置项交互(通用体验相关)
|
|
1145
|
-
changed = (
|
|
1146
|
-
_ask_and_set(
|
|
1147
|
-
"JARVIS_PRETTY_OUTPUT",
|
|
1148
|
-
"是否启用更美观的终端输出(Pretty Output)?",
|
|
1149
|
-
False,
|
|
1150
|
-
"bool",
|
|
1151
|
-
)
|
|
1152
|
-
or changed
|
|
1153
|
-
)
|
|
1154
|
-
changed = (
|
|
1155
|
-
_ask_and_set(
|
|
1156
|
-
"JARVIS_PRINT_PROMPT",
|
|
1157
|
-
"是否打印发送给模型的提示词(Prompt)?",
|
|
1158
|
-
False,
|
|
1159
|
-
"bool",
|
|
1160
|
-
)
|
|
1161
|
-
or changed
|
|
1162
|
-
)
|
|
1163
|
-
changed = (
|
|
1164
|
-
_ask_and_set(
|
|
1165
|
-
"JARVIS_IMMEDIATE_ABORT",
|
|
1166
|
-
"是否启用立即中断?\n- 选择 是/true:在对话输出流的每次迭代中检测到用户中断(例如 Ctrl+C)时,立即返回当前已生成的内容并停止继续输出。\n- 选择 否/false:不会在输出过程中立刻返回,而是按既有流程处理(不中途打断输出)。",
|
|
1167
|
-
False,
|
|
1168
|
-
"bool",
|
|
1169
|
-
)
|
|
1170
|
-
or changed
|
|
1171
|
-
)
|
|
1172
|
-
changed = (
|
|
1173
|
-
_ask_and_set(
|
|
1174
|
-
"JARVIS_ENABLE_STATIC_ANALYSIS",
|
|
1175
|
-
"是否启用静态代码分析(Static Analysis)?",
|
|
1176
|
-
True,
|
|
1177
|
-
"bool",
|
|
1178
|
-
)
|
|
1179
|
-
or changed
|
|
1180
|
-
)
|
|
1181
|
-
changed = (
|
|
1182
|
-
_ask_and_set(
|
|
1183
|
-
"JARVIS_USE_METHODOLOGY",
|
|
1184
|
-
"是否启用方法论系统(Methodology)?",
|
|
1185
|
-
True,
|
|
1186
|
-
"bool",
|
|
1187
|
-
)
|
|
1188
|
-
or changed
|
|
1189
|
-
)
|
|
1190
|
-
changed = (
|
|
1191
|
-
_ask_and_set(
|
|
1192
|
-
"JARVIS_USE_ANALYSIS",
|
|
1193
|
-
"是否启用分析流程(Analysis)?",
|
|
1194
|
-
True,
|
|
1195
|
-
"bool",
|
|
1196
|
-
)
|
|
1197
|
-
or changed
|
|
1198
|
-
)
|
|
1199
|
-
changed = (
|
|
1200
|
-
_ask_and_set(
|
|
1201
|
-
"JARVIS_FORCE_SAVE_MEMORY",
|
|
1202
|
-
"是否强制保存会话记忆?",
|
|
1203
|
-
True,
|
|
1204
|
-
"bool",
|
|
1205
|
-
)
|
|
1206
|
-
or changed
|
|
1207
|
-
)
|
|
1208
1300
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1301
|
+
def _collect_ui_experience_config(config_data: dict, ask_all: bool) -> bool:
|
|
1302
|
+
"""收集UI体验相关配置"""
|
|
1303
|
+
changed = False
|
|
1304
|
+
try:
|
|
1305
|
+
import platform as _platform_mod
|
|
1306
|
+
_default_pretty = False if _platform_mod.system() == "Windows" else True
|
|
1307
|
+
except Exception:
|
|
1308
|
+
_default_pretty = True
|
|
1309
|
+
|
|
1310
|
+
changed = _ask_config_bool(
|
|
1311
|
+
config_data, ask_all,
|
|
1312
|
+
"JARVIS_PRETTY_OUTPUT",
|
|
1313
|
+
"是否启用更美观的终端输出(Pretty Output)?",
|
|
1314
|
+
_default_pretty,
|
|
1315
|
+
) or changed
|
|
1316
|
+
changed = _ask_config_bool(
|
|
1317
|
+
config_data, ask_all,
|
|
1318
|
+
"JARVIS_PRINT_PROMPT",
|
|
1319
|
+
"是否打印发送给模型的提示词(Prompt)?",
|
|
1320
|
+
False,
|
|
1321
|
+
) or changed
|
|
1322
|
+
changed = _ask_config_bool(
|
|
1323
|
+
config_data, ask_all,
|
|
1324
|
+
"JARVIS_IMMEDIATE_ABORT",
|
|
1325
|
+
"是否启用立即中断?\n- 选择 是/true:在对话输出流的每次迭代中检测到用户中断(例如 Ctrl+C)时,立即返回当前已生成的内容并停止继续输出。\n- 选择 否/false:不会在输出过程中立刻返回,而是按既有流程处理(不中途打断输出)。",
|
|
1326
|
+
False,
|
|
1327
|
+
) or changed
|
|
1328
|
+
return changed
|
|
1228
1329
|
|
|
1229
|
-
# 数据目录与最大输入Token
|
|
1230
|
-
from jarvis.jarvis_utils.config import get_data_dir as _get_data_dir # lazy import
|
|
1231
1330
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
)
|
|
1247
|
-
changed = (
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1331
|
+
def _collect_analysis_config(config_data: dict, ask_all: bool) -> bool:
|
|
1332
|
+
"""收集代码分析相关配置"""
|
|
1333
|
+
changed = False
|
|
1334
|
+
changed = _ask_config_bool(
|
|
1335
|
+
config_data, ask_all,
|
|
1336
|
+
"JARVIS_ENABLE_STATIC_ANALYSIS",
|
|
1337
|
+
"是否启用静态代码分析(Static Analysis)?",
|
|
1338
|
+
True,
|
|
1339
|
+
) or changed
|
|
1340
|
+
changed = _ask_config_bool(
|
|
1341
|
+
config_data, ask_all,
|
|
1342
|
+
"JARVIS_ENABLE_BUILD_VALIDATION",
|
|
1343
|
+
"是否启用构建验证(Build Validation)?在代码编辑后自动验证代码能否成功编译/构建。",
|
|
1344
|
+
True,
|
|
1345
|
+
) or changed
|
|
1346
|
+
changed = _ask_config_int(
|
|
1347
|
+
config_data, ask_all,
|
|
1348
|
+
"JARVIS_BUILD_VALIDATION_TIMEOUT",
|
|
1349
|
+
"构建验证的超时时间(秒,默认30秒)",
|
|
1350
|
+
30,
|
|
1351
|
+
) or changed
|
|
1352
|
+
changed = _ask_config_bool(
|
|
1353
|
+
config_data, ask_all,
|
|
1354
|
+
"JARVIS_ENABLE_IMPACT_ANALYSIS",
|
|
1355
|
+
"是否启用编辑影响范围分析(Impact Analysis)?分析代码编辑的影响范围,识别可能受影响的文件、函数、测试等。",
|
|
1356
|
+
True,
|
|
1357
|
+
) or changed
|
|
1358
|
+
return changed
|
|
1255
1359
|
|
|
1256
|
-
# 目录类配置(逗号分隔)
|
|
1257
|
-
changed = (
|
|
1258
|
-
_ask_and_set_list(
|
|
1259
|
-
"JARVIS_TOOL_LOAD_DIRS",
|
|
1260
|
-
"指定工具加载目录(逗号分隔,留空跳过):",
|
|
1261
|
-
)
|
|
1262
|
-
or changed
|
|
1263
|
-
)
|
|
1264
|
-
changed = (
|
|
1265
|
-
_ask_and_set_list(
|
|
1266
|
-
"JARVIS_METHODOLOGY_DIRS",
|
|
1267
|
-
"指定方法论加载目录(逗号分隔,留空跳过):",
|
|
1268
|
-
)
|
|
1269
|
-
or changed
|
|
1270
|
-
)
|
|
1271
|
-
changed = (
|
|
1272
|
-
_ask_and_set_list(
|
|
1273
|
-
"JARVIS_AGENT_DEFINITION_DIRS",
|
|
1274
|
-
"指定 agent 定义加载目录(逗号分隔,留空跳过):",
|
|
1275
|
-
)
|
|
1276
|
-
or changed
|
|
1277
|
-
)
|
|
1278
|
-
changed = (
|
|
1279
|
-
_ask_and_set_list(
|
|
1280
|
-
"JARVIS_MULTI_AGENT_DIRS",
|
|
1281
|
-
"指定 multi_agent 加载目录(逗号分隔,留空跳过):",
|
|
1282
|
-
)
|
|
1283
|
-
or changed
|
|
1284
|
-
)
|
|
1285
|
-
changed = (
|
|
1286
|
-
_ask_and_set_list(
|
|
1287
|
-
"JARVIS_ROLES_DIRS",
|
|
1288
|
-
"指定 roles 加载目录(逗号分隔,留空跳过):",
|
|
1289
|
-
)
|
|
1290
|
-
or changed
|
|
1291
|
-
)
|
|
1292
1360
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
)
|
|
1361
|
+
def _collect_agent_features_config(config_data: dict, ask_all: bool) -> bool:
|
|
1362
|
+
"""收集Agent功能相关配置"""
|
|
1363
|
+
changed = False
|
|
1364
|
+
changed = _ask_config_bool(
|
|
1365
|
+
config_data, ask_all,
|
|
1366
|
+
"JARVIS_USE_METHODOLOGY",
|
|
1367
|
+
"是否启用方法论系统(Methodology)?",
|
|
1368
|
+
True,
|
|
1369
|
+
) or changed
|
|
1370
|
+
changed = _ask_config_bool(
|
|
1371
|
+
config_data, ask_all,
|
|
1372
|
+
"JARVIS_USE_ANALYSIS",
|
|
1373
|
+
"是否启用分析流程(Analysis)?",
|
|
1374
|
+
True,
|
|
1375
|
+
) or changed
|
|
1376
|
+
changed = _ask_config_bool(
|
|
1377
|
+
config_data, ask_all,
|
|
1378
|
+
"JARVIS_FORCE_SAVE_MEMORY",
|
|
1379
|
+
"是否强制保存会话记忆?",
|
|
1380
|
+
False,
|
|
1381
|
+
) or changed
|
|
1382
|
+
return changed
|
|
1308
1383
|
|
|
1309
|
-
# Git 校验模式
|
|
1310
|
-
def _ask_git_check_mode() -> bool:
|
|
1311
|
-
try:
|
|
1312
|
-
_key = "JARVIS_GIT_CHECK_MODE"
|
|
1313
|
-
if not ask_all and _key in config_data:
|
|
1314
|
-
return False
|
|
1315
1384
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1385
|
+
def _collect_session_config(config_data: dict, ask_all: bool) -> bool:
|
|
1386
|
+
"""收集会话与调试相关配置"""
|
|
1387
|
+
changed = False
|
|
1388
|
+
changed = _ask_config_bool(
|
|
1389
|
+
config_data, ask_all,
|
|
1390
|
+
"JARVIS_SAVE_SESSION_HISTORY",
|
|
1391
|
+
"是否保存会话记录?",
|
|
1392
|
+
False,
|
|
1393
|
+
) or changed
|
|
1394
|
+
changed = _ask_config_bool(
|
|
1395
|
+
config_data, ask_all,
|
|
1396
|
+
"JARVIS_PRINT_ERROR_TRACEBACK",
|
|
1397
|
+
"是否在错误输出时打印回溯调用链?",
|
|
1398
|
+
False,
|
|
1399
|
+
) or changed
|
|
1400
|
+
changed = _ask_config_bool(
|
|
1401
|
+
config_data, ask_all,
|
|
1402
|
+
"JARVIS_SKIP_PREDEFINED_TASKS",
|
|
1403
|
+
"是否跳过预定义任务加载(不读取 pre-command 列表)?",
|
|
1404
|
+
False,
|
|
1405
|
+
) or changed
|
|
1406
|
+
changed = _ask_config_int(
|
|
1407
|
+
config_data, ask_all,
|
|
1408
|
+
"JARVIS_CONVERSATION_TURN_THRESHOLD",
|
|
1409
|
+
"对话轮次阈值(达到此轮次时触发总结,建议50-100):",
|
|
1410
|
+
50,
|
|
1411
|
+
) or changed
|
|
1412
|
+
return changed
|
|
1330
1413
|
|
|
1331
1414
|
|
|
1415
|
+
def _collect_safety_config(config_data: dict, ask_all: bool) -> bool:
|
|
1416
|
+
"""收集代码与工具操作安全提示配置"""
|
|
1417
|
+
changed = False
|
|
1418
|
+
changed = _ask_config_bool(
|
|
1419
|
+
config_data, ask_all,
|
|
1420
|
+
"JARVIS_EXECUTE_TOOL_CONFIRM",
|
|
1421
|
+
"执行工具前是否需要确认?",
|
|
1422
|
+
False,
|
|
1423
|
+
) or changed
|
|
1424
|
+
changed = _ask_config_bool(
|
|
1425
|
+
config_data, ask_all,
|
|
1426
|
+
"JARVIS_CONFIRM_BEFORE_APPLY_PATCH",
|
|
1427
|
+
"应用补丁前是否需要确认?",
|
|
1428
|
+
False,
|
|
1429
|
+
) or changed
|
|
1430
|
+
return changed
|
|
1332
1431
|
|
|
1333
|
-
new_mode = get_choice(
|
|
1334
|
-
tip,
|
|
1335
|
-
choices,
|
|
1336
|
-
)
|
|
1337
1432
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1433
|
+
def _collect_data_and_token_config(config_data: dict, ask_all: bool) -> bool:
|
|
1434
|
+
"""收集数据目录与最大输入Token配置"""
|
|
1435
|
+
changed = False
|
|
1436
|
+
from jarvis.jarvis_utils.config import get_data_dir as _get_data_dir
|
|
1437
|
+
changed = _ask_config_optional_str(
|
|
1438
|
+
config_data, ask_all,
|
|
1439
|
+
"JARVIS_DATA_PATH",
|
|
1440
|
+
f"是否自定义数据目录路径(JARVIS_DATA_PATH)?留空使用默认: {_get_data_dir()}",
|
|
1441
|
+
) or changed
|
|
1442
|
+
changed = _ask_config_int(
|
|
1443
|
+
config_data, ask_all,
|
|
1444
|
+
"JARVIS_MAX_INPUT_TOKEN_COUNT",
|
|
1445
|
+
"自定义最大输入Token数量(留空使用默认: 32000)",
|
|
1446
|
+
32000,
|
|
1447
|
+
) or changed
|
|
1448
|
+
changed = _ask_config_int(
|
|
1449
|
+
config_data, ask_all,
|
|
1450
|
+
"JARVIS_TOOL_FILTER_THRESHOLD",
|
|
1451
|
+
"设置AI工具筛选阈值 (当可用工具数超过此值时触发AI筛选, 默认30)",
|
|
1452
|
+
30,
|
|
1453
|
+
) or changed
|
|
1454
|
+
return changed
|
|
1340
1455
|
|
|
1341
|
-
config_data[_key] = new_mode
|
|
1342
|
-
return True
|
|
1343
|
-
except Exception:
|
|
1344
|
-
return False
|
|
1345
1456
|
|
|
1346
|
-
|
|
1457
|
+
def _collect_advanced_config(config_data: dict, ask_all: bool) -> bool:
|
|
1458
|
+
"""收集高级配置(自动总结、脚本超时等)"""
|
|
1459
|
+
changed = False
|
|
1460
|
+
changed = _ask_config_int(
|
|
1461
|
+
config_data, ask_all,
|
|
1462
|
+
"JARVIS_SCRIPT_EXECUTION_TIMEOUT",
|
|
1463
|
+
"脚本执行超时时间(秒,默认300,仅非交互模式生效)",
|
|
1464
|
+
300,
|
|
1465
|
+
) or changed
|
|
1466
|
+
changed = _ask_config_int(
|
|
1467
|
+
config_data, ask_all,
|
|
1468
|
+
"JARVIS_ADDON_PROMPT_THRESHOLD",
|
|
1469
|
+
"附加提示的触发阈值(字符数,默认1024)。当消息长度超过此值时,会自动添加默认的附加提示",
|
|
1470
|
+
1024,
|
|
1471
|
+
) or changed
|
|
1472
|
+
changed = _ask_config_bool(
|
|
1473
|
+
config_data, ask_all,
|
|
1474
|
+
"JARVIS_ENABLE_INTENT_RECOGNITION",
|
|
1475
|
+
"是否启用意图识别功能?用于智能上下文推荐中的LLM意图提取和语义分析",
|
|
1476
|
+
True,
|
|
1477
|
+
) or changed
|
|
1478
|
+
return changed
|
|
1479
|
+
|
|
1347
1480
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1481
|
+
def _collect_directory_config(config_data: dict, ask_all: bool) -> bool:
|
|
1482
|
+
"""收集目录类配置(逗号分隔)"""
|
|
1483
|
+
changed = False
|
|
1484
|
+
changed = _ask_config_list(
|
|
1485
|
+
config_data, ask_all,
|
|
1486
|
+
"JARVIS_TOOL_LOAD_DIRS",
|
|
1487
|
+
"指定工具加载目录(逗号分隔,留空跳过):",
|
|
1488
|
+
) or changed
|
|
1489
|
+
changed = _ask_config_list(
|
|
1490
|
+
config_data, ask_all,
|
|
1491
|
+
"JARVIS_METHODOLOGY_DIRS",
|
|
1492
|
+
"指定方法论加载目录(逗号分隔,留空跳过):",
|
|
1493
|
+
) or changed
|
|
1494
|
+
changed = _ask_config_list(
|
|
1495
|
+
config_data, ask_all,
|
|
1496
|
+
"JARVIS_AGENT_DEFINITION_DIRS",
|
|
1497
|
+
"指定 agent 定义加载目录(逗号分隔,留空跳过):",
|
|
1498
|
+
) or changed
|
|
1499
|
+
changed = _ask_config_list(
|
|
1500
|
+
config_data, ask_all,
|
|
1501
|
+
"JARVIS_MULTI_AGENT_DIRS",
|
|
1502
|
+
"指定 multi_agent 加载目录(逗号分隔,留空跳过):",
|
|
1503
|
+
) or changed
|
|
1504
|
+
changed = _ask_config_list(
|
|
1505
|
+
config_data, ask_all,
|
|
1506
|
+
"JARVIS_ROLES_DIRS",
|
|
1507
|
+
"指定 roles 加载目录(逗号分隔,留空跳过):",
|
|
1508
|
+
) or changed
|
|
1509
|
+
changed = _ask_config_list(
|
|
1510
|
+
config_data, ask_all,
|
|
1511
|
+
"JARVIS_AFTER_TOOL_CALL_CB_DIRS",
|
|
1512
|
+
"指定工具调用后回调实现目录(逗号分隔,留空跳过):",
|
|
1513
|
+
) or changed
|
|
1514
|
+
return changed
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
def _collect_web_search_config(config_data: dict, ask_all: bool) -> bool:
|
|
1518
|
+
"""收集Web搜索配置"""
|
|
1519
|
+
changed = False
|
|
1520
|
+
changed = _ask_config_optional_str(
|
|
1521
|
+
config_data, ask_all,
|
|
1522
|
+
"JARVIS_WEB_SEARCH_PLATFORM",
|
|
1523
|
+
"配置 Web 搜索平台名称(留空跳过):",
|
|
1524
|
+
) or changed
|
|
1525
|
+
changed = _ask_config_optional_str(
|
|
1526
|
+
config_data, ask_all,
|
|
1527
|
+
"JARVIS_WEB_SEARCH_MODEL",
|
|
1528
|
+
"配置 Web 搜索模型名称(留空跳过):",
|
|
1529
|
+
) or changed
|
|
1530
|
+
return changed
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
def _ask_git_check_mode(config_data: dict, ask_all: bool) -> bool:
|
|
1534
|
+
"""询问Git校验模式"""
|
|
1535
|
+
try:
|
|
1536
|
+
_key = "JARVIS_GIT_CHECK_MODE"
|
|
1537
|
+
if not ask_all and _key in config_data:
|
|
1538
|
+
return False
|
|
1539
|
+
from jarvis.jarvis_utils.input import get_choice
|
|
1540
|
+
from jarvis.jarvis_utils.config import get_git_check_mode
|
|
1541
|
+
current_mode = config_data.get(_key, get_git_check_mode())
|
|
1542
|
+
choices = ["strict", "warn"]
|
|
1543
|
+
tip = (
|
|
1544
|
+
"请选择 Git 仓库检查模式 (JARVIS_GIT_CHECK_MODE):\n"
|
|
1545
|
+
"此设置决定了当在 Git 仓库中检测到未提交的更改时,Jarvis应如何处理。\n"
|
|
1546
|
+
"这对于确保代码修改和提交操作在干净的工作区上进行至关重要。\n"
|
|
1547
|
+
" - strict: (推荐) 如果存在未提交的更改,则中断相关操作(如代码修改、自动提交)。\n"
|
|
1548
|
+
" 这可以防止意外覆盖或丢失本地工作。\n"
|
|
1549
|
+
" - warn: 如果存在未提交的更改,仅显示警告信息,然后继续执行操作。\n"
|
|
1550
|
+
" 适用于您希望绕过检查并自行管理仓库状态的场景。"
|
|
1353
1551
|
)
|
|
1354
|
-
|
|
1355
|
-
|
|
1552
|
+
new_mode = get_choice(tip, choices)
|
|
1553
|
+
if new_mode == current_mode:
|
|
1554
|
+
return False
|
|
1555
|
+
config_data[_key] = new_mode
|
|
1556
|
+
return True
|
|
1557
|
+
except Exception:
|
|
1558
|
+
return False
|
|
1559
|
+
|
|
1560
|
+
|
|
1561
|
+
def _collect_git_config(config_data: dict, ask_all: bool) -> bool:
|
|
1562
|
+
"""收集Git相关配置"""
|
|
1563
|
+
changed = False
|
|
1564
|
+
changed = _ask_git_check_mode(config_data, ask_all) or changed
|
|
1565
|
+
changed = _ask_config_optional_str(
|
|
1566
|
+
config_data, ask_all,
|
|
1567
|
+
"JARVIS_GIT_COMMIT_PROMPT",
|
|
1568
|
+
"自定义 Git 提交提示模板(留空跳过):",
|
|
1569
|
+
) or changed
|
|
1570
|
+
return changed
|
|
1356
1571
|
|
|
1357
|
-
|
|
1572
|
+
|
|
1573
|
+
def _collect_rag_config(config_data: dict, ask_all: bool) -> bool:
|
|
1574
|
+
"""收集RAG配置"""
|
|
1575
|
+
changed = False
|
|
1358
1576
|
try:
|
|
1359
1577
|
from jarvis.jarvis_utils.config import (
|
|
1360
1578
|
get_rag_embedding_model as _get_rag_embedding_model,
|
|
1361
1579
|
get_rag_rerank_model as _get_rag_rerank_model,
|
|
1362
1580
|
)
|
|
1363
|
-
|
|
1581
|
+
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
|
1582
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1583
|
+
|
|
1364
1584
|
rag_default_embed = _get_rag_embedding_model()
|
|
1365
1585
|
rag_default_rerank = _get_rag_rerank_model()
|
|
1366
1586
|
except Exception:
|
|
1367
1587
|
rag_default_embed = "BAAI/bge-m3"
|
|
1368
1588
|
rag_default_rerank = "BAAI/bge-reranker-v2-m3"
|
|
1369
|
-
|
|
1589
|
+
get_yes_no = None
|
|
1590
|
+
get_single_line_input = None
|
|
1591
|
+
|
|
1370
1592
|
try:
|
|
1371
|
-
if "JARVIS_RAG" not in config_data:
|
|
1593
|
+
if "JARVIS_RAG" not in config_data and get_yes_no:
|
|
1372
1594
|
if get_yes_no("是否配置 RAG 检索增强参数?", default=False):
|
|
1373
1595
|
rag_conf: Dict[str, Any] = {}
|
|
1374
1596
|
emb = get_single_line_input(
|
|
@@ -1395,49 +1617,74 @@ def _collect_optional_config_interactively(
|
|
|
1395
1617
|
changed = True
|
|
1396
1618
|
except Exception:
|
|
1397
1619
|
pass
|
|
1620
|
+
return changed
|
|
1398
1621
|
|
|
1399
|
-
# 中心仓库配置
|
|
1400
|
-
changed = (
|
|
1401
|
-
_ask_and_set(
|
|
1402
|
-
"JARVIS_CENTRAL_METHODOLOGY_REPO",
|
|
1403
|
-
"请输入中心方法论仓库地址(可留空跳过):",
|
|
1404
|
-
"",
|
|
1405
|
-
"str",
|
|
1406
|
-
)
|
|
1407
|
-
or changed
|
|
1408
|
-
)
|
|
1409
|
-
changed = (
|
|
1410
|
-
_ask_and_set(
|
|
1411
|
-
"JARVIS_CENTRAL_TOOL_REPO",
|
|
1412
|
-
"请输入中心工具仓库地址(可留空跳过):",
|
|
1413
|
-
"",
|
|
1414
|
-
"str",
|
|
1415
|
-
)
|
|
1416
|
-
or changed
|
|
1417
|
-
)
|
|
1418
|
-
|
|
1419
|
-
# 已移除 LLM 组配置交互
|
|
1420
1622
|
|
|
1421
|
-
|
|
1623
|
+
def _collect_central_repo_config(config_data: dict, ask_all: bool) -> bool:
|
|
1624
|
+
"""收集中心仓库配置"""
|
|
1625
|
+
changed = False
|
|
1626
|
+
changed = _ask_config_str(
|
|
1627
|
+
config_data, ask_all,
|
|
1628
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO",
|
|
1629
|
+
"请输入中心方法论仓库路径或Git地址(可留空跳过):",
|
|
1630
|
+
"",
|
|
1631
|
+
) or changed
|
|
1632
|
+
changed = _ask_config_str(
|
|
1633
|
+
config_data, ask_all,
|
|
1634
|
+
"JARVIS_CENTRAL_TOOL_REPO",
|
|
1635
|
+
"请输入中心工具仓库路径或Git地址(可留空跳过):",
|
|
1636
|
+
"",
|
|
1637
|
+
) or changed
|
|
1638
|
+
return changed
|
|
1422
1639
|
|
|
1423
|
-
# 已移除 工具组配置交互
|
|
1424
1640
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1641
|
+
def _collect_shell_config(config_data: dict, ask_all: bool) -> bool:
|
|
1642
|
+
"""收集SHELL覆盖配置"""
|
|
1643
|
+
changed = False
|
|
1427
1644
|
try:
|
|
1645
|
+
import os
|
|
1428
1646
|
default_shell = os.getenv("SHELL", "/bin/bash")
|
|
1429
|
-
changed = (
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
or changed
|
|
1436
|
-
)
|
|
1647
|
+
changed = _ask_config_optional_str(
|
|
1648
|
+
config_data, ask_all,
|
|
1649
|
+
"SHELL",
|
|
1650
|
+
f"覆盖 SHELL 路径(留空使用系统默认: {default_shell}):",
|
|
1651
|
+
default_shell,
|
|
1652
|
+
) or changed
|
|
1437
1653
|
except Exception:
|
|
1438
1654
|
pass
|
|
1655
|
+
return changed
|
|
1656
|
+
|
|
1439
1657
|
|
|
1440
|
-
|
|
1658
|
+
def _collect_optional_config_interactively(
|
|
1659
|
+
config_data: dict, ask_all: bool = False
|
|
1660
|
+
) -> bool:
|
|
1661
|
+
"""
|
|
1662
|
+
复用的交互式配置收集逻辑:
|
|
1663
|
+
- ask_all=False(默认):仅对缺省的新功能开关/可选项逐项询问,已存在项跳过
|
|
1664
|
+
- ask_all=True:对所有项进行询问,默认值取自当前配置文件,可覆盖现有设置
|
|
1665
|
+
- 修改传入的 config_data
|
|
1666
|
+
- 包含更多来自 config.py 的可选项
|
|
1667
|
+
返回:
|
|
1668
|
+
bool: 是否有变更
|
|
1669
|
+
"""
|
|
1670
|
+
changed = False
|
|
1671
|
+
|
|
1672
|
+
# 收集各类配置
|
|
1673
|
+
changed = _collect_basic_switches(config_data, ask_all) or changed
|
|
1674
|
+
changed = _collect_ui_experience_config(config_data, ask_all) or changed
|
|
1675
|
+
changed = _collect_analysis_config(config_data, ask_all) or changed
|
|
1676
|
+
changed = _collect_agent_features_config(config_data, ask_all) or changed
|
|
1677
|
+
changed = _collect_session_config(config_data, ask_all) or changed
|
|
1678
|
+
changed = _collect_safety_config(config_data, ask_all) or changed
|
|
1679
|
+
changed = _collect_data_and_token_config(config_data, ask_all) or changed
|
|
1680
|
+
changed = _collect_advanced_config(config_data, ask_all) or changed
|
|
1681
|
+
changed = _collect_directory_config(config_data, ask_all) or changed
|
|
1682
|
+
changed = _collect_web_search_config(config_data, ask_all) or changed
|
|
1683
|
+
changed = _collect_git_config(config_data, ask_all) or changed
|
|
1684
|
+
changed = _collect_rag_config(config_data, ask_all) or changed
|
|
1685
|
+
changed = _collect_central_repo_config(config_data, ask_all) or changed
|
|
1686
|
+
changed = _collect_shell_config(config_data, ask_all) or changed
|
|
1687
|
+
|
|
1441
1688
|
return changed
|
|
1442
1689
|
|
|
1443
1690
|
|
|
@@ -1492,15 +1739,13 @@ def _load_and_process_config(jarvis_dir: str, config_file: str) -> None:
|
|
|
1492
1739
|
# 更新全局配置
|
|
1493
1740
|
set_global_env_data(config_data)
|
|
1494
1741
|
except Exception:
|
|
1495
|
-
|
|
1742
|
+
print("❌ 加载配置文件失败")
|
|
1496
1743
|
if get_yes_no("配置文件格式错误,是否删除并重新配置?"):
|
|
1497
1744
|
try:
|
|
1498
1745
|
os.remove(config_file)
|
|
1499
|
-
|
|
1500
|
-
"已删除损坏的配置文件,请重启Jarvis以重新配置。", OutputType.SUCCESS
|
|
1501
|
-
)
|
|
1746
|
+
print("✅ 已删除损坏的配置文件,请重启Jarvis以重新配置。")
|
|
1502
1747
|
except Exception:
|
|
1503
|
-
|
|
1748
|
+
print("❌ 删除配置文件失败")
|
|
1504
1749
|
sys.exit(1)
|
|
1505
1750
|
|
|
1506
1751
|
|
|
@@ -1657,69 +1902,113 @@ def _read_old_config_file(config_file):
|
|
|
1657
1902
|
{str(k): str(v) for k, v in config_data.items() if v is not None}
|
|
1658
1903
|
)
|
|
1659
1904
|
set_global_env_data(config_data)
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1905
|
+
print("⚠️ 检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式")
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
# 线程本地存储,用于共享重试计数器
|
|
1909
|
+
_retry_context = threading.local()
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
def _get_retry_count() -> int:
|
|
1913
|
+
"""获取当前线程的重试计数"""
|
|
1914
|
+
if not hasattr(_retry_context, 'count'):
|
|
1915
|
+
_retry_context.count = 0
|
|
1916
|
+
return _retry_context.count
|
|
1917
|
+
|
|
1918
|
+
|
|
1919
|
+
def _increment_retry_count() -> int:
|
|
1920
|
+
"""增加重试计数并返回新的计数值"""
|
|
1921
|
+
if not hasattr(_retry_context, 'count'):
|
|
1922
|
+
_retry_context.count = 0
|
|
1923
|
+
_retry_context.count += 1
|
|
1924
|
+
return _retry_context.count
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
def _reset_retry_count():
|
|
1928
|
+
"""重置重试计数"""
|
|
1929
|
+
_retry_context.count = 0
|
|
1664
1930
|
|
|
1665
1931
|
|
|
1666
|
-
def while_success(func: Callable[[], Any]
|
|
1932
|
+
def while_success(func: Callable[[], Any]) -> Any:
|
|
1667
1933
|
"""循环执行函数直到成功(累计日志后统一打印,避免逐次加框)
|
|
1668
1934
|
|
|
1669
1935
|
参数:
|
|
1670
1936
|
func -- 要执行的函数
|
|
1671
|
-
sleep_time -- 每次失败后的等待时间(秒)
|
|
1672
|
-
max_retries -- 最大重试次数,默认5次
|
|
1673
1937
|
|
|
1674
1938
|
返回:
|
|
1675
1939
|
函数执行结果
|
|
1940
|
+
|
|
1941
|
+
注意:
|
|
1942
|
+
与while_true共享重试计数器,累计重试6次,使用指数退避(第一次等待1s)
|
|
1676
1943
|
"""
|
|
1944
|
+
MAX_RETRIES = 6
|
|
1677
1945
|
result: Any = None
|
|
1678
|
-
|
|
1679
|
-
while
|
|
1946
|
+
|
|
1947
|
+
while True:
|
|
1680
1948
|
try:
|
|
1681
1949
|
result = func()
|
|
1950
|
+
_reset_retry_count() # 成功后重置计数器
|
|
1682
1951
|
break
|
|
1683
|
-
except Exception:
|
|
1684
|
-
retry_count
|
|
1685
|
-
if retry_count
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1952
|
+
except Exception as e:
|
|
1953
|
+
retry_count = _increment_retry_count()
|
|
1954
|
+
if retry_count <= MAX_RETRIES:
|
|
1955
|
+
# 指数退避:第1次等待1s (2^0),第2次等待2s (2^1),第3次等待4s (2^2),第4次等待8s (2^3),第6次等待32s (2^5)
|
|
1956
|
+
sleep_time = 2 ** (retry_count - 1)
|
|
1957
|
+
if retry_count < MAX_RETRIES:
|
|
1958
|
+
print(f"⚠️ 发生异常:\n{e}\n重试中 ({retry_count}/{MAX_RETRIES}),等待 {sleep_time}s...")
|
|
1959
|
+
time.sleep(sleep_time)
|
|
1960
|
+
else:
|
|
1961
|
+
print(f"⚠️ 发生异常:\n{e}\n已达到最大重试次数 ({retry_count}/{MAX_RETRIES})")
|
|
1962
|
+
_reset_retry_count()
|
|
1963
|
+
raise
|
|
1964
|
+
else:
|
|
1965
|
+
_reset_retry_count()
|
|
1966
|
+
raise
|
|
1692
1967
|
return result
|
|
1693
1968
|
|
|
1694
1969
|
|
|
1695
|
-
def while_true(func: Callable[[], bool]
|
|
1970
|
+
def while_true(func: Callable[[], bool]) -> Any:
|
|
1696
1971
|
"""循环执行函数直到返回True(累计日志后统一打印,避免逐次加框)
|
|
1697
1972
|
|
|
1698
1973
|
参数:
|
|
1699
1974
|
func: 要执行的函数,必须返回布尔值
|
|
1700
|
-
sleep_time: 每次失败后的等待时间(秒)
|
|
1701
|
-
max_retries: 最大重试次数,默认5次
|
|
1702
1975
|
|
|
1703
1976
|
返回:
|
|
1704
1977
|
函数最终返回的True值
|
|
1705
1978
|
|
|
1706
1979
|
注意:
|
|
1707
1980
|
与while_success不同,此函数只检查返回是否为True,
|
|
1708
|
-
|
|
1981
|
+
不捕获异常,异常会直接抛出。
|
|
1982
|
+
与while_success共享重试计数器,累计重试6次,使用指数退避(第一次等待1s)
|
|
1709
1983
|
"""
|
|
1984
|
+
MAX_RETRIES = 6
|
|
1710
1985
|
ret: bool = False
|
|
1711
|
-
|
|
1712
|
-
while
|
|
1713
|
-
|
|
1714
|
-
|
|
1986
|
+
|
|
1987
|
+
while True:
|
|
1988
|
+
try:
|
|
1989
|
+
ret = func()
|
|
1990
|
+
if ret:
|
|
1991
|
+
_reset_retry_count() # 成功后重置计数器
|
|
1992
|
+
break
|
|
1993
|
+
except Exception:
|
|
1994
|
+
# 异常直接抛出,不捕获
|
|
1995
|
+
_reset_retry_count()
|
|
1996
|
+
raise
|
|
1997
|
+
|
|
1998
|
+
retry_count = _increment_retry_count()
|
|
1999
|
+
if retry_count <= MAX_RETRIES:
|
|
2000
|
+
# 指数退避:第1次等待1s (2^0),第2次等待2s (2^1),第3次等待4s (2^2),第4次等待8s (2^3),第6次等待32s (2^5)
|
|
2001
|
+
sleep_time = 2 ** (retry_count - 1)
|
|
2002
|
+
if retry_count < MAX_RETRIES:
|
|
2003
|
+
print(f"⚠️ 返回空值,重试中 ({retry_count}/{MAX_RETRIES}),等待 {sleep_time}s...")
|
|
2004
|
+
time.sleep(sleep_time)
|
|
2005
|
+
else:
|
|
2006
|
+
print(f"⚠️ 返回空值,已达到最大重试次数 ({retry_count}/{MAX_RETRIES})")
|
|
2007
|
+
_reset_retry_count()
|
|
2008
|
+
break
|
|
2009
|
+
else:
|
|
2010
|
+
_reset_retry_count()
|
|
1715
2011
|
break
|
|
1716
|
-
retry_count += 1
|
|
1717
|
-
if retry_count < max_retries:
|
|
1718
|
-
PrettyOutput.print(
|
|
1719
|
-
f"返回空值,重试中 ({retry_count}/{max_retries}),等待 {sleep_time}s...",
|
|
1720
|
-
OutputType.WARNING,
|
|
1721
|
-
)
|
|
1722
|
-
time.sleep(sleep_time)
|
|
1723
2012
|
return ret
|
|
1724
2013
|
|
|
1725
2014
|
|
|
@@ -1730,9 +2019,22 @@ def get_file_md5(filepath: str) -> str:
|
|
|
1730
2019
|
filepath: 要计算哈希的文件路径
|
|
1731
2020
|
|
|
1732
2021
|
返回:
|
|
1733
|
-
str: 文件内容的MD5
|
|
2022
|
+
str: 文件内容的MD5哈希值(为降低内存占用,仅读取前100MB进行计算)
|
|
1734
2023
|
"""
|
|
1735
|
-
|
|
2024
|
+
# 采用流式读取,避免一次性加载100MB到内存
|
|
2025
|
+
h = hashlib.md5()
|
|
2026
|
+
max_bytes = 100 * 1024 * 1024 # 与原实现保持一致:仅读取前100MB
|
|
2027
|
+
buf_size = 8 * 1024 * 1024 # 8MB缓冲
|
|
2028
|
+
read_bytes = 0
|
|
2029
|
+
with open(filepath, "rb") as f:
|
|
2030
|
+
while read_bytes < max_bytes:
|
|
2031
|
+
to_read = min(buf_size, max_bytes - read_bytes)
|
|
2032
|
+
chunk = f.read(to_read)
|
|
2033
|
+
if not chunk:
|
|
2034
|
+
break
|
|
2035
|
+
h.update(chunk)
|
|
2036
|
+
read_bytes += len(chunk)
|
|
2037
|
+
return h.hexdigest()
|
|
1736
2038
|
|
|
1737
2039
|
|
|
1738
2040
|
def get_file_line_count(filename: str) -> int:
|
|
@@ -1745,7 +2047,9 @@ def get_file_line_count(filename: str) -> int:
|
|
|
1745
2047
|
int: 文件中的行数,如果文件无法读取则返回0
|
|
1746
2048
|
"""
|
|
1747
2049
|
try:
|
|
1748
|
-
|
|
2050
|
+
# 使用流式逐行计数,避免将整个文件读入内存
|
|
2051
|
+
with open(filename, "r", encoding="utf-8", errors="ignore") as f:
|
|
2052
|
+
return sum(1 for _ in f)
|
|
1749
2053
|
except Exception:
|
|
1750
2054
|
return 0
|
|
1751
2055
|
|
|
@@ -1771,14 +2075,52 @@ def count_cmd_usage() -> None:
|
|
|
1771
2075
|
|
|
1772
2076
|
|
|
1773
2077
|
def is_context_overflow(
|
|
1774
|
-
content: str,
|
|
2078
|
+
content: str,
|
|
2079
|
+
model_group_override: Optional[str] = None,
|
|
2080
|
+
platform: Optional[Any] = None
|
|
1775
2081
|
) -> bool:
|
|
1776
|
-
"""判断文件内容是否超出上下文限制
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2082
|
+
"""判断文件内容是否超出上下文限制
|
|
2083
|
+
|
|
2084
|
+
参数:
|
|
2085
|
+
content: 要检查的内容
|
|
2086
|
+
model_group_override: 模型组覆盖(可选)
|
|
2087
|
+
platform: 平台实例(可选),如果提供则使用剩余token数量判断
|
|
2088
|
+
|
|
2089
|
+
返回:
|
|
2090
|
+
bool: 如果内容超出上下文限制返回True
|
|
2091
|
+
"""
|
|
2092
|
+
# 快速长度预估:如果内容长度明显超过限制,直接返回True,无需精确计算token
|
|
2093
|
+
if content:
|
|
2094
|
+
# 粗略估算:假设平均每个token约4个字符,保守估计使用3.5个字符/token
|
|
2095
|
+
estimated_tokens = len(content) // 3.5
|
|
2096
|
+
|
|
2097
|
+
# 获取最大token限制
|
|
2098
|
+
max_tokens = get_max_big_content_size(model_group_override)
|
|
2099
|
+
|
|
2100
|
+
# 如果预估token数超过限制的150%,直接认为超出(避免精确计算)
|
|
2101
|
+
if estimated_tokens > max_tokens * 1.5:
|
|
2102
|
+
return True
|
|
2103
|
+
|
|
2104
|
+
# 如果预估token数小于限制的50%,直接认为安全
|
|
2105
|
+
if estimated_tokens < max_tokens * 0.5:
|
|
2106
|
+
return False
|
|
2107
|
+
|
|
2108
|
+
# 只有在预估结果不明确时,才进行精确的token计算
|
|
2109
|
+
content_tokens = get_context_token_count(content)
|
|
2110
|
+
|
|
2111
|
+
# 优先使用剩余token数量
|
|
2112
|
+
if platform is not None:
|
|
2113
|
+
try:
|
|
2114
|
+
remaining_tokens = platform.get_remaining_token_count()
|
|
2115
|
+
# 如果内容token数超过剩余token的80%,认为超出限制
|
|
2116
|
+
threshold = int(remaining_tokens * 0.8)
|
|
2117
|
+
if threshold > 0:
|
|
2118
|
+
return content_tokens > threshold
|
|
2119
|
+
except Exception:
|
|
2120
|
+
pass
|
|
2121
|
+
|
|
2122
|
+
# 回退方案:使用输入窗口限制
|
|
2123
|
+
return content_tokens > get_max_big_content_size(model_group_override)
|
|
1782
2124
|
def get_loc_stats() -> str:
|
|
1783
2125
|
"""使用loc命令获取当前目录的代码统计信息
|
|
1784
2126
|
|
|
@@ -1846,16 +2188,10 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
|
1846
2188
|
subprocess.TimeoutExpired,
|
|
1847
2189
|
FileNotFoundError,
|
|
1848
2190
|
) as e:
|
|
1849
|
-
|
|
1850
|
-
f"放弃 '{repo_path.name}' 的更改失败: {str(e)}",
|
|
1851
|
-
OutputType.ERROR,
|
|
1852
|
-
)
|
|
2191
|
+
print(f"❌ 放弃 '{repo_path.name}' 的更改失败: {str(e)}")
|
|
1853
2192
|
return
|
|
1854
2193
|
else:
|
|
1855
|
-
|
|
1856
|
-
f"跳过更新 '{repo_path.name}' 以保留未提交的更改。",
|
|
1857
|
-
OutputType.INFO,
|
|
1858
|
-
)
|
|
2194
|
+
print(f"ℹ️ 跳过更新 '{repo_path.name}' 以保留未提交的更改。")
|
|
1859
2195
|
return
|
|
1860
2196
|
|
|
1861
2197
|
# 获取更新前的commit hash
|
|
@@ -1908,25 +2244,17 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
|
1908
2244
|
after_hash = after_hash_result.stdout.strip()
|
|
1909
2245
|
|
|
1910
2246
|
if before_hash != after_hash:
|
|
1911
|
-
|
|
1912
|
-
f"{repo_type}库 '{repo_path.name}' 已更新。", OutputType.SUCCESS
|
|
1913
|
-
)
|
|
2247
|
+
print(f"✅ {repo_type}库 '{repo_path.name}' 已更新。")
|
|
1914
2248
|
|
|
1915
2249
|
except FileNotFoundError:
|
|
1916
|
-
|
|
1917
|
-
f"git 命令未找到,跳过更新 '{repo_path.name}'。", OutputType.WARNING
|
|
1918
|
-
)
|
|
2250
|
+
print(f"⚠️ git 命令未找到,跳过更新 '{repo_path.name}'。")
|
|
1919
2251
|
except subprocess.TimeoutExpired:
|
|
1920
|
-
|
|
2252
|
+
print(f"❌ 更新 '{repo_path.name}' 超时。")
|
|
1921
2253
|
except subprocess.CalledProcessError as e:
|
|
1922
2254
|
error_message = e.stderr.strip() if e.stderr else str(e)
|
|
1923
|
-
|
|
1924
|
-
f"更新 '{repo_path.name}' 失败: {error_message}", OutputType.ERROR
|
|
1925
|
-
)
|
|
2255
|
+
print(f"❌ 更新 '{repo_path.name}' 失败: {error_message}")
|
|
1926
2256
|
except Exception as e:
|
|
1927
|
-
|
|
1928
|
-
f"更新 '{repo_path.name}' 时发生未知错误: {str(e)}", OutputType.ERROR
|
|
1929
|
-
)
|
|
2257
|
+
print(f"❌ 更新 '{repo_path.name}' 时发生未知错误: {str(e)}")
|
|
1930
2258
|
|
|
1931
2259
|
|
|
1932
2260
|
def daily_check_git_updates(repo_dirs: List[str], repo_type: str):
|
|
@@ -1959,4 +2287,4 @@ def daily_check_git_updates(repo_dirs: List[str], repo_type: str):
|
|
|
1959
2287
|
try:
|
|
1960
2288
|
last_check_file.write_text(str(time.time()))
|
|
1961
2289
|
except IOError as e:
|
|
1962
|
-
|
|
2290
|
+
print(f"⚠️ 无法写入git更新检查时间戳: {e}")
|