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,356 @@
1
+ """QCR-LLM Quantum-Inspired Combinatorial Reasoning.
2
+
3
+ Reformulates multi-sample Chain-of-Thought aggregation as a Higher-Order
4
+ Unconstrained Binary Optimization (HUBO) problem, then solves it using
5
+ classical simulated annealing (default) with optional QPU offload.
6
+
7
+ Energy function:
8
+ H(x) = sum_{S subset [R], |S|<=K} w_S * prod_{i in S} x_i
9
+
10
+ Weights w_S encode:
11
+ - Popularity: how many samples agree on fragment i
12
+ - Correlations: pairwise cosine similarity between fragment embeddings
13
+ - Higher-order coherence: 3-body+ semantic consistency
14
+
15
+ Local-first: uses simulated annealing by default.
16
+ Quantum-ready: HUBO problem structure is QUBO-compatible for D-Wave,
17
+ IBM Quantum (BF-DCQO), or IonQ via PennyLane/Qiskit adapters.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import math
23
+ import random
24
+ from dataclasses import dataclass, field
25
+ from typing import Any, Callable
26
+
27
+ from ..chat_protocol import get_attr
28
+
29
+
30
+ @dataclass
31
+ class HuboProblem:
32
+ """A Higher-Order Unconstrained Binary Optimization problem.
33
+
34
+ Represents the combinatorial optimization over which reasoning
35
+ fragments to include in the final aggregated answer.
36
+ """
37
+ n_variables: int # Number of reasoning fragments (binary variables)
38
+ weights: dict[tuple[int, ...], float] = field(default_factory=dict)
39
+ # weights maps subset tuples to their coefficients:
40
+ # (i,) -> unary weight (popularity)
41
+ # (i, j) -> pairwise correlation
42
+ # (i, j, k) -> higher-order coherence
43
+ labels: list[str] = field(default_factory=list) # Fragment text for each variable
44
+
45
+ def energy(self, x: list[int]) -> float:
46
+ """Compute H(x) for a binary configuration."""
47
+ total = 0.0
48
+ for subset, weight in self.weights.items():
49
+ product = 1
50
+ for idx in subset:
51
+ if idx < len(x):
52
+ product *= x[idx]
53
+ else:
54
+ product = 0
55
+ break
56
+ total += weight * product
57
+ return total
58
+
59
+ def to_qubo(self) -> dict[tuple[int, int], float]:
60
+ """Reduce HUBO to QUBO (pairwise) using substitution method.
61
+
62
+ Introduces auxiliary variables for higher-order terms.
63
+ For a term w_{ijk} * x_i * x_j * x_k, substitute x_i * x_j -> x_a
64
+ with penalty M * (x_a - 2*x_a*x_i - 2*x_a*x_j + 2*x_i*x_j).
65
+ """
66
+ qubo: dict[tuple[int, int], float] = {}
67
+ aux_var = self.n_variables # Next available auxiliary variable index
68
+ M = max(abs(w) for w in self.weights.values()) * 2 if self.weights else 10.0
69
+
70
+ for subset, weight in self.weights.items():
71
+ if len(subset) == 1:
72
+ # Unary term
73
+ key = (subset[0], subset[0])
74
+ qubo[key] = qubo.get(key, 0.0) + weight
75
+ elif len(subset) == 2:
76
+ # Pairwise term
77
+ key = (subset[0], subset[1])
78
+ qubo[key] = qubo.get(key, 0.0) + weight
79
+ elif len(subset) >= 3:
80
+ # Higher-order: reduce iteratively
81
+ remaining = list(subset)
82
+ current_pair = (remaining[0], remaining[1])
83
+ # Create auxiliary variable for this pair
84
+ a = aux_var
85
+ aux_var += 1
86
+ # Penalty terms for substitution
87
+ qubo[(a, a)] = qubo.get((a, a), 0.0) - M * weight
88
+ qubo[(a, current_pair[0])] = qubo.get((a, current_pair[0]), 0.0) + M * weight
89
+ qubo[(a, current_pair[1])] = qubo.get((a, current_pair[1]), 0.0) + M * weight
90
+ qubo[(current_pair[0], current_pair[1])] = qubo.get(
91
+ (current_pair[0], current_pair[1]), 0.0
92
+ ) - M * weight
93
+ # Remaining variables chain with auxiliary
94
+ for k in remaining[2:]:
95
+ new_a = aux_var
96
+ aux_var += 1
97
+ qubo[(new_a, new_a)] = qubo.get((new_a, new_a), 0.0) - M * weight
98
+ qubo[(new_a, a)] = qubo.get((new_a, a), 0.0) + M * weight
99
+ qubo[(new_a, k)] = qubo.get((new_a, k), 0.0) + M * weight
100
+ qubo[(a, k)] = qubo.get((a, k), 0.0) - M * weight
101
+ a = new_a
102
+
103
+ return qubo
104
+
105
+
106
+ def build_hubo_from_fragments(
107
+ fragments: list[str],
108
+ embeddings: list[list[float]] | None = None,
109
+ popularity: list[float] | None = None,
110
+ order: int = 3,
111
+ correlation_weight: float = 0.5,
112
+ coherence_weight: float = 0.2,
113
+ ) -> HuboProblem:
114
+ """Build a HUBO problem from reasoning fragments.
115
+
116
+ Args:
117
+ fragments: List of reasoning fragment texts.
118
+ embeddings: Optional pre-computed embeddings for correlation computation.
119
+ popularity: Optional pre-computed popularity scores (uniform if not given).
120
+ order: Maximum interaction order (1=unary, 2=pairwise, 3=three-body).
121
+ correlation_weight: Weight for pairwise correlation terms.
122
+ coherence_weight: Weight for higher-order coherence terms.
123
+
124
+ Returns:
125
+ A HuboProblem ready for optimization.
126
+ """
127
+ n = len(fragments)
128
+ problem = HuboProblem(n_variables=n, labels=fragments)
129
+
130
+ # Unary: popularity
131
+ pop = popularity or [1.0 / n] * n
132
+ for i in range(n):
133
+ problem.weights[(i,)] = pop[i]
134
+
135
+ # Pairwise: cosine similarity
136
+ if embeddings and len(embeddings) == n:
137
+ for i in range(n):
138
+ for j in range(i + 1, n):
139
+ sim = _cosine_sim(embeddings[i], embeddings[j])
140
+ if abs(sim) > 0.01:
141
+ problem.weights[(i, j)] = correlation_weight * sim
142
+
143
+ # Higher-order coherence (3-body)
144
+ if order >= 3 and embeddings and len(embeddings) == n:
145
+ for i in range(n):
146
+ for j in range(i + 1, n):
147
+ for k in range(j + 1, n):
148
+ # Three-body coherence: geometric mean of pairwise similarities
149
+ sim_ij = _cosine_sim(embeddings[i], embeddings[j])
150
+ sim_ik = _cosine_sim(embeddings[i], embeddings[k])
151
+ sim_jk = _cosine_sim(embeddings[j], embeddings[k])
152
+ coherence = math.pow(max(0, sim_ij * sim_ik * sim_jk), 1.0 / 3)
153
+ if coherence > 0.01:
154
+ problem.weights[(i, j, k)] = coherence_weight * coherence
155
+
156
+ return problem
157
+
158
+
159
+ def _cosine_sim(a: list[float], b: list[float]) -> float:
160
+ """Compute cosine similarity between two vectors."""
161
+ if not a or not b or len(a) != len(b):
162
+ return 0.0
163
+ dot = sum(x * y for x, y in zip(a, b))
164
+ na = math.sqrt(sum(x * x for x in a))
165
+ nb = math.sqrt(sum(x * x for x in b))
166
+ if na == 0.0 or nb == 0.0:
167
+ return 0.0
168
+ return dot / (na * nb)
169
+
170
+
171
+ def simulated_annealing(
172
+ problem: HuboProblem,
173
+ *,
174
+ initial_temp: float = 2.0,
175
+ final_temp: float = 0.01,
176
+ cooling_rate: float = 0.95,
177
+ steps_per_temp: int = 50,
178
+ seed: int | None = None,
179
+ ) -> tuple[list[int], float]:
180
+ """Solve HUBO using classical simulated annealing.
181
+
182
+ Args:
183
+ problem: The HUBO problem.
184
+ initial_temp: Starting temperature.
185
+ final_temp: Stopping temperature.
186
+ cooling_rate: Temperature decay factor.
187
+ steps_per_temp: Monte Carlo steps per temperature.
188
+ seed: Random seed for reproducibility.
189
+
190
+ Returns:
191
+ (best_solution, best_energy) where solution is binary vector.
192
+ """
193
+ rng = random.Random(seed)
194
+ n = problem.n_variables
195
+
196
+ # Initialize randomly
197
+ current = [rng.randint(0, 1) for _ in range(n)]
198
+ current_energy = problem.energy(current)
199
+ best = list(current)
200
+ best_energy = current_energy
201
+
202
+ temp = initial_temp
203
+ while temp > final_temp:
204
+ for _ in range(steps_per_temp):
205
+ # Flip a random variable
206
+ flip_idx = rng.randint(0, n - 1)
207
+ candidate = list(current)
208
+ candidate[flip_idx] = 1 - candidate[flip_idx]
209
+ candidate_energy = problem.energy(candidate)
210
+
211
+ delta = candidate_energy - current_energy
212
+ # We MINIMIZE energy (HUBO: lower is better for negative correlation weights)
213
+ # But for QCR-LLM, we want to MAXIMIZE quality, so we negate
214
+ # Convention: positive weights = good, so we maximize energy
215
+ if delta > 0 or rng.random() < math.exp(delta / temp):
216
+ current = candidate
217
+ current_energy = candidate_energy
218
+ if current_energy > best_energy:
219
+ best = list(current)
220
+ best_energy = current_energy
221
+
222
+ temp *= cooling_rate
223
+
224
+ return best, best_energy
225
+
226
+
227
+ @dataclass
228
+ class QCRAggregator:
229
+ """QCR-LLM style combinatorial reasoning aggregator.
230
+
231
+ Generates multiple CoT fragments from the LLM, embeds them,
232
+ builds a HUBO problem, and solves it to select the best subset.
233
+ """
234
+ n_samples: int = 5
235
+ order: int = 3
236
+ correlation_weight: float = 0.5
237
+ coherence_weight: float = 0.2
238
+ sa_initial_temp: float = 2.0
239
+ sa_cooling_rate: float = 0.95
240
+ embed_fn: Callable[[list[str]], list[list[float]]] | None = None
241
+
242
+ def generate_fragments(
243
+ self, task: str, client: Any, model: str, n: int | None = None,
244
+ ) -> list[str]:
245
+ """Generate multiple CoT reasoning fragments."""
246
+ n = n or self.n_samples
247
+ fragments: list[str] = []
248
+ for _ in range(n):
249
+ try:
250
+ response = client.chat(
251
+ model=model,
252
+ messages=[
253
+ {"role": "system", "content": "You are a reasoning agent. Think step by step."},
254
+ {"role": "user", "content": task},
255
+ ],
256
+ stream=False,
257
+ options={"temperature": 0.7}, # Higher temp for diversity
258
+ )
259
+ text = get_attr(get_attr(response, "message", {}), "content", "").strip()
260
+ if text:
261
+ fragments.append(text)
262
+ except Exception:
263
+ continue
264
+ return fragments
265
+
266
+ def aggregate(self, fragments: list[str]) -> tuple[list[int], float, HuboProblem]:
267
+ """Build HUBO and solve to select the best fragment subset.
268
+
269
+ Returns (selected_indices, total_energy, problem).
270
+ """
271
+ embeddings = None
272
+ if self.embed_fn and fragments:
273
+ try:
274
+ embeddings = self.embed_fn(fragments)
275
+ except Exception:
276
+ pass
277
+
278
+ problem = build_hubo_from_fragments(
279
+ fragments,
280
+ embeddings=embeddings,
281
+ order=self.order,
282
+ correlation_weight=self.correlation_weight,
283
+ coherence_weight=self.coherence_weight,
284
+ )
285
+
286
+ solution, energy = simulated_annealing(
287
+ problem,
288
+ initial_temp=self.sa_initial_temp,
289
+ cooling_rate=self.sa_cooling_rate,
290
+ )
291
+ selected = [i for i, x in enumerate(solution) if x == 1]
292
+ return selected, energy, problem
293
+
294
+
295
+ def run_qcr_aggregation(
296
+ *,
297
+ task: str,
298
+ client: Any,
299
+ model: str,
300
+ n_samples: int = 5,
301
+ embed_fn: Callable[[list[str]], list[list[float]]] | None = None,
302
+ order: int = 3,
303
+ ) -> tuple[str, list[str], dict[str, Any]]:
304
+ """Run a complete QCR-LLM aggregation pipeline.
305
+
306
+ Args:
307
+ task: The reasoning task.
308
+ client: Ollama client.
309
+ model: Model name for fragment generation.
310
+ n_samples: Number of CoT fragments to generate.
311
+ embed_fn: Optional embedding function for correlation computation.
312
+ order: Maximum HUBO interaction order.
313
+
314
+ Returns:
315
+ (aggregated_answer, selected_fragments, metadata)
316
+ """
317
+ aggregator = QCRAggregator(n_samples=n_samples, embed_fn=embed_fn, order=order)
318
+
319
+ # 1. Generate diverse reasoning fragments
320
+ fragments = aggregator.generate_fragments(task, client, model)
321
+ if not fragments:
322
+ return "(no fragments generated)", [], {"error": "generation_failed"}
323
+
324
+ # 2. Build and solve HUBO
325
+ selected_indices, energy, problem = aggregator.aggregate(fragments)
326
+ selected_fragments = [fragments[i] for i in selected_indices if i < len(fragments)]
327
+
328
+ # 3. Synthesize selected fragments into a final answer
329
+ if not selected_fragments:
330
+ selected_fragments = fragments[:1] # Fallback to first fragment
331
+
332
+ synthesis_prompt = (
333
+ "Synthesize these reasoning fragments into a single coherent answer:\n\n"
334
+ + "\n\n---\n\n".join(f"Fragment {i+1}:\n{f[:1500]}" for i, f in enumerate(selected_fragments))
335
+ )
336
+ try:
337
+ response = client.chat(
338
+ model=model,
339
+ messages=[
340
+ {"role": "system", "content": "You are a reasoning synthesizer. Combine diverse perspectives into a unified answer."},
341
+ {"role": "user", "content": synthesis_prompt},
342
+ ],
343
+ stream=False,
344
+ )
345
+ answer = get_attr(get_attr(response, "message", {}), "content", "").strip()
346
+ except Exception:
347
+ answer = selected_fragments[0] if selected_fragments else "(synthesis failed)"
348
+
349
+ metadata = {
350
+ "total_fragments": len(fragments),
351
+ "selected_count": len(selected_fragments),
352
+ "selected_indices": selected_indices,
353
+ "energy": energy,
354
+ "hubo_terms": len(problem.weights),
355
+ }
356
+ return answer, selected_fragments, metadata
@@ -0,0 +1,297 @@
1
+ """Graph-of-Thoughts (GoT) Reasoning.
2
+
3
+ Generalizes ToT to arbitrary directed acyclic graphs (DAGs) where:
4
+ - Thoughts are vertices with typed edges
5
+ - Supports merge (combine multiple thoughts), feedback (revise), and distill (compact)
6
+ - Closer to recurrent/human cognition patterns
7
+ - Better for tasks requiring iterative refinement or synthesis of partial results
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import time
14
+ from dataclasses import dataclass, field
15
+ from enum import Enum
16
+ from typing import Any
17
+
18
+ from ..chat_protocol import get_attr
19
+
20
+
21
+ class EdgeType(Enum):
22
+ NEXT = "next" # Sequential reasoning step
23
+ MERGE = "merge" # Combine multiple thoughts
24
+ FEEDBACK = "feedback" # Revise a thought based on downstream info
25
+ DISTILL = "distill" # Compact intermediate reasoning
26
+
27
+
28
+ @dataclass
29
+ class ThoughtVertex:
30
+ """A vertex in the Graph-of-Thought."""
31
+ id: str
32
+ thought: str
33
+ value: float
34
+ kind: str = "reasoning" # reasoning, hypothesis, evidence, synthesis
35
+ state: str = ""
36
+ timestamp: float = field(default_factory=time.time)
37
+ metadata: dict[str, Any] = field(default_factory=dict)
38
+
39
+ def to_context(self) -> str:
40
+ return f"[{self.id}] ({self.kind}, val={self.value:.2f}): {self.thought[:300]}"
41
+
42
+
43
+ @dataclass
44
+ class ThoughtEdge:
45
+ """A directed edge connecting two thought vertices."""
46
+ source_id: str
47
+ target_id: str
48
+ edge_type: EdgeType
49
+ label: str = ""
50
+ weight: float = 1.0
51
+
52
+
53
+ @dataclass
54
+ class GraphOfThought:
55
+ """Graph-of-Thought reasoner."""
56
+ vertices: dict[str, ThoughtVertex] = field(default_factory=dict)
57
+ edges: list[ThoughtEdge] = field(default_factory=list)
58
+ next_id: int = 0
59
+ best_vertex: ThoughtVertex | None = None
60
+ best_score: float = 0.0
61
+
62
+ def add_vertex(self, thought: str, value: float, kind: str = "reasoning", state: str = "") -> ThoughtVertex:
63
+ vid = f"v{self.next_id}"
64
+ self.next_id += 1
65
+ vertex = ThoughtVertex(id=vid, thought=thought, value=value, kind=kind, state=state)
66
+ self.vertices[vid] = vertex
67
+ if value > self.best_score:
68
+ self.best_score = value
69
+ self.best_vertex = vertex
70
+ return vertex
71
+
72
+ def add_edge(self, source_id: str, target_id: str, edge_type: EdgeType, label: str = "") -> ThoughtEdge:
73
+ edge = ThoughtEdge(source_id=source_id, target_id=target_id, edge_type=edge_type, label=label)
74
+ self.edges.append(edge)
75
+ return edge
76
+
77
+ def predecessors(self, vertex_id: str) -> list[ThoughtVertex]:
78
+ """Return vertices with edges pointing into this vertex."""
79
+ pred_ids = {e.source_id for e in self.edges if e.target_id == vertex_id}
80
+ return [self.vertices[pid] for pid in pred_ids if pid in self.vertices]
81
+
82
+ def successors(self, vertex_id: str) -> list[ThoughtVertex]:
83
+ """Return vertices this vertex points to."""
84
+ succ_ids = {e.target_id for e in self.edges if e.source_id == vertex_id}
85
+ return [self.vertices[sid] for sid in succ_ids if sid in self.vertices]
86
+
87
+ def frontier(self) -> list[ThoughtVertex]:
88
+ """Return leaf vertices (no successors) that could be expanded."""
89
+ has_children = {e.source_id for e in self.edges}
90
+ return [v for v in self.vertices.values() if v.id not in has_children]
91
+
92
+ def context_snapshot(self, max_chars: int = 4000) -> str:
93
+ """Serialize the graph state for LLM context."""
94
+ lines = ["## Current Reasoning Graph"]
95
+ total = 0
96
+ for v in self.vertices.values():
97
+ entry = v.to_context()
98
+ if total + len(entry) > max_chars:
99
+ lines.append(f"... ({len(self.vertices)} total vertices)")
100
+ break
101
+ lines.append(entry)
102
+ total += len(entry)
103
+ if self.edges:
104
+ lines.append(f"\nEdges: {len(self.edges)} connections")
105
+ return "\n".join(lines)
106
+
107
+ def _merge_thoughts(self, vertex_ids: list[str], client: Any, model: str) -> ThoughtVertex | None:
108
+ """Merge multiple thoughts into a synthesis vertex."""
109
+ vertices = [self.vertices[vid] for vid in vertex_ids if vid in self.vertices]
110
+ if not vertices:
111
+ return None
112
+ parts = [v.to_context() for v in vertices]
113
+ prompt = (
114
+ "Synthesize these reasoning steps into one unified thought:\n\n"
115
+ + "\n\n".join(parts)
116
+ + "\n\nProduce a single concise synthesis."
117
+ )
118
+ try:
119
+ response = client.chat(
120
+ model=model,
121
+ messages=[
122
+ {"role": "system", "content": "You are a reasoning synthesizer."},
123
+ {"role": "user", "content": prompt},
124
+ ],
125
+ stream=False,
126
+ )
127
+ text = get_attr(get_attr(response, "message", {}), "content", "").strip()
128
+ except Exception:
129
+ text = " + ".join(v.thought[:80] for v in vertices)
130
+
131
+ merged = self.add_vertex(thought=text, value=max(v.value for v in vertices), kind="synthesis")
132
+ for vid in vertex_ids:
133
+ if vid in self.vertices:
134
+ self.add_edge(vid, merged.id, EdgeType.MERGE, label="merge")
135
+ return merged
136
+
137
+ def _feedback_loop(self, vertex_id: str, client: Any, model: str) -> ThoughtVertex | None:
138
+ """Apply feedback: revise a thought using information from its successors."""
139
+ vertex = self.vertices.get(vertex_id)
140
+ if not vertex:
141
+ return None
142
+ successors = self.successors(vertex_id)
143
+ if not successors:
144
+ return None
145
+ succ_context = "\n".join(s.to_context() for s in successors)
146
+ prompt = (
147
+ f"Original thought [{vertex_id}]: {vertex.thought}\n\n"
148
+ f"Downstream findings:\n{succ_context}\n\n"
149
+ "Revise the original thought to incorporate these findings."
150
+ )
151
+ try:
152
+ response = client.chat(
153
+ model=model,
154
+ messages=[
155
+ {"role": "system", "content": "You are a reasoning reviser."},
156
+ {"role": "user", "content": prompt},
157
+ ],
158
+ stream=False,
159
+ )
160
+ text = get_attr(get_attr(response, "message", {}), "content", "").strip()
161
+ except Exception:
162
+ text = vertex.thought
163
+
164
+ revised = self.add_vertex(thought=text, value=vertex.value + 0.1, kind="revised")
165
+ self.add_edge(vertex_id, revised.id, EdgeType.FEEDBACK, label="revise")
166
+ # Connect the original successors to the revised version
167
+ for s in successors:
168
+ self.add_edge(revised.id, s.id, EdgeType.NEXT, label="post-revision")
169
+ return revised
170
+
171
+ def _distill(self, vertex_ids: list[str], client: Any, model: str) -> ThoughtVertex | None:
172
+ """Distill a set of vertices into a compact summary vertex."""
173
+ vertices = [self.vertices[vid] for vid in vertex_ids if vid in self.vertices]
174
+ if not vertices:
175
+ return None
176
+ prompt = (
177
+ "Distill these reasoning steps into a compact summary (max 200 words):\n\n"
178
+ + "\n\n".join(v.to_context() for v in vertices)
179
+ )
180
+ try:
181
+ response = client.chat(
182
+ model=model,
183
+ messages=[
184
+ {"role": "system", "content": "You are a reasoning compactor."},
185
+ {"role": "user", "content": prompt},
186
+ ],
187
+ stream=False,
188
+ )
189
+ text = get_attr(get_attr(response, "message", {}), "content", "").strip()
190
+ except Exception:
191
+ text = " | ".join(v.thought[:60] for v in vertices)
192
+
193
+ distilled = self.add_vertex(thought=text, value=max(v.value for v in vertices), kind="distilled")
194
+ for vid in vertex_ids:
195
+ if vid in self.vertices:
196
+ self.add_edge(vid, distilled.id, EdgeType.DISTILL, label="distill")
197
+ return distilled
198
+
199
+
200
+ EXPANSION_PROMPT = """Given the reasoning graph state and the task, generate {n} next reasoning steps.
201
+
202
+ Task: {task}
203
+
204
+ {graph_context}
205
+
206
+ Produce {n} distinct next thoughts as JSON array:
207
+ [{{"thought": "...", "kind": "reasoning|hypthesis|evidence", "state": "updated state"}}]"""
208
+
209
+
210
+ def run_got(
211
+ *,
212
+ task: str,
213
+ client: Any,
214
+ model: str,
215
+ max_rounds: int = 4,
216
+ branch_factor: int = 2,
217
+ enable_merge: bool = True,
218
+ enable_feedback: bool = True,
219
+ enable_distill: bool = True,
220
+ distill_threshold: int = 6,
221
+ ) -> GraphOfThought:
222
+ """Run a Graph-of-Thought reasoning episode.
223
+
224
+ Args:
225
+ task: The reasoning task.
226
+ client: Ollama client.
227
+ model: Model name.
228
+ max_rounds: Maximum expansion rounds.
229
+ branch_factor: Thoughts generated per frontier node.
230
+ enable_merge: Allow merge operations.
231
+ enable_feedback: Allow feedback loops.
232
+ enable_distill: Allow distillation when graph gets large.
233
+ distill_threshold: Distill when vertex count exceeds this.
234
+
235
+ Returns:
236
+ The GraphOfThought with best_vertex set.
237
+ """
238
+ got = GraphOfThought()
239
+
240
+ # Seed with initial thought
241
+ got.add_vertex(thought=f"Task: {task}", value=1.0, kind="hypothesis", state=task)
242
+
243
+ for round_num in range(max_rounds):
244
+ frontier = got.frontier()
245
+ if not frontier:
246
+ break
247
+
248
+ # Expand frontier
249
+ for vertex in frontier[:3]: # Limit expansion breadth
250
+ prompt = EXPANSION_PROMPT.format(
251
+ n=branch_factor,
252
+ task=task,
253
+ graph_context=got.context_snapshot(),
254
+ )
255
+ try:
256
+ response = client.chat(
257
+ model=model,
258
+ messages=[
259
+ {"role": "system", "content": "You are a reasoning graph expander."},
260
+ {"role": "user", "content": prompt},
261
+ ],
262
+ stream=False,
263
+ format="json",
264
+ )
265
+ text = get_attr(get_attr(response, "message", {}), "content", "")
266
+ parsed = json.loads(text)
267
+ if isinstance(parsed, list):
268
+ for item in parsed[:branch_factor]:
269
+ if isinstance(item, dict):
270
+ new_v = got.add_vertex(
271
+ thought=str(item.get("thought", "")),
272
+ value=0.5,
273
+ kind=str(item.get("kind", "reasoning")),
274
+ state=str(item.get("state", vertex.state)),
275
+ )
276
+ got.add_edge(vertex.id, new_v.id, EdgeType.NEXT)
277
+ except (json.JSONDecodeError, ValueError, TypeError):
278
+ # Fallback: add a simple continuation
279
+ new_v = got.add_vertex(thought=f"(continuation from {vertex.id})", value=0.4, kind="reasoning")
280
+ got.add_edge(vertex.id, new_v.id, EdgeType.NEXT)
281
+
282
+ # Merge phase: combine related frontier nodes
283
+ if enable_merge and len(frontier) >= 2:
284
+ got._merge_thoughts([v.id for v in frontier[:3]], client, model)
285
+
286
+ # Feedback phase: revise high-value nodes with successors
287
+ if enable_feedback and round_num >= 1:
288
+ high_value = [v for v in got.vertices.values() if v.value >= 0.6 and v.kind in ("hypothesis", "reasoning")]
289
+ for v in high_value[:2]:
290
+ got._feedback_loop(v.id, client, model)
291
+
292
+ # Distill phase: compact when graph is large
293
+ if enable_distill and len(got.vertices) > distill_threshold:
294
+ leaves = got.frontier()[:distill_threshold]
295
+ got._distill([v.id for v in leaves], client, model)
296
+
297
+ return got