jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.8__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 +243 -139
- jarvis/jarvis_agent/agent_manager.py +5 -10
- 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 +265 -15
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +113 -98
- 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 +6 -12
- 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 +77 -14
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +12 -21
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/task_analyzer.py +26 -4
- 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/web_server.py +55 -20
- jarvis/jarvis_c2rust/__init__.py +5 -5
- jarvis/jarvis_c2rust/cli.py +461 -499
- jarvis/jarvis_c2rust/collector.py +45 -53
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +264 -132
- jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +1592 -395
- jarvis/jarvis_c2rust/transpiler.py +1722 -1064
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +2 -3
- jarvis/jarvis_code_agent/code_agent.py +394 -320
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
- 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 +52 -2
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
- jarvis/jarvis_code_agent/lint.py +258 -27
- jarvis/jarvis_code_agent/utils.py +0 -1
- jarvis/jarvis_code_analysis/code_review.py +19 -24
- jarvis/jarvis_data/config_schema.json +53 -26
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +44 -49
- jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
- jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
- 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 +79 -61
- jarvis/jarvis_multi_agent/main.py +3 -7
- jarvis/jarvis_platform/base.py +469 -199
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +65 -27
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +31 -42
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +49 -51
- 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 +220 -3520
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/cli.py +29 -6
- 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 +83 -4
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +7 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +7 -7
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +20 -23
- jarvis/jarvis_tools/edit_file.py +1066 -0
- jarvis/jarvis_tools/execute_script.py +42 -21
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +11 -20
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1525 -87
- jarvis/jarvis_tools/read_symbols.py +2 -3
- jarvis/jarvis_tools/read_webpage.py +7 -10
- jarvis/jarvis_tools/registry.py +370 -181
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +105 -0
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +3 -7
- jarvis/jarvis_tools/sub_agent.py +17 -6
- jarvis/jarvis_tools/sub_code_agent.py +14 -16
- jarvis/jarvis_tools/virtual_tty.py +54 -32
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +98 -63
- jarvis/jarvis_utils/embedding.py +5 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +81 -67
- jarvis/jarvis_utils/input.py +24 -49
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +33 -35
- jarvis/jarvis_utils/utils.py +245 -202
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/METADATA +205 -70
- jarvis_ai_assistant-0.7.8.dist-info/RECORD +218 -0
- jarvis/jarvis_agent/edit_file_handler.py +0 -584
- jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
- jarvis/jarvis_agent/task_planner.py +0 -496
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -8,6 +8,7 @@ import sys
|
|
|
8
8
|
import time
|
|
9
9
|
import atexit
|
|
10
10
|
import errno
|
|
11
|
+
import threading
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
13
14
|
from datetime import datetime, date
|
|
@@ -25,7 +26,6 @@ from jarvis.jarvis_utils.config import (
|
|
|
25
26
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
|
26
27
|
from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
|
|
27
28
|
from jarvis.jarvis_utils.input import user_confirm
|
|
28
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
29
29
|
|
|
30
30
|
# 向后兼容:导出 get_yes_no 供外部模块引用
|
|
31
31
|
get_yes_no = user_confirm
|
|
@@ -284,20 +284,13 @@ def _acquire_single_instance_lock(lock_name: str = "instance.lock") -> None:
|
|
|
284
284
|
if lock_path.exists():
|
|
285
285
|
pid = _read_lock_owner_pid(lock_path)
|
|
286
286
|
if pid and _is_process_alive(pid):
|
|
287
|
-
|
|
288
|
-
f"检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。\n"
|
|
289
|
-
f"如果确认不存在正在运行的实例,请删除锁文件后重试:{lock_path}",
|
|
290
|
-
OutputType.WARNING,
|
|
291
|
-
)
|
|
287
|
+
print(f"⚠️ 检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。\n如果确认不存在正在运行的实例,请删除锁文件后重试:{lock_path}")
|
|
292
288
|
sys.exit(0)
|
|
293
289
|
# 尝试移除陈旧锁
|
|
294
290
|
try:
|
|
295
291
|
lock_path.unlink()
|
|
296
292
|
except Exception:
|
|
297
|
-
|
|
298
|
-
f"无法删除旧锁文件:{lock_path},请手动清理后重试。",
|
|
299
|
-
OutputType.ERROR,
|
|
300
|
-
)
|
|
293
|
+
print(f"❌ 无法删除旧锁文件:{lock_path},请手动清理后重试。")
|
|
301
294
|
sys.exit(1)
|
|
302
295
|
|
|
303
296
|
# 原子创建锁文件,避免并发竞争
|
|
@@ -320,18 +313,12 @@ def _acquire_single_instance_lock(lock_name: str = "instance.lock") -> None:
|
|
|
320
313
|
# 极端并发下再次校验
|
|
321
314
|
pid = _read_lock_owner_pid(lock_path)
|
|
322
315
|
if pid and _is_process_alive(pid):
|
|
323
|
-
|
|
324
|
-
f"检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。",
|
|
325
|
-
OutputType.WARNING,
|
|
326
|
-
)
|
|
316
|
+
print(f"⚠️ 检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。")
|
|
327
317
|
sys.exit(0)
|
|
328
|
-
|
|
329
|
-
f"锁文件已存在但可能为陈旧状态:{lock_path},请手动删除后重试。",
|
|
330
|
-
OutputType.ERROR,
|
|
331
|
-
)
|
|
318
|
+
print(f"❌ 锁文件已存在但可能为陈旧状态:{lock_path},请手动删除后重试。")
|
|
332
319
|
sys.exit(1)
|
|
333
320
|
except Exception as e:
|
|
334
|
-
|
|
321
|
+
print(f"❌ 创建实例锁失败: {e}")
|
|
335
322
|
sys.exit(1)
|
|
336
323
|
|
|
337
324
|
|
|
@@ -364,7 +351,7 @@ def _check_pip_updates() -> bool:
|
|
|
364
351
|
with urllib.request.urlopen(url, timeout=5) as response:
|
|
365
352
|
data = json.loads(response.read().decode())
|
|
366
353
|
latest_version = data["info"]["version"]
|
|
367
|
-
except (urllib.error.URLError, KeyError,
|
|
354
|
+
except (urllib.error.URLError, KeyError, ValueError):
|
|
368
355
|
return False
|
|
369
356
|
|
|
370
357
|
# 比较版本
|
|
@@ -372,10 +359,7 @@ def _check_pip_updates() -> bool:
|
|
|
372
359
|
latest_ver = version.parse(latest_version)
|
|
373
360
|
|
|
374
361
|
if latest_ver > current_ver:
|
|
375
|
-
|
|
376
|
-
f"检测到新版本 v{latest_version} (当前版本: v{__version__})",
|
|
377
|
-
OutputType.INFO,
|
|
378
|
-
)
|
|
362
|
+
print(f"ℹ️ 检测到新版本 v{latest_version} (当前版本: v{__version__})")
|
|
379
363
|
|
|
380
364
|
# 检测是否在虚拟环境中
|
|
381
365
|
hasattr(sys, "real_prefix") or (
|
|
@@ -422,7 +406,7 @@ def _check_pip_updates() -> bool:
|
|
|
422
406
|
|
|
423
407
|
# 自动尝试升级(失败时提供手动命令)
|
|
424
408
|
try:
|
|
425
|
-
|
|
409
|
+
print("ℹ️ 正在自动更新 Jarvis,请稍候...")
|
|
426
410
|
result = subprocess.run(
|
|
427
411
|
cmd_list,
|
|
428
412
|
capture_output=True,
|
|
@@ -432,25 +416,18 @@ def _check_pip_updates() -> bool:
|
|
|
432
416
|
timeout=600,
|
|
433
417
|
)
|
|
434
418
|
if result.returncode == 0:
|
|
435
|
-
|
|
419
|
+
print("✅ 更新成功,正在重启以应用新版本...")
|
|
436
420
|
# 更新检查日期,避免重复触发
|
|
437
421
|
last_check_file.write_text(today_str)
|
|
438
422
|
return True
|
|
439
423
|
else:
|
|
440
424
|
err = (result.stderr or result.stdout or "").strip()
|
|
441
425
|
if err:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
OutputType.WARNING,
|
|
445
|
-
)
|
|
446
|
-
PrettyOutput.print(
|
|
447
|
-
f"请手动执行以下命令更新: {update_cmd}", OutputType.INFO
|
|
448
|
-
)
|
|
426
|
+
print(f"⚠️ 自动更新失败,错误信息(已截断): {err[:500]}")
|
|
427
|
+
print(f"ℹ️ 请手动执行以下命令更新: {update_cmd}")
|
|
449
428
|
except Exception:
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
f"请手动执行以下命令更新: {update_cmd}", OutputType.INFO
|
|
453
|
-
)
|
|
429
|
+
print("⚠️ 自动更新出现异常,已切换为手动更新方式。")
|
|
430
|
+
print(f"ℹ️ 请手动执行以下命令更新: {update_cmd}")
|
|
454
431
|
|
|
455
432
|
# 更新检查日期
|
|
456
433
|
last_check_file.write_text(today_str)
|
|
@@ -491,8 +468,6 @@ def _check_jarvis_updates() -> bool:
|
|
|
491
468
|
|
|
492
469
|
def _show_usage_stats(welcome_str: str) -> None:
|
|
493
470
|
"""显示Jarvis使用统计信息"""
|
|
494
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
495
|
-
|
|
496
471
|
try:
|
|
497
472
|
|
|
498
473
|
from rich.console import Console, Group
|
|
@@ -503,6 +478,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
503
478
|
console = Console()
|
|
504
479
|
|
|
505
480
|
from jarvis.jarvis_stats.stats import StatsManager
|
|
481
|
+
from jarvis.jarvis_stats.storage import StatsStorage
|
|
506
482
|
|
|
507
483
|
# 获取所有可用的指标
|
|
508
484
|
all_metrics = StatsManager.list_metrics()
|
|
@@ -518,19 +494,47 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
518
494
|
"other": {"title": "📦 其他指标", "metrics": {}, "suffix": ""},
|
|
519
495
|
}
|
|
520
496
|
|
|
521
|
-
#
|
|
522
|
-
|
|
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():
|
|
523
511
|
try:
|
|
524
|
-
|
|
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
|
|
525
521
|
except Exception:
|
|
526
|
-
|
|
522
|
+
pass
|
|
527
523
|
|
|
524
|
+
# 遍历所有指标,使用批量读取的数据
|
|
525
|
+
for metric in all_metrics:
|
|
526
|
+
# 从批量读取的数据中获取总量
|
|
527
|
+
total = metric_totals.get(metric, 0.0)
|
|
528
|
+
|
|
528
529
|
if not total or total <= 0:
|
|
529
530
|
continue
|
|
530
531
|
|
|
531
|
-
#
|
|
532
|
-
|
|
533
|
-
|
|
532
|
+
# 从已加载的元数据中获取分组信息,避免重复读取
|
|
533
|
+
try:
|
|
534
|
+
info = metrics_info.get(metric, {})
|
|
535
|
+
group = info.get("group", "other")
|
|
536
|
+
except Exception:
|
|
537
|
+
group = "other"
|
|
534
538
|
|
|
535
539
|
if group == "tool":
|
|
536
540
|
categorized_stats["tool"]["metrics"][metric] = int(total)
|
|
@@ -893,11 +897,11 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
893
897
|
# 输出错误信息以便调试
|
|
894
898
|
import traceback
|
|
895
899
|
|
|
896
|
-
|
|
897
|
-
|
|
900
|
+
print(f"❌ 统计显示出错: {str(e)}")
|
|
901
|
+
print(f"❌ {traceback.format_exc()}")
|
|
898
902
|
|
|
899
903
|
|
|
900
|
-
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:
|
|
901
905
|
"""初始化Jarvis环境
|
|
902
906
|
|
|
903
907
|
参数:
|
|
@@ -907,35 +911,60 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
|
907
911
|
# 0. 检查是否处于Jarvis打开的终端环境,避免嵌套
|
|
908
912
|
try:
|
|
909
913
|
if os.environ.get("JARVIS_TERMINAL") == "1":
|
|
910
|
-
|
|
911
|
-
"检测到当前终端由 Jarvis 打开。再次启动可能导致嵌套。",
|
|
912
|
-
OutputType.WARNING,
|
|
913
|
-
)
|
|
914
|
+
print("⚠️ 检测到当前终端由 Jarvis 打开。再次启动可能导致嵌套。")
|
|
914
915
|
if not user_confirm("是否仍要继续启动 Jarvis?", default=False):
|
|
915
|
-
|
|
916
|
+
print("ℹ️ 已取消启动以避免终端嵌套。")
|
|
916
917
|
sys.exit(0)
|
|
917
918
|
except Exception:
|
|
918
919
|
pass
|
|
919
920
|
|
|
920
921
|
# 1. 设置信号处理
|
|
921
|
-
|
|
922
|
+
try:
|
|
923
|
+
_setup_signal_handler()
|
|
924
|
+
except Exception:
|
|
925
|
+
pass
|
|
922
926
|
|
|
923
|
-
# 2.
|
|
924
|
-
|
|
927
|
+
# 2. 统计命令使用(异步执行,避免阻塞初始化)
|
|
928
|
+
try:
|
|
929
|
+
count_cmd_usage()
|
|
930
|
+
except Exception:
|
|
931
|
+
# 静默失败,不影响正常使用
|
|
932
|
+
pass
|
|
925
933
|
|
|
926
934
|
# 3. 设置配置文件
|
|
927
935
|
global g_config_file
|
|
928
936
|
g_config_file = config_file
|
|
929
|
-
|
|
937
|
+
try:
|
|
938
|
+
load_config()
|
|
939
|
+
except Exception:
|
|
940
|
+
# 静默失败,不影响正常使用
|
|
941
|
+
pass
|
|
930
942
|
|
|
931
943
|
# 4. 显示历史统计数据(仅在显示欢迎信息时显示)
|
|
944
|
+
# 使用延迟加载,避免阻塞初始化
|
|
932
945
|
if welcome_str:
|
|
933
|
-
|
|
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
|
|
934
959
|
|
|
935
|
-
# 5. 检查Jarvis
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
|
939
968
|
|
|
940
969
|
|
|
941
970
|
def _interactive_config_setup(config_file_path: Path):
|
|
@@ -947,9 +976,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
947
976
|
user_confirm as get_yes_no,
|
|
948
977
|
)
|
|
949
978
|
|
|
950
|
-
|
|
951
|
-
"欢迎使用 Jarvis!未找到配置文件,现在开始引导配置。", OutputType.INFO
|
|
952
|
-
)
|
|
979
|
+
print("ℹ️ 欢迎使用 Jarvis!未找到配置文件,现在开始引导配置。")
|
|
953
980
|
|
|
954
981
|
# 1. 选择平台
|
|
955
982
|
registry = PlatformRegistry.get_global_platform_registry()
|
|
@@ -959,7 +986,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
959
986
|
# 2. 配置环境变量
|
|
960
987
|
platform_class = registry.platforms.get(platform_name)
|
|
961
988
|
if not platform_class:
|
|
962
|
-
|
|
989
|
+
print(f"❌ 平台 '{platform_name}' 加载失败。")
|
|
963
990
|
sys.exit(1)
|
|
964
991
|
|
|
965
992
|
env_vars = {}
|
|
@@ -967,9 +994,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
967
994
|
defaults = platform_class.get_env_defaults()
|
|
968
995
|
config_guide = platform_class.get_env_config_guide()
|
|
969
996
|
if required_keys:
|
|
970
|
-
|
|
971
|
-
f"请输入 {platform_name} 平台所需的配置信息:", OutputType.INFO
|
|
972
|
-
)
|
|
997
|
+
print(f"ℹ️ 请输入 {platform_name} 平台所需的配置信息:")
|
|
973
998
|
|
|
974
999
|
# 如果有配置指导,先显示总体说明
|
|
975
1000
|
if config_guide:
|
|
@@ -980,7 +1005,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
980
1005
|
guide_lines.append("")
|
|
981
1006
|
guide_lines.append(f"{key} 获取方法:")
|
|
982
1007
|
guide_lines.append(str(config_guide[key]))
|
|
983
|
-
|
|
1008
|
+
print("ℹ️ " + "\n".join(guide_lines))
|
|
984
1009
|
else:
|
|
985
1010
|
# 若无指导,仍需遍历以保持后续逻辑一致
|
|
986
1011
|
pass
|
|
@@ -1002,7 +1027,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
1002
1027
|
try:
|
|
1003
1028
|
platform_instance = registry.create_platform(platform_name)
|
|
1004
1029
|
if not platform_instance:
|
|
1005
|
-
|
|
1030
|
+
print(f"❌ 无法创建平台 '{platform_name}'。")
|
|
1006
1031
|
sys.exit(1)
|
|
1007
1032
|
|
|
1008
1033
|
model_list_tuples = platform_instance.get_model_list()
|
|
@@ -1014,13 +1039,13 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
1014
1039
|
model_name, _ = model_list_tuples[selected_index]
|
|
1015
1040
|
|
|
1016
1041
|
except Exception:
|
|
1017
|
-
|
|
1042
|
+
print("❌ 获取模型列表失败")
|
|
1018
1043
|
if not get_yes_no("无法获取模型列表,是否继续配置?"):
|
|
1019
1044
|
sys.exit(1)
|
|
1020
1045
|
model_name = get_input("请输入模型名称:")
|
|
1021
1046
|
|
|
1022
1047
|
# 4. 测试配置
|
|
1023
|
-
|
|
1048
|
+
print("ℹ️ 正在测试配置...")
|
|
1024
1049
|
test_passed = False
|
|
1025
1050
|
try:
|
|
1026
1051
|
platform_instance = registry.create_platform(platform_name)
|
|
@@ -1029,16 +1054,14 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
1029
1054
|
response_generator = platform_instance.chat("hello")
|
|
1030
1055
|
response = "".join(response_generator)
|
|
1031
1056
|
if response:
|
|
1032
|
-
|
|
1033
|
-
f"测试成功,模型响应: {response}", OutputType.SUCCESS
|
|
1034
|
-
)
|
|
1057
|
+
print(f"✅ 测试成功,模型响应: {response}")
|
|
1035
1058
|
test_passed = True
|
|
1036
1059
|
else:
|
|
1037
|
-
|
|
1060
|
+
print("❌ 测试失败,模型没有响应。")
|
|
1038
1061
|
else:
|
|
1039
|
-
|
|
1062
|
+
print("❌ 测试失败,无法创建平台实例。")
|
|
1040
1063
|
except Exception:
|
|
1041
|
-
|
|
1064
|
+
print("❌ 测试失败")
|
|
1042
1065
|
|
|
1043
1066
|
# 5. 交互式确认并应用配置(不直接生成配置文件)
|
|
1044
1067
|
config_data = {
|
|
@@ -1049,7 +1072,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
1049
1072
|
|
|
1050
1073
|
if not test_passed:
|
|
1051
1074
|
if not get_yes_no("配置测试失败,是否仍要应用该配置并继续?", default=False):
|
|
1052
|
-
|
|
1075
|
+
print("ℹ️ 已取消配置。")
|
|
1053
1076
|
sys.exit(0)
|
|
1054
1077
|
|
|
1055
1078
|
# 6. 选择其他功能开关与可选项(复用统一逻辑)
|
|
@@ -1072,11 +1095,11 @@ def _interactive_config_setup(config_file_path: Path):
|
|
|
1072
1095
|
if header:
|
|
1073
1096
|
f.write(header)
|
|
1074
1097
|
f.write(yaml_str)
|
|
1075
|
-
|
|
1076
|
-
|
|
1098
|
+
print(f"✅ 配置文件已生成: {config_file_path}")
|
|
1099
|
+
print("ℹ️ 配置完成,请重新启动Jarvis。")
|
|
1077
1100
|
sys.exit(0)
|
|
1078
1101
|
except Exception:
|
|
1079
|
-
|
|
1102
|
+
print("❌ 写入配置文件失败")
|
|
1080
1103
|
sys.exit(1)
|
|
1081
1104
|
|
|
1082
1105
|
|
|
@@ -1380,6 +1403,12 @@ def _collect_session_config(config_data: dict, ask_all: bool) -> bool:
|
|
|
1380
1403
|
"是否跳过预定义任务加载(不读取 pre-command 列表)?",
|
|
1381
1404
|
False,
|
|
1382
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
|
|
1383
1412
|
return changed
|
|
1384
1413
|
|
|
1385
1414
|
|
|
@@ -1425,33 +1454,9 @@ def _collect_data_and_token_config(config_data: dict, ask_all: bool) -> bool:
|
|
|
1425
1454
|
return changed
|
|
1426
1455
|
|
|
1427
1456
|
|
|
1428
|
-
def _collect_planning_config(config_data: dict, ask_all: bool) -> bool:
|
|
1429
|
-
"""收集规划相关配置"""
|
|
1430
|
-
changed = False
|
|
1431
|
-
changed = _ask_config_bool(
|
|
1432
|
-
config_data, ask_all,
|
|
1433
|
-
"JARVIS_PLAN_ENABLED",
|
|
1434
|
-
"是否默认启用任务规划?当 Agent 初始化时 plan 参数未指定,将从此配置加载",
|
|
1435
|
-
True,
|
|
1436
|
-
) or changed
|
|
1437
|
-
changed = _ask_config_int(
|
|
1438
|
-
config_data, ask_all,
|
|
1439
|
-
"JARVIS_PLAN_MAX_DEPTH",
|
|
1440
|
-
"任务规划的最大层数(限制递归拆分深度,默认2;仅在启用规划时生效)",
|
|
1441
|
-
2,
|
|
1442
|
-
) or changed
|
|
1443
|
-
return changed
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
1457
|
def _collect_advanced_config(config_data: dict, ask_all: bool) -> bool:
|
|
1447
1458
|
"""收集高级配置(自动总结、脚本超时等)"""
|
|
1448
1459
|
changed = False
|
|
1449
|
-
changed = _ask_config_int(
|
|
1450
|
-
config_data, ask_all,
|
|
1451
|
-
"JARVIS_AUTO_SUMMARY_ROUNDS",
|
|
1452
|
-
"基于对话轮次的自动总结阈值(达到该轮次后自动总结并清理历史,默认50)",
|
|
1453
|
-
50,
|
|
1454
|
-
) or changed
|
|
1455
1460
|
changed = _ask_config_int(
|
|
1456
1461
|
config_data, ask_all,
|
|
1457
1462
|
"JARVIS_SCRIPT_EXECUTION_TIMEOUT",
|
|
@@ -1553,37 +1558,10 @@ def _ask_git_check_mode(config_data: dict, ask_all: bool) -> bool:
|
|
|
1553
1558
|
return False
|
|
1554
1559
|
|
|
1555
1560
|
|
|
1556
|
-
def _ask_patch_format_mode(config_data: dict, ask_all: bool) -> bool:
|
|
1557
|
-
"""询问补丁格式模式"""
|
|
1558
|
-
try:
|
|
1559
|
-
_key = "JARVIS_PATCH_FORMAT"
|
|
1560
|
-
if not ask_all and _key in config_data:
|
|
1561
|
-
return False
|
|
1562
|
-
from jarvis.jarvis_utils.input import get_choice
|
|
1563
|
-
from jarvis.jarvis_utils.config import get_patch_format
|
|
1564
|
-
current_mode = config_data.get(_key, get_patch_format())
|
|
1565
|
-
choices = ["all", "search", "search_range"]
|
|
1566
|
-
tip = (
|
|
1567
|
-
"请选择补丁格式处理模式 (JARVIS_PATCH_FORMAT):\n"
|
|
1568
|
-
"该设置影响 edit_file_handler 在处理补丁时允许的匹配方式。\n"
|
|
1569
|
-
" - all: 同时支持 SEARCH 与 SEARCH_START/SEARCH_END 两种模式(默认)。\n"
|
|
1570
|
-
" - search: 仅允许精确片段匹配(SEARCH)。更稳定,适合较弱模型或严格控制改动。\n"
|
|
1571
|
-
" - search_range: 仅允许范围匹配(SEARCH_START/SEARCH_END)。更灵活,适合较强模型和块内细粒度修改。"
|
|
1572
|
-
)
|
|
1573
|
-
new_mode = get_choice(tip, choices)
|
|
1574
|
-
if new_mode == current_mode:
|
|
1575
|
-
return False
|
|
1576
|
-
config_data[_key] = new_mode
|
|
1577
|
-
return True
|
|
1578
|
-
except Exception:
|
|
1579
|
-
return False
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
1561
|
def _collect_git_config(config_data: dict, ask_all: bool) -> bool:
|
|
1583
1562
|
"""收集Git相关配置"""
|
|
1584
1563
|
changed = False
|
|
1585
1564
|
changed = _ask_git_check_mode(config_data, ask_all) or changed
|
|
1586
|
-
changed = _ask_patch_format_mode(config_data, ask_all) or changed
|
|
1587
1565
|
changed = _ask_config_optional_str(
|
|
1588
1566
|
config_data, ask_all,
|
|
1589
1567
|
"JARVIS_GIT_COMMIT_PROMPT",
|
|
@@ -1699,7 +1677,6 @@ def _collect_optional_config_interactively(
|
|
|
1699
1677
|
changed = _collect_session_config(config_data, ask_all) or changed
|
|
1700
1678
|
changed = _collect_safety_config(config_data, ask_all) or changed
|
|
1701
1679
|
changed = _collect_data_and_token_config(config_data, ask_all) or changed
|
|
1702
|
-
changed = _collect_planning_config(config_data, ask_all) or changed
|
|
1703
1680
|
changed = _collect_advanced_config(config_data, ask_all) or changed
|
|
1704
1681
|
changed = _collect_directory_config(config_data, ask_all) or changed
|
|
1705
1682
|
changed = _collect_web_search_config(config_data, ask_all) or changed
|
|
@@ -1762,15 +1739,13 @@ def _load_and_process_config(jarvis_dir: str, config_file: str) -> None:
|
|
|
1762
1739
|
# 更新全局配置
|
|
1763
1740
|
set_global_env_data(config_data)
|
|
1764
1741
|
except Exception:
|
|
1765
|
-
|
|
1742
|
+
print("❌ 加载配置文件失败")
|
|
1766
1743
|
if get_yes_no("配置文件格式错误,是否删除并重新配置?"):
|
|
1767
1744
|
try:
|
|
1768
1745
|
os.remove(config_file)
|
|
1769
|
-
|
|
1770
|
-
"已删除损坏的配置文件,请重启Jarvis以重新配置。", OutputType.SUCCESS
|
|
1771
|
-
)
|
|
1746
|
+
print("✅ 已删除损坏的配置文件,请重启Jarvis以重新配置。")
|
|
1772
1747
|
except Exception:
|
|
1773
|
-
|
|
1748
|
+
print("❌ 删除配置文件失败")
|
|
1774
1749
|
sys.exit(1)
|
|
1775
1750
|
|
|
1776
1751
|
|
|
@@ -1927,69 +1902,113 @@ def _read_old_config_file(config_file):
|
|
|
1927
1902
|
{str(k): str(v) for k, v in config_data.items() if v is not None}
|
|
1928
1903
|
)
|
|
1929
1904
|
set_global_env_data(config_data)
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1905
|
+
print("⚠️ 检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式")
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
# 线程本地存储,用于共享重试计数器
|
|
1909
|
+
_retry_context = threading.local()
|
|
1910
|
+
|
|
1934
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
|
|
1935
1917
|
|
|
1936
|
-
|
|
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
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
def while_success(func: Callable[[], Any]) -> Any:
|
|
1937
1933
|
"""循环执行函数直到成功(累计日志后统一打印,避免逐次加框)
|
|
1938
1934
|
|
|
1939
1935
|
参数:
|
|
1940
1936
|
func -- 要执行的函数
|
|
1941
|
-
sleep_time -- 每次失败后的等待时间(秒)
|
|
1942
|
-
max_retries -- 最大重试次数,默认5次
|
|
1943
1937
|
|
|
1944
1938
|
返回:
|
|
1945
1939
|
函数执行结果
|
|
1940
|
+
|
|
1941
|
+
注意:
|
|
1942
|
+
与while_true共享重试计数器,累计重试6次,使用指数退避(第一次等待1s)
|
|
1946
1943
|
"""
|
|
1944
|
+
MAX_RETRIES = 6
|
|
1947
1945
|
result: Any = None
|
|
1948
|
-
|
|
1949
|
-
while
|
|
1946
|
+
|
|
1947
|
+
while True:
|
|
1950
1948
|
try:
|
|
1951
1949
|
result = func()
|
|
1950
|
+
_reset_retry_count() # 成功后重置计数器
|
|
1952
1951
|
break
|
|
1953
1952
|
except Exception as e:
|
|
1954
|
-
retry_count
|
|
1955
|
-
if retry_count
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
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
|
|
1962
1967
|
return result
|
|
1963
1968
|
|
|
1964
1969
|
|
|
1965
|
-
def while_true(func: Callable[[], bool]
|
|
1970
|
+
def while_true(func: Callable[[], bool]) -> Any:
|
|
1966
1971
|
"""循环执行函数直到返回True(累计日志后统一打印,避免逐次加框)
|
|
1967
1972
|
|
|
1968
1973
|
参数:
|
|
1969
1974
|
func: 要执行的函数,必须返回布尔值
|
|
1970
|
-
sleep_time: 每次失败后的等待时间(秒)
|
|
1971
|
-
max_retries: 最大重试次数,默认5次
|
|
1972
1975
|
|
|
1973
1976
|
返回:
|
|
1974
1977
|
函数最终返回的True值
|
|
1975
1978
|
|
|
1976
1979
|
注意:
|
|
1977
1980
|
与while_success不同,此函数只检查返回是否为True,
|
|
1978
|
-
|
|
1981
|
+
不捕获异常,异常会直接抛出。
|
|
1982
|
+
与while_success共享重试计数器,累计重试6次,使用指数退避(第一次等待1s)
|
|
1979
1983
|
"""
|
|
1984
|
+
MAX_RETRIES = 6
|
|
1980
1985
|
ret: bool = False
|
|
1981
|
-
|
|
1982
|
-
while
|
|
1983
|
-
|
|
1984
|
-
|
|
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()
|
|
1985
2011
|
break
|
|
1986
|
-
retry_count += 1
|
|
1987
|
-
if retry_count < max_retries:
|
|
1988
|
-
PrettyOutput.print(
|
|
1989
|
-
f"返回空值,重试中 ({retry_count}/{max_retries}),等待 {sleep_time}s...",
|
|
1990
|
-
OutputType.WARNING,
|
|
1991
|
-
)
|
|
1992
|
-
time.sleep(sleep_time)
|
|
1993
2012
|
return ret
|
|
1994
2013
|
|
|
1995
2014
|
|
|
@@ -2056,14 +2075,52 @@ def count_cmd_usage() -> None:
|
|
|
2056
2075
|
|
|
2057
2076
|
|
|
2058
2077
|
def is_context_overflow(
|
|
2059
|
-
content: str,
|
|
2078
|
+
content: str,
|
|
2079
|
+
model_group_override: Optional[str] = None,
|
|
2080
|
+
platform: Optional[Any] = None
|
|
2060
2081
|
) -> bool:
|
|
2061
|
-
"""判断文件内容是否超出上下文限制
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
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)
|
|
2067
2124
|
def get_loc_stats() -> str:
|
|
2068
2125
|
"""使用loc命令获取当前目录的代码统计信息
|
|
2069
2126
|
|
|
@@ -2131,16 +2188,10 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
|
2131
2188
|
subprocess.TimeoutExpired,
|
|
2132
2189
|
FileNotFoundError,
|
|
2133
2190
|
) as e:
|
|
2134
|
-
|
|
2135
|
-
f"放弃 '{repo_path.name}' 的更改失败: {str(e)}",
|
|
2136
|
-
OutputType.ERROR,
|
|
2137
|
-
)
|
|
2191
|
+
print(f"❌ 放弃 '{repo_path.name}' 的更改失败: {str(e)}")
|
|
2138
2192
|
return
|
|
2139
2193
|
else:
|
|
2140
|
-
|
|
2141
|
-
f"跳过更新 '{repo_path.name}' 以保留未提交的更改。",
|
|
2142
|
-
OutputType.INFO,
|
|
2143
|
-
)
|
|
2194
|
+
print(f"ℹ️ 跳过更新 '{repo_path.name}' 以保留未提交的更改。")
|
|
2144
2195
|
return
|
|
2145
2196
|
|
|
2146
2197
|
# 获取更新前的commit hash
|
|
@@ -2193,25 +2244,17 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
|
2193
2244
|
after_hash = after_hash_result.stdout.strip()
|
|
2194
2245
|
|
|
2195
2246
|
if before_hash != after_hash:
|
|
2196
|
-
|
|
2197
|
-
f"{repo_type}库 '{repo_path.name}' 已更新。", OutputType.SUCCESS
|
|
2198
|
-
)
|
|
2247
|
+
print(f"✅ {repo_type}库 '{repo_path.name}' 已更新。")
|
|
2199
2248
|
|
|
2200
2249
|
except FileNotFoundError:
|
|
2201
|
-
|
|
2202
|
-
f"git 命令未找到,跳过更新 '{repo_path.name}'。", OutputType.WARNING
|
|
2203
|
-
)
|
|
2250
|
+
print(f"⚠️ git 命令未找到,跳过更新 '{repo_path.name}'。")
|
|
2204
2251
|
except subprocess.TimeoutExpired:
|
|
2205
|
-
|
|
2252
|
+
print(f"❌ 更新 '{repo_path.name}' 超时。")
|
|
2206
2253
|
except subprocess.CalledProcessError as e:
|
|
2207
2254
|
error_message = e.stderr.strip() if e.stderr else str(e)
|
|
2208
|
-
|
|
2209
|
-
f"更新 '{repo_path.name}' 失败: {error_message}", OutputType.ERROR
|
|
2210
|
-
)
|
|
2255
|
+
print(f"❌ 更新 '{repo_path.name}' 失败: {error_message}")
|
|
2211
2256
|
except Exception as e:
|
|
2212
|
-
|
|
2213
|
-
f"更新 '{repo_path.name}' 时发生未知错误: {str(e)}", OutputType.ERROR
|
|
2214
|
-
)
|
|
2257
|
+
print(f"❌ 更新 '{repo_path.name}' 时发生未知错误: {str(e)}")
|
|
2215
2258
|
|
|
2216
2259
|
|
|
2217
2260
|
def daily_check_git_updates(repo_dirs: List[str], repo_type: str):
|
|
@@ -2244,4 +2287,4 @@ def daily_check_git_updates(repo_dirs: List[str], repo_type: str):
|
|
|
2244
2287
|
try:
|
|
2245
2288
|
last_check_file.write_text(str(time.time()))
|
|
2246
2289
|
except IOError as e:
|
|
2247
|
-
|
|
2290
|
+
print(f"⚠️ 无法写入git更新检查时间戳: {e}")
|