algo-cli-runtime 0.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- algo_cli/__init__.py +3 -0
- algo_cli/__main__.py +7 -0
- algo_cli/_internal/__init__.py +12 -0
- algo_cli/_internal/policy_chain.py +259 -0
- algo_cli/action_registry.py +1047 -0
- algo_cli/agent_blocks.py +550 -0
- algo_cli/agent_pipeline.py +1457 -0
- algo_cli/agent_threads.py +308 -0
- algo_cli/animations.py +316 -0
- algo_cli/cache_admission.py +209 -0
- algo_cli/capability_mask.py +66 -0
- algo_cli/chat_protocol.py +116 -0
- algo_cli/chatgpt_auth.py +510 -0
- algo_cli/chatgpt_client.py +657 -0
- algo_cli/code_rag.py +479 -0
- algo_cli/config.py +651 -0
- algo_cli/context_budget.py +679 -0
- algo_cli/credential_helpers.py +315 -0
- algo_cli/deliberation.py +29 -0
- algo_cli/display.py +1470 -0
- algo_cli/evals/__init__.py +21 -0
- algo_cli/evals/algorithm_effectiveness.py +560 -0
- algo_cli/evals/competitive_harness_rating.py +702 -0
- algo_cli/evals/cot_quality.py +220 -0
- algo_cli/evals/harness_retrieval_benchmark.py +401 -0
- algo_cli/evals/performance_regression.py +136 -0
- algo_cli/evals/scorecard_grading.py +308 -0
- algo_cli/evals/session_distribution.py +84 -0
- algo_cli/execution_guardrails.py +806 -0
- algo_cli/extensions_manifest.py +84 -0
- algo_cli/git_evidence.py +227 -0
- algo_cli/google_workspace.py +407 -0
- algo_cli/google_workspace_auth.py +523 -0
- algo_cli/harness.py +2587 -0
- algo_cli/identity.py +557 -0
- algo_cli/index_compute_lab.py +228 -0
- algo_cli/inference_harness.py +70 -0
- algo_cli/intelligence/__init__.py +1103 -0
- algo_cli/intelligence/acrobat_config.py +307 -0
- algo_cli/intelligence/acrobat_manifests.py +338 -0
- algo_cli/intelligence/acrobat_models.py +195 -0
- algo_cli/intelligence/acrobat_pipeline.py +295 -0
- algo_cli/intelligence/acrobat_runtime.py +302 -0
- algo_cli/intelligence/acrobat_security.py +261 -0
- algo_cli/intelligence/acrobat_workflows.py +226 -0
- algo_cli/intelligence/actionability.py +165 -0
- algo_cli/intelligence/adversarial_audit.py +136 -0
- algo_cli/intelligence/agent_arena.py +92 -0
- algo_cli/intelligence/agent_benchmark.py +236 -0
- algo_cli/intelligence/agent_runtime.py +171 -0
- algo_cli/intelligence/agents_as_tools.py +70 -0
- algo_cli/intelligence/artifact_binding.py +80 -0
- algo_cli/intelligence/autonomous_engineer.py +1976 -0
- algo_cli/intelligence/backpressure.py +99 -0
- algo_cli/intelligence/bloom_filter.py +186 -0
- algo_cli/intelligence/bonferroni.py +66 -0
- algo_cli/intelligence/boundary_compaction.py +98 -0
- algo_cli/intelligence/catalog_verifier.py +172 -0
- algo_cli/intelligence/cavecrew.py +118 -0
- algo_cli/intelligence/changelog.py +176 -0
- algo_cli/intelligence/checkpoint_resume.py +92 -0
- algo_cli/intelligence/circuit_breaker.py +88 -0
- algo_cli/intelligence/clarification_gate.py +101 -0
- algo_cli/intelligence/code_graph.py +180 -0
- algo_cli/intelligence/coderank.py +97 -0
- algo_cli/intelligence/consistent_hash.py +150 -0
- algo_cli/intelligence/consortium_synthesis.py +139 -0
- algo_cli/intelligence/construction/__init__.py +241 -0
- algo_cli/intelligence/construction/common.py +273 -0
- algo_cli/intelligence/construction/documents.py +496 -0
- algo_cli/intelligence/construction/labor_units.py +1395 -0
- algo_cli/intelligence/construction/payments.py +470 -0
- algo_cli/intelligence/construction/risk.py +784 -0
- algo_cli/intelligence/content_extractor.py +132 -0
- algo_cli/intelligence/context_adaptive.py +102 -0
- algo_cli/intelligence/context_ops.py +95 -0
- algo_cli/intelligence/count_min.py +145 -0
- algo_cli/intelligence/cow_state.py +103 -0
- algo_cli/intelligence/critic_loop.py +119 -0
- algo_cli/intelligence/cross_source.py +113 -0
- algo_cli/intelligence/daemon_mode.py +99 -0
- algo_cli/intelligence/dag_orchestration.py +151 -0
- algo_cli/intelligence/deep_research.py +155 -0
- algo_cli/intelligence/degenerate_detector.py +78 -0
- algo_cli/intelligence/delta_report.py +92 -0
- algo_cli/intelligence/discovery_event_log.py +92 -0
- algo_cli/intelligence/document_ingest.py +298 -0
- algo_cli/intelligence/dual_layer_validate.py +151 -0
- algo_cli/intelligence/echo_fidelity.py +73 -0
- algo_cli/intelligence/ema_tuning.py +104 -0
- algo_cli/intelligence/event_log.py +92 -0
- algo_cli/intelligence/evidence_graph.py +114 -0
- algo_cli/intelligence/extension_host.py +162 -0
- algo_cli/intelligence/extension_manifest.py +115 -0
- algo_cli/intelligence/falsification_suite.py +178 -0
- algo_cli/intelligence/finance/__init__.py +169 -0
- algo_cli/intelligence/finance/anomalies.py +135 -0
- algo_cli/intelligence/finance/ap_ar.py +351 -0
- algo_cli/intelligence/finance/cash.py +162 -0
- algo_cli/intelligence/finance/close.py +332 -0
- algo_cli/intelligence/finance/common.py +244 -0
- algo_cli/intelligence/finance/construction.py +135 -0
- algo_cli/intelligence/finance/controls.py +172 -0
- algo_cli/intelligence/finance/evidence.py +119 -0
- algo_cli/intelligence/finance/exceptions.py +157 -0
- algo_cli/intelligence/finance/reconciliations.py +254 -0
- algo_cli/intelligence/finance/revenue.py +109 -0
- algo_cli/intelligence/finance/tax.py +74 -0
- algo_cli/intelligence/finance/workpapers.py +111 -0
- algo_cli/intelligence/finding_record.py +120 -0
- algo_cli/intelligence/flow_dag.py +267 -0
- algo_cli/intelligence/gatherer.py +223 -0
- algo_cli/intelligence/golden_master.py +98 -0
- algo_cli/intelligence/graph_rag.py +195 -0
- algo_cli/intelligence/group_chat.py +143 -0
- algo_cli/intelligence/hash_dedup.py +145 -0
- algo_cli/intelligence/hyperloglog.py +128 -0
- algo_cli/intelligence/incremental_index.py +316 -0
- algo_cli/intelligence/index_store.py +16 -0
- algo_cli/intelligence/iteration_plan.py +133 -0
- algo_cli/intelligence/kernel_plugins.py +167 -0
- algo_cli/intelligence/lesson_catalog.py +135 -0
- algo_cli/intelligence/llm_fallback.py +169 -0
- algo_cli/intelligence/log2_histogram.py +267 -0
- algo_cli/intelligence/lsp_integration.py +147 -0
- algo_cli/intelligence/memory_evolution.py +117 -0
- algo_cli/intelligence/minhash_lsh.py +182 -0
- algo_cli/intelligence/multi_model_score.py +174 -0
- algo_cli/intelligence/multi_tier_grade.py +211 -0
- algo_cli/intelligence/negative_controls.py +113 -0
- algo_cli/intelligence/numeric_clamp.py +63 -0
- algo_cli/intelligence/occ_editor.py +66 -0
- algo_cli/intelligence/output_normalize.py +112 -0
- algo_cli/intelligence/parallel_delegation.py +98 -0
- algo_cli/intelligence/parallel_fanout.py +104 -0
- algo_cli/intelligence/permission_modes.py +105 -0
- algo_cli/intelligence/pre_push_gate.py +68 -0
- algo_cli/intelligence/prefetch.py +171 -0
- algo_cli/intelligence/process_framework.py +217 -0
- algo_cli/intelligence/project_graph.py +387 -0
- algo_cli/intelligence/query_expansion.py +146 -0
- algo_cli/intelligence/ralph_loop.py +117 -0
- algo_cli/intelligence/rate_limiter.py +153 -0
- algo_cli/intelligence/refactor_transaction.py +94 -0
- algo_cli/intelligence/research_workspace.py +108 -0
- algo_cli/intelligence/retraction_ledger.py +72 -0
- algo_cli/intelligence/saga_pattern.py +88 -0
- algo_cli/intelligence/session_fork.py +100 -0
- algo_cli/intelligence/shadow_editor.py +67 -0
- algo_cli/intelligence/shell_session.py +213 -0
- algo_cli/intelligence/source_registry.py +143 -0
- algo_cli/intelligence/spawn_scales.py +99 -0
- algo_cli/intelligence/stat_stability.py +104 -0
- algo_cli/intelligence/structural_validator.py +148 -0
- algo_cli/intelligence/subagent_spawner.py +111 -0
- algo_cli/intelligence/symmetric_verify.py +70 -0
- algo_cli/intelligence/task_classifier.py +129 -0
- algo_cli/intelligence/team_execution.py +122 -0
- algo_cli/intelligence/tiered_access.py +121 -0
- algo_cli/intelligence/utility_registry.py +159 -0
- algo_cli/intuition_engine.py +560 -0
- algo_cli/intuition_injector.py +82 -0
- algo_cli/kernels/__init__.py +5 -0
- algo_cli/kernels/manifest.py +763 -0
- algo_cli/main.py +3903 -0
- algo_cli/memory_candidates.py +541 -0
- algo_cli/memory_echo_veil.py +394 -0
- algo_cli/memory_runtime.py +112 -0
- algo_cli/model_info.py +548 -0
- algo_cli/model_profile.py +160 -0
- algo_cli/model_routing.py +74 -0
- algo_cli/oneshot.py +331 -0
- algo_cli/perf_telemetry.py +389 -0
- algo_cli/plugins.py +245 -0
- algo_cli/private_event_store.py +654 -0
- algo_cli/quantization/__init__.py +24 -0
- algo_cli/quantization/lloyd_max.py +98 -0
- algo_cli/quantization/turbo_quant.py +308 -0
- algo_cli/reasoning/__init__.py +46 -0
- algo_cli/reasoning/combinatorial.py +356 -0
- algo_cli/reasoning/graph_of_thought.py +297 -0
- algo_cli/reasoning/mcts.py +220 -0
- algo_cli/reasoning/neuro_symbolic.py +250 -0
- algo_cli/reasoning/react.py +246 -0
- algo_cli/reasoning/reflexion.py +225 -0
- algo_cli/reasoning/tree_of_thought.py +241 -0
- algo_cli/reasoning_bridge.py +150 -0
- algo_cli/reconciliation.py +284 -0
- algo_cli/reflex.py +385 -0
- algo_cli/resources/docs/ALGO.md +13958 -0
- algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
- algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
- algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
- algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
- algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
- algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
- algo_cli/resources/docs/main-split-map.md +35 -0
- algo_cli/resources/docs/privacy-and-context.md +48 -0
- algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
- algo_cli/resources/skills/README.md +26 -0
- algo_cli/resources/skills/algo-cli.md +59 -0
- algo_cli/resources/skills/edit-file-precision.md +49 -0
- algo_cli/resources/skills/harness-search-first.md +47 -0
- algo_cli/resources/skills/memory-recall-ritual.md +51 -0
- algo_cli/resources/skills/qol-algorithms.md +224 -0
- algo_cli/resources/skills/smart-error-recovery.md +56 -0
- algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
- algo_cli/retrieval_algorithms.py +127 -0
- algo_cli/runtime_qos.py +236 -0
- algo_cli/runtime_services.py +320 -0
- algo_cli/session_commands.py +95 -0
- algo_cli/session_mode.py +113 -0
- algo_cli/skills.py +430 -0
- algo_cli/slash_dispatch.py +1265 -0
- algo_cli/small_context.py +206 -0
- algo_cli/spawn_budget.py +89 -0
- algo_cli/task_ledger.py +84 -0
- algo_cli/task_router.py +197 -0
- algo_cli/tool_context.py +94 -0
- algo_cli/tool_contract.py +99 -0
- algo_cli/tool_policy.py +357 -0
- algo_cli/tool_runtime.py +647 -0
- algo_cli/tools.py +3056 -0
- algo_cli/url_scheme.py +174 -0
- algo_cli/verify.py +154 -0
- algo_cli/version_manifest.py +178 -0
- algo_cli/vision_screenshot_verify.py +76 -0
- algo_cli/workspace_resolver.py +68 -0
- algo_cli/x_account.py +209 -0
- algo_cli/xai_auth.py +374 -0
- algo_cli/xai_client.py +600 -0
- algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
- algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
- algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
- algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
- algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
- ollama_cli/__init__.py +67 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""B77. AST-Based Code Knowledge Graph.
|
|
2
|
+
|
|
3
|
+
Build a queryable graph from AST: CONTAINS, CALLS, IMPORTS, INHERITS.
|
|
4
|
+
Source: PyCodeKG pattern.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import ast
|
|
9
|
+
import hashlib
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum, auto
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NodeKind(Enum):
|
|
16
|
+
MODULE = auto()
|
|
17
|
+
CLASS = auto()
|
|
18
|
+
FUNCTION = auto()
|
|
19
|
+
METHOD = auto()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EdgeKind(Enum):
|
|
23
|
+
CONTAINS = auto()
|
|
24
|
+
CALLS = auto()
|
|
25
|
+
IMPORTS = auto()
|
|
26
|
+
INHERITS = auto()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CodeNode:
|
|
31
|
+
id: str
|
|
32
|
+
kind: NodeKind
|
|
33
|
+
name: str
|
|
34
|
+
qualified_name: str
|
|
35
|
+
file: str
|
|
36
|
+
line: int
|
|
37
|
+
signature: str = ""
|
|
38
|
+
docstring: str = ""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class CodeEdge:
|
|
43
|
+
source: str
|
|
44
|
+
target: str
|
|
45
|
+
kind: EdgeKind
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CodeGraph:
|
|
49
|
+
"""Queryable AST-derived graph of codebase structure."""
|
|
50
|
+
|
|
51
|
+
def __init__(self) -> None:
|
|
52
|
+
self._nodes: dict[str, CodeNode] = {}
|
|
53
|
+
self._edges: list[CodeEdge] = []
|
|
54
|
+
self._by_name: dict[str, list[str]] = {} # name → node IDs
|
|
55
|
+
|
|
56
|
+
def build(self, root: Path, pattern: str = "*.py") -> int:
|
|
57
|
+
"""Build graph from Python files. Returns node count."""
|
|
58
|
+
for py_file in root.rglob(pattern):
|
|
59
|
+
try:
|
|
60
|
+
source = py_file.read_text(encoding="utf-8")
|
|
61
|
+
tree = ast.parse(source, filename=str(py_file))
|
|
62
|
+
self._process_module(tree, str(py_file), str(root))
|
|
63
|
+
except Exception:
|
|
64
|
+
continue
|
|
65
|
+
return len(self._nodes)
|
|
66
|
+
|
|
67
|
+
def _process_module(self, tree: ast.Module, filepath: str, root: str) -> None:
|
|
68
|
+
module_id = self._node_id(filepath, "module", 0)
|
|
69
|
+
module_node = CodeNode(
|
|
70
|
+
id=module_id, kind=NodeKind.MODULE, name=Path(filepath).stem,
|
|
71
|
+
qualified_name=Path(filepath).relative_to(root).as_posix() if filepath.startswith(root) else filepath,
|
|
72
|
+
file=filepath, line=0,
|
|
73
|
+
)
|
|
74
|
+
self._add_node(module_node)
|
|
75
|
+
|
|
76
|
+
for node in ast.walk(tree):
|
|
77
|
+
if isinstance(node, ast.ClassDef):
|
|
78
|
+
cls_id = self._node_id(filepath, node.name, node.lineno)
|
|
79
|
+
cls_node = CodeNode(
|
|
80
|
+
id=cls_id, kind=NodeKind.CLASS, name=node.name,
|
|
81
|
+
qualified_name=f"{module_node.name}.{node.name}",
|
|
82
|
+
file=filepath, line=node.lineno,
|
|
83
|
+
docstring=ast.get_docstring(node) or "",
|
|
84
|
+
)
|
|
85
|
+
self._add_node(cls_node)
|
|
86
|
+
self._edges.append(CodeEdge(source=module_id, target=cls_id, kind=EdgeKind.CONTAINS))
|
|
87
|
+
|
|
88
|
+
# Inheritance
|
|
89
|
+
for base in node.bases:
|
|
90
|
+
if isinstance(base, ast.Name):
|
|
91
|
+
self._edges.append(CodeEdge(
|
|
92
|
+
source=cls_id, target=base.id, kind=EdgeKind.INHERITS
|
|
93
|
+
))
|
|
94
|
+
|
|
95
|
+
# Methods
|
|
96
|
+
for item in node.body:
|
|
97
|
+
if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
98
|
+
method_id = self._node_id(filepath, f"{node.name}.{item.name}", item.lineno)
|
|
99
|
+
method_node = CodeNode(
|
|
100
|
+
id=method_id, kind=NodeKind.METHOD, name=item.name,
|
|
101
|
+
qualified_name=f"{module_node.name}.{node.name}.{item.name}",
|
|
102
|
+
file=filepath, line=item.lineno,
|
|
103
|
+
signature=self._signature(item),
|
|
104
|
+
docstring=ast.get_docstring(item) or "",
|
|
105
|
+
)
|
|
106
|
+
self._add_node(method_node)
|
|
107
|
+
self._edges.append(CodeEdge(source=cls_id, target=method_id, kind=EdgeKind.CONTAINS))
|
|
108
|
+
self._process_calls(method_id, item)
|
|
109
|
+
|
|
110
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
111
|
+
func_id = self._node_id(filepath, node.name, node.lineno)
|
|
112
|
+
func_node = CodeNode(
|
|
113
|
+
id=func_id, kind=NodeKind.FUNCTION, name=node.name,
|
|
114
|
+
qualified_name=f"{module_node.name}.{node.name}",
|
|
115
|
+
file=filepath, line=node.lineno,
|
|
116
|
+
signature=self._signature(node),
|
|
117
|
+
docstring=ast.get_docstring(node) or "",
|
|
118
|
+
)
|
|
119
|
+
self._add_node(func_node)
|
|
120
|
+
self._edges.append(CodeEdge(source=module_id, target=func_id, kind=EdgeKind.CONTAINS))
|
|
121
|
+
self._process_calls(func_id, node)
|
|
122
|
+
|
|
123
|
+
elif isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
124
|
+
self._process_imports(module_id, node)
|
|
125
|
+
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def _process_calls(self, source_id: str, func_node: ast.FunctionDef) -> None:
|
|
129
|
+
for node in ast.walk(func_node):
|
|
130
|
+
if isinstance(node, ast.Call):
|
|
131
|
+
if isinstance(node.func, ast.Name):
|
|
132
|
+
self._edges.append(CodeEdge(source=source_id, target=node.func.id, kind=EdgeKind.CALLS))
|
|
133
|
+
elif isinstance(node.func, ast.Attribute):
|
|
134
|
+
self._edges.append(CodeEdge(source=source_id, target=node.func.attr, kind=EdgeKind.CALLS))
|
|
135
|
+
|
|
136
|
+
def _process_imports(self, module_id: str, node: ast.Import | ast.ImportFrom) -> None:
|
|
137
|
+
if isinstance(node, ast.Import):
|
|
138
|
+
for alias in node.names:
|
|
139
|
+
self._edges.append(CodeEdge(source=module_id, target=alias.name, kind=EdgeKind.IMPORTS))
|
|
140
|
+
elif isinstance(node, ast.ImportFrom):
|
|
141
|
+
module = node.module or ""
|
|
142
|
+
for alias in node.names:
|
|
143
|
+
target = f"{module}.{alias.name}" if module else alias.name
|
|
144
|
+
self._edges.append(CodeEdge(source=module_id, target=target, kind=EdgeKind.IMPORTS))
|
|
145
|
+
|
|
146
|
+
def _signature(self, node: ast.FunctionDef) -> str:
|
|
147
|
+
args = [a.arg for a in node.args.args]
|
|
148
|
+
return f"({', '.join(args)})"
|
|
149
|
+
|
|
150
|
+
def _node_id(self, filepath: str, name: str, line: int) -> str:
|
|
151
|
+
return hashlib.md5(f"{filepath}:{name}:{line}".encode()).hexdigest()[:12]
|
|
152
|
+
|
|
153
|
+
def _add_node(self, node: CodeNode) -> None:
|
|
154
|
+
self._nodes[node.id] = node
|
|
155
|
+
self._by_name.setdefault(node.name, []).append(node.id)
|
|
156
|
+
|
|
157
|
+
def callers(self, name: str) -> list[CodeNode]:
|
|
158
|
+
"""Find all nodes that call the given function name."""
|
|
159
|
+
caller_ids = {e.source for e in self._edges if e.kind == EdgeKind.CALLS and e.target == name}
|
|
160
|
+
return [self._nodes[cid] for cid in caller_ids if cid in self._nodes]
|
|
161
|
+
|
|
162
|
+
def callees(self, name: str) -> list[str]:
|
|
163
|
+
"""Find all functions called by the given function name."""
|
|
164
|
+
return [e.target for e in self._edges if e.kind == EdgeKind.CALLS
|
|
165
|
+
and any(self._nodes[nid].name == name for nid in [e.source] if nid in self._nodes)]
|
|
166
|
+
|
|
167
|
+
def dead_code(self) -> list[CodeNode]:
|
|
168
|
+
"""Find functions/methods with zero callers."""
|
|
169
|
+
called_names = {e.target for e in self._edges if e.kind == EdgeKind.CALLS}
|
|
170
|
+
return [n for n in self._nodes.values()
|
|
171
|
+
if n.kind in (NodeKind.FUNCTION, NodeKind.METHOD)
|
|
172
|
+
and n.name not in called_names]
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def node_count(self) -> int:
|
|
176
|
+
return len(self._nodes)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def edge_count(self) -> int:
|
|
180
|
+
return len(self._edges)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""B85. CodeRank: Structural Importance Ranking.
|
|
2
|
+
|
|
3
|
+
PageRank over code graph to find most structurally important symbols.
|
|
4
|
+
Source: PyCodeKG pattern.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class CodeRankResult:
|
|
13
|
+
symbol: str
|
|
14
|
+
rank: float
|
|
15
|
+
kind: str = ""
|
|
16
|
+
file: str = ""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CodeRank:
|
|
20
|
+
"""Compute PageRank over a code graph to find critical symbols."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, damping: float = 0.85, max_iterations: int = 100,
|
|
23
|
+
tolerance: float = 1e-6) -> None:
|
|
24
|
+
self._damping = damping
|
|
25
|
+
self._max_iter = max_iterations
|
|
26
|
+
self._tolerance = tolerance
|
|
27
|
+
|
|
28
|
+
def compute(self, nodes: list[str], edges: list[tuple[str, str]],
|
|
29
|
+
weights: dict[tuple[str, str], float] | None = None) -> list[CodeRankResult]:
|
|
30
|
+
"""Compute PageRank for code symbols.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
nodes: List of node IDs
|
|
34
|
+
edges: List of (source, target) directed edges
|
|
35
|
+
weights: Optional edge weights
|
|
36
|
+
"""
|
|
37
|
+
if not nodes:
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
n = len(nodes)
|
|
41
|
+
node_idx = {node: i for i, node in enumerate(nodes)}
|
|
42
|
+
|
|
43
|
+
# Build adjacency: out-links and in-links
|
|
44
|
+
out_links: dict[int, list[int]] = {i: [] for i in range(n)}
|
|
45
|
+
in_links: dict[int, list[int]] = {i: [] for i in range(n)}
|
|
46
|
+
|
|
47
|
+
for src, tgt in edges:
|
|
48
|
+
if src in node_idx and tgt in node_idx:
|
|
49
|
+
si, ti = node_idx[src], node_idx[tgt]
|
|
50
|
+
out_links[si].append(ti)
|
|
51
|
+
in_links[ti].append(si)
|
|
52
|
+
|
|
53
|
+
# Initialize ranks
|
|
54
|
+
ranks = [1.0 / n] * n
|
|
55
|
+
|
|
56
|
+
# Iterate
|
|
57
|
+
for _ in range(self._max_iter):
|
|
58
|
+
new_ranks = [0.0] * n
|
|
59
|
+
for i in range(n):
|
|
60
|
+
# Base rank from random jump
|
|
61
|
+
new_ranks[i] = (1 - self._damping) / n
|
|
62
|
+
|
|
63
|
+
# Rank from in-links
|
|
64
|
+
for j in in_links[i]:
|
|
65
|
+
out_count = len(out_links[j])
|
|
66
|
+
if out_count > 0:
|
|
67
|
+
new_ranks[i] += self._damping * ranks[j] / out_count
|
|
68
|
+
else:
|
|
69
|
+
# Dangling node: distribute rank evenly
|
|
70
|
+
new_ranks[i] += self._damping * ranks[j] / n
|
|
71
|
+
|
|
72
|
+
# Check convergence
|
|
73
|
+
diff = sum(abs(new_ranks[i] - ranks[i]) for i in range(n))
|
|
74
|
+
ranks = new_ranks
|
|
75
|
+
if diff < self._tolerance:
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
# Sort by rank descending
|
|
79
|
+
results = [
|
|
80
|
+
CodeRankResult(symbol=nodes[i], rank=ranks[i])
|
|
81
|
+
for i in range(n)
|
|
82
|
+
]
|
|
83
|
+
results.sort(key=lambda x: x.rank, reverse=True)
|
|
84
|
+
return results
|
|
85
|
+
|
|
86
|
+
def find_critical(self, results: list[CodeRankResult],
|
|
87
|
+
top_k: int = 10) -> list[CodeRankResult]:
|
|
88
|
+
"""Find the top-K most structurally critical symbols."""
|
|
89
|
+
return results[:top_k]
|
|
90
|
+
|
|
91
|
+
def find_bottlenecks(self, results: list[CodeRankResult],
|
|
92
|
+
threshold: float = 0.05) -> list[CodeRankResult]:
|
|
93
|
+
"""Find symbols with unusually high rank (potential bottlenecks)."""
|
|
94
|
+
if not results:
|
|
95
|
+
return []
|
|
96
|
+
avg_rank = sum(r.rank for r in results) / len(results)
|
|
97
|
+
return [r for r in results if r.rank > avg_rank * (1 + threshold)]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Consistent Hashing — minimal redistribution on topology change.
|
|
2
|
+
|
|
3
|
+
Distributes keys across N nodes using a ring of virtual nodes (vnodes).
|
|
4
|
+
When a node is added or removed, only K/N keys need to move (where K is
|
|
5
|
+
total keys and N is node count), vs. K keys with naive mod-N hashing.
|
|
6
|
+
|
|
7
|
+
Harness use:
|
|
8
|
+
- Route requests across multiple Ollama instances
|
|
9
|
+
- Route embedding batches across multiple model endpoints
|
|
10
|
+
- Distribute harness indexing work across worker processes
|
|
11
|
+
- Session affinity — keep the same user on the same node
|
|
12
|
+
|
|
13
|
+
Operations:
|
|
14
|
+
- add_node(node): add a node to the ring
|
|
15
|
+
- remove_node(node): remove a node from the ring
|
|
16
|
+
- get_node(key): find the node responsible for a key
|
|
17
|
+
- get_nodes(key, count): get the next N nodes (for replication)
|
|
18
|
+
|
|
19
|
+
Properties:
|
|
20
|
+
- O(log V) lookup where V = num_nodes * vnodes_per_node
|
|
21
|
+
- Monotonic: only keys mapped to removed/added nodes move
|
|
22
|
+
- Load balanced via virtual nodes
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import hashlib
|
|
27
|
+
from collections import defaultdict
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConsistentHashRing:
|
|
32
|
+
"""Consistent hash ring with virtual nodes.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
vnodes_per_node: Number of virtual nodes per physical node.
|
|
36
|
+
Higher = better load distribution, more memory.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, vnodes_per_node: int = 150) -> None:
|
|
40
|
+
self.vnodes_per_node = vnodes_per_node
|
|
41
|
+
self._ring: dict[int, str] = {} # hash -> node_name
|
|
42
|
+
self._sorted_hashes: list[int] = []
|
|
43
|
+
self._nodes: set[str] = set()
|
|
44
|
+
|
|
45
|
+
def _hash(self, key: str) -> int:
|
|
46
|
+
"""Hash a key to a 64-bit integer."""
|
|
47
|
+
return int.from_bytes(
|
|
48
|
+
hashlib.md5(key.encode("utf-8")).digest()[:8],
|
|
49
|
+
"little",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def add_node(self, node: str) -> None:
|
|
53
|
+
"""Add a node to the ring with vnodes_per_node virtual nodes."""
|
|
54
|
+
if node in self._nodes:
|
|
55
|
+
return
|
|
56
|
+
self._nodes.add(node)
|
|
57
|
+
for i in range(self.vnodes_per_node):
|
|
58
|
+
vnode_key = f"{node}#{i}"
|
|
59
|
+
h = self._hash(vnode_key)
|
|
60
|
+
self._ring[h] = node
|
|
61
|
+
self._sorted_hashes = sorted(self._ring.keys())
|
|
62
|
+
|
|
63
|
+
def remove_node(self, node: str) -> None:
|
|
64
|
+
"""Remove a node and all its virtual nodes."""
|
|
65
|
+
if node not in self._nodes:
|
|
66
|
+
return
|
|
67
|
+
self._nodes.discard(node)
|
|
68
|
+
for i in range(self.vnodes_per_node):
|
|
69
|
+
vnode_key = f"{node}#{i}"
|
|
70
|
+
h = self._hash(vnode_key)
|
|
71
|
+
self._ring.pop(h, None)
|
|
72
|
+
self._sorted_hashes = sorted(self._ring.keys())
|
|
73
|
+
|
|
74
|
+
def get_node(self, key: Any) -> str | None:
|
|
75
|
+
"""Find the node responsible for a key.
|
|
76
|
+
|
|
77
|
+
Returns the first node clockwise from the key's hash position.
|
|
78
|
+
Returns None if the ring is empty.
|
|
79
|
+
"""
|
|
80
|
+
if not self._sorted_hashes:
|
|
81
|
+
return None
|
|
82
|
+
h = self._hash(str(key))
|
|
83
|
+
# Binary search for the first hash >= h
|
|
84
|
+
lo, hi = 0, len(self._sorted_hashes)
|
|
85
|
+
while lo < hi:
|
|
86
|
+
mid = (lo + hi) // 2
|
|
87
|
+
if self._sorted_hashes[mid] < h:
|
|
88
|
+
lo = mid + 1
|
|
89
|
+
else:
|
|
90
|
+
hi = mid
|
|
91
|
+
# Wrap around if we passed the end
|
|
92
|
+
if lo == len(self._sorted_hashes):
|
|
93
|
+
lo = 0
|
|
94
|
+
return self._ring[self._sorted_hashes[lo]]
|
|
95
|
+
|
|
96
|
+
def get_nodes(self, key: Any, count: int = 3) -> list[str]:
|
|
97
|
+
"""Get the next *count* distinct nodes for a key (for replication).
|
|
98
|
+
|
|
99
|
+
Walks clockwise around the ring collecting distinct nodes.
|
|
100
|
+
"""
|
|
101
|
+
if not self._sorted_hashes or count <= 0:
|
|
102
|
+
return []
|
|
103
|
+
h = self._hash(str(key))
|
|
104
|
+
lo, hi = 0, len(self._sorted_hashes)
|
|
105
|
+
while lo < hi:
|
|
106
|
+
mid = (lo + hi) // 2
|
|
107
|
+
if self._sorted_hashes[mid] < h:
|
|
108
|
+
lo = mid + 1
|
|
109
|
+
else:
|
|
110
|
+
hi = mid
|
|
111
|
+
result: list[str] = []
|
|
112
|
+
seen: set[str] = set()
|
|
113
|
+
n = len(self._sorted_hashes)
|
|
114
|
+
for i in range(n):
|
|
115
|
+
idx = (lo + i) % n
|
|
116
|
+
node = self._ring[self._sorted_hashes[idx]]
|
|
117
|
+
if node not in seen:
|
|
118
|
+
seen.add(node)
|
|
119
|
+
result.append(node)
|
|
120
|
+
if len(result) >= count:
|
|
121
|
+
break
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
def node_distribution(self, sample_keys: list[Any]) -> dict[str, int]:
|
|
125
|
+
"""Simulate key distribution across nodes.
|
|
126
|
+
|
|
127
|
+
Useful for verifying load balance.
|
|
128
|
+
"""
|
|
129
|
+
dist: dict[str, int] = defaultdict(int)
|
|
130
|
+
for key in sample_keys:
|
|
131
|
+
node = self.get_node(key)
|
|
132
|
+
if node:
|
|
133
|
+
dist[node] += 1
|
|
134
|
+
return dict(dist)
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def node_count(self) -> int:
|
|
138
|
+
return len(self._nodes)
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def ring_size(self) -> int:
|
|
142
|
+
return len(self._ring)
|
|
143
|
+
|
|
144
|
+
def stats(self) -> dict[str, Any]:
|
|
145
|
+
return {
|
|
146
|
+
"nodes": list(self._nodes),
|
|
147
|
+
"node_count": len(self._nodes),
|
|
148
|
+
"vnodes_per_node": self.vnodes_per_node,
|
|
149
|
+
"ring_size": len(self._ring),
|
|
150
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""H25 — Consortium Synthesis.
|
|
2
|
+
|
|
3
|
+
Synthesize ground truth from all model responses (vs H12's pick-winner).
|
|
4
|
+
Mined from G0DM0D3 api/lib/consortium.ts.
|
|
5
|
+
|
|
6
|
+
Unlike ULTRAPLINIAN (H12) which picks the BEST single response, CONSORTIUM
|
|
7
|
+
collects ALL responses and feeds them to an orchestrator model that
|
|
8
|
+
synthesizes ground truth. Key principles: ground truth over popularity,
|
|
9
|
+
specificity wins, no hedging, attribution-free.
|
|
10
|
+
|
|
11
|
+
LLM integration: requires an orchestrator model for synthesis. Falls back
|
|
12
|
+
to rule-based merge when no model is available.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ConsortiumResponse:
|
|
22
|
+
"""A single model's response in the consortium."""
|
|
23
|
+
|
|
24
|
+
model_name: str
|
|
25
|
+
response: str
|
|
26
|
+
confidence: float = 0.0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SynthesisResult:
|
|
31
|
+
"""Result of consortium synthesis."""
|
|
32
|
+
|
|
33
|
+
synthesized: str
|
|
34
|
+
source_models: list[str] = field(default_factory=list)
|
|
35
|
+
agreement_score: float = 0.0
|
|
36
|
+
method: str = "rule-based" # or "llm-orchestrated"
|
|
37
|
+
dissenting_models: list[str] = field(default_factory=list)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_claims(text: str) -> set[str]:
|
|
41
|
+
"""Extract claim-like sentences from text (rule-based)."""
|
|
42
|
+
import re
|
|
43
|
+
# Split on sentence boundaries
|
|
44
|
+
sentences = re.split(r"[.!?]\s+", text)
|
|
45
|
+
# Filter to substantive sentences (not too short, not questions)
|
|
46
|
+
claims = set()
|
|
47
|
+
for s in sentences:
|
|
48
|
+
s = s.strip()
|
|
49
|
+
if len(s) > 10 and not s.endswith("?"):
|
|
50
|
+
claims.add(s.lower())
|
|
51
|
+
return claims
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _rule_based_synthesize(responses: list[ConsortiumResponse]) -> SynthesisResult:
|
|
55
|
+
"""Rule-based synthesis: find common claims across models."""
|
|
56
|
+
if not responses:
|
|
57
|
+
return SynthesisResult(synthesized="", method="rule-based")
|
|
58
|
+
|
|
59
|
+
# Extract claims from each response
|
|
60
|
+
all_claims: dict[str, set[str]] = {}
|
|
61
|
+
for resp in responses:
|
|
62
|
+
all_claims[resp.model_name] = _extract_claims(resp.response)
|
|
63
|
+
|
|
64
|
+
# Find claims that appear in multiple responses (agreement)
|
|
65
|
+
claim_counts: dict[str, int] = {}
|
|
66
|
+
for model_claims in all_claims.values():
|
|
67
|
+
for claim in model_claims:
|
|
68
|
+
claim_counts[claim] = claim_counts.get(claim, 0) + 1
|
|
69
|
+
|
|
70
|
+
# Synthesize: claims that appear in majority of responses
|
|
71
|
+
threshold = max(2, len(responses) // 2 + 1)
|
|
72
|
+
consensus_claims = [c for c, count in claim_counts.items() if count >= threshold]
|
|
73
|
+
|
|
74
|
+
# Sort by frequency (most agreed first)
|
|
75
|
+
consensus_claims.sort(key=lambda c: -claim_counts[c])
|
|
76
|
+
|
|
77
|
+
synthesized = ". ".join(consensus_claims[:10]) if consensus_claims else responses[0].response
|
|
78
|
+
|
|
79
|
+
# Agreement score: fraction of claims that reached consensus
|
|
80
|
+
total_claims = len(claim_counts)
|
|
81
|
+
consensus_count = len(consensus_claims)
|
|
82
|
+
agreement = consensus_count / total_claims if total_claims > 0 else 0.0
|
|
83
|
+
|
|
84
|
+
# Find dissenting models (low overlap with consensus)
|
|
85
|
+
consensus_set = set(consensus_claims)
|
|
86
|
+
dissenting = []
|
|
87
|
+
for model_name, model_claims in all_claims.items():
|
|
88
|
+
overlap = len(model_claims & consensus_set)
|
|
89
|
+
if overlap < len(consensus_set) * 0.3:
|
|
90
|
+
dissenting.append(model_name)
|
|
91
|
+
|
|
92
|
+
return SynthesisResult(
|
|
93
|
+
synthesized=synthesized,
|
|
94
|
+
source_models=[r.model_name for r in responses],
|
|
95
|
+
agreement_score=agreement,
|
|
96
|
+
method="rule-based",
|
|
97
|
+
dissenting_models=dissenting,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def synthesize(
|
|
102
|
+
responses: list[ConsortiumResponse],
|
|
103
|
+
orchestrator_client: Any | None = None,
|
|
104
|
+
) -> SynthesisResult:
|
|
105
|
+
"""Synthesize ground truth from multiple model responses.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
responses: List of model responses.
|
|
109
|
+
orchestrator_client: Optional orchestrator model that synthesizes
|
|
110
|
+
from all responses. Falls back to rule-based merge.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
SynthesisResult with synthesized ground truth.
|
|
114
|
+
"""
|
|
115
|
+
if not responses:
|
|
116
|
+
return SynthesisResult(synthesized="", method="empty")
|
|
117
|
+
|
|
118
|
+
# Try LLM orchestrator if available
|
|
119
|
+
if orchestrator_client is not None:
|
|
120
|
+
try:
|
|
121
|
+
prompt_parts = ["Synthesize ground truth from these model responses:"]
|
|
122
|
+
for resp in responses:
|
|
123
|
+
prompt_parts.append(f"\n[{resp.model_name}]: {resp.response}")
|
|
124
|
+
prompt_parts.append("\n\nPrinciples: ground truth over popularity, specificity wins, no hedging, attribution-free.")
|
|
125
|
+
prompt = "\n".join(prompt_parts)
|
|
126
|
+
|
|
127
|
+
result = orchestrator_client.generate(prompt)
|
|
128
|
+
if result:
|
|
129
|
+
return SynthesisResult(
|
|
130
|
+
synthesized=result,
|
|
131
|
+
source_models=[r.model_name for r in responses],
|
|
132
|
+
agreement_score=1.0,
|
|
133
|
+
method="llm-orchestrated",
|
|
134
|
+
)
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
# Fall back to rule-based synthesis
|
|
139
|
+
return _rule_based_synthesize(responses)
|