codeboarding 0.12.4__tar.gz → 0.13.0__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.12.4/codeboarding.egg-info → codeboarding-0.13.0}/PKG-INFO +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/README.md +8 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/abstraction_agent.py +103 -33
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/agent.py +104 -40
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/agent_responses.py +364 -55
- codeboarding-0.13.0/agents/cluster_ids.py +49 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/cluster_methods_mixin.py +154 -86
- codeboarding-0.13.0/agents/content_hash.py +113 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/details_agent.py +130 -36
- codeboarding-0.13.0/agents/file_index_models.py +104 -0
- codeboarding-0.13.0/agents/incremental_agent.py +707 -0
- codeboarding-0.13.0/agents/incremental_planning_agent.py +295 -0
- codeboarding-0.13.0/agents/incremental_results.py +31 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/llm_config.py +17 -0
- codeboarding-0.13.0/agents/llm_errors.py +125 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/__init__.py +8 -0
- codeboarding-0.13.0/agents/prompts/abstract_prompt_factory.py +114 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/claude_prompts.py +63 -39
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/deepseek_prompts.py +58 -41
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/gemini_flash_prompts.py +55 -42
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/glm_prompts.py +62 -42
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/gpt_prompts.py +57 -42
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/kimi_prompts.py +53 -42
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/prompts/prompt_factory.py +28 -0
- codeboarding-0.13.0/agents/relation_edges.py +52 -0
- codeboarding-0.13.0/agents/repair.py +206 -0
- codeboarding-0.13.0/agents/scope_ids.py +1 -0
- codeboarding-0.13.0/agents/scope_operations.py +35 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/__init__.py +1 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/base.py +7 -0
- codeboarding-0.13.0/agents/tools/component_bridge_edges.py +84 -0
- codeboarding-0.13.0/agents/tools/list_git_changes.py +27 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_cfg.py +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/toolkit.py +17 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/validation.py +273 -193
- {codeboarding-0.12.4 → codeboarding-0.13.0/codeboarding.egg-info}/PKG-INFO +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding.egg-info/SOURCES.txt +21 -3
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/bootstrap.py +3 -12
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/commands/full_analysis.py +9 -12
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/commands/incremental_analysis.py +7 -45
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/commands/partial_analysis.py +7 -6
- codeboarding-0.13.0/codeboarding_cli/view_instructions.py +41 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/analysis.py +65 -88
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/rendering.py +50 -3
- {codeboarding-0.12.4 → codeboarding-0.13.0}/constants.py +7 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/__init__.py +5 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/analysis_json.py +141 -56
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/cluster_delta.py +334 -68
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/diagram_generator.py +471 -218
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/exceptions.py +10 -0
- codeboarding-0.13.0/diagram_analysis/file_index.py +80 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/io_utils.py +84 -113
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/run_context.py +9 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/github_action.py +2 -2
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/config.py +2 -1
- codeboarding-0.13.0/health_main.py +152 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/install.py +22 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/main.py +21 -7
- {codeboarding-0.12.4 → codeboarding-0.13.0}/pyproject.toml +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/repo_utils/__init__.py +8 -15
- {codeboarding-0.12.4 → codeboarding-0.13.0}/repo_utils/change_detector.py +23 -11
- codeboarding-0.13.0/repo_utils/fingerprint_diff.py +71 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/repo_utils/git_ops.py +2 -134
- {codeboarding-0.12.4 → codeboarding-0.13.0}/repo_utils/ignore.py +6 -4
- codeboarding-0.13.0/repo_utils/path_utils.py +41 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/__init__.py +103 -35
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/analysis_cache.py +9 -6
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/analysis_result.py +10 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/cluster_helpers.py +3 -1
- codeboarding-0.13.0/static_analyzer/cluster_relations.py +324 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/constants.py +3 -3
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/java_adapter.py +2 -2
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/rust_adapter.py +43 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/call_graph_builder.py +26 -8
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/edge_builder.py +86 -61
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/language_adapter.py +4 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/lsp_client.py +13 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/models.py +34 -4
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/result_converter.py +16 -1
- codeboarding-0.13.0/static_analyzer/engine/source_inspector.py +365 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/utils.py +19 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/graph.py +109 -41
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/incremental_orchestrator.py +83 -48
- codeboarding-0.13.0/static_analyzer/internal_references.py +72 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/java_utils.py +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/language_results.py +25 -4
- codeboarding-0.13.0/static_analyzer/method_cluster_paths.py +56 -0
- codeboarding-0.13.0/static_analyzer/reference_resolver.py +481 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/scanner.py +82 -9
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_github_action.py +12 -11
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_install.py +33 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_main.py +64 -74
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_pyproject_packages.py +1 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_tool_registry.py +4 -4
- codeboarding-0.13.0/tests/test_view_instructions.py +32 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_windows_compatibility.py +30 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tool_registry/paths.py +20 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tool_registry/registry.py +3 -5
- {codeboarding-0.12.4 → codeboarding-0.13.0}/user_config.py +3 -1
- {codeboarding-0.12.4 → codeboarding-0.13.0}/utils.py +5 -21
- codeboarding-0.12.4/agents/incremental_agent.py +0 -787
- codeboarding-0.12.4/agents/prompts/abstract_prompt_factory.py +0 -67
- codeboarding-0.12.4/diagram_analysis/version.py +0 -6
- codeboarding-0.12.4/repo_utils/diff_parser.py +0 -290
- codeboarding-0.12.4/static_analyzer/cluster_relations.py +0 -175
- codeboarding-0.12.4/static_analyzer/engine/source_inspector.py +0 -203
- codeboarding-0.12.4/static_analyzer/reference_resolve_mixin.py +0 -191
- {codeboarding-0.12.4 → codeboarding-0.13.0}/LICENSE +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/PYPI.md +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/change_status.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/cluster_budget.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/constants.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/dependency_discovery.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/meta_agent.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/model_capabilities.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/planner_agent.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/retry.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_file.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_file_structure.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_source.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/caching/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/caching/cache.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/caching/details_cache.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/caching/meta_cache.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/orchestration.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/core/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/core/plugin_loader.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/core/protocols.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/core/registry.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/cluster_snapshot.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/cohesion.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/coupling.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/function_size.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/god_class.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/inheritance.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/instability.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/models.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/health/runner.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/logging_config.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/callbacks.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/context.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/mixin.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/paths.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/stats.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/monitoring/writers.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/html.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/html_template.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/markdown.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/mdx.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/output_generators/sphinx.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/repo_utils/errors.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/setup.cfg +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/cfg_skip_planner.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/csharp_config_scanner.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/dotnet_sdk.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/csharp_adapter.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/go_adapter.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/php_adapter.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/python_adapter.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/lsp_constants.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/protocols.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/engine/symbol_table.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/leiden_utils.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/node.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/telemetry/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/telemetry/device_id.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/telemetry/events.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/telemetry/schemas.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/telemetry/service.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_cli_parser.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_logging_config.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_registry_coverage.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_telemetry_events.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_user_config.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tests/test_windows_encoding.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tool_registry/__init__.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tool_registry/installers.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/tool_registry/manifest.py +0 -0
- {codeboarding-0.12.4 → codeboarding-0.13.0}/vscode_constants.py +0 -0
|
@@ -137,6 +137,14 @@ python main.py partial --local ./my-project --component-id "1.2"
|
|
|
137
137
|
python main.py full https://github.com/pytorch/pytorch
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
> **Incremental needs a baseline.** `incremental` diffs the working tree against the previous
|
|
141
|
+
> analysis in `.codeboarding/` (`analysis.json` + `fingerprint.json`). That baseline can live
|
|
142
|
+
> purely locally — a prior `full`/`incremental` run in the same output dir is enough. Commit
|
|
143
|
+
> `.codeboarding/` only if you want the baseline to travel with the branch (so a teammate or a
|
|
144
|
+
> fresh checkout can run incremental too). With no baseline at all — or one that predates content
|
|
145
|
+
> versioning — `incremental` fails fast with "run a full analysis first" rather than silently
|
|
146
|
+
> doing a full run.
|
|
147
|
+
|
|
140
148
|
## Where to use it
|
|
141
149
|
|
|
142
150
|
- [CLI](https://github.com/CodeBoarding/CodeBoarding) for local analysis, automation, and CI workflows.
|
|
@@ -7,22 +7,31 @@ from langchain_core.prompts import PromptTemplate
|
|
|
7
7
|
from agents.agent import CodeBoardingAgent
|
|
8
8
|
from agents.agent_responses import (
|
|
9
9
|
AnalysisInsights,
|
|
10
|
+
ComponentApiSurfaces,
|
|
11
|
+
ComponentArchitecture,
|
|
12
|
+
ComponentRelations,
|
|
10
13
|
ClusterAnalysis,
|
|
11
14
|
MetaAnalysisInsights,
|
|
12
15
|
assign_component_ids,
|
|
16
|
+
assign_relation_ids,
|
|
13
17
|
)
|
|
14
18
|
from agents.cluster_methods_mixin import ClusterMethodsMixin
|
|
15
19
|
from agents.prompts import (
|
|
16
20
|
get_cluster_grouping_message,
|
|
17
21
|
get_final_analysis_message,
|
|
22
|
+
get_api_surfaces_message,
|
|
23
|
+
get_relation_analysis_message,
|
|
18
24
|
get_system_message,
|
|
25
|
+
format_project_system_message,
|
|
19
26
|
)
|
|
27
|
+
from agents.relation_edges import index_relation_endpoints
|
|
28
|
+
from agents.repair import ComponentRepairContext, repair_component_group_names, repair_key_entities
|
|
20
29
|
from agents.validation import (
|
|
21
30
|
ValidationContext,
|
|
22
31
|
validate_cluster_coverage,
|
|
23
32
|
validate_group_name_coverage,
|
|
24
33
|
validate_key_entities,
|
|
25
|
-
|
|
34
|
+
validate_relations,
|
|
26
35
|
)
|
|
27
36
|
from monitoring import trace
|
|
28
37
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
@@ -45,7 +54,8 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
45
54
|
agent_llm: BaseChatModel,
|
|
46
55
|
parsing_llm: BaseChatModel,
|
|
47
56
|
):
|
|
48
|
-
|
|
57
|
+
system_message = format_project_system_message(get_system_message(), project_name, meta_context)
|
|
58
|
+
super().__init__(repo_dir, static_analysis, system_message, agent_llm, parsing_llm)
|
|
49
59
|
|
|
50
60
|
self.project_name = project_name
|
|
51
61
|
self.meta_context = meta_context
|
|
@@ -53,11 +63,26 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
53
63
|
self.prompts = {
|
|
54
64
|
"group_clusters": PromptTemplate(
|
|
55
65
|
template=get_cluster_grouping_message(),
|
|
56
|
-
input_variables=["
|
|
66
|
+
input_variables=["cfg_clusters"],
|
|
57
67
|
),
|
|
58
68
|
"final_analysis": PromptTemplate(
|
|
59
69
|
template=get_final_analysis_message(),
|
|
60
|
-
input_variables=["
|
|
70
|
+
input_variables=["cluster_analysis"],
|
|
71
|
+
),
|
|
72
|
+
"api_surfaces": PromptTemplate(
|
|
73
|
+
template=get_api_surfaces_message(),
|
|
74
|
+
input_variables=[
|
|
75
|
+
"component_summaries",
|
|
76
|
+
"static_call_evidence",
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
"relation_analysis": PromptTemplate(
|
|
80
|
+
template=get_relation_analysis_message(),
|
|
81
|
+
input_variables=[
|
|
82
|
+
"component_summaries",
|
|
83
|
+
"api_surfaces",
|
|
84
|
+
"static_call_evidence",
|
|
85
|
+
],
|
|
61
86
|
),
|
|
62
87
|
}
|
|
63
88
|
|
|
@@ -65,9 +90,6 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
65
90
|
def step_clusters_grouping(self, cluster_results: dict[str, ClusterResult]) -> ClusterAnalysis:
|
|
66
91
|
logger.info(f"[AbstractionAgent] Grouping CFG clusters for: {self.project_name}")
|
|
67
92
|
|
|
68
|
-
meta_context_str = self.meta_context.llm_str() if self.meta_context else "No project context available."
|
|
69
|
-
project_type = self.meta_context.project_type if self.meta_context else "unknown"
|
|
70
|
-
|
|
71
93
|
programming_langs = self.static_analysis.get_languages()
|
|
72
94
|
|
|
73
95
|
# Measure everything that wraps cfg_clusters (system message + rendered
|
|
@@ -75,10 +97,7 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
75
97
|
# the input window before budgeting the cluster string.
|
|
76
98
|
overhead_chars = len(str(self.system_message.content)) + len(
|
|
77
99
|
self.prompts["group_clusters"].format(
|
|
78
|
-
project_name=self.project_name,
|
|
79
100
|
cfg_clusters="",
|
|
80
|
-
meta_context=meta_context_str,
|
|
81
|
-
project_type=project_type,
|
|
82
101
|
)
|
|
83
102
|
)
|
|
84
103
|
cluster_str = self._build_cluster_string(
|
|
@@ -86,17 +105,14 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
86
105
|
)
|
|
87
106
|
|
|
88
107
|
prompt = self.prompts["group_clusters"].format(
|
|
89
|
-
project_name=self.project_name,
|
|
90
108
|
cfg_clusters=cluster_str,
|
|
91
|
-
meta_context=meta_context_str,
|
|
92
|
-
project_type=project_type,
|
|
93
109
|
)
|
|
94
110
|
|
|
95
|
-
cluster_analysis = self.
|
|
111
|
+
cluster_analysis = self._invoke_validate(
|
|
96
112
|
prompt,
|
|
97
113
|
ClusterAnalysis,
|
|
98
114
|
validators=[validate_cluster_coverage],
|
|
99
|
-
|
|
115
|
+
validation_context=ValidationContext(
|
|
100
116
|
cluster_results=cluster_results,
|
|
101
117
|
expected_cluster_ids=get_all_cluster_ids(cluster_results),
|
|
102
118
|
),
|
|
@@ -108,20 +124,14 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
108
124
|
def step_final_analysis(
|
|
109
125
|
self, llm_cluster_analysis: ClusterAnalysis, cluster_results: dict[str, ClusterResult]
|
|
110
126
|
) -> AnalysisInsights:
|
|
111
|
-
logger.info(f"[AbstractionAgent] Generating final analysis for: {self.project_name}")
|
|
112
|
-
|
|
113
|
-
meta_context_str = self.meta_context.llm_str() if self.meta_context else "No project context available."
|
|
114
|
-
project_type = self.meta_context.project_type if self.meta_context else "unknown"
|
|
127
|
+
logger.info(f"[AbstractionAgent] Generating final component analysis for: {self.project_name}")
|
|
115
128
|
|
|
116
129
|
cluster_str = llm_cluster_analysis.llm_str() if llm_cluster_analysis else "No cluster analysis available."
|
|
117
130
|
|
|
118
131
|
group_names = [cc.name for cc in llm_cluster_analysis.cluster_components] if llm_cluster_analysis else []
|
|
119
132
|
|
|
120
133
|
prompt = self.prompts["final_analysis"].format(
|
|
121
|
-
project_name=self.project_name,
|
|
122
134
|
cluster_analysis=cluster_str,
|
|
123
|
-
meta_context=meta_context_str,
|
|
124
|
-
project_type=project_type,
|
|
125
135
|
)
|
|
126
136
|
|
|
127
137
|
if group_names:
|
|
@@ -130,25 +140,80 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
130
140
|
f"Every one of these names must appear in exactly one component's source_group_names: {group_names}\n"
|
|
131
141
|
)
|
|
132
142
|
|
|
133
|
-
# Build validation context with CFG graphs for edge checking
|
|
134
143
|
context = ValidationContext(
|
|
135
144
|
cluster_results=cluster_results,
|
|
136
|
-
cfg_graphs={lang: self.static_analysis.get_cfg(lang) for lang in self.static_analysis.get_languages()},
|
|
137
145
|
static_analysis=self.static_analysis,
|
|
138
146
|
llm_cluster_analysis=llm_cluster_analysis,
|
|
139
147
|
)
|
|
140
148
|
|
|
141
|
-
|
|
149
|
+
architecture = self._invoke_repair_validate(
|
|
142
150
|
prompt,
|
|
143
|
-
|
|
151
|
+
ComponentArchitecture,
|
|
152
|
+
repairs=[repair_component_group_names, repair_key_entities],
|
|
144
153
|
validators=[
|
|
145
|
-
validate_relation_component_names,
|
|
146
154
|
validate_group_name_coverage,
|
|
147
155
|
validate_key_entities,
|
|
148
156
|
],
|
|
149
|
-
|
|
157
|
+
repair_context=ComponentRepairContext(
|
|
158
|
+
reference_resolver=self.reference_resolver,
|
|
159
|
+
cluster_results=cluster_results,
|
|
160
|
+
llm_cluster_analysis=llm_cluster_analysis,
|
|
161
|
+
),
|
|
162
|
+
validation_context=context,
|
|
163
|
+
max_validation_attempts=3,
|
|
164
|
+
)
|
|
165
|
+
return AnalysisInsights(
|
|
166
|
+
description=architecture.description,
|
|
167
|
+
components=architecture.components,
|
|
168
|
+
components_relations=[],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
@trace
|
|
172
|
+
def step_api_surfaces(self, analysis: AnalysisInsights) -> ComponentApiSurfaces:
|
|
173
|
+
logger.info(f"[AbstractionAgent] Analyzing component API surfaces for: {self.project_name}")
|
|
174
|
+
static_call_evidence = self.build_scope_cfg_string(analysis)
|
|
175
|
+
prompt = self.prompts["api_surfaces"].format(
|
|
176
|
+
component_summaries=analysis.llm_str(),
|
|
177
|
+
static_call_evidence=static_call_evidence,
|
|
178
|
+
)
|
|
179
|
+
return self._parse_invoke(prompt, ComponentApiSurfaces)
|
|
180
|
+
|
|
181
|
+
@trace
|
|
182
|
+
def step_relation_analysis(
|
|
183
|
+
self,
|
|
184
|
+
analysis: AnalysisInsights,
|
|
185
|
+
api_surfaces: ComponentApiSurfaces,
|
|
186
|
+
cluster_analysis: ClusterAnalysis,
|
|
187
|
+
cluster_results: dict[str, ClusterResult],
|
|
188
|
+
) -> None:
|
|
189
|
+
logger.info(f"[AbstractionAgent] Discovering component relations for: {self.project_name}")
|
|
190
|
+
static_call_evidence = self.build_scope_cfg_string(analysis)
|
|
191
|
+
cfg_graphs = self.static_analysis.available_cfgs()
|
|
192
|
+
self.toolkit.context.cluster_analysis = cluster_analysis
|
|
193
|
+
self.toolkit.context.cluster_results = cluster_results
|
|
194
|
+
self.toolkit.context.cfg_graphs = cfg_graphs
|
|
195
|
+
prompt = self.prompts["relation_analysis"].format(
|
|
196
|
+
component_summaries=analysis.llm_str(),
|
|
197
|
+
api_surfaces=api_surfaces.llm_str(),
|
|
198
|
+
static_call_evidence=static_call_evidence,
|
|
199
|
+
)
|
|
200
|
+
relation_result = self._invoke_validate(
|
|
201
|
+
prompt,
|
|
202
|
+
ComponentRelations,
|
|
203
|
+
validators=[validate_relations],
|
|
204
|
+
validation_context=ValidationContext(
|
|
205
|
+
cluster_results=cluster_results,
|
|
206
|
+
cfg_graphs=cfg_graphs,
|
|
207
|
+
repo_dir=str(self.repo_dir),
|
|
208
|
+
static_analysis=self.static_analysis,
|
|
209
|
+
llm_cluster_analysis=cluster_analysis,
|
|
210
|
+
components=analysis.components,
|
|
211
|
+
),
|
|
150
212
|
max_validation_attempts=3,
|
|
151
213
|
)
|
|
214
|
+
analysis.components_relations = relation_result.components_relations
|
|
215
|
+
assign_relation_ids(analysis)
|
|
216
|
+
self.build_static_relations(analysis)
|
|
152
217
|
|
|
153
218
|
def run(self):
|
|
154
219
|
# Build full cluster results dict for all languages ONCE
|
|
@@ -166,12 +231,17 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
166
231
|
# Step 5: Populate file_methods deterministically from cluster results + orphan assignment
|
|
167
232
|
self.populate_file_methods(analysis, cluster_results)
|
|
168
233
|
|
|
169
|
-
# Step 6:
|
|
170
|
-
self.
|
|
234
|
+
# Step 6: Analyze component API surfaces
|
|
235
|
+
api_surfaces = self.step_api_surfaces(analysis)
|
|
236
|
+
|
|
237
|
+
# Step 7: Discover relations from API surfaces and attach deterministic all_edges
|
|
238
|
+
self.step_relation_analysis(analysis, api_surfaces, cluster_analysis, cluster_results)
|
|
171
239
|
|
|
172
|
-
# Step
|
|
173
|
-
analysis = self.fix_source_code_reference_lines(analysis)
|
|
174
|
-
# Step
|
|
240
|
+
# Step 8: Fix source code reference lines (resolves reference_file paths for key_entities and key_edges)
|
|
241
|
+
analysis = self.reference_resolver.fix_source_code_reference_lines(analysis)
|
|
242
|
+
# Step 9: Index relation endpoints after reference resolution
|
|
243
|
+
index_relation_endpoints(analysis, self.repo_dir)
|
|
244
|
+
# Step 10: Ensure unique key entities across components
|
|
175
245
|
self._ensure_unique_key_entities(analysis)
|
|
176
246
|
|
|
177
247
|
return analysis, cluster_results
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
from collections.abc import Callable
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from typing import Protocol, TypeVar
|
|
4
6
|
|
|
5
7
|
from google.api_core.exceptions import ResourceExhausted
|
|
6
8
|
from langchain_core.exceptions import OutputParserException
|
|
@@ -21,18 +23,43 @@ from agents.validation import ValidationResult, score_validation_results, VALIDA
|
|
|
21
23
|
from monitoring.mixin import MonitoringMixin
|
|
22
24
|
from repo_utils.ignore import RepoIgnoreManager
|
|
23
25
|
from agents.agent_responses import LLMBaseModel
|
|
24
|
-
from agents.llm_config import MONITORING_CALLBACK
|
|
26
|
+
from agents.llm_config import MONITORING_CALLBACK, current_provider_key_context
|
|
27
|
+
from agents.llm_errors import detect_auth_error
|
|
25
28
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
26
|
-
from static_analyzer.
|
|
29
|
+
from static_analyzer.reference_resolver import StaticReferenceResolver
|
|
27
30
|
|
|
28
31
|
logger = logging.getLogger(__name__)
|
|
29
32
|
|
|
33
|
+
ParseResultT = TypeVar("ParseResultT")
|
|
34
|
+
ResultT = TypeVar("ResultT", bound="RepairValidationResult")
|
|
35
|
+
RepairContextT = TypeVar("RepairContextT")
|
|
36
|
+
ValidationContextT = TypeVar("ValidationContextT")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RepairValidationResult(Protocol):
|
|
40
|
+
def llm_str(self) -> str: ...
|
|
41
|
+
|
|
30
42
|
|
|
31
43
|
class EmptyExtractorMessageError(ValueError):
|
|
32
44
|
"""Raised when extractor returns an empty message payload."""
|
|
33
45
|
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
def _raise_if_auth_error(exc: Exception) -> None:
|
|
48
|
+
"""Re-raise *exc* as a typed :class:`LLMAuthError` when it's a rejected key.
|
|
49
|
+
|
|
50
|
+
Shared by the retry classifiers in ``_invoke`` and ``_parse_response``: a
|
|
51
|
+
401 is permanent for the run, so raising here propagates it terminally (the
|
|
52
|
+
retry loop never sees a RETRY decision) and the CLI can surface an
|
|
53
|
+
actionable message. Non-auth exceptions fall through untouched.
|
|
54
|
+
"""
|
|
55
|
+
provider, key_tail = current_provider_key_context()
|
|
56
|
+
auth_error = detect_auth_error(exc, provider=provider, key_tail=key_tail)
|
|
57
|
+
if auth_error is not None:
|
|
58
|
+
logger.error("LLM auth failure — not retrying: %s", auth_error)
|
|
59
|
+
raise auth_error from exc
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class CodeBoardingAgent(MonitoringMixin):
|
|
36
63
|
def __init__(
|
|
37
64
|
self,
|
|
38
65
|
repo_dir: Path,
|
|
@@ -41,11 +68,12 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
41
68
|
agent_llm: BaseChatModel,
|
|
42
69
|
parsing_llm: BaseChatModel,
|
|
43
70
|
):
|
|
44
|
-
ReferenceResolverMixin.__init__(self, repo_dir, static_analysis)
|
|
45
71
|
MonitoringMixin.__init__(self)
|
|
46
72
|
self.parsing_llm = parsing_llm
|
|
47
73
|
self.agent_llm = agent_llm
|
|
48
74
|
self.repo_dir = repo_dir
|
|
75
|
+
self.static_analysis = static_analysis
|
|
76
|
+
self.reference_resolver = StaticReferenceResolver(repo_dir, static_analysis)
|
|
49
77
|
self.ignore_manager = RepoIgnoreManager(repo_dir)
|
|
50
78
|
|
|
51
79
|
context = RepoContext(repo_dir=repo_dir, ignore_manager=self.ignore_manager, static_analysis=static_analysis)
|
|
@@ -55,7 +83,6 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
55
83
|
model=agent_llm,
|
|
56
84
|
tools=self.toolkit.get_agent_tools(),
|
|
57
85
|
)
|
|
58
|
-
self.static_analysis = static_analysis
|
|
59
86
|
self.system_message = SystemMessage(content=system_message)
|
|
60
87
|
|
|
61
88
|
@property
|
|
@@ -82,6 +109,10 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
82
109
|
def read_method_invocations_tool(self):
|
|
83
110
|
return self.toolkit.read_method_invocations
|
|
84
111
|
|
|
112
|
+
@property
|
|
113
|
+
def component_bridge_edges_tool(self):
|
|
114
|
+
return self.toolkit.component_bridge_edges
|
|
115
|
+
|
|
85
116
|
@property
|
|
86
117
|
def read_file_tool(self):
|
|
87
118
|
return self.toolkit.read_file
|
|
@@ -132,6 +163,7 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
132
163
|
return "" # unreachable for AIMessage but satisfies typing
|
|
133
164
|
|
|
134
165
|
def classify(exc: Exception, attempt: int) -> RetryDecision:
|
|
166
|
+
_raise_if_auth_error(exc)
|
|
135
167
|
if getattr(exc, "status_code", None) == 404:
|
|
136
168
|
logger.error(f"Permanent HTTP 404 — not retrying: {type(exc).__name__}: {exc}")
|
|
137
169
|
return RetryDecision(action=RetryAction.GIVE_UP)
|
|
@@ -201,12 +233,33 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
201
233
|
except Empty:
|
|
202
234
|
raise RuntimeError("Agent invocation completed but no result was returned")
|
|
203
235
|
|
|
204
|
-
def _parse_invoke(
|
|
236
|
+
def _parse_invoke(
|
|
237
|
+
self,
|
|
238
|
+
prompt: str,
|
|
239
|
+
return_type: type[ParseResultT],
|
|
240
|
+
include_hidden: bool = False,
|
|
241
|
+
) -> ParseResultT:
|
|
205
242
|
response = self._invoke(prompt)
|
|
206
243
|
assert isinstance(response, str), f"Expected a string as response type got {response}"
|
|
207
|
-
return self._parse_response(prompt, response,
|
|
244
|
+
return self._parse_response(prompt, response, return_type, include_hidden=include_hidden)
|
|
208
245
|
|
|
209
|
-
def
|
|
246
|
+
def _repair_result(
|
|
247
|
+
self,
|
|
248
|
+
result: ResultT,
|
|
249
|
+
repairs: list[Callable[[ResultT, RepairContextT], None]],
|
|
250
|
+
repair_context: RepairContextT,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Apply deterministic repairs to one parsed candidate."""
|
|
253
|
+
for repair in repairs:
|
|
254
|
+
logger.info("[Repair] Applying %s", repair.__name__)
|
|
255
|
+
repair(result, repair_context)
|
|
256
|
+
|
|
257
|
+
def _score_result(
|
|
258
|
+
self,
|
|
259
|
+
result: ResultT,
|
|
260
|
+
validators: list[Callable[[ResultT, ValidationContextT], ValidationResult]],
|
|
261
|
+
validation_context: ValidationContextT,
|
|
262
|
+
) -> tuple[float, list[tuple[float, str]]]:
|
|
210
263
|
"""Run all validators on a result and return (score, prioritized_feedback).
|
|
211
264
|
|
|
212
265
|
The score is computed using weighted validators where coverage-related
|
|
@@ -217,10 +270,10 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
217
270
|
weight descending, so that the LLM focuses on the most critical issues
|
|
218
271
|
(cluster/group coverage) before lower-priority ones (key entities).
|
|
219
272
|
"""
|
|
220
|
-
validator_results: list[tuple] = []
|
|
273
|
+
validator_results: list[tuple[Callable[[ResultT, ValidationContextT], ValidationResult], ValidationResult]] = []
|
|
221
274
|
weighted_feedback: list[tuple[float, str]] = []
|
|
222
275
|
for validator in validators:
|
|
223
|
-
validator_result: ValidationResult = validator(result,
|
|
276
|
+
validator_result: ValidationResult = validator(result, validation_context)
|
|
224
277
|
validator_results.append((validator, validator_result))
|
|
225
278
|
if not validator_result.is_valid:
|
|
226
279
|
weight = VALIDATOR_WEIGHTS.get(validator.__name__, DEFAULT_VALIDATOR_WEIGHT)
|
|
@@ -233,44 +286,52 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
233
286
|
score = score_validation_results(validator_results)
|
|
234
287
|
return score, weighted_feedback
|
|
235
288
|
|
|
236
|
-
def
|
|
289
|
+
def _invoke_repair_validate(
|
|
237
290
|
self,
|
|
238
291
|
prompt: str,
|
|
239
|
-
return_type: type,
|
|
240
|
-
|
|
241
|
-
|
|
292
|
+
return_type: type[ResultT],
|
|
293
|
+
repairs: list[Callable[[ResultT, RepairContextT], None]],
|
|
294
|
+
validators: list[Callable[[ResultT, ValidationContextT], ValidationResult]],
|
|
295
|
+
repair_context: RepairContextT,
|
|
296
|
+
validation_context: ValidationContextT,
|
|
242
297
|
max_validation_attempts: int = 1,
|
|
243
298
|
include_hidden: bool = False,
|
|
244
|
-
):
|
|
245
|
-
"""
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
299
|
+
) -> ResultT:
|
|
300
|
+
"""Bind deterministic repairs to every candidate before validation."""
|
|
301
|
+
|
|
302
|
+
def repair_candidate(result: ResultT) -> None:
|
|
303
|
+
self._repair_result(result, repairs, repair_context)
|
|
304
|
+
|
|
305
|
+
return self._invoke_validate(
|
|
306
|
+
prompt,
|
|
307
|
+
return_type,
|
|
308
|
+
validators,
|
|
309
|
+
validation_context,
|
|
310
|
+
max_validation_attempts=max_validation_attempts,
|
|
311
|
+
include_hidden=include_hidden,
|
|
312
|
+
candidate_repair=repair_candidate,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def _invoke_validate(
|
|
316
|
+
self,
|
|
317
|
+
prompt: str,
|
|
318
|
+
return_type: type[ResultT],
|
|
319
|
+
validators: list[Callable[[ResultT, ValidationContextT], ValidationResult]],
|
|
320
|
+
validation_context: ValidationContextT,
|
|
321
|
+
max_validation_attempts: int = 1,
|
|
322
|
+
include_hidden: bool = False,
|
|
323
|
+
candidate_repair: Callable[[ResultT], None] | None = None,
|
|
324
|
+
) -> ResultT:
|
|
325
|
+
"""Validate parsed candidates and return the best-scoring result."""
|
|
268
326
|
# Compute the maximum possible score so we can detect a perfect result
|
|
269
327
|
max_possible_score = sum(VALIDATOR_WEIGHTS.get(v.__name__, DEFAULT_VALIDATOR_WEIGHT) for v in validators)
|
|
270
328
|
|
|
271
329
|
result = self._parse_invoke(prompt, return_type, include_hidden=include_hidden)
|
|
330
|
+
if candidate_repair is not None:
|
|
331
|
+
candidate_repair(result)
|
|
272
332
|
logger.info(
|
|
273
|
-
"[Validation] Parsed %s: %s",
|
|
333
|
+
"[Validation] Parsed%s %s: %s",
|
|
334
|
+
" and repaired" if candidate_repair is not None else "",
|
|
274
335
|
return_type.__name__,
|
|
275
336
|
result.llm_str()[:500],
|
|
276
337
|
)
|
|
@@ -283,7 +344,7 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
283
344
|
critical_threshold = 10.0
|
|
284
345
|
|
|
285
346
|
for attempt in range(1, max_validation_attempts + 1):
|
|
286
|
-
score, weighted_feedback = self._score_result(result, validators,
|
|
347
|
+
score, weighted_feedback = self._score_result(result, validators, validation_context)
|
|
287
348
|
|
|
288
349
|
logger.info(
|
|
289
350
|
f"[Validation] Attempt {attempt}/{max_validation_attempts} "
|
|
@@ -327,6 +388,8 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
327
388
|
f"with {len(weighted_feedback)} feedback items"
|
|
328
389
|
)
|
|
329
390
|
result = self._parse_invoke(feedback_prompt, return_type, include_hidden=include_hidden)
|
|
391
|
+
if candidate_repair is not None:
|
|
392
|
+
candidate_repair(result)
|
|
330
393
|
|
|
331
394
|
return best_result
|
|
332
395
|
|
|
@@ -355,6 +418,7 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
|
|
|
355
418
|
return self._extractor_parse(response, return_type, parser, include_hidden=include_hidden)
|
|
356
419
|
|
|
357
420
|
def classify(exc: Exception, attempt: int) -> RetryDecision:
|
|
421
|
+
_raise_if_auth_error(exc)
|
|
358
422
|
if isinstance(exc, ResourceExhausted):
|
|
359
423
|
return RetryDecision(
|
|
360
424
|
action=RetryAction.RETRY,
|