codeboarding 0.9.5__tar.gz → 0.9.6__tar.gz
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.
- {codeboarding-0.9.5/codeboarding.egg-info → codeboarding-0.9.6}/PKG-INFO +16 -20
- {codeboarding-0.9.5 → codeboarding-0.9.6}/PYPI.md +14 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/README.md +12 -2
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/abstraction_agent.py +27 -20
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/agent.py +92 -156
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/agent_responses.py +98 -30
- codeboarding-0.9.6/agents/change_status.py +9 -0
- codeboarding-0.9.6/agents/cluster_methods_mixin.py +601 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/constants.py +1 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/details_agent.py +82 -25
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/llm_config.py +3 -3
- codeboarding-0.9.6/agents/meta_agent.py +67 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/planner_agent.py +19 -7
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/__init__.py +0 -3
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/abstract_prompt_factory.py +0 -4
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/claude_prompts.py +66 -59
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/deepseek_prompts.py +66 -62
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/gemini_flash_prompts.py +66 -59
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/glm_prompts.py +70 -65
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/gpt_prompts.py +66 -59
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/kimi_prompts.py +65 -65
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/prompts/prompt_factory.py +0 -4
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_cfg.py +6 -5
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_file_structure.py +1 -2
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_source.py +10 -30
- codeboarding-0.9.6/agents/validation.py +630 -0
- codeboarding-0.9.6/caching/__init__.py +12 -0
- codeboarding-0.9.6/caching/cache.py +310 -0
- codeboarding-0.9.6/caching/details_cache.py +58 -0
- codeboarding-0.9.6/caching/meta_cache.py +111 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6/codeboarding.egg-info}/PKG-INFO +16 -20
- {codeboarding-0.9.5 → codeboarding-0.9.6}/codeboarding.egg-info/SOURCES.txt +10 -16
- {codeboarding-0.9.5 → codeboarding-0.9.6}/codeboarding.egg-info/requires.txt +0 -17
- {codeboarding-0.9.5 → codeboarding-0.9.6}/codeboarding.egg-info/top_level.txt +1 -0
- codeboarding-0.9.6/constants.py +14 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/__init__.py +1 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/analysis_json.py +237 -82
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/diagram_generator.py +79 -110
- codeboarding-0.9.6/diagram_analysis/incremental_types.py +70 -0
- codeboarding-0.9.6/diagram_analysis/incremental_updater.py +325 -0
- {codeboarding-0.9.5/diagram_analysis/incremental → codeboarding-0.9.6/diagram_analysis}/io_utils.py +26 -7
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/manifest.py +3 -2
- codeboarding-0.9.6/diagram_analysis/run_context.py +57 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/github_action.py +5 -4
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/function_size.py +2 -2
- codeboarding-0.9.6/health_main.py +151 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/install.py +71 -50
- codeboarding-0.9.6/logging_config.py +136 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/main.py +68 -30
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/paths.py +3 -4
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/markdown.py +17 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/mdx.py +24 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/sphinx.py +22 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/pyproject.toml +4 -22
- {codeboarding-0.9.5 → codeboarding-0.9.6}/repo_utils/__init__.py +1 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/repo_utils/change_detector.py +12 -7
- codeboarding-0.9.6/repo_utils/ignore.py +305 -0
- codeboarding-0.9.6/repo_utils/method_diff.py +177 -0
- codeboarding-0.9.6/static_analyzer/__init__.py +524 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/analysis_cache.py +80 -20
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/analysis_result.py +200 -19
- codeboarding-0.9.6/static_analyzer/cluster_relations.py +175 -0
- codeboarding-0.9.6/static_analyzer/constants.py +164 -0
- codeboarding-0.9.6/static_analyzer/graph.py +619 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/incremental_orchestrator.py +194 -221
- codeboarding-0.9.6/static_analyzer/node.py +67 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/reference_resolve_mixin.py +46 -25
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/scanner.py +9 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_github_action.py +6 -9
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_install.py +83 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_logging_config.py +50 -33
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_main.py +48 -7
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_windows_compatibility.py +1 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_windows_encoding.py +1 -1
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tool_registry.py +24 -4
- {codeboarding-0.9.5 → codeboarding-0.9.6}/utils.py +36 -0
- codeboarding-0.9.5/agents/cluster_methods_mixin.py +0 -284
- codeboarding-0.9.5/agents/meta_agent.py +0 -105
- codeboarding-0.9.5/agents/validation.py +0 -383
- codeboarding-0.9.5/caching/__init__.py +0 -4
- codeboarding-0.9.5/caching/cache.py +0 -29
- codeboarding-0.9.5/caching/meta_cache.py +0 -227
- codeboarding-0.9.5/diagram_analysis/incremental/__init__.py +0 -63
- codeboarding-0.9.5/diagram_analysis/incremental/component_checker.py +0 -236
- codeboarding-0.9.5/diagram_analysis/incremental/file_manager.py +0 -217
- codeboarding-0.9.5/diagram_analysis/incremental/impact_analyzer.py +0 -238
- codeboarding-0.9.5/diagram_analysis/incremental/models.py +0 -72
- codeboarding-0.9.5/diagram_analysis/incremental/path_patching.py +0 -164
- codeboarding-0.9.5/diagram_analysis/incremental/reexpansion.py +0 -166
- codeboarding-0.9.5/diagram_analysis/incremental/scoped_analysis.py +0 -227
- codeboarding-0.9.5/diagram_analysis/incremental/updater.py +0 -464
- codeboarding-0.9.5/diagram_analysis/incremental/validation.py +0 -48
- codeboarding-0.9.5/health_main.py +0 -122
- codeboarding-0.9.5/logging_config.py +0 -119
- codeboarding-0.9.5/repo_utils/ignore.py +0 -341
- codeboarding-0.9.5/static_analyzer/__init__.py +0 -475
- codeboarding-0.9.5/static_analyzer/constants.py +0 -166
- codeboarding-0.9.5/static_analyzer/graph.py +0 -746
- codeboarding-0.9.5/static_analyzer/lsp_client/client.py +0 -1750
- codeboarding-0.9.5/static_analyzer/lsp_client/java_client.py +0 -517
- codeboarding-0.9.5/static_analyzer/lsp_client/language_settings.py +0 -97
- codeboarding-0.9.5/static_analyzer/lsp_client/typescript_client.py +0 -235
- codeboarding-0.9.5/tests/test_incremental_analyzer.py +0 -204
- {codeboarding-0.9.5 → codeboarding-0.9.6}/LICENSE +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/dependency_discovery.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/base.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_file.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_git_diff.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/agents/tools/toolkit.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/core/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/core/plugin_loader.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/core/protocols.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/core/registry.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/diagram_analysis/version.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/duckdb_crud.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/cohesion.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/coupling.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/god_class.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/inheritance.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/instability.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/config.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/constants.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/models.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/health/runner.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/callbacks.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/context.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/mixin.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/stats.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/monitoring/writers.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/html.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/output_generators/html_template.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/repo_utils/errors.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/repo_utils/git_diff.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/setup.cfg +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/cluster_change_analyzer.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/cluster_helpers.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/git_diff_analyzer.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_tool_registry.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_user_config.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/user_config.py +0 -0
- {codeboarding-0.9.5 → codeboarding-0.9.6}/vscode_constants.py +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeboarding
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.6
|
|
4
4
|
Summary: Interactive Diagrams for Code
|
|
5
5
|
Author: CodeBoarding Team
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Repository, https://github.com/CodeBoarding/CodeBoarding
|
|
8
8
|
Project-URL: Documentation, https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/overview.md
|
|
9
9
|
Keywords: code-understanding,code-visualization,code-analysis,diagrams,documentation,llm,static-analysis,mermaid,onboarding
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
14
|
Classifier: Topic :: Software Development :: Documentation
|
|
@@ -17,23 +16,14 @@ Classifier: Topic :: Software Development :: Code Generators
|
|
|
17
16
|
Requires-Python: <3.14,>=3.12
|
|
18
17
|
Description-Content-Type: text/markdown
|
|
19
18
|
License-File: LICENSE
|
|
20
|
-
Requires-Dist: asgiref>=3.9
|
|
21
|
-
Requires-Dist: async-timeout>=4.0
|
|
22
19
|
Requires-Dist: docker>=7.1
|
|
23
20
|
Requires-Dist: dotenv>=0.9
|
|
24
21
|
Requires-Dist: duckdb>=1.3
|
|
25
22
|
Requires-Dist: dulwich>=0.22
|
|
26
|
-
Requires-Dist: email-validator>=2.2
|
|
27
|
-
Requires-Dist: exceptiongroup>=1.2
|
|
28
23
|
Requires-Dist: fastapi>=0.115
|
|
29
24
|
Requires-Dist: filelock>=3.12
|
|
30
25
|
Requires-Dist: gitpython>=3.1
|
|
31
26
|
Requires-Dist: google-api-core>=2.10
|
|
32
|
-
Requires-Dist: google-genai>=1.10
|
|
33
|
-
Requires-Dist: gql>=3.5
|
|
34
|
-
Requires-Dist: injector>=0.21
|
|
35
|
-
Requires-Dist: ipykernel>=6.29
|
|
36
|
-
Requires-Dist: isort>=6.0
|
|
37
27
|
Requires-Dist: jsonschema>=4.25
|
|
38
28
|
Requires-Dist: langchain>=1.2
|
|
39
29
|
Requires-Dist: langchain-anthropic>=1.3
|
|
@@ -46,21 +36,13 @@ Requires-Dist: langchain-openai>=1.1
|
|
|
46
36
|
Requires-Dist: markdown>=3.8
|
|
47
37
|
Requires-Dist: markdown-it-py>=3.0
|
|
48
38
|
Requires-Dist: markitdown>=0.1
|
|
49
|
-
Requires-Dist: matplotlib>=3.10
|
|
50
39
|
Requires-Dist: networkx>=3.4
|
|
51
|
-
Requires-Dist: openpyxl>=3.1
|
|
52
|
-
Requires-Dist: pandas>=2.2
|
|
53
40
|
Requires-Dist: pathspec>=0.12
|
|
54
|
-
Requires-Dist: pydot>=3.0
|
|
55
41
|
Requires-Dist: pyyaml>=6.0
|
|
56
42
|
Requires-Dist: regex>=2024.11
|
|
57
43
|
Requires-Dist: rich>=12.6
|
|
58
|
-
Requires-Dist: seaborn>=0.13
|
|
59
|
-
Requires-Dist: tomli>=2.2
|
|
60
44
|
Requires-Dist: trustcall>=0.0.39
|
|
61
|
-
Requires-Dist: typer>=0.9
|
|
62
45
|
Requires-Dist: uvicorn>=0.23
|
|
63
|
-
Requires-Dist: wcwidth>=0.2.13
|
|
64
46
|
Provides-Extra: dev
|
|
65
47
|
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
66
48
|
Requires-Dist: pytest-cov>=7.0; extra == "dev"
|
|
@@ -91,12 +73,26 @@ Dynamic: license-file
|
|
|
91
73
|
|
|
92
74
|
---
|
|
93
75
|
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- **Python 3.12 or 3.13** — other versions are currently not supported.
|
|
79
|
+
|
|
94
80
|
## Installation
|
|
95
81
|
|
|
82
|
+
The recommended way to install the CLI is with [pipx](https://pipx.pypa.io), which automatically creates an isolated environment:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pipx install codeboarding --python python3.12
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Alternatively, install into an existing virtual environment with pip:
|
|
89
|
+
|
|
96
90
|
```bash
|
|
97
91
|
pip install codeboarding
|
|
98
92
|
```
|
|
99
93
|
|
|
94
|
+
> Installing into the global Python environment with `pip` is not recommended — it can cause dependency conflicts and will fail if the system Python is not 3.12 or 3.13.
|
|
95
|
+
|
|
100
96
|
Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
|
|
101
97
|
|
|
102
98
|
```bash
|
|
@@ -14,12 +14,26 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- **Python 3.12 or 3.13** — other versions are currently not supported.
|
|
20
|
+
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
23
|
+
The recommended way to install the CLI is with [pipx](https://pipx.pypa.io), which automatically creates an isolated environment:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pipx install codeboarding --python python3.12
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Alternatively, install into an existing virtual environment with pip:
|
|
30
|
+
|
|
19
31
|
```bash
|
|
20
32
|
pip install codeboarding
|
|
21
33
|
```
|
|
22
34
|
|
|
35
|
+
> Installing into the global Python environment with `pip` is not recommended — it can cause dependency conflicts and will fail if the system Python is not 3.12 or 3.13.
|
|
36
|
+
|
|
23
37
|
Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
|
|
24
38
|
|
|
25
39
|
```bash
|
|
@@ -6,7 +6,7 @@ CodeBoarding gives developers and coding agents a visual map of a codebase. It c
|
|
|
6
6
|
|
|
7
7
|
[Website](https://codeboarding.org) · [Open VSX extension](https://open-vsx.org/extension/CodeBoarding/codeboarding) · [Explore examples](https://codeboarding.org/diagrams) · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) · [GitHub Action](https://github.com/marketplace/actions/codeboarding-diagram-first-documentation) ·[Discord](https://discord.gg/T5zHTJYFuy)
|
|
8
8
|
|
|
9
|
-
[](https://open-vsx.org/extension/CodeBoarding/codeboarding)
|
|
10
10
|
|
|
11
11
|
Install the extension from Open VSX.
|
|
12
12
|
|
|
@@ -73,6 +73,16 @@ python main.py --local /path/to/repo
|
|
|
73
73
|
|
|
74
74
|
### Use the packaged CLI
|
|
75
75
|
|
|
76
|
+
Requires **Python 3.12 or 3.13**. The recommended install method is [pipx](https://pipx.pypa.io), which keeps the CLI in its own isolated environment:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pipx install codeboarding --python python3.12
|
|
80
|
+
codeboarding-setup
|
|
81
|
+
codeboarding --local /path/to/repo
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or, if you prefer pip, install into a virtual environment (not the global Python):
|
|
85
|
+
|
|
76
86
|
```bash
|
|
77
87
|
pip install codeboarding
|
|
78
88
|
codeboarding-setup
|
|
@@ -117,7 +127,7 @@ python main.py --local ./my-project --depth-level 2
|
|
|
117
127
|
python main.py --local ./my-project --incremental
|
|
118
128
|
|
|
119
129
|
# Update a single component by ID
|
|
120
|
-
python main.py --local ./my-project --partial-component-id "
|
|
130
|
+
python main.py --local ./my-project --partial-component-id "1.2"
|
|
121
131
|
|
|
122
132
|
# Analyze a remote GitHub repository
|
|
123
133
|
python main.py https://github.com/pytorch/pytorch
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
from langchain_core.prompts import PromptTemplate
|
|
5
4
|
from langchain_core.language_models import BaseChatModel
|
|
5
|
+
from langchain_core.prompts import PromptTemplate
|
|
6
6
|
|
|
7
7
|
from agents.agent import CodeBoardingAgent
|
|
8
8
|
from agents.agent_responses import (
|
|
@@ -11,24 +11,26 @@ from agents.agent_responses import (
|
|
|
11
11
|
MetaAnalysisInsights,
|
|
12
12
|
assign_component_ids,
|
|
13
13
|
)
|
|
14
|
+
from agents.cluster_methods_mixin import ClusterMethodsMixin
|
|
14
15
|
from agents.prompts import (
|
|
15
|
-
get_system_message,
|
|
16
16
|
get_cluster_grouping_message,
|
|
17
17
|
get_final_analysis_message,
|
|
18
|
+
get_system_message,
|
|
18
19
|
)
|
|
19
|
-
from agents.cluster_methods_mixin import ClusterMethodsMixin
|
|
20
20
|
from agents.validation import (
|
|
21
21
|
ValidationContext,
|
|
22
22
|
validate_cluster_coverage,
|
|
23
|
-
|
|
23
|
+
validate_group_name_coverage,
|
|
24
24
|
validate_key_entities,
|
|
25
|
-
validate_cluster_ids_populated,
|
|
26
25
|
validate_relation_component_names,
|
|
27
26
|
)
|
|
28
27
|
from monitoring import trace
|
|
29
28
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
29
|
+
from static_analyzer.cluster_helpers import (
|
|
30
|
+
build_all_cluster_results,
|
|
31
|
+
get_all_cluster_ids,
|
|
32
|
+
)
|
|
30
33
|
from static_analyzer.graph import ClusterResult
|
|
31
|
-
from static_analyzer.cluster_helpers import build_all_cluster_results, get_all_cluster_ids
|
|
32
34
|
|
|
33
35
|
logger = logging.getLogger(__name__)
|
|
34
36
|
|
|
@@ -86,19 +88,20 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
86
88
|
cluster_results=cluster_results,
|
|
87
89
|
expected_cluster_ids=get_all_cluster_ids(cluster_results),
|
|
88
90
|
),
|
|
91
|
+
max_validation_attempts=3,
|
|
89
92
|
)
|
|
90
93
|
return cluster_analysis
|
|
91
94
|
|
|
92
95
|
@trace
|
|
93
96
|
def step_final_analysis(
|
|
94
|
-
self,
|
|
97
|
+
self, llm_cluster_analysis: ClusterAnalysis, cluster_results: dict[str, ClusterResult]
|
|
95
98
|
) -> AnalysisInsights:
|
|
96
99
|
logger.info(f"[AbstractionAgent] Generating final analysis for: {self.project_name}")
|
|
97
100
|
|
|
98
101
|
meta_context_str = self.meta_context.llm_str() if self.meta_context else "No project context available."
|
|
99
102
|
project_type = self.meta_context.project_type if self.meta_context else "unknown"
|
|
100
103
|
|
|
101
|
-
cluster_str =
|
|
104
|
+
cluster_str = llm_cluster_analysis.llm_str() if llm_cluster_analysis else "No cluster analysis available."
|
|
102
105
|
|
|
103
106
|
prompt = self.prompts["final_analysis"].format(
|
|
104
107
|
project_name=self.project_name,
|
|
@@ -111,6 +114,8 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
111
114
|
context = ValidationContext(
|
|
112
115
|
cluster_results=cluster_results,
|
|
113
116
|
cfg_graphs={lang: self.static_analysis.get_cfg(lang) for lang in self.static_analysis.get_languages()},
|
|
117
|
+
static_analysis=self.static_analysis,
|
|
118
|
+
llm_cluster_analysis=llm_cluster_analysis,
|
|
114
119
|
)
|
|
115
120
|
|
|
116
121
|
return self._validation_invoke(
|
|
@@ -118,11 +123,11 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
118
123
|
AnalysisInsights,
|
|
119
124
|
validators=[
|
|
120
125
|
validate_relation_component_names,
|
|
121
|
-
|
|
126
|
+
validate_group_name_coverage,
|
|
122
127
|
validate_key_entities,
|
|
123
|
-
validate_cluster_ids_populated,
|
|
124
128
|
],
|
|
125
129
|
context=context,
|
|
130
|
+
max_validation_attempts=3,
|
|
126
131
|
)
|
|
127
132
|
|
|
128
133
|
def run(self):
|
|
@@ -134,17 +139,19 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
134
139
|
|
|
135
140
|
# Step 2: Generate abstract components from grouped clusters
|
|
136
141
|
analysis = self.step_final_analysis(cluster_analysis, cluster_results)
|
|
137
|
-
# Step 3:
|
|
138
|
-
|
|
139
|
-
# Step 4:
|
|
140
|
-
self.
|
|
141
|
-
# Step 5:
|
|
142
|
+
# Step 3: Assign hierarchical component IDs ("1", "2", "3", ...)
|
|
143
|
+
assign_component_ids(analysis)
|
|
144
|
+
# Step 4: Resolve cluster IDs deterministically from group names
|
|
145
|
+
self._resolve_cluster_ids_from_groups(analysis, cluster_analysis)
|
|
146
|
+
# Step 5: Populate file_methods deterministically from cluster results + orphan assignment
|
|
147
|
+
self.populate_file_methods(analysis, cluster_results)
|
|
148
|
+
|
|
149
|
+
# Step 6: Build static inter-component relations from CFG edges
|
|
150
|
+
self.build_static_relations(analysis)
|
|
151
|
+
|
|
152
|
+
# Step 7: Fix source code reference lines (resolves reference_file paths for key_entities)
|
|
142
153
|
analysis = self.fix_source_code_reference_lines(analysis)
|
|
143
|
-
# Step
|
|
154
|
+
# Step 8: Ensure unique key entities across components
|
|
144
155
|
self._ensure_unique_key_entities(analysis)
|
|
145
|
-
# Step 7: Ensure unique file assignments across components
|
|
146
|
-
self._ensure_unique_file_assignments(analysis)
|
|
147
|
-
# Step 8: Assign deterministic component IDs
|
|
148
|
-
assign_component_ids(analysis)
|
|
149
156
|
|
|
150
157
|
return analysis, cluster_results
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
import os
|
|
4
3
|
import time
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
|
|
@@ -15,14 +14,10 @@ from langgraph.graph.state import CompiledStateGraph
|
|
|
15
14
|
from pydantic import ValidationError
|
|
16
15
|
from trustcall import create_extractor
|
|
17
16
|
|
|
18
|
-
from agents.
|
|
19
|
-
from agents.prompts import (
|
|
20
|
-
get_unassigned_files_classification_message,
|
|
21
|
-
get_validation_feedback_message,
|
|
22
|
-
)
|
|
17
|
+
from agents.prompts import get_validation_feedback_message
|
|
23
18
|
from agents.tools.base import RepoContext
|
|
24
19
|
from agents.tools.toolkit import CodeBoardingToolkit
|
|
25
|
-
from agents.validation import
|
|
20
|
+
from agents.validation import ValidationResult, score_validation_results, VALIDATOR_WEIGHTS, DEFAULT_VALIDATOR_WEIGHT
|
|
26
21
|
from monitoring.mixin import MonitoringMixin
|
|
27
22
|
from repo_utils.ignore import RepoIgnoreManager
|
|
28
23
|
from agents.llm_config import MONITORING_CALLBACK
|
|
@@ -217,55 +212,123 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
217
212
|
except Empty:
|
|
218
213
|
raise RuntimeError("Agent invocation completed but no result was returned")
|
|
219
214
|
|
|
220
|
-
def _parse_invoke(self, prompt, type):
|
|
215
|
+
def _parse_invoke(self, prompt: str, type: type):
|
|
221
216
|
response = self._invoke(prompt)
|
|
222
217
|
assert isinstance(response, str), f"Expected a string as response type got {response}"
|
|
223
218
|
return self._parse_response(prompt, response, type)
|
|
224
219
|
|
|
220
|
+
def _score_result(self, result, validators: list, context) -> tuple[float, list[tuple[float, str]]]:
|
|
221
|
+
"""Run all validators on a result and return (score, prioritized_feedback).
|
|
222
|
+
|
|
223
|
+
The score is computed using weighted validators where coverage-related
|
|
224
|
+
validators (cluster coverage, group name coverage) carry significantly
|
|
225
|
+
more weight than others.
|
|
226
|
+
|
|
227
|
+
Feedback messages are returned as (weight, message) tuples sorted by
|
|
228
|
+
weight descending, so that the LLM focuses on the most critical issues
|
|
229
|
+
(cluster/group coverage) before lower-priority ones (key entities).
|
|
230
|
+
"""
|
|
231
|
+
validator_results: list[tuple] = []
|
|
232
|
+
weighted_feedback: list[tuple[float, str]] = []
|
|
233
|
+
for validator in validators:
|
|
234
|
+
validator_result: ValidationResult = validator(result, context)
|
|
235
|
+
validator_results.append((validator, validator_result))
|
|
236
|
+
if not validator_result.is_valid:
|
|
237
|
+
weight = VALIDATOR_WEIGHTS.get(validator.__name__, DEFAULT_VALIDATOR_WEIGHT)
|
|
238
|
+
for msg in validator_result.feedback_messages:
|
|
239
|
+
weighted_feedback.append((weight, msg))
|
|
240
|
+
|
|
241
|
+
# Sort by weight descending so critical feedback comes first
|
|
242
|
+
weighted_feedback.sort(key=lambda x: x[0], reverse=True)
|
|
243
|
+
|
|
244
|
+
score = score_validation_results(validator_results)
|
|
245
|
+
return score, weighted_feedback
|
|
246
|
+
|
|
225
247
|
def _validation_invoke(
|
|
226
|
-
self, prompt: str, return_type: type, validators: list, context,
|
|
248
|
+
self, prompt: str, return_type: type, validators: list, context, max_validation_attempts: int = 1
|
|
227
249
|
):
|
|
228
250
|
"""
|
|
229
|
-
Invoke LLM with validation and
|
|
251
|
+
Invoke LLM with validation, feedback loop, and best-of-N selection.
|
|
252
|
+
|
|
253
|
+
Each attempt (initial + retries) is scored using weighted validators.
|
|
254
|
+
Coverage validators (validate_cluster_coverage, validate_group_name_coverage)
|
|
255
|
+
are weighted ~2x higher than other validators, so the selection strongly
|
|
256
|
+
favours results with complete coverage.
|
|
257
|
+
|
|
258
|
+
If any attempt scores perfectly (all validators pass), it is returned
|
|
259
|
+
immediately. Otherwise the highest-scoring result across all attempts is
|
|
260
|
+
returned.
|
|
230
261
|
|
|
231
262
|
Args:
|
|
232
263
|
prompt: The original prompt
|
|
233
264
|
return_type: Pydantic type to parse into
|
|
234
265
|
validators: List of validation functions to run
|
|
235
266
|
context: ValidationContext with data needed for validation
|
|
236
|
-
|
|
267
|
+
max_validation_attempts: Maximum validation attempts (initial attempt included).
|
|
268
|
+
Retries occur only when this value is greater than 1. (default: 1)
|
|
237
269
|
|
|
238
270
|
Returns:
|
|
239
|
-
|
|
271
|
+
The highest-scoring result of return_type across all attempts
|
|
240
272
|
"""
|
|
273
|
+
# Compute the maximum possible score so we can detect a perfect result
|
|
274
|
+
max_possible_score = sum(VALIDATOR_WEIGHTS.get(v.__name__, DEFAULT_VALIDATOR_WEIGHT) for v in validators)
|
|
275
|
+
|
|
241
276
|
result = self._parse_invoke(prompt, return_type)
|
|
242
277
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
278
|
+
# Track the best candidate across all attempts
|
|
279
|
+
best_result = result
|
|
280
|
+
best_score = -1.0
|
|
281
|
+
|
|
282
|
+
# Weight threshold: validators above this are tagged [CRITICAL]
|
|
283
|
+
critical_threshold = 10.0
|
|
284
|
+
|
|
285
|
+
for attempt in range(1, max_validation_attempts + 1):
|
|
286
|
+
score, weighted_feedback = self._score_result(result, validators, context)
|
|
287
|
+
|
|
288
|
+
logger.info(
|
|
289
|
+
f"[Validation] Attempt {attempt}/{max_validation_attempts} "
|
|
290
|
+
f"score: {score}/{max_possible_score} "
|
|
291
|
+
f"({len(weighted_feedback)} issue(s))"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if score > best_score:
|
|
295
|
+
best_score = score
|
|
296
|
+
best_result = result
|
|
297
|
+
|
|
298
|
+
# Perfect score — return immediately
|
|
299
|
+
if score >= max_possible_score:
|
|
300
|
+
logger.info(f"[Validation] Perfect score on attempt {attempt}, returning result")
|
|
301
|
+
return result
|
|
250
302
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
303
|
+
# On the last attempt, don't retry — just fall through to return best
|
|
304
|
+
if attempt == max_validation_attempts:
|
|
305
|
+
logger.warning(
|
|
306
|
+
f"[Validation] Final attempt reached. Best score: {best_score}/{max_possible_score}. "
|
|
307
|
+
f"Returning best result."
|
|
308
|
+
)
|
|
309
|
+
break
|
|
310
|
+
|
|
311
|
+
# Build feedback prompt for the next attempt.
|
|
312
|
+
# Feedback is sorted by weight; high-weight items are tagged [CRITICAL].
|
|
313
|
+
feedback_lines: list[str] = []
|
|
314
|
+
for weight, msg in weighted_feedback:
|
|
315
|
+
tag = "CRITICAL" if weight >= critical_threshold else "Secondary"
|
|
316
|
+
feedback_lines.append(f"- [{tag}] {msg}")
|
|
254
317
|
|
|
255
|
-
# Build feedback prompt using the prompt factory
|
|
256
318
|
feedback_template = get_validation_feedback_message()
|
|
257
319
|
feedback_prompt = feedback_template.format(
|
|
258
320
|
original_output=result.llm_str(),
|
|
259
|
-
feedback_list="\n".join(
|
|
321
|
+
feedback_list="\n".join(feedback_lines),
|
|
260
322
|
original_prompt=prompt,
|
|
261
323
|
)
|
|
262
324
|
|
|
263
325
|
logger.info(
|
|
264
|
-
f"[Validation]
|
|
326
|
+
f"[Validation] Preparing attempt {attempt + 1}/{max_validation_attempts} "
|
|
327
|
+
f"with {len(weighted_feedback)} feedback items"
|
|
265
328
|
)
|
|
266
329
|
result = self._parse_invoke(feedback_prompt, return_type)
|
|
267
330
|
|
|
268
|
-
return
|
|
331
|
+
return best_result
|
|
269
332
|
|
|
270
333
|
def _parse_response(self, prompt, response, return_type, max_retries=5, attempt=0):
|
|
271
334
|
if attempt >= max_retries:
|
|
@@ -305,6 +368,9 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
305
368
|
# try to parse with the json parser if possible
|
|
306
369
|
logger.warning(f"IndexError while parsing response (attempt {attempt + 1}/{max_retries}): {e}")
|
|
307
370
|
return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
|
|
371
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
372
|
+
logger.warning(f"Parse error (attempt {attempt + 1}/{max_retries}): {e}")
|
|
373
|
+
return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
|
|
308
374
|
except ResourceExhausted as e:
|
|
309
375
|
# Parsing uses exponential backoff for rate limits
|
|
310
376
|
if attempt < max_retries - 1:
|
|
@@ -344,133 +410,3 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
344
410
|
except:
|
|
345
411
|
pass
|
|
346
412
|
raise ValueError(f"Couldn't parse {message_content}")
|
|
347
|
-
|
|
348
|
-
def classify_files(self, analysis: AnalysisInsights, cluster_results: dict, scope_files: list[str]) -> None:
|
|
349
|
-
"""
|
|
350
|
-
Two-pass file assignment for AnalysisInsights:
|
|
351
|
-
1. Deterministic: assign files from cluster_ids and key_entities
|
|
352
|
-
2. LLM-based: classify remaining unassigned files
|
|
353
|
-
|
|
354
|
-
Args:
|
|
355
|
-
analysis: AnalysisInsights object to classify files for
|
|
356
|
-
cluster_results: Dict mapping language -> ClusterResult (for the relevant scope)
|
|
357
|
-
scope_files: List of file paths to limit classification scope.
|
|
358
|
-
|
|
359
|
-
Requires self to be a mixin with ClusterMethodsMixin for helper methods.
|
|
360
|
-
"""
|
|
361
|
-
for comp in analysis.components:
|
|
362
|
-
# Deterministic assignment (uses mixin methods)
|
|
363
|
-
self._assign_files_to_component(comp, cluster_results) # type: ignore[attr-defined]
|
|
364
|
-
self._classify_unassigned_files_llm(analysis, scope_files)
|
|
365
|
-
self._log_unclassified_files_count(analysis, scope_files)
|
|
366
|
-
|
|
367
|
-
def _classify_unassigned_files_llm(self, analysis: AnalysisInsights, scope_files: list[str]) -> None:
|
|
368
|
-
"""
|
|
369
|
-
Classify files from the scope files that weren't assigned to any component.
|
|
370
|
-
Uses a single LLM call to classify all unassigned files.
|
|
371
|
-
Args:
|
|
372
|
-
analysis: AnalysisInsights object
|
|
373
|
-
scope_files: List of file paths to limit classification scope.
|
|
374
|
-
"""
|
|
375
|
-
# Get unassigned files using the helper method
|
|
376
|
-
unassigned_files = self._get_unassigned_files(analysis, scope_files)
|
|
377
|
-
|
|
378
|
-
if not unassigned_files:
|
|
379
|
-
logger.info("[Agent] All files already assigned, skipping LLM classification")
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
logger.info(f"[Agent] Found {len(unassigned_files)} unassigned files, using LLM classification")
|
|
383
|
-
|
|
384
|
-
# 4. Build component summary for LLM using llm_str()
|
|
385
|
-
valid_components = [comp for comp in analysis.components if comp.name != "Unclassified"]
|
|
386
|
-
components_summary = "\n\n".join(comp.llm_str() for comp in valid_components)
|
|
387
|
-
component_map = {comp.name: comp for comp in valid_components}
|
|
388
|
-
|
|
389
|
-
# 5. Classify all unassigned files with LLM
|
|
390
|
-
classifications: list[FileClassification] = self._classify_unassigned_files_with_llm(
|
|
391
|
-
unassigned_files, components_summary, analysis
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
# 6. Append successfully classified files to components
|
|
395
|
-
for fc in classifications:
|
|
396
|
-
if fc.component_name in component_map:
|
|
397
|
-
comp = component_map[fc.component_name]
|
|
398
|
-
if fc.file_path not in comp.assigned_files:
|
|
399
|
-
comp.assigned_files.append(fc.file_path)
|
|
400
|
-
logger.debug(f"[Agent] Assigned {fc.file_path} to {fc.component_name}")
|
|
401
|
-
else:
|
|
402
|
-
logger.warning(
|
|
403
|
-
f"[Agent] Invalid component name '{fc.component_name}' for file {fc.file_path}, skipping"
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
logger.info(f"[Agent] File classification complete: {len(classifications)} files classified")
|
|
407
|
-
|
|
408
|
-
def _get_unassigned_files(self, analysis: AnalysisInsights, scope_files: list[str]) -> list[str]:
|
|
409
|
-
"""
|
|
410
|
-
Check which files remain unassigned after classification.
|
|
411
|
-
Args:
|
|
412
|
-
analysis: AnalysisInsights object with classified components
|
|
413
|
-
scope_files: List of file paths to limit the scope.
|
|
414
|
-
Returns:
|
|
415
|
-
List of file paths that are still unassigned
|
|
416
|
-
"""
|
|
417
|
-
# 1. Gather all assigned files
|
|
418
|
-
assigned_files = set()
|
|
419
|
-
for comp in analysis.components:
|
|
420
|
-
for f in comp.assigned_files:
|
|
421
|
-
abs_path = os.path.join(self.repo_dir, f) if not os.path.isabs(f) else f
|
|
422
|
-
assigned_files.add(os.path.relpath(abs_path, self.repo_dir))
|
|
423
|
-
|
|
424
|
-
# 2. Get files to consider for classification
|
|
425
|
-
# If scope_files is provided (e.g., DetailsAgent), use those
|
|
426
|
-
# Otherwise use all source files from static_analysis (e.g., AbstractionAgent)
|
|
427
|
-
all_files = set()
|
|
428
|
-
for file_path in scope_files:
|
|
429
|
-
file_path_str = str(file_path)
|
|
430
|
-
rel_path = os.path.relpath(file_path_str, self.repo_dir) if os.path.isabs(file_path_str) else file_path_str
|
|
431
|
-
all_files.add(rel_path)
|
|
432
|
-
|
|
433
|
-
# 3. Return unassigned files
|
|
434
|
-
return sorted(all_files - assigned_files)
|
|
435
|
-
|
|
436
|
-
def _log_unclassified_files_count(self, analysis: AnalysisInsights, scope_files: list[str]) -> None:
|
|
437
|
-
"""
|
|
438
|
-
Log how many files remain unclassified within the analysis.
|
|
439
|
-
|
|
440
|
-
Args:
|
|
441
|
-
analysis: AnalysisInsights object with classified components
|
|
442
|
-
scope_files: List of file paths which are expected to be within the analysis.
|
|
443
|
-
"""
|
|
444
|
-
unassigned = self._get_unassigned_files(analysis, scope_files)
|
|
445
|
-
if unassigned:
|
|
446
|
-
logger.warning(f"[Agent] {len(unassigned)} files have not been classified successfully: {unassigned}")
|
|
447
|
-
else:
|
|
448
|
-
logger.info("[Agent] All files have been classified successfully")
|
|
449
|
-
|
|
450
|
-
def _classify_unassigned_files_with_llm(
|
|
451
|
-
self, unassigned_files: list[str], components_summary: str, analysis: AnalysisInsights
|
|
452
|
-
) -> list[FileClassification]:
|
|
453
|
-
"""
|
|
454
|
-
Classify unassigned files using LLM with validation.
|
|
455
|
-
Returns list of FileClassification objects.
|
|
456
|
-
"""
|
|
457
|
-
|
|
458
|
-
prompt = PromptTemplate(
|
|
459
|
-
template=get_unassigned_files_classification_message(), input_variables=["unassigned_files", "components"]
|
|
460
|
-
).format(unassigned_files="\n".join(unassigned_files), components=components_summary)
|
|
461
|
-
|
|
462
|
-
# Get valid component names from the components_summary
|
|
463
|
-
# Parse component names from the summary (components have format "**Component:** `ComponentName`")
|
|
464
|
-
valid_component_names = set([comp.name for comp in analysis.components])
|
|
465
|
-
|
|
466
|
-
# Build validation context
|
|
467
|
-
context = ValidationContext(
|
|
468
|
-
expected_files=set(unassigned_files),
|
|
469
|
-
valid_component_names=valid_component_names,
|
|
470
|
-
repo_dir=str(self.repo_dir),
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
file_classifications = self._validation_invoke(
|
|
474
|
-
prompt, ComponentFiles, validators=[validate_file_classifications], context=context
|
|
475
|
-
)
|
|
476
|
-
return file_classifications.file_paths
|