codeboarding 0.12.0__tar.gz → 0.12.3__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.0/codeboarding.egg-info → codeboarding-0.12.3}/PKG-INFO +7 -3
- {codeboarding-0.12.0 → codeboarding-0.12.3}/PYPI.md +4 -2
- {codeboarding-0.12.0 → codeboarding-0.12.3}/README.md +24 -7
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/cluster_methods_mixin.py +27 -5
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/llm_config.py +155 -43
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/model_capabilities.py +6 -2
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_file_structure.py +1 -1
- {codeboarding-0.12.0 → codeboarding-0.12.3/codeboarding.egg-info}/PKG-INFO +7 -3
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding.egg-info/SOURCES.txt +7 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding.egg-info/requires.txt +2 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding.egg-info/top_level.txt +1 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/analysis.py +4 -3
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/diagram_generator.py +11 -2
- {codeboarding-0.12.0 → codeboarding-0.12.3}/github_action.py +1 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/main.py +2 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/pyproject.toml +4 -1
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/__init__.py +88 -12
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/cfg_skip_planner.py +7 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/csharp_config_scanner.py +14 -7
- codeboarding-0.12.3/static_analyzer/dotnet_sdk.py +327 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/csharp_adapter.py +75 -29
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/go_adapter.py +1 -1
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/language_adapter.py +6 -1
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/lsp_client.py +15 -8
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/scanner.py +2 -0
- codeboarding-0.12.3/telemetry/__init__.py +10 -0
- codeboarding-0.12.3/telemetry/device_id.py +91 -0
- codeboarding-0.12.3/telemetry/events.py +246 -0
- codeboarding-0.12.3/telemetry/schemas.py +67 -0
- codeboarding-0.12.3/telemetry/service.py +97 -0
- codeboarding-0.12.3/tests/test_telemetry_events.py +196 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_tool_registry.py +93 -8
- codeboarding-0.12.3/tests/test_user_config.py +220 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tool_registry/__init__.py +1 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tool_registry/installers.py +79 -4
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tool_registry/manifest.py +8 -8
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tool_registry/paths.py +13 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tool_registry/registry.py +5 -7
- {codeboarding-0.12.0 → codeboarding-0.12.3}/user_config.py +31 -5
- codeboarding-0.12.0/tests/test_user_config.py +0 -98
- {codeboarding-0.12.0 → codeboarding-0.12.3}/LICENSE +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/abstraction_agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/agent_responses.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/change_status.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/cluster_budget.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/constants.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/dependency_discovery.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/details_agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/incremental_agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/meta_agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/planner_agent.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/abstract_prompt_factory.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/claude_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/deepseek_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/gemini_flash_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/glm_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/gpt_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/kimi_prompts.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/prompts/prompt_factory.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/retry.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/base.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_cfg.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_file.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_source.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/tools/toolkit.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/agents/validation.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/caching/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/caching/cache.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/caching/details_cache.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/caching/meta_cache.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/bootstrap.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/commands/full_analysis.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/commands/incremental_analysis.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_cli/commands/partial_analysis.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/orchestration.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/rendering.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/constants.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/core/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/core/plugin_loader.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/core/protocols.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/core/registry.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/analysis_json.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/cluster_delta.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/cluster_snapshot.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/exceptions.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/io_utils.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/run_context.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/diagram_analysis/version.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/cohesion.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/coupling.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/function_size.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/god_class.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/inheritance.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/instability.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/config.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/models.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/health/runner.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/install.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/logging_config.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/callbacks.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/context.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/mixin.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/paths.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/stats.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/monitoring/writers.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/html.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/html_template.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/markdown.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/mdx.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/output_generators/sphinx.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/change_detector.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/diff_parser.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/errors.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/git_ops.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/repo_utils/ignore.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/setup.cfg +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/analysis_cache.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/analysis_result.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/cluster_helpers.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/cluster_relations.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/constants.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/java_adapter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/php_adapter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/python_adapter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/rust_adapter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/call_graph_builder.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/edge_builder.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/lsp_constants.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/models.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/protocols.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/result_converter.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/source_inspector.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/symbol_table.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/engine/utils.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/graph.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/incremental_orchestrator.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/language_results.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/leiden_utils.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/node.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/reference_resolve_mixin.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_cli_parser.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_github_action.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_install.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_logging_config.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_main.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_pyproject_packages.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_registry_coverage.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/tests/test_windows_encoding.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/utils.py +0 -0
- {codeboarding-0.12.0 → codeboarding-0.12.3}/vscode_constants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeboarding
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.3
|
|
4
4
|
Summary: Interactive Diagrams for Code
|
|
5
5
|
Author: CodeBoarding Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,6 +40,8 @@ Requires-Dist: markitdown>=0.1
|
|
|
40
40
|
Requires-Dist: networkx>=3.4
|
|
41
41
|
Requires-Dist: nodeenv>=1.10.0
|
|
42
42
|
Requires-Dist: pathspec>=0.12
|
|
43
|
+
Requires-Dist: posthog>=3.7
|
|
44
|
+
Requires-Dist: pydantic>=2.0
|
|
43
45
|
Requires-Dist: pyyaml>=6.0
|
|
44
46
|
Requires-Dist: regex>=2024.11
|
|
45
47
|
Requires-Dist: rich>=12.6
|
|
@@ -93,15 +95,17 @@ Dynamic: license-file
|
|
|
93
95
|
The recommended way to install the CLI is with [pipx](https://pipx.pypa.io), which automatically creates an isolated environment:
|
|
94
96
|
|
|
95
97
|
```bash
|
|
96
|
-
pipx install codeboarding --python python3.12
|
|
98
|
+
pipx install codeboarding --python python3.12 --pip-args="--extra-index-url https://pip.codeboarding.org/simple/"
|
|
97
99
|
```
|
|
98
100
|
|
|
99
101
|
Alternatively, install into an existing virtual environment with pip:
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
|
-
pip install codeboarding
|
|
104
|
+
pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
|
|
103
105
|
```
|
|
104
106
|
|
|
107
|
+
|
|
108
|
+
|
|
105
109
|
> 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.
|
|
106
110
|
|
|
107
111
|
Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
|
|
@@ -23,15 +23,17 @@
|
|
|
23
23
|
The recommended way to install the CLI is with [pipx](https://pipx.pypa.io), which automatically creates an isolated environment:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
pipx install codeboarding --python python3.12
|
|
26
|
+
pipx install codeboarding --python python3.12 --pip-args="--extra-index-url https://pip.codeboarding.org/simple/"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Alternatively, install into an existing virtual environment with pip:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
pip install codeboarding
|
|
32
|
+
pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
|
|
36
|
+
|
|
35
37
|
> 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
38
|
|
|
37
39
|
Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
|
|
@@ -4,9 +4,9 @@ See what your AI is building before it breaks.
|
|
|
4
4
|
|
|
5
5
|
CodeBoarding gives developers and coding agents a visual map of a codebase. It combines static analysis with LLM reasoning to generate architecture diagrams, component-level documentation, and navigable outputs you can use in your IDE, CI, and docs.
|
|
6
6
|
|
|
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-
|
|
7
|
+
[Website](https://codeboarding.org) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=0855d476-b2d0-44cc-b93d-69b47504719c" width="0" height="0" /> · [Open VSX extension](https://open-vsx.org/extension/CodeBoarding/codeboarding) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=ce87464c-2792-46b0-9ea2-87eefe853d7e" width="0" height="0" /> · [Explore examples](https://codeboarding.org/diagrams) · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=8a3d26e0-6f6b-49c0-8482-114445de56a5" width="0" height="0" /> · [GitHub Action](https://github.com/marketplace/actions/codeboarding-action) ·[Discord](https://discord.gg/T5zHTJYFuy)
|
|
8
8
|
|
|
9
|
-
[](https://open-vsx.org/extension/CodeBoarding/codeboarding)
|
|
9
|
+
[](https://open-vsx.org/extension/CodeBoarding/codeboarding) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=ce87464c-2792-46b0-9ea2-87eefe853d7e" width="0" height="0" />
|
|
10
10
|
|
|
11
11
|
Install the extension from Open VSX.
|
|
12
12
|
|
|
@@ -86,7 +86,7 @@ codeboarding full --local /path/to/repo
|
|
|
86
86
|
Or, if you prefer pip, install into a virtual environment (not the global Python):
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
pip install codeboarding
|
|
89
|
+
pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
|
|
90
90
|
codeboarding-setup
|
|
91
91
|
codeboarding full --local /path/to/repo
|
|
92
92
|
```
|
|
@@ -108,6 +108,8 @@ On first run, CodeBoarding creates `~/.codeboarding/config.toml`. Set one provid
|
|
|
108
108
|
# aws_bearer_token_bedrock = "..."
|
|
109
109
|
# ollama_base_url = "http://localhost:11434"
|
|
110
110
|
# openrouter_api_key = "sk-..."
|
|
111
|
+
# litellm_base_url = "http://localhost:4000" # LiteLLM proxy server URL (required)
|
|
112
|
+
# litellm_api_key = "sk-..." # LiteLLM proxy server key (optional)
|
|
111
113
|
|
|
112
114
|
[llm]
|
|
113
115
|
# agent_model = "gemini-3-flash"
|
|
@@ -138,19 +140,32 @@ python main.py full https://github.com/pytorch/pytorch
|
|
|
138
140
|
## Where to use it
|
|
139
141
|
|
|
140
142
|
- [CLI](https://github.com/CodeBoarding/CodeBoarding) for local analysis, automation, and CI workflows.
|
|
141
|
-
- [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) for in-editor visual architecture.
|
|
142
|
-
- [GitHub Action](https://github.com/marketplace/actions/codeboarding-
|
|
143
|
+
- [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=8a3d26e0-6f6b-49c0-8482-114445de56a5" width="0" height="0" /> for in-editor visual architecture.
|
|
144
|
+
- [GitHub Action](https://github.com/marketplace/actions/codeboarding-action) to keep diagrams updated in CI.
|
|
143
145
|
|
|
144
146
|
## Supported stack
|
|
145
147
|
|
|
146
148
|
- Languages: Python, TypeScript, JavaScript, Java, Go, PHP, Rust, C#.
|
|
147
|
-
- LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, and more.
|
|
149
|
+
- LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, LiteLLM proxy, and more.
|
|
148
150
|
|
|
149
151
|
## Examples
|
|
150
152
|
|
|
151
153
|
- Visualized 800+ open-source repositories.
|
|
152
154
|
- Browse generated examples in [GeneratedOnBoardings](https://github.com/CodeBoarding/GeneratedOnBoardings).
|
|
153
|
-
- Try the hosted explorer at [codeboarding.org/diagrams](https://codeboarding.org/diagrams).
|
|
155
|
+
- Try the hosted explorer at [codeboarding.org/diagrams](https://codeboarding.org/diagrams) <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=0855d476-b2d0-44cc-b93d-69b47504719c" width="0" height="0" />.
|
|
156
|
+
|
|
157
|
+
## Telemetry
|
|
158
|
+
|
|
159
|
+
CodeBoarding collects anonymous, aggregate usage telemetry (which command ran,
|
|
160
|
+
success/failure, duration, and token cost) to help us improve the tool. It is on
|
|
161
|
+
by default and never collects source code, file names, repository names, paths,
|
|
162
|
+
prompts, model outputs, API keys, or any personal information. Opt out anytime:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
export CODEBOARDING_TELEMETRY=false # or: export DO_NOT_TRACK=1
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
See [TELEMETRY.md](TELEMETRY.md) for the full list of events and properties.
|
|
154
169
|
|
|
155
170
|
## Contributing
|
|
156
171
|
|
|
@@ -159,3 +174,5 @@ If you want to improve CodeBoarding, open an [issue](https://github.com/CodeBoar
|
|
|
159
174
|
## Vision
|
|
160
175
|
|
|
161
176
|
CodeBoarding is building an open standard for code understanding: a visual, accurate, high-level representation of a codebase that both humans and agents can use.
|
|
177
|
+
|
|
178
|
+
<img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=1942e7e7-0762-4cdd-9f08-024acc098071" />
|
|
@@ -16,7 +16,8 @@ from agents.agent_responses import (
|
|
|
16
16
|
MethodEntry,
|
|
17
17
|
)
|
|
18
18
|
from agents.cluster_budget import ClusterPromptBudget
|
|
19
|
-
from agents.llm_config import get_current_agent_context_window
|
|
19
|
+
from agents.llm_config import get_current_agent_context_window, get_current_agent_model_ref
|
|
20
|
+
from agents.model_capabilities import ContextWindow
|
|
20
21
|
from constants import MIN_CLUSTERS_THRESHOLD
|
|
21
22
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
22
23
|
from static_analyzer.cfg_skip_planner import ContextBudgetExceededError, plan_skip_set
|
|
@@ -46,6 +47,20 @@ class _RenderedClusterString:
|
|
|
46
47
|
cluster_ids: set[int]
|
|
47
48
|
|
|
48
49
|
|
|
50
|
+
def _describe_window(ctx: ContextWindow) -> str:
|
|
51
|
+
suffix = "; fallback default, model window unresolved" if ctx.is_fallback else ""
|
|
52
|
+
return f"{ctx.input_tokens} input tokens for {get_current_agent_model_ref()}{suffix}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _window_telemetry(ctx: ContextWindow, char_budget: int) -> dict:
|
|
56
|
+
return {
|
|
57
|
+
"char_budget": char_budget,
|
|
58
|
+
"window_input_tokens": ctx.input_tokens,
|
|
59
|
+
"window_is_fallback": ctx.is_fallback,
|
|
60
|
+
"agent_model": get_current_agent_model_ref(),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
49
64
|
class ClusterMethodsMixin:
|
|
50
65
|
"""
|
|
51
66
|
Mixin providing shared cluster-related functionality for agents.
|
|
@@ -156,10 +171,10 @@ class ClusterMethodsMixin:
|
|
|
156
171
|
ctx = get_current_agent_context_window()
|
|
157
172
|
msg = (
|
|
158
173
|
f"Prompt overhead ({prompt_overhead_chars} chars) consumes the entire agent input "
|
|
159
|
-
f"window ({ctx
|
|
174
|
+
f"window ({_describe_window(ctx)}); no room for cluster renderings."
|
|
160
175
|
)
|
|
161
176
|
logger.error("[CFG skip planner] %s", msg)
|
|
162
|
-
raise ContextBudgetExceededError(msg)
|
|
177
|
+
raise ContextBudgetExceededError(msg, telemetry_properties=_window_telemetry(ctx, char_budget))
|
|
163
178
|
|
|
164
179
|
langs_with_clusters = [l for l in programming_langs if cluster_results.get(l)]
|
|
165
180
|
if not langs_with_clusters:
|
|
@@ -240,14 +255,21 @@ class ClusterMethodsMixin:
|
|
|
240
255
|
rendered: _RenderedClusterString,
|
|
241
256
|
skip_sets: dict[str, set[str]],
|
|
242
257
|
) -> NoReturn:
|
|
258
|
+
ctx = get_current_agent_context_window()
|
|
243
259
|
per_lang_sizes = {lang: len(text) for lang, text in rendered.by_language.items()}
|
|
244
260
|
skipped_counts = {lang: len(skip) for lang, skip in skip_sets.items() if skip}
|
|
245
261
|
msg = (
|
|
246
|
-
f"Cluster render {len(rendered.text)} chars exceeds budget {char_budget}
|
|
262
|
+
f"Cluster render {len(rendered.text)} chars exceeds budget {char_budget} "
|
|
263
|
+
f"(agent window: {_describe_window(ctx)}). "
|
|
247
264
|
f"Per-language sizes: {per_lang_sizes}; skipped nodes: {skipped_counts}."
|
|
248
265
|
)
|
|
249
266
|
logger.error("[CFG skip planner] %s", msg)
|
|
250
|
-
|
|
267
|
+
telemetry = _window_telemetry(ctx, char_budget) | {
|
|
268
|
+
"render_chars": len(rendered.text),
|
|
269
|
+
"per_language_chars": per_lang_sizes,
|
|
270
|
+
"skipped_node_counts": skipped_counts,
|
|
271
|
+
}
|
|
272
|
+
raise ContextBudgetExceededError(msg, telemetry_properties=telemetry)
|
|
251
273
|
|
|
252
274
|
@staticmethod
|
|
253
275
|
def _cluster_prompt_budget(prompt_overhead_chars: int) -> int:
|
|
@@ -23,6 +23,25 @@ MONITORING_CALLBACK = MonitoringCallback(stats_container=RunStats())
|
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
26
|
+
_OPENROUTER_FALLBACK_CONTEXT_WINDOW = ContextWindow(1_048_576, 65_536, is_fallback=True)
|
|
27
|
+
|
|
28
|
+
# Model families that reject sampling params (temperature/top_p/top_k) with HTTP 400.
|
|
29
|
+
# Why: Anthropic removed them starting with Opus 4.7; sending temperature (even 0) 400s.
|
|
30
|
+
# Substrings match bare IDs and Bedrock-prefixed forms (e.g. us.anthropic.claude-opus-4-8-v1:0).
|
|
31
|
+
_SAMPLING_PARAM_FREE_MODEL_SUBSTRINGS = (
|
|
32
|
+
"claude-opus-4-7",
|
|
33
|
+
"claude-opus-4-8",
|
|
34
|
+
"claude-fable-5",
|
|
35
|
+
"claude-mythos-5",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _model_accepts_temperature(model_name: str) -> bool:
|
|
40
|
+
"""False for models that reject sampling params; temperature must be omitted for those."""
|
|
41
|
+
lowered = model_name.lower()
|
|
42
|
+
return not any(s in lowered for s in _SAMPLING_PARAM_FREE_MODEL_SUBSTRINGS)
|
|
43
|
+
|
|
44
|
+
|
|
26
45
|
# ---------------------------------------------------------------------------
|
|
27
46
|
# Module-level model overrides – set once by the orchestrator (main.py) and
|
|
28
47
|
# consumed by initialize_llms() without needing to thread the values through
|
|
@@ -67,6 +86,9 @@ class LLMConfig:
|
|
|
67
86
|
Configuration for LLM providers.
|
|
68
87
|
|
|
69
88
|
Attributes:
|
|
89
|
+
selection_envs: Env vars that select this provider — any one being set selects it.
|
|
90
|
+
api_key_env: Env var holding the provider's secret, or None when the
|
|
91
|
+
underlying SDK reads its credentials from the environment itself.
|
|
70
92
|
agent_model: The "agent" model used for complex reasoning and agentic tasks.
|
|
71
93
|
parsing_model: The "parsing" model used for fast, cost-effective extraction and parsing tasks.
|
|
72
94
|
agent_temperature: Temperature for the agent model. Defaults to 0 for deterministic behavior
|
|
@@ -77,23 +99,37 @@ class LLMConfig:
|
|
|
77
99
|
"""
|
|
78
100
|
|
|
79
101
|
chat_class: Type[BaseChatModel]
|
|
80
|
-
|
|
102
|
+
selection_envs: list[str]
|
|
81
103
|
agent_model: str
|
|
82
104
|
parsing_model: str
|
|
83
105
|
llm_type: LLMType
|
|
84
106
|
agent_temperature: float = LLMDefaults.DEFAULT_AGENT_TEMPERATURE
|
|
85
107
|
parsing_temperature: float = LLMDefaults.DEFAULT_PARSING_TEMPERATURE
|
|
86
108
|
extra_args: dict[str, Any] = field(default_factory=dict)
|
|
87
|
-
|
|
109
|
+
api_key_env: str | None = None
|
|
110
|
+
keyless_capable: bool = False
|
|
111
|
+
"""Whether this provider can run without a real API key.
|
|
112
|
+
|
|
113
|
+
True for self-hosted / OpenAI-compatible endpoints that accept
|
|
114
|
+
unauthenticated requests. When such a provider is the sole selected one
|
|
115
|
+
and no real key is set, key validation warns instead of failing, and the
|
|
116
|
+
client uses a placeholder.
|
|
117
|
+
"""
|
|
88
118
|
|
|
89
119
|
def get_api_key(self) -> str | None:
|
|
90
|
-
return os.getenv(self.api_key_env)
|
|
120
|
+
return os.getenv(self.api_key_env) if self.api_key_env else None
|
|
121
|
+
|
|
122
|
+
def has_real_api_key(self) -> bool:
|
|
123
|
+
"""True if the provider's API-key env var holds a value.
|
|
124
|
+
|
|
125
|
+
Distinct from ``is_selected_by_env()``: a keyless-capable provider can
|
|
126
|
+
be selected via a base-URL var while having no real key here.
|
|
127
|
+
"""
|
|
128
|
+
return bool(self.get_api_key())
|
|
91
129
|
|
|
92
|
-
def
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
return True
|
|
96
|
-
return any(os.getenv(var) for var in self.alt_env_vars)
|
|
130
|
+
def is_selected_by_env(self) -> bool:
|
|
131
|
+
"""True when any of this provider's selection env vars is set."""
|
|
132
|
+
return any(os.getenv(var) for var in self.selection_envs)
|
|
97
133
|
|
|
98
134
|
def get_resolved_extra_args(self) -> dict[str, Any]:
|
|
99
135
|
resolved = {}
|
|
@@ -108,11 +144,12 @@ class LLMConfig:
|
|
|
108
144
|
LLM_PROVIDERS = {
|
|
109
145
|
"openai": LLMConfig(
|
|
110
146
|
chat_class=ChatOpenAI,
|
|
147
|
+
selection_envs=["OPENAI_API_KEY", "OPENAI_BASE_URL"],
|
|
111
148
|
api_key_env="OPENAI_API_KEY",
|
|
112
149
|
agent_model="gpt-4o",
|
|
113
150
|
parsing_model="gpt-4o-mini",
|
|
114
151
|
llm_type=LLMType.GPT4,
|
|
115
|
-
|
|
152
|
+
keyless_capable=True,
|
|
116
153
|
extra_args={
|
|
117
154
|
"base_url": lambda: os.getenv("OPENAI_BASE_URL"),
|
|
118
155
|
"max_tokens": None,
|
|
@@ -122,11 +159,11 @@ LLM_PROVIDERS = {
|
|
|
122
159
|
),
|
|
123
160
|
"vercel": LLMConfig(
|
|
124
161
|
chat_class=ChatOpenAI,
|
|
162
|
+
selection_envs=["VERCEL_API_KEY", "VERCEL_BASE_URL"],
|
|
125
163
|
api_key_env="VERCEL_API_KEY",
|
|
126
164
|
agent_model="google/gemini-3-flash",
|
|
127
165
|
parsing_model="openai/gpt-5-mini",
|
|
128
166
|
llm_type=LLMType.GEMINI_FLASH,
|
|
129
|
-
alt_env_vars=["VERCEL_BASE_URL"],
|
|
130
167
|
extra_args={
|
|
131
168
|
"base_url": lambda: os.getenv("VERCEL_BASE_URL", f"https://ai-gateway.vercel.sh/v1"),
|
|
132
169
|
"max_tokens": None,
|
|
@@ -136,6 +173,7 @@ LLM_PROVIDERS = {
|
|
|
136
173
|
),
|
|
137
174
|
"anthropic": LLMConfig(
|
|
138
175
|
chat_class=ChatAnthropic,
|
|
176
|
+
selection_envs=["ANTHROPIC_API_KEY"],
|
|
139
177
|
api_key_env="ANTHROPIC_API_KEY",
|
|
140
178
|
agent_model="claude-sonnet-4-6",
|
|
141
179
|
parsing_model="claude-haiku-4-5",
|
|
@@ -148,6 +186,7 @@ LLM_PROVIDERS = {
|
|
|
148
186
|
),
|
|
149
187
|
"google": LLMConfig(
|
|
150
188
|
chat_class=ChatGoogleGenerativeAI,
|
|
189
|
+
selection_envs=["GOOGLE_API_KEY"],
|
|
151
190
|
api_key_env="GOOGLE_API_KEY",
|
|
152
191
|
agent_model="gemini-3-flash-preview",
|
|
153
192
|
parsing_model="gemini-3.1-flash-lite",
|
|
@@ -160,7 +199,8 @@ LLM_PROVIDERS = {
|
|
|
160
199
|
),
|
|
161
200
|
"aws": LLMConfig(
|
|
162
201
|
chat_class=ChatBedrockConverse,
|
|
163
|
-
api_key_env
|
|
202
|
+
# No api_key_env: botocore reads AWS_BEARER_TOKEN_BEDROCK from the environment itself.
|
|
203
|
+
selection_envs=["AWS_BEARER_TOKEN_BEDROCK"],
|
|
164
204
|
agent_model="anthropic.claude-sonnet-4-6",
|
|
165
205
|
parsing_model="claude-haiku-4-5",
|
|
166
206
|
llm_type=LLMType.CLAUDE_SONNET,
|
|
@@ -172,6 +212,7 @@ LLM_PROVIDERS = {
|
|
|
172
212
|
),
|
|
173
213
|
"cerebras": LLMConfig(
|
|
174
214
|
chat_class=ChatCerebras,
|
|
215
|
+
selection_envs=["CEREBRAS_API_KEY"],
|
|
175
216
|
api_key_env="CEREBRAS_API_KEY",
|
|
176
217
|
agent_model="zai-glm-4.7",
|
|
177
218
|
parsing_model="gpt-oss-120b",
|
|
@@ -184,7 +225,11 @@ LLM_PROVIDERS = {
|
|
|
184
225
|
),
|
|
185
226
|
"ollama": LLMConfig(
|
|
186
227
|
chat_class=ChatOllama,
|
|
187
|
-
|
|
228
|
+
# OLLAMA_HOST is Ollama's canonical host var; the client falls back to it
|
|
229
|
+
# when no base_url is passed, and sends OLLAMA_API_KEY (Ollama cloud) itself.
|
|
230
|
+
selection_envs=["OLLAMA_BASE_URL", "OLLAMA_HOST"],
|
|
231
|
+
api_key_env="OLLAMA_API_KEY",
|
|
232
|
+
keyless_capable=True,
|
|
188
233
|
agent_model="qwen3:30b",
|
|
189
234
|
parsing_model="qwen2.5:7b",
|
|
190
235
|
llm_type=LLMType.GEMINI_FLASH,
|
|
@@ -196,11 +241,11 @@ LLM_PROVIDERS = {
|
|
|
196
241
|
),
|
|
197
242
|
"deepseek": LLMConfig(
|
|
198
243
|
chat_class=ChatOpenAI,
|
|
244
|
+
selection_envs=["DEEPSEEK_API_KEY", "DEEPSEEK_BASE_URL"],
|
|
199
245
|
api_key_env="DEEPSEEK_API_KEY",
|
|
200
246
|
agent_model="deepseek-chat",
|
|
201
247
|
parsing_model="deepseek-chat",
|
|
202
248
|
llm_type=LLMType.DEEPSEEK,
|
|
203
|
-
alt_env_vars=["DEEPSEEK_BASE_URL"],
|
|
204
249
|
extra_args={
|
|
205
250
|
"base_url": lambda: os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1"),
|
|
206
251
|
"max_tokens": None,
|
|
@@ -210,11 +255,11 @@ LLM_PROVIDERS = {
|
|
|
210
255
|
),
|
|
211
256
|
"glm": LLMConfig(
|
|
212
257
|
chat_class=ChatOpenAI,
|
|
258
|
+
selection_envs=["GLM_API_KEY", "GLM_BASE_URL"],
|
|
213
259
|
api_key_env="GLM_API_KEY",
|
|
214
260
|
agent_model="glm-4.7-flash",
|
|
215
261
|
parsing_model="glm-4.7-flash",
|
|
216
262
|
llm_type=LLMType.GLM,
|
|
217
|
-
alt_env_vars=["GLM_BASE_URL"],
|
|
218
263
|
extra_args={
|
|
219
264
|
"base_url": lambda: os.getenv("GLM_BASE_URL", "https://open.bigmodel.cn/api/paas/v4"),
|
|
220
265
|
"max_tokens": None,
|
|
@@ -224,11 +269,11 @@ LLM_PROVIDERS = {
|
|
|
224
269
|
),
|
|
225
270
|
"kimi": LLMConfig(
|
|
226
271
|
chat_class=ChatOpenAI,
|
|
272
|
+
selection_envs=["KIMI_API_KEY", "KIMI_BASE_URL"],
|
|
227
273
|
api_key_env="KIMI_API_KEY",
|
|
228
274
|
agent_model="kimi-k2.5",
|
|
229
275
|
parsing_model="kimi-k2.5",
|
|
230
276
|
llm_type=LLMType.KIMI,
|
|
231
|
-
alt_env_vars=["KIMI_BASE_URL"],
|
|
232
277
|
extra_args={
|
|
233
278
|
"base_url": lambda: os.getenv("KIMI_BASE_URL", "https://api.moonshot.cn/v1"),
|
|
234
279
|
"max_tokens": None,
|
|
@@ -238,6 +283,7 @@ LLM_PROVIDERS = {
|
|
|
238
283
|
),
|
|
239
284
|
"openrouter": LLMConfig(
|
|
240
285
|
chat_class=ChatOpenAI,
|
|
286
|
+
selection_envs=["OPENROUTER_API_KEY"],
|
|
241
287
|
api_key_env="OPENROUTER_API_KEY",
|
|
242
288
|
agent_model="google/gemini-3-flash-preview",
|
|
243
289
|
parsing_model="google/gemini-3-flash-preview",
|
|
@@ -249,9 +295,45 @@ LLM_PROVIDERS = {
|
|
|
249
295
|
"max_retries": 0,
|
|
250
296
|
},
|
|
251
297
|
),
|
|
298
|
+
"litellm": LLMConfig(
|
|
299
|
+
chat_class=ChatOpenAI,
|
|
300
|
+
# Base URL only: a key alone must not select litellm, since there is no
|
|
301
|
+
# universal proxy endpoint to default to.
|
|
302
|
+
selection_envs=["LITELLM_BASE_URL"],
|
|
303
|
+
api_key_env="LITELLM_API_KEY",
|
|
304
|
+
agent_model="gpt-4o",
|
|
305
|
+
parsing_model="gpt-4o-mini",
|
|
306
|
+
llm_type=LLMType.GPT4,
|
|
307
|
+
keyless_capable=True,
|
|
308
|
+
extra_args={
|
|
309
|
+
"base_url": lambda: os.getenv("LITELLM_BASE_URL"),
|
|
310
|
+
"max_tokens": None,
|
|
311
|
+
"timeout": None,
|
|
312
|
+
"max_retries": 0,
|
|
313
|
+
},
|
|
314
|
+
),
|
|
252
315
|
}
|
|
253
316
|
|
|
254
317
|
|
|
318
|
+
def _all_selection_envs() -> list[str]:
|
|
319
|
+
return sorted({var for config in LLM_PROVIDERS.values() for var in config.selection_envs})
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _unselected_key_hints() -> list[str]:
|
|
323
|
+
"""Messages for providers whose API key is set but which nothing selects."""
|
|
324
|
+
return [
|
|
325
|
+
f"{config.api_key_env} is set, but the '{name}' provider is selected by "
|
|
326
|
+
f"{' or '.join(config.selection_envs)}."
|
|
327
|
+
for name, config in LLM_PROVIDERS.items()
|
|
328
|
+
if config.has_real_api_key() and not config.is_selected_by_env()
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def selected_providers() -> list[str]:
|
|
333
|
+
"""Names of providers the environment currently selects."""
|
|
334
|
+
return [name for name, config in LLM_PROVIDERS.items() if config.is_selected_by_env()]
|
|
335
|
+
|
|
336
|
+
|
|
255
337
|
def _initialize_llm(
|
|
256
338
|
model_override: str | None,
|
|
257
339
|
model_attr: str,
|
|
@@ -259,16 +341,10 @@ def _initialize_llm(
|
|
|
259
341
|
log_prefix: str,
|
|
260
342
|
init_factory: bool = False,
|
|
261
343
|
) -> tuple[BaseChatModel, str]:
|
|
262
|
-
resolved =
|
|
344
|
+
resolved = _resolve_selected_provider(model_override, model_attr)
|
|
263
345
|
if resolved is None:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
required_vars.append(config.api_key_env)
|
|
267
|
-
required_vars.extend(config.alt_env_vars)
|
|
268
|
-
|
|
269
|
-
raise ValueError(
|
|
270
|
-
f"No valid LLM configuration found. Please set one of: {', '.join(sorted(set(required_vars)))}"
|
|
271
|
-
)
|
|
346
|
+
message = f"No valid LLM configuration found. Please set one of: {', '.join(_all_selection_envs())}."
|
|
347
|
+
raise ValueError(" ".join([message, *_unselected_key_hints()]))
|
|
272
348
|
|
|
273
349
|
name, config, model_name = resolved
|
|
274
350
|
|
|
@@ -282,12 +358,13 @@ def _initialize_llm(
|
|
|
282
358
|
|
|
283
359
|
logger.info(f"Using {name.title()} {log_prefix}LLM with model: {model_name}")
|
|
284
360
|
|
|
285
|
-
kwargs = {
|
|
286
|
-
|
|
287
|
-
"temperature"
|
|
288
|
-
}
|
|
361
|
+
kwargs: dict[str, Any] = {"model": model_name}
|
|
362
|
+
if _model_accepts_temperature(model_name):
|
|
363
|
+
kwargs["temperature"] = getattr(config, temperature_attr)
|
|
289
364
|
kwargs.update(config.get_resolved_extra_args())
|
|
290
365
|
|
|
366
|
+
# ChatBedrockConverse and ChatOllama take no api_key kwarg; their SDKs read
|
|
367
|
+
# AWS_BEARER_TOKEN_BEDROCK / OLLAMA_API_KEY from the environment directly.
|
|
291
368
|
if name not in ["aws", "ollama"]:
|
|
292
369
|
api_key = config.get_api_key()
|
|
293
370
|
kwargs["api_key"] = api_key or "no-key-required"
|
|
@@ -296,13 +373,13 @@ def _initialize_llm(
|
|
|
296
373
|
return model, model_name
|
|
297
374
|
|
|
298
375
|
|
|
299
|
-
def
|
|
376
|
+
def _resolve_selected_provider(
|
|
300
377
|
model_override: str | None,
|
|
301
378
|
model_attr: str,
|
|
302
379
|
) -> tuple[str, LLMConfig, str] | None:
|
|
303
|
-
"""Return the
|
|
380
|
+
"""Return the selected provider, config, and resolved model name."""
|
|
304
381
|
for name, config in LLM_PROVIDERS.items():
|
|
305
|
-
if not config.
|
|
382
|
+
if not config.is_selected_by_env():
|
|
306
383
|
continue
|
|
307
384
|
return name, config, model_override or getattr(config, model_attr)
|
|
308
385
|
return None
|
|
@@ -313,13 +390,36 @@ class LLMConfigError(ValueError):
|
|
|
313
390
|
|
|
314
391
|
|
|
315
392
|
def validate_api_key_provided() -> None:
|
|
316
|
-
"""Raise LLMConfigError
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
393
|
+
"""Raise LLMConfigError unless exactly one LLM provider is selected.
|
|
394
|
+
|
|
395
|
+
A provider is selected when any of its ``selection_envs`` is set. Keyless-
|
|
396
|
+
capable providers (self-hosted / OpenAI-compatible endpoints, e.g. an
|
|
397
|
+
``OPENAI_BASE_URL`` with no key) are therefore valid: they are selected by
|
|
398
|
+
their base URL, and the client falls back to a placeholder key downstream.
|
|
399
|
+
In that case we log a warning rather than fail. Ambiguity detection (more
|
|
400
|
+
than one selected provider) is preserved so a stray second key is still
|
|
401
|
+
surfaced, and a key set for an unselected provider (e.g. LITELLM_API_KEY
|
|
402
|
+
without LITELLM_BASE_URL) is reported rather than silently ignored.
|
|
403
|
+
"""
|
|
404
|
+
hints = _unselected_key_hints()
|
|
405
|
+
selected = selected_providers()
|
|
406
|
+
if not selected:
|
|
407
|
+
message = f"No LLM provider selected. Set one of: {', '.join(_all_selection_envs())}."
|
|
408
|
+
raise LLMConfigError(" ".join([message, *hints]))
|
|
409
|
+
if len(selected) > 1:
|
|
410
|
+
raise LLMConfigError(f"Multiple LLM providers selected ({', '.join(selected)}); please set only one.")
|
|
411
|
+
for hint in hints:
|
|
412
|
+
logger.warning(hint)
|
|
413
|
+
|
|
414
|
+
(name,) = selected
|
|
415
|
+
config = LLM_PROVIDERS[name]
|
|
416
|
+
if config.keyless_capable and not config.has_real_api_key():
|
|
417
|
+
logger.warning(
|
|
418
|
+
"Provider '%s' is selected via a base URL with no %s set; "
|
|
419
|
+
"treating as a keyless local endpoint (a placeholder key is used).",
|
|
420
|
+
name,
|
|
421
|
+
config.api_key_env,
|
|
422
|
+
)
|
|
323
423
|
|
|
324
424
|
|
|
325
425
|
def initialize_agent_llm(model_override: str | None = None) -> BaseChatModel:
|
|
@@ -329,17 +429,29 @@ def initialize_agent_llm(model_override: str | None = None) -> BaseChatModel:
|
|
|
329
429
|
|
|
330
430
|
|
|
331
431
|
def get_current_agent_context_window() -> ContextWindow:
|
|
332
|
-
"""Context window for the currently
|
|
432
|
+
"""Context window for the currently selected agent provider/model.
|
|
333
433
|
|
|
334
|
-
Resolves the first
|
|
434
|
+
Resolves the first selected provider (same rule as ``_initialize_llm``) on
|
|
335
435
|
every call. ``get_context_window`` handles its own caching, so this is
|
|
336
436
|
cheap enough to call without a module-level cache.
|
|
337
437
|
"""
|
|
338
|
-
resolved =
|
|
438
|
+
resolved = _resolve_selected_provider(_agent_model_override or os.getenv("AGENT_MODEL"), "agent_model")
|
|
339
439
|
if resolved is not None:
|
|
340
440
|
name, _config, model_name = resolved
|
|
341
|
-
|
|
342
|
-
|
|
441
|
+
ctx = get_context_window(name, model_name)
|
|
442
|
+
if name == "openrouter" and ctx.is_fallback:
|
|
443
|
+
return _OPENROUTER_FALLBACK_CONTEXT_WINDOW
|
|
444
|
+
return ctx
|
|
445
|
+
return ContextWindow(ModelCapabilities.FALLBACK_INPUT, ModelCapabilities.FALLBACK_OUTPUT, is_fallback=True)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def get_current_agent_model_ref() -> str:
|
|
449
|
+
"""``provider/model`` for the currently active agent LLM, or ``"unknown"``."""
|
|
450
|
+
resolved = _resolve_selected_provider(_agent_model_override or os.getenv("AGENT_MODEL"), "agent_model")
|
|
451
|
+
if resolved is None:
|
|
452
|
+
return "unknown"
|
|
453
|
+
name, _config, model_name = resolved
|
|
454
|
+
return f"{name}/{model_name}"
|
|
343
455
|
|
|
344
456
|
|
|
345
457
|
def initialize_parsing_llm(model_override: str | None = None) -> BaseChatModel:
|
|
@@ -24,6 +24,7 @@ _OLLAMA_CACHE: dict[tuple[str, str], tuple[int, int]] = {}
|
|
|
24
24
|
class ContextWindow:
|
|
25
25
|
input_tokens: int
|
|
26
26
|
output_tokens: int
|
|
27
|
+
is_fallback: bool = False
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def get_context_window(provider: str, model_name: str) -> ContextWindow:
|
|
@@ -40,7 +41,7 @@ def get_context_window(provider: str, model_name: str) -> ContextWindow:
|
|
|
40
41
|
if hit is not None:
|
|
41
42
|
return ContextWindow(*hit)
|
|
42
43
|
logger.warning(f"No context window for {provider}/{model_name}; using fallback {ModelCapabilities.FALLBACK_INPUT}")
|
|
43
|
-
return ContextWindow(ModelCapabilities.FALLBACK_INPUT, ModelCapabilities.FALLBACK_OUTPUT)
|
|
44
|
+
return ContextWindow(ModelCapabilities.FALLBACK_INPUT, ModelCapabilities.FALLBACK_OUTPUT, is_fallback=True)
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def _resolve_env(provider: str, model_name: str) -> tuple[int, int] | None:
|
|
@@ -81,9 +82,12 @@ def _user_context_window_override() -> int | None:
|
|
|
81
82
|
def _resolve_ollama(provider: str, model_name: str) -> tuple[int, int] | None:
|
|
82
83
|
if provider != "ollama":
|
|
83
84
|
return None
|
|
84
|
-
base = os.getenv("OLLAMA_BASE_URL")
|
|
85
|
+
base = os.getenv("OLLAMA_BASE_URL") or os.getenv("OLLAMA_HOST")
|
|
85
86
|
if not base:
|
|
86
87
|
return None
|
|
88
|
+
if "://" not in base:
|
|
89
|
+
# OLLAMA_HOST conventionally allows bare host:port.
|
|
90
|
+
base = f"http://{base}"
|
|
87
91
|
return _ollama_show(model_name, base.rstrip("/"))
|
|
88
92
|
|
|
89
93
|
|
|
@@ -36,7 +36,7 @@ class FileStructureTool(BaseRepoTool):
|
|
|
36
36
|
# Ensure they are sorted by depth for is_subsequence logic
|
|
37
37
|
return sorted(dirs, key=lambda x: len(x.parts))
|
|
38
38
|
|
|
39
|
-
def _run(self, dir: str) -> str:
|
|
39
|
+
def _run(self, dir: str = ".") -> str:
|
|
40
40
|
"""
|
|
41
41
|
Run the tool with the given input.
|
|
42
42
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeboarding
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.3
|
|
4
4
|
Summary: Interactive Diagrams for Code
|
|
5
5
|
Author: CodeBoarding Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,6 +40,8 @@ Requires-Dist: markitdown>=0.1
|
|
|
40
40
|
Requires-Dist: networkx>=3.4
|
|
41
41
|
Requires-Dist: nodeenv>=1.10.0
|
|
42
42
|
Requires-Dist: pathspec>=0.12
|
|
43
|
+
Requires-Dist: posthog>=3.7
|
|
44
|
+
Requires-Dist: pydantic>=2.0
|
|
43
45
|
Requires-Dist: pyyaml>=6.0
|
|
44
46
|
Requires-Dist: regex>=2024.11
|
|
45
47
|
Requires-Dist: rich>=12.6
|
|
@@ -93,15 +95,17 @@ Dynamic: license-file
|
|
|
93
95
|
The recommended way to install the CLI is with [pipx](https://pipx.pypa.io), which automatically creates an isolated environment:
|
|
94
96
|
|
|
95
97
|
```bash
|
|
96
|
-
pipx install codeboarding --python python3.12
|
|
98
|
+
pipx install codeboarding --python python3.12 --pip-args="--extra-index-url https://pip.codeboarding.org/simple/"
|
|
97
99
|
```
|
|
98
100
|
|
|
99
101
|
Alternatively, install into an existing virtual environment with pip:
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
|
-
pip install codeboarding
|
|
104
|
+
pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
|
|
103
105
|
```
|
|
104
106
|
|
|
107
|
+
|
|
108
|
+
|
|
105
109
|
> 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.
|
|
106
110
|
|
|
107
111
|
Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
|