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.
Files changed (237) hide show
  1. algo_cli/__init__.py +3 -0
  2. algo_cli/__main__.py +7 -0
  3. algo_cli/_internal/__init__.py +12 -0
  4. algo_cli/_internal/policy_chain.py +259 -0
  5. algo_cli/action_registry.py +1047 -0
  6. algo_cli/agent_blocks.py +550 -0
  7. algo_cli/agent_pipeline.py +1457 -0
  8. algo_cli/agent_threads.py +308 -0
  9. algo_cli/animations.py +316 -0
  10. algo_cli/cache_admission.py +209 -0
  11. algo_cli/capability_mask.py +66 -0
  12. algo_cli/chat_protocol.py +116 -0
  13. algo_cli/chatgpt_auth.py +510 -0
  14. algo_cli/chatgpt_client.py +657 -0
  15. algo_cli/code_rag.py +479 -0
  16. algo_cli/config.py +651 -0
  17. algo_cli/context_budget.py +679 -0
  18. algo_cli/credential_helpers.py +315 -0
  19. algo_cli/deliberation.py +29 -0
  20. algo_cli/display.py +1470 -0
  21. algo_cli/evals/__init__.py +21 -0
  22. algo_cli/evals/algorithm_effectiveness.py +560 -0
  23. algo_cli/evals/competitive_harness_rating.py +702 -0
  24. algo_cli/evals/cot_quality.py +220 -0
  25. algo_cli/evals/harness_retrieval_benchmark.py +401 -0
  26. algo_cli/evals/performance_regression.py +136 -0
  27. algo_cli/evals/scorecard_grading.py +308 -0
  28. algo_cli/evals/session_distribution.py +84 -0
  29. algo_cli/execution_guardrails.py +806 -0
  30. algo_cli/extensions_manifest.py +84 -0
  31. algo_cli/git_evidence.py +227 -0
  32. algo_cli/google_workspace.py +407 -0
  33. algo_cli/google_workspace_auth.py +523 -0
  34. algo_cli/harness.py +2587 -0
  35. algo_cli/identity.py +557 -0
  36. algo_cli/index_compute_lab.py +228 -0
  37. algo_cli/inference_harness.py +70 -0
  38. algo_cli/intelligence/__init__.py +1103 -0
  39. algo_cli/intelligence/acrobat_config.py +307 -0
  40. algo_cli/intelligence/acrobat_manifests.py +338 -0
  41. algo_cli/intelligence/acrobat_models.py +195 -0
  42. algo_cli/intelligence/acrobat_pipeline.py +295 -0
  43. algo_cli/intelligence/acrobat_runtime.py +302 -0
  44. algo_cli/intelligence/acrobat_security.py +261 -0
  45. algo_cli/intelligence/acrobat_workflows.py +226 -0
  46. algo_cli/intelligence/actionability.py +165 -0
  47. algo_cli/intelligence/adversarial_audit.py +136 -0
  48. algo_cli/intelligence/agent_arena.py +92 -0
  49. algo_cli/intelligence/agent_benchmark.py +236 -0
  50. algo_cli/intelligence/agent_runtime.py +171 -0
  51. algo_cli/intelligence/agents_as_tools.py +70 -0
  52. algo_cli/intelligence/artifact_binding.py +80 -0
  53. algo_cli/intelligence/autonomous_engineer.py +1976 -0
  54. algo_cli/intelligence/backpressure.py +99 -0
  55. algo_cli/intelligence/bloom_filter.py +186 -0
  56. algo_cli/intelligence/bonferroni.py +66 -0
  57. algo_cli/intelligence/boundary_compaction.py +98 -0
  58. algo_cli/intelligence/catalog_verifier.py +172 -0
  59. algo_cli/intelligence/cavecrew.py +118 -0
  60. algo_cli/intelligence/changelog.py +176 -0
  61. algo_cli/intelligence/checkpoint_resume.py +92 -0
  62. algo_cli/intelligence/circuit_breaker.py +88 -0
  63. algo_cli/intelligence/clarification_gate.py +101 -0
  64. algo_cli/intelligence/code_graph.py +180 -0
  65. algo_cli/intelligence/coderank.py +97 -0
  66. algo_cli/intelligence/consistent_hash.py +150 -0
  67. algo_cli/intelligence/consortium_synthesis.py +139 -0
  68. algo_cli/intelligence/construction/__init__.py +241 -0
  69. algo_cli/intelligence/construction/common.py +273 -0
  70. algo_cli/intelligence/construction/documents.py +496 -0
  71. algo_cli/intelligence/construction/labor_units.py +1395 -0
  72. algo_cli/intelligence/construction/payments.py +470 -0
  73. algo_cli/intelligence/construction/risk.py +784 -0
  74. algo_cli/intelligence/content_extractor.py +132 -0
  75. algo_cli/intelligence/context_adaptive.py +102 -0
  76. algo_cli/intelligence/context_ops.py +95 -0
  77. algo_cli/intelligence/count_min.py +145 -0
  78. algo_cli/intelligence/cow_state.py +103 -0
  79. algo_cli/intelligence/critic_loop.py +119 -0
  80. algo_cli/intelligence/cross_source.py +113 -0
  81. algo_cli/intelligence/daemon_mode.py +99 -0
  82. algo_cli/intelligence/dag_orchestration.py +151 -0
  83. algo_cli/intelligence/deep_research.py +155 -0
  84. algo_cli/intelligence/degenerate_detector.py +78 -0
  85. algo_cli/intelligence/delta_report.py +92 -0
  86. algo_cli/intelligence/discovery_event_log.py +92 -0
  87. algo_cli/intelligence/document_ingest.py +298 -0
  88. algo_cli/intelligence/dual_layer_validate.py +151 -0
  89. algo_cli/intelligence/echo_fidelity.py +73 -0
  90. algo_cli/intelligence/ema_tuning.py +104 -0
  91. algo_cli/intelligence/event_log.py +92 -0
  92. algo_cli/intelligence/evidence_graph.py +114 -0
  93. algo_cli/intelligence/extension_host.py +162 -0
  94. algo_cli/intelligence/extension_manifest.py +115 -0
  95. algo_cli/intelligence/falsification_suite.py +178 -0
  96. algo_cli/intelligence/finance/__init__.py +169 -0
  97. algo_cli/intelligence/finance/anomalies.py +135 -0
  98. algo_cli/intelligence/finance/ap_ar.py +351 -0
  99. algo_cli/intelligence/finance/cash.py +162 -0
  100. algo_cli/intelligence/finance/close.py +332 -0
  101. algo_cli/intelligence/finance/common.py +244 -0
  102. algo_cli/intelligence/finance/construction.py +135 -0
  103. algo_cli/intelligence/finance/controls.py +172 -0
  104. algo_cli/intelligence/finance/evidence.py +119 -0
  105. algo_cli/intelligence/finance/exceptions.py +157 -0
  106. algo_cli/intelligence/finance/reconciliations.py +254 -0
  107. algo_cli/intelligence/finance/revenue.py +109 -0
  108. algo_cli/intelligence/finance/tax.py +74 -0
  109. algo_cli/intelligence/finance/workpapers.py +111 -0
  110. algo_cli/intelligence/finding_record.py +120 -0
  111. algo_cli/intelligence/flow_dag.py +267 -0
  112. algo_cli/intelligence/gatherer.py +223 -0
  113. algo_cli/intelligence/golden_master.py +98 -0
  114. algo_cli/intelligence/graph_rag.py +195 -0
  115. algo_cli/intelligence/group_chat.py +143 -0
  116. algo_cli/intelligence/hash_dedup.py +145 -0
  117. algo_cli/intelligence/hyperloglog.py +128 -0
  118. algo_cli/intelligence/incremental_index.py +316 -0
  119. algo_cli/intelligence/index_store.py +16 -0
  120. algo_cli/intelligence/iteration_plan.py +133 -0
  121. algo_cli/intelligence/kernel_plugins.py +167 -0
  122. algo_cli/intelligence/lesson_catalog.py +135 -0
  123. algo_cli/intelligence/llm_fallback.py +169 -0
  124. algo_cli/intelligence/log2_histogram.py +267 -0
  125. algo_cli/intelligence/lsp_integration.py +147 -0
  126. algo_cli/intelligence/memory_evolution.py +117 -0
  127. algo_cli/intelligence/minhash_lsh.py +182 -0
  128. algo_cli/intelligence/multi_model_score.py +174 -0
  129. algo_cli/intelligence/multi_tier_grade.py +211 -0
  130. algo_cli/intelligence/negative_controls.py +113 -0
  131. algo_cli/intelligence/numeric_clamp.py +63 -0
  132. algo_cli/intelligence/occ_editor.py +66 -0
  133. algo_cli/intelligence/output_normalize.py +112 -0
  134. algo_cli/intelligence/parallel_delegation.py +98 -0
  135. algo_cli/intelligence/parallel_fanout.py +104 -0
  136. algo_cli/intelligence/permission_modes.py +105 -0
  137. algo_cli/intelligence/pre_push_gate.py +68 -0
  138. algo_cli/intelligence/prefetch.py +171 -0
  139. algo_cli/intelligence/process_framework.py +217 -0
  140. algo_cli/intelligence/project_graph.py +387 -0
  141. algo_cli/intelligence/query_expansion.py +146 -0
  142. algo_cli/intelligence/ralph_loop.py +117 -0
  143. algo_cli/intelligence/rate_limiter.py +153 -0
  144. algo_cli/intelligence/refactor_transaction.py +94 -0
  145. algo_cli/intelligence/research_workspace.py +108 -0
  146. algo_cli/intelligence/retraction_ledger.py +72 -0
  147. algo_cli/intelligence/saga_pattern.py +88 -0
  148. algo_cli/intelligence/session_fork.py +100 -0
  149. algo_cli/intelligence/shadow_editor.py +67 -0
  150. algo_cli/intelligence/shell_session.py +213 -0
  151. algo_cli/intelligence/source_registry.py +143 -0
  152. algo_cli/intelligence/spawn_scales.py +99 -0
  153. algo_cli/intelligence/stat_stability.py +104 -0
  154. algo_cli/intelligence/structural_validator.py +148 -0
  155. algo_cli/intelligence/subagent_spawner.py +111 -0
  156. algo_cli/intelligence/symmetric_verify.py +70 -0
  157. algo_cli/intelligence/task_classifier.py +129 -0
  158. algo_cli/intelligence/team_execution.py +122 -0
  159. algo_cli/intelligence/tiered_access.py +121 -0
  160. algo_cli/intelligence/utility_registry.py +159 -0
  161. algo_cli/intuition_engine.py +560 -0
  162. algo_cli/intuition_injector.py +82 -0
  163. algo_cli/kernels/__init__.py +5 -0
  164. algo_cli/kernels/manifest.py +763 -0
  165. algo_cli/main.py +3903 -0
  166. algo_cli/memory_candidates.py +541 -0
  167. algo_cli/memory_echo_veil.py +394 -0
  168. algo_cli/memory_runtime.py +112 -0
  169. algo_cli/model_info.py +548 -0
  170. algo_cli/model_profile.py +160 -0
  171. algo_cli/model_routing.py +74 -0
  172. algo_cli/oneshot.py +331 -0
  173. algo_cli/perf_telemetry.py +389 -0
  174. algo_cli/plugins.py +245 -0
  175. algo_cli/private_event_store.py +654 -0
  176. algo_cli/quantization/__init__.py +24 -0
  177. algo_cli/quantization/lloyd_max.py +98 -0
  178. algo_cli/quantization/turbo_quant.py +308 -0
  179. algo_cli/reasoning/__init__.py +46 -0
  180. algo_cli/reasoning/combinatorial.py +356 -0
  181. algo_cli/reasoning/graph_of_thought.py +297 -0
  182. algo_cli/reasoning/mcts.py +220 -0
  183. algo_cli/reasoning/neuro_symbolic.py +250 -0
  184. algo_cli/reasoning/react.py +246 -0
  185. algo_cli/reasoning/reflexion.py +225 -0
  186. algo_cli/reasoning/tree_of_thought.py +241 -0
  187. algo_cli/reasoning_bridge.py +150 -0
  188. algo_cli/reconciliation.py +284 -0
  189. algo_cli/reflex.py +385 -0
  190. algo_cli/resources/docs/ALGO.md +13958 -0
  191. algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
  192. algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
  193. algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
  194. algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
  195. algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
  196. algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
  197. algo_cli/resources/docs/main-split-map.md +35 -0
  198. algo_cli/resources/docs/privacy-and-context.md +48 -0
  199. algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
  200. algo_cli/resources/skills/README.md +26 -0
  201. algo_cli/resources/skills/algo-cli.md +59 -0
  202. algo_cli/resources/skills/edit-file-precision.md +49 -0
  203. algo_cli/resources/skills/harness-search-first.md +47 -0
  204. algo_cli/resources/skills/memory-recall-ritual.md +51 -0
  205. algo_cli/resources/skills/qol-algorithms.md +224 -0
  206. algo_cli/resources/skills/smart-error-recovery.md +56 -0
  207. algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
  208. algo_cli/retrieval_algorithms.py +127 -0
  209. algo_cli/runtime_qos.py +236 -0
  210. algo_cli/runtime_services.py +320 -0
  211. algo_cli/session_commands.py +95 -0
  212. algo_cli/session_mode.py +113 -0
  213. algo_cli/skills.py +430 -0
  214. algo_cli/slash_dispatch.py +1265 -0
  215. algo_cli/small_context.py +206 -0
  216. algo_cli/spawn_budget.py +89 -0
  217. algo_cli/task_ledger.py +84 -0
  218. algo_cli/task_router.py +197 -0
  219. algo_cli/tool_context.py +94 -0
  220. algo_cli/tool_contract.py +99 -0
  221. algo_cli/tool_policy.py +357 -0
  222. algo_cli/tool_runtime.py +647 -0
  223. algo_cli/tools.py +3056 -0
  224. algo_cli/url_scheme.py +174 -0
  225. algo_cli/verify.py +154 -0
  226. algo_cli/version_manifest.py +178 -0
  227. algo_cli/vision_screenshot_verify.py +76 -0
  228. algo_cli/workspace_resolver.py +68 -0
  229. algo_cli/x_account.py +209 -0
  230. algo_cli/xai_auth.py +374 -0
  231. algo_cli/xai_client.py +600 -0
  232. algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
  233. algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
  234. algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
  235. algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
  236. algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
  237. 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)