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,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
|