jarvis-ai-assistant 0.7.16__py3-none-any.whl → 1.0.2__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 +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""库替换器的检查点管理模块。"""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
from typing import Dict
|
|
9
|
+
from typing import List
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_CHECKPOINT_INTERVAL
|
|
13
|
+
from jarvis.jarvis_c2rust.constants import JSON_INDENT
|
|
14
|
+
from jarvis.jarvis_c2rust.library_replacer_utils import normalize_list
|
|
15
|
+
from jarvis.jarvis_c2rust.library_replacer_utils import normalize_list_lower
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def make_checkpoint_key(
|
|
19
|
+
sjsonl: Path,
|
|
20
|
+
library_name: str,
|
|
21
|
+
llm_group: Optional[str],
|
|
22
|
+
candidates: Optional[List[str]],
|
|
23
|
+
disabled_libraries: Optional[List[str]],
|
|
24
|
+
max_funcs: Optional[int],
|
|
25
|
+
) -> Dict[str, Any]:
|
|
26
|
+
"""构建检查点键"""
|
|
27
|
+
try:
|
|
28
|
+
abs_sym = str(Path(sjsonl).resolve())
|
|
29
|
+
except Exception:
|
|
30
|
+
abs_sym = str(sjsonl)
|
|
31
|
+
key: Dict[str, Any] = {
|
|
32
|
+
"symbols": abs_sym,
|
|
33
|
+
"library_name": str(library_name),
|
|
34
|
+
"llm_group": str(llm_group or ""),
|
|
35
|
+
"candidates": normalize_list(candidates),
|
|
36
|
+
"disabled_libraries": normalize_list_lower(disabled_libraries),
|
|
37
|
+
"max_funcs": (
|
|
38
|
+
int(max_funcs)
|
|
39
|
+
if isinstance(max_funcs, int)
|
|
40
|
+
or (isinstance(max_funcs, float) and max_funcs.is_integer()) # type: ignore
|
|
41
|
+
else None
|
|
42
|
+
),
|
|
43
|
+
}
|
|
44
|
+
return key
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def load_checkpoint_if_match(
|
|
48
|
+
ckpt_path: Path,
|
|
49
|
+
resume: bool,
|
|
50
|
+
checkpoint_key: Dict[str, Any],
|
|
51
|
+
) -> Optional[Dict[str, Any]]:
|
|
52
|
+
"""加载匹配的检查点"""
|
|
53
|
+
try:
|
|
54
|
+
if not resume:
|
|
55
|
+
return None
|
|
56
|
+
if not ckpt_path.exists():
|
|
57
|
+
return None
|
|
58
|
+
obj = json.loads(ckpt_path.read_text(encoding="utf-8"))
|
|
59
|
+
if not isinstance(obj, dict):
|
|
60
|
+
return None
|
|
61
|
+
if obj.get("key") != checkpoint_key:
|
|
62
|
+
return None
|
|
63
|
+
return obj
|
|
64
|
+
except Exception:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def atomic_write(path: Path, content: str) -> None:
|
|
69
|
+
"""原子写入文件"""
|
|
70
|
+
try:
|
|
71
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
72
|
+
tmp.write_text(content, encoding="utf-8")
|
|
73
|
+
tmp.replace(path)
|
|
74
|
+
except Exception:
|
|
75
|
+
try:
|
|
76
|
+
path.write_text(content, encoding="utf-8")
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def create_checkpoint_state(
|
|
82
|
+
checkpoint_key: Dict[str, Any],
|
|
83
|
+
eval_counter: int,
|
|
84
|
+
processed_roots: set,
|
|
85
|
+
pruned_dynamic: set,
|
|
86
|
+
selected_roots: list,
|
|
87
|
+
) -> Dict[str, Any]:
|
|
88
|
+
"""创建检查点状态字典"""
|
|
89
|
+
try:
|
|
90
|
+
ts = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
|
|
91
|
+
except Exception:
|
|
92
|
+
ts = ""
|
|
93
|
+
return {
|
|
94
|
+
"key": checkpoint_key,
|
|
95
|
+
"eval_counter": eval_counter,
|
|
96
|
+
"processed_roots": sorted(list(processed_roots)),
|
|
97
|
+
"pruned_dynamic": sorted(list(pruned_dynamic)),
|
|
98
|
+
"selected_roots": [{"fid": fid, "res": res} for fid, res in selected_roots],
|
|
99
|
+
"timestamp": ts,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def periodic_checkpoint_save(
|
|
104
|
+
ckpt_path: Path,
|
|
105
|
+
checkpoint_state: Dict[str, Any],
|
|
106
|
+
eval_counter: int,
|
|
107
|
+
last_ckpt_saved: int,
|
|
108
|
+
checkpoint_interval: int,
|
|
109
|
+
resume: bool,
|
|
110
|
+
) -> int:
|
|
111
|
+
"""
|
|
112
|
+
定期保存检查点。
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
更新后的 last_ckpt_saved 值
|
|
116
|
+
"""
|
|
117
|
+
if not resume:
|
|
118
|
+
return last_ckpt_saved
|
|
119
|
+
try:
|
|
120
|
+
interval = int(checkpoint_interval)
|
|
121
|
+
except Exception:
|
|
122
|
+
interval = DEFAULT_CHECKPOINT_INTERVAL
|
|
123
|
+
need_save = (interval <= 0) or ((eval_counter - last_ckpt_saved) >= interval)
|
|
124
|
+
if not need_save:
|
|
125
|
+
return last_ckpt_saved
|
|
126
|
+
try:
|
|
127
|
+
atomic_write(
|
|
128
|
+
ckpt_path,
|
|
129
|
+
json.dumps(checkpoint_state, ensure_ascii=False, indent=JSON_INDENT),
|
|
130
|
+
)
|
|
131
|
+
return eval_counter
|
|
132
|
+
except Exception:
|
|
133
|
+
return last_ckpt_saved
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""库替换器的 LLM 模型创建和评估模块。"""
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing import Callable
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import List
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from typing import Tuple
|
|
10
|
+
|
|
11
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
12
|
+
|
|
13
|
+
from jarvis.jarvis_c2rust.constants import MAX_LLM_RETRIES
|
|
14
|
+
from jarvis.jarvis_c2rust.library_replacer_prompts import build_subtree_prompt
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_llm_availability() -> tuple[bool, Any, Any, Any]:
|
|
18
|
+
"""检查LLM可用性,返回(是否可用, PlatformRegistry, get_smart_platform_name, get_smart_model_name)
|
|
19
|
+
使用smart平台,适用于代码生成等复杂场景
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
23
|
+
from jarvis.jarvis_utils.config import get_smart_model_name
|
|
24
|
+
from jarvis.jarvis_utils.config import get_smart_platform_name
|
|
25
|
+
|
|
26
|
+
return True, PlatformRegistry, get_smart_platform_name, get_smart_model_name
|
|
27
|
+
except Exception:
|
|
28
|
+
return False, None, None, None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def create_llm_model(
|
|
32
|
+
llm_group: Optional[str],
|
|
33
|
+
disabled_display: str,
|
|
34
|
+
model_available: bool,
|
|
35
|
+
PlatformRegistry: Any,
|
|
36
|
+
get_smart_platform_name: Any,
|
|
37
|
+
get_smart_model_name: Any,
|
|
38
|
+
) -> Optional[Any]:
|
|
39
|
+
"""创建LLM模型,使用smart平台,适用于代码生成等复杂场景"""
|
|
40
|
+
if not model_available:
|
|
41
|
+
return None
|
|
42
|
+
try:
|
|
43
|
+
registry = PlatformRegistry.get_global_platform_registry()
|
|
44
|
+
model = None
|
|
45
|
+
if llm_group:
|
|
46
|
+
try:
|
|
47
|
+
platform_name = get_smart_platform_name(llm_group)
|
|
48
|
+
if platform_name:
|
|
49
|
+
model = registry.create_platform(platform_name)
|
|
50
|
+
except Exception:
|
|
51
|
+
model = None
|
|
52
|
+
if model is None:
|
|
53
|
+
model = registry.get_smart_platform()
|
|
54
|
+
try:
|
|
55
|
+
model.set_model_group(llm_group)
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
if llm_group:
|
|
59
|
+
try:
|
|
60
|
+
mn = get_smart_model_name(llm_group)
|
|
61
|
+
if mn:
|
|
62
|
+
model.set_model_name(mn)
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
model.set_system_prompt(
|
|
66
|
+
"你是资深 C→Rust 迁移专家。任务:给定一个函数及其调用子树(依赖图摘要、函数签名、源码片段),"
|
|
67
|
+
"判断是否可以使用一个或多个成熟的 Rust 库整体替代该子树的功能(允许库内多个 API 协同,允许多个库组合;不允许使用不成熟/不常见库)。"
|
|
68
|
+
"如可替代,请给出 libraries 列表(库名),可选给出代表性 API/模块与实现备注 notes(如何用这些库协作实现)。"
|
|
69
|
+
"输出格式:仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签),字段: replaceable(bool), libraries(list[str]), confidence(float 0..1),可选 library(str,首选主库), api(str) 或 apis(list),notes(str)。"
|
|
70
|
+
)
|
|
71
|
+
return model
|
|
72
|
+
except Exception as e:
|
|
73
|
+
PrettyOutput.auto_print(
|
|
74
|
+
f"⚠️ [c2rust-library] 初始化 LLM 平台失败,将回退为保守策略: {e}"
|
|
75
|
+
)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def parse_agent_json_summary(
|
|
80
|
+
text: str,
|
|
81
|
+
) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
|
|
82
|
+
"""
|
|
83
|
+
解析Agent返回的JSON摘要
|
|
84
|
+
返回(解析结果, 错误信息)
|
|
85
|
+
如果解析成功,返回(data, None)
|
|
86
|
+
如果解析失败,返回(None, 错误信息)
|
|
87
|
+
"""
|
|
88
|
+
if not isinstance(text, str) or not text.strip():
|
|
89
|
+
return None, "摘要文本为空"
|
|
90
|
+
import re as _re
|
|
91
|
+
|
|
92
|
+
from jarvis.jarvis_utils.jsonnet_compat import loads as _json_loads
|
|
93
|
+
|
|
94
|
+
# 提取 <SUMMARY> 块
|
|
95
|
+
m_sum = _re.search(r"<SUMMARY>([\s\S]*?)</SUMMARY>", text, flags=_re.IGNORECASE)
|
|
96
|
+
block = (m_sum.group(1) if m_sum else text).strip()
|
|
97
|
+
|
|
98
|
+
if not block:
|
|
99
|
+
return None, "未找到 <SUMMARY> 或 </SUMMARY> 标签,或标签内容为空"
|
|
100
|
+
|
|
101
|
+
# 直接解析 <SUMMARY> 块内的内容为 JSON
|
|
102
|
+
# jsonnet_compat.loads 会自动处理 markdown 代码块标记(如 ```json5、```json、``` 等)
|
|
103
|
+
try:
|
|
104
|
+
data = _json_loads(block)
|
|
105
|
+
if isinstance(data, dict):
|
|
106
|
+
return data, None
|
|
107
|
+
return None, f"JSON 解析结果不是字典,而是 {type(data).__name__}"
|
|
108
|
+
except Exception as json_err:
|
|
109
|
+
return None, f"JSON 解析失败: {str(json_err)}"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def llm_evaluate_subtree(
|
|
113
|
+
fid: int,
|
|
114
|
+
desc: set,
|
|
115
|
+
by_id: Dict[int, Dict[str, Any]],
|
|
116
|
+
adj_func: Dict[int, List[int]],
|
|
117
|
+
disabled_norm: List[str],
|
|
118
|
+
disabled_display: str,
|
|
119
|
+
model_available: bool,
|
|
120
|
+
new_model_func: Callable[[], Optional[Any]],
|
|
121
|
+
additional_notes: str = "",
|
|
122
|
+
) -> Dict[str, Any]:
|
|
123
|
+
"""使用LLM评估子树是否可替代,支持最多3次重试"""
|
|
124
|
+
if not model_available:
|
|
125
|
+
return {"replaceable": False}
|
|
126
|
+
model = new_model_func()
|
|
127
|
+
if not model:
|
|
128
|
+
return {"replaceable": False}
|
|
129
|
+
|
|
130
|
+
base_prompt = build_subtree_prompt(
|
|
131
|
+
fid, desc, by_id, adj_func, disabled_display, additional_notes
|
|
132
|
+
)
|
|
133
|
+
last_parse_error = None
|
|
134
|
+
|
|
135
|
+
for attempt in range(1, MAX_LLM_RETRIES + 1):
|
|
136
|
+
try:
|
|
137
|
+
# 构建当前尝试的提示词
|
|
138
|
+
if attempt == 1:
|
|
139
|
+
prompt = base_prompt
|
|
140
|
+
else:
|
|
141
|
+
# 重试时包含之前的错误信息
|
|
142
|
+
error_hint = ""
|
|
143
|
+
if last_parse_error:
|
|
144
|
+
error_hint = (
|
|
145
|
+
f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n- {last_parse_error}\n\n"
|
|
146
|
+
+ "请确保输出的JSON格式正确,包括正确的引号、逗号、大括号等。仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签)。"
|
|
147
|
+
)
|
|
148
|
+
prompt = base_prompt + error_hint
|
|
149
|
+
|
|
150
|
+
# 调用LLM
|
|
151
|
+
result = model.chat_until_success(prompt)
|
|
152
|
+
parsed, parse_error = parse_agent_json_summary(result or "")
|
|
153
|
+
|
|
154
|
+
if parse_error:
|
|
155
|
+
# JSON解析失败,记录错误并准备重试
|
|
156
|
+
last_parse_error = parse_error
|
|
157
|
+
PrettyOutput.auto_print(
|
|
158
|
+
f"⚠️ [c2rust-library] 第 {attempt}/{MAX_LLM_RETRIES} 次尝试:JSON解析失败: {parse_error}"
|
|
159
|
+
)
|
|
160
|
+
# 打印原始内容以便调试
|
|
161
|
+
result_text = str(result or "").strip()
|
|
162
|
+
if result_text:
|
|
163
|
+
PrettyOutput.auto_print(
|
|
164
|
+
f"📄 [c2rust-library] 原始LLM响应内容(前1000字符):\n{result_text[:1000]}"
|
|
165
|
+
)
|
|
166
|
+
if len(result_text) > 1000:
|
|
167
|
+
PrettyOutput.auto_print(
|
|
168
|
+
f"📄 [c2rust-library] ... (还有 {len(result_text) - 1000} 个字符未显示)"
|
|
169
|
+
)
|
|
170
|
+
if attempt < MAX_LLM_RETRIES:
|
|
171
|
+
continue # 继续重试
|
|
172
|
+
else:
|
|
173
|
+
# 最后一次尝试也失败,使用默认值
|
|
174
|
+
PrettyOutput.auto_print(
|
|
175
|
+
f"⚠️ [c2rust-library] 重试 {MAX_LLM_RETRIES} 次后JSON解析仍然失败: {parse_error},使用默认值"
|
|
176
|
+
)
|
|
177
|
+
return {"replaceable": False}
|
|
178
|
+
|
|
179
|
+
# 解析成功,检查是否为字典
|
|
180
|
+
if not isinstance(parsed, dict):
|
|
181
|
+
last_parse_error = f"解析结果不是字典,而是 {type(parsed).__name__}"
|
|
182
|
+
PrettyOutput.auto_print(
|
|
183
|
+
f"⚠️ [c2rust-library] 第 {attempt}/{MAX_LLM_RETRIES} 次尝试:{last_parse_error}"
|
|
184
|
+
)
|
|
185
|
+
# 打印解析结果和原始内容以便调试
|
|
186
|
+
PrettyOutput.auto_print(
|
|
187
|
+
f"📊 [c2rust-library] 解析结果类型: {type(parsed).__name__}, 值: {repr(parsed)[:500]}"
|
|
188
|
+
)
|
|
189
|
+
result_text = str(result or "").strip()
|
|
190
|
+
if result_text:
|
|
191
|
+
PrettyOutput.auto_print(
|
|
192
|
+
f"📄 [c2rust-library] 原始LLM响应内容(前1000字符):\n{result_text[:1000]}"
|
|
193
|
+
)
|
|
194
|
+
if attempt < MAX_LLM_RETRIES:
|
|
195
|
+
continue # 继续重试
|
|
196
|
+
else:
|
|
197
|
+
PrettyOutput.auto_print(
|
|
198
|
+
f"🔴 [c2rust-library] 重试 {MAX_LLM_RETRIES} 次后结果格式仍然不正确,视为不可替代。"
|
|
199
|
+
)
|
|
200
|
+
return {"replaceable": False}
|
|
201
|
+
|
|
202
|
+
# 成功解析为字典,处理结果
|
|
203
|
+
rep = bool(parsed.get("replaceable") is True)
|
|
204
|
+
lib = str(parsed.get("library") or "").strip()
|
|
205
|
+
api = str(parsed.get("api") or parsed.get("function") or "").strip()
|
|
206
|
+
apis = parsed.get("apis")
|
|
207
|
+
libs_raw = parsed.get("libraries")
|
|
208
|
+
notes = str(parsed.get("notes") or "").strip()
|
|
209
|
+
# 归一化 libraries
|
|
210
|
+
libraries: List[str] = []
|
|
211
|
+
if isinstance(libs_raw, list):
|
|
212
|
+
libraries = [str(x).strip() for x in libs_raw if str(x).strip()]
|
|
213
|
+
elif isinstance(libs_raw, str):
|
|
214
|
+
libraries = [s.strip() for s in libs_raw.split(",") if s.strip()]
|
|
215
|
+
conf = parsed.get("confidence")
|
|
216
|
+
try:
|
|
217
|
+
conf = float(conf) if conf is not None else 0.0
|
|
218
|
+
except Exception:
|
|
219
|
+
conf = 0.0
|
|
220
|
+
# 不强制要求具体 API 或特定库名;若缺省且存在 library 字段,则纳入 libraries
|
|
221
|
+
if not libraries and lib:
|
|
222
|
+
libraries = [lib]
|
|
223
|
+
|
|
224
|
+
# 禁用库命中时,强制视为不可替代
|
|
225
|
+
if disabled_norm:
|
|
226
|
+
libs_lower = [lib_name.lower() for lib_name in libraries]
|
|
227
|
+
lib_single_lower = lib.lower() if lib else ""
|
|
228
|
+
banned_hit = any(
|
|
229
|
+
lower_lib in disabled_norm for lower_lib in libs_lower
|
|
230
|
+
) or (lib_single_lower and lib_single_lower in disabled_norm)
|
|
231
|
+
if banned_hit:
|
|
232
|
+
rep = False
|
|
233
|
+
warn_libs = (
|
|
234
|
+
", ".join(sorted(set([lib] + libraries)))
|
|
235
|
+
if (libraries or lib)
|
|
236
|
+
else "(未提供库名)"
|
|
237
|
+
)
|
|
238
|
+
root_rec = by_id.get(fid, {})
|
|
239
|
+
root_name = (
|
|
240
|
+
root_rec.get("qualified_name")
|
|
241
|
+
or root_rec.get("name")
|
|
242
|
+
or f"sym_{fid}"
|
|
243
|
+
)
|
|
244
|
+
PrettyOutput.auto_print(
|
|
245
|
+
f"🚫 [c2rust-library] 评估结果包含禁用库,强制判定为不可替代: {root_name} | 命中库: {warn_libs}"
|
|
246
|
+
)
|
|
247
|
+
if notes:
|
|
248
|
+
notes = notes + f" | 禁用库命中: {warn_libs}"
|
|
249
|
+
else:
|
|
250
|
+
notes = f"禁用库命中: {warn_libs}"
|
|
251
|
+
|
|
252
|
+
result_obj: Dict[str, Any] = {
|
|
253
|
+
"replaceable": rep,
|
|
254
|
+
"library": lib,
|
|
255
|
+
"libraries": libraries,
|
|
256
|
+
"api": api,
|
|
257
|
+
"confidence": conf,
|
|
258
|
+
}
|
|
259
|
+
if isinstance(apis, list):
|
|
260
|
+
result_obj["apis"] = apis
|
|
261
|
+
if notes:
|
|
262
|
+
result_obj["notes"] = notes
|
|
263
|
+
|
|
264
|
+
# 成功获取结果,返回
|
|
265
|
+
if attempt > 1:
|
|
266
|
+
PrettyOutput.auto_print(
|
|
267
|
+
f"✅ [c2rust-library] 第 {attempt} 次尝试成功获取评估结果"
|
|
268
|
+
)
|
|
269
|
+
return result_obj
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
# LLM调用异常,记录并准备重试
|
|
273
|
+
last_parse_error = f"LLM调用异常: {str(e)}"
|
|
274
|
+
PrettyOutput.auto_print(
|
|
275
|
+
f"⚠️ [c2rust-library] 第 {attempt}/{MAX_LLM_RETRIES} 次尝试:LLM评估失败: {e}"
|
|
276
|
+
)
|
|
277
|
+
if attempt < MAX_LLM_RETRIES:
|
|
278
|
+
continue # 继续重试
|
|
279
|
+
else:
|
|
280
|
+
# 最后一次尝试也失败,返回默认值
|
|
281
|
+
PrettyOutput.auto_print(
|
|
282
|
+
f"🔴 [c2rust-library] 重试 {MAX_LLM_RETRIES} 次后LLM评估仍然失败: {e},视为不可替代"
|
|
283
|
+
)
|
|
284
|
+
return {"replaceable": False}
|
|
285
|
+
|
|
286
|
+
# 理论上不会到达这里,但作为保险
|
|
287
|
+
return {"replaceable": False}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""库替换器的符号表加载和图构建模块。"""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
from typing import Dict
|
|
8
|
+
from typing import List
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from typing import Set
|
|
11
|
+
|
|
12
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
13
|
+
from jarvis.jarvis_c2rust.scanner import find_root_function_ids
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_symbols(
|
|
17
|
+
sjsonl: Path,
|
|
18
|
+
) -> tuple[
|
|
19
|
+
List[Dict[str, Any]],
|
|
20
|
+
Dict[int, Dict[str, Any]],
|
|
21
|
+
Dict[str, int],
|
|
22
|
+
Set[int],
|
|
23
|
+
Dict[int, List[str]],
|
|
24
|
+
]:
|
|
25
|
+
"""加载符号表,返回(所有记录, id到记录映射, 名称到id映射, 函数id集合, id到引用名称映射)"""
|
|
26
|
+
all_records: List[Dict[str, Any]] = []
|
|
27
|
+
by_id: Dict[int, Dict[str, Any]] = {}
|
|
28
|
+
name_to_id: Dict[str, int] = {}
|
|
29
|
+
func_ids: Set[int] = set()
|
|
30
|
+
id_refs_names: Dict[int, List[str]] = {}
|
|
31
|
+
|
|
32
|
+
with open(sjsonl, "r", encoding="utf-8") as f:
|
|
33
|
+
idx = 0
|
|
34
|
+
for line in f:
|
|
35
|
+
line = line.strip()
|
|
36
|
+
if not line:
|
|
37
|
+
continue
|
|
38
|
+
try:
|
|
39
|
+
obj = json.loads(line)
|
|
40
|
+
except Exception:
|
|
41
|
+
continue
|
|
42
|
+
idx += 1
|
|
43
|
+
fid = int(obj.get("id") or idx)
|
|
44
|
+
obj["id"] = fid
|
|
45
|
+
nm = obj.get("name") or ""
|
|
46
|
+
qn = obj.get("qualified_name") or ""
|
|
47
|
+
cat = obj.get("category") or "" # "function" | "type"
|
|
48
|
+
refs = obj.get("ref")
|
|
49
|
+
if not isinstance(refs, list):
|
|
50
|
+
refs = []
|
|
51
|
+
refs = [r for r in refs if isinstance(r, str) and r]
|
|
52
|
+
|
|
53
|
+
all_records.append(obj)
|
|
54
|
+
by_id[fid] = obj
|
|
55
|
+
id_refs_names[fid] = refs
|
|
56
|
+
if nm:
|
|
57
|
+
name_to_id.setdefault(nm, fid)
|
|
58
|
+
if qn:
|
|
59
|
+
name_to_id.setdefault(qn, fid)
|
|
60
|
+
if cat == "function":
|
|
61
|
+
func_ids.add(fid)
|
|
62
|
+
|
|
63
|
+
return all_records, by_id, name_to_id, func_ids, id_refs_names
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def build_function_graph(
|
|
67
|
+
func_ids: Set[int],
|
|
68
|
+
id_refs_names: Dict[int, List[str]],
|
|
69
|
+
name_to_id: Dict[str, int],
|
|
70
|
+
) -> Dict[int, List[int]]:
|
|
71
|
+
"""构建函数依赖图,返回id到依赖id列表的映射"""
|
|
72
|
+
adj_func: Dict[int, List[int]] = {}
|
|
73
|
+
for fid in func_ids:
|
|
74
|
+
internal: List[int] = []
|
|
75
|
+
for target in id_refs_names.get(fid, []):
|
|
76
|
+
tid = name_to_id.get(target)
|
|
77
|
+
if tid is not None and tid in func_ids and tid != fid:
|
|
78
|
+
internal.append(tid)
|
|
79
|
+
try:
|
|
80
|
+
internal = list(dict.fromkeys(internal))
|
|
81
|
+
except Exception:
|
|
82
|
+
internal = sorted(list(set(internal)))
|
|
83
|
+
adj_func[fid] = internal
|
|
84
|
+
return adj_func
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def build_evaluation_order(
|
|
88
|
+
sjsonl: Path,
|
|
89
|
+
func_ids: Set[int],
|
|
90
|
+
adj_func: Dict[int, List[int]],
|
|
91
|
+
) -> List[int]:
|
|
92
|
+
"""构建评估顺序(广度优先,父先子后)"""
|
|
93
|
+
# 评估队列:从所有无入边函数作为种子开始,按层次遍历整个图,使"父先于子"被评估;
|
|
94
|
+
# 若不存在无入边节点(如强连通环),则回退为全量函数集合。
|
|
95
|
+
try:
|
|
96
|
+
roots_all = find_root_function_ids(sjsonl)
|
|
97
|
+
except Exception:
|
|
98
|
+
roots_all = []
|
|
99
|
+
seeds = [rid for rid in roots_all if rid in func_ids]
|
|
100
|
+
if not seeds:
|
|
101
|
+
seeds = sorted(list(func_ids))
|
|
102
|
+
|
|
103
|
+
visited: Set[int] = set()
|
|
104
|
+
order: List[int] = []
|
|
105
|
+
q: List[int] = list(seeds)
|
|
106
|
+
qi = 0
|
|
107
|
+
while qi < len(q):
|
|
108
|
+
u = q[qi]
|
|
109
|
+
qi += 1
|
|
110
|
+
if u in visited or u not in func_ids:
|
|
111
|
+
continue
|
|
112
|
+
visited.add(u)
|
|
113
|
+
order.append(u)
|
|
114
|
+
for v in adj_func.get(u, []):
|
|
115
|
+
if v not in visited and v in func_ids:
|
|
116
|
+
q.append(v)
|
|
117
|
+
# 若存在未覆盖的孤立/循环组件,补充其节点(确保每个函数节点都将被作为"候选根"参与评估)
|
|
118
|
+
if len(visited) < len(func_ids):
|
|
119
|
+
leftovers = [fid for fid in sorted(func_ids) if fid not in visited]
|
|
120
|
+
order.extend(leftovers)
|
|
121
|
+
|
|
122
|
+
return order
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def collect_descendants(
|
|
126
|
+
start: int,
|
|
127
|
+
adj_func: Dict[int, List[int]],
|
|
128
|
+
desc_cache: Dict[int, Set[int]],
|
|
129
|
+
) -> Set[int]:
|
|
130
|
+
"""收集从start开始的所有后代节点(使用缓存)"""
|
|
131
|
+
if start in desc_cache:
|
|
132
|
+
return desc_cache[start]
|
|
133
|
+
visited: Set[int] = set()
|
|
134
|
+
stack: List[int] = [start]
|
|
135
|
+
visited.add(start)
|
|
136
|
+
while stack:
|
|
137
|
+
u = stack.pop()
|
|
138
|
+
for v in adj_func.get(u, []):
|
|
139
|
+
if v not in visited:
|
|
140
|
+
visited.add(v)
|
|
141
|
+
stack.append(v)
|
|
142
|
+
desc_cache[start] = visited
|
|
143
|
+
return visited
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def process_candidate_scope(
|
|
147
|
+
candidates: Optional[List[str]],
|
|
148
|
+
all_records: List[Dict[str, Any]],
|
|
149
|
+
root_funcs: List[int],
|
|
150
|
+
func_ids: Set[int],
|
|
151
|
+
adj_func: Dict[int, List[int]],
|
|
152
|
+
desc_cache: Dict[int, Set[int]],
|
|
153
|
+
) -> tuple[List[int], Set[int]]:
|
|
154
|
+
"""处理候选根和作用域,返回(过滤后的根函数列表, 不可达函数集合)"""
|
|
155
|
+
|
|
156
|
+
scope_unreachable_funcs: Set[int] = set()
|
|
157
|
+
if not candidates:
|
|
158
|
+
return root_funcs, scope_unreachable_funcs
|
|
159
|
+
|
|
160
|
+
cand_ids: Set[int] = set()
|
|
161
|
+
# 支持重载:同名/同限定名可能对应多个函数ID,需全部纳入候选
|
|
162
|
+
key_set = set(candidates)
|
|
163
|
+
for rec in all_records:
|
|
164
|
+
if (rec.get("category") or "") != "function":
|
|
165
|
+
continue
|
|
166
|
+
nm = rec.get("name") or ""
|
|
167
|
+
qn = rec.get("qualified_name") or ""
|
|
168
|
+
if nm in key_set or qn in key_set:
|
|
169
|
+
try:
|
|
170
|
+
rec_id = rec.get("id")
|
|
171
|
+
if rec_id is not None:
|
|
172
|
+
cand_ids.add(int(rec_id))
|
|
173
|
+
except Exception:
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
if not cand_ids:
|
|
177
|
+
return root_funcs, scope_unreachable_funcs
|
|
178
|
+
|
|
179
|
+
filtered_roots = [rid for rid in root_funcs if rid in cand_ids]
|
|
180
|
+
# 计算从候选根出发的可达函数集合(含根)
|
|
181
|
+
reachable_all: Set[int] = set()
|
|
182
|
+
for rid in filtered_roots:
|
|
183
|
+
reachable_all.update(collect_descendants(rid, adj_func, desc_cache))
|
|
184
|
+
# 不可达函数(仅限函数类别)将被直接删除
|
|
185
|
+
scope_unreachable_funcs = {fid for fid in func_ids if fid not in reachable_all}
|
|
186
|
+
if scope_unreachable_funcs:
|
|
187
|
+
PrettyOutput.auto_print(
|
|
188
|
+
f"⚠️ [c2rust-library] 根据根列表,标记不可达函数删除: {len(scope_unreachable_funcs)} 个"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return filtered_roots, scope_unreachable_funcs
|