fable-engine 1.3.1__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.
- fable_compressor.py +356 -0
- fable_engine/__init__.py +1 -0
- fable_engine/actions/__init__.py +291 -0
- fable_engine/actions/cas.py +182 -0
- fable_engine/actions/deliberation.py +523 -0
- fable_engine/actions/fleet.py +807 -0
- fable_engine/actions/lifecycle.py +298 -0
- fable_engine/actions/scrapers.py +116 -0
- fable_engine/actions/system3.py +815 -0
- fable_engine/browser.py +824 -0
- fable_engine/cas.py +974 -0
- fable_engine/fable_session.json +510 -0
- fable_engine/guards.py +283 -0
- fable_engine/schema.py +714 -0
- fable_engine/scrapers/__init__.py +32 -0
- fable_engine/scrapers/arxiv.py +115 -0
- fable_engine/scrapers/base.py +386 -0
- fable_engine/scrapers/github.py +129 -0
- fable_engine/scrapers/reddit.py +154 -0
- fable_engine/scrapers/web.py +120 -0
- fable_engine/scrapers/x.py +125 -0
- fable_engine/scrapers/youtube.py +132 -0
- fable_engine/server.py +414 -0
- fable_engine/session.py +1819 -0
- fable_engine/test_server.py +1362 -0
- fable_engine/updater.py +541 -0
- fable_engine-1.3.1.dist-info/LICENSE +22 -0
- fable_engine-1.3.1.dist-info/METADATA +173 -0
- fable_engine-1.3.1.dist-info/RECORD +104 -0
- fable_engine-1.3.1.dist-info/WHEEL +5 -0
- fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
- fable_engine-1.3.1.dist-info/top_level.txt +6 -0
- fable_mode/__init__.py +3 -0
- fable_mode/__main__.py +4 -0
- fable_mode/adapters.py +1014 -0
- fable_mode/installer.py +553 -0
- fable_mode/launcher.py +437 -0
- fable_mode/manifest.py +142 -0
- fable_mode/resources.json +114 -0
- fable_mode/safety.py +103 -0
- fable_mode_entry.py +10 -0
- fable_v2/__init__.py +146 -0
- fable_v2/adapters.py +151 -0
- fable_v2/coder_fleet/__init__.py +100 -0
- fable_v2/coder_fleet/ast_tools.py +158 -0
- fable_v2/coder_fleet/compute.py +199 -0
- fable_v2/coder_fleet/design_engine.py +1316 -0
- fable_v2/coder_fleet/diagnostics.py +293 -0
- fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
- fable_v2/coder_fleet/mock_auditor.py +306 -0
- fable_v2/coder_fleet/mutation.py +216 -0
- fable_v2/coder_fleet/property_oracle.py +260 -0
- fable_v2/coder_fleet/receipt_attestor.py +122 -0
- fable_v2/coder_fleet/red_team_swarm.py +908 -0
- fable_v2/coder_fleet/test_harness.py +198 -0
- fable_v2/coder_fleet/vector_engine.py +1287 -0
- fable_v2/coder_fleet/visual.py +357 -0
- fable_v2/coder_fleet/workspace.py +153 -0
- fable_v2/cortical/__init__.py +20 -0
- fable_v2/cortical/plasticity_engine.py +992 -0
- fable_v2/execution_broker.py +811 -0
- fable_v2/proof_engine.py +1141 -0
- fable_v2/protocol.py +485 -0
- fable_v2/runtime.py +1010 -0
- fable_v2/system3/__init__.py +204 -0
- fable_v2/system3/causal.py +558 -0
- fable_v2/system3/dialectical.py +577 -0
- fable_v2/system3/evolution.py +503 -0
- fable_v2/system3/executive.py +338 -0
- fable_v2/system3/free_energy.py +479 -0
- fable_v2/system3/hyperbolic.py +555 -0
- fable_v2/system3/induction.py +336 -0
- fable_v2/system3/kripke.py +548 -0
- fable_v2/system3/oracle.py +745 -0
- fable_v2/verifiers.py +72 -0
- tests/__init__.py +1 -0
- tests/test_anti_loop_circuit_breaker.py +64 -0
- tests/test_auto_updater.py +407 -0
- tests/test_coder_fleet.py +535 -0
- tests/test_delegation_compiler.py +54 -0
- tests/test_descriptor_boundaries.py +126 -0
- tests/test_design_engine.py +603 -0
- tests/test_epistemic_evidence_validator.py +66 -0
- tests/test_execution_broker.py +233 -0
- tests/test_fable_v2.py +406 -0
- tests/test_fleet_transitions.py +116 -0
- tests/test_fsm_redteam_evolution.py +406 -0
- tests/test_goal_rubric_and_pipeline.py +367 -0
- tests/test_hebbian_plasticity.py +585 -0
- tests/test_packaging_runtime.py +194 -0
- tests/test_proof_engine.py +259 -0
- tests/test_red_team_swarm.py +645 -0
- tests/test_redteam_remediation.py +169 -0
- tests/test_registration_transaction.py +375 -0
- tests/test_requested_regressions.py +467 -0
- tests/test_scrapers.py +370 -0
- tests/test_server_actions.py +93 -0
- tests/test_server_frontier_actions.py +269 -0
- tests/test_server_protocol.py +88 -0
- tests/test_stealth_browser.py +970 -0
- tests/test_system3.py +381 -0
- tests/test_system3_deep_integration.py +385 -0
- tests/test_system3_frontier.py +436 -0
- tests/test_vector_engine.py +608 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""System 3 Friston Active Inference & Variational Free Energy Engine.
|
|
2
|
+
|
|
3
|
+
Implements Karl Friston's Free Energy Principle for autonomous agentic reasoning:
|
|
4
|
+
- Variational Free Energy F = Complexity - Accuracy (KL-Divergence + Surprisal bound)
|
|
5
|
+
- Expected Free Energy G(pi) decomposition: Epistemic Value (Information Gain) + Pragmatic Value (Goal Utility)
|
|
6
|
+
- POMDP/MDP generative models (A likelihood, B transitions, C preferences, D priors)
|
|
7
|
+
- Policy evaluation, action selection, and variational belief updates in pure standard library Python.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field, asdict
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Union
|
|
14
|
+
import copy
|
|
15
|
+
import json
|
|
16
|
+
import math
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
EPS = 1e-12
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _normalize(dist: Sequence[float]) -> List[float]:
|
|
23
|
+
"""Normalize a vector to a valid probability distribution."""
|
|
24
|
+
total = sum(dist)
|
|
25
|
+
if total < EPS:
|
|
26
|
+
# Uniform fallback
|
|
27
|
+
n = len(dist)
|
|
28
|
+
return [1.0 / max(1, n)] * n
|
|
29
|
+
return [max(EPS, x / total) for x in dist]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _softmax(values: Sequence[float], temperature: float = 1.0) -> List[float]:
|
|
33
|
+
"""Numerically stable softmax."""
|
|
34
|
+
if not values:
|
|
35
|
+
return []
|
|
36
|
+
temp = max(1e-6, temperature)
|
|
37
|
+
scaled = [v / temp for v in values]
|
|
38
|
+
max_v = max(scaled)
|
|
39
|
+
exps = [math.exp(v - max_v) for v in scaled]
|
|
40
|
+
sum_exps = sum(exps)
|
|
41
|
+
return [e / sum_exps for e in exps]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _kl_divergence(p: Sequence[float], q: Sequence[float]) -> float:
|
|
45
|
+
"""Compute Kullback-Leibler divergence D_KL(P || Q) = sum(P_i * ln(P_i / Q_i))."""
|
|
46
|
+
p_norm = _normalize(p)
|
|
47
|
+
q_norm = _normalize(q)
|
|
48
|
+
div = 0.0
|
|
49
|
+
for pi, qi in zip(p_norm, q_norm):
|
|
50
|
+
if pi > EPS:
|
|
51
|
+
div += pi * math.log(pi / max(EPS, qi))
|
|
52
|
+
return max(0.0, div)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _entropy(dist: Sequence[float]) -> float:
|
|
56
|
+
"""Compute Shannon entropy H(P) = -sum(P_i * ln(P_i))."""
|
|
57
|
+
p_norm = _normalize(dist)
|
|
58
|
+
h = 0.0
|
|
59
|
+
for pi in p_norm:
|
|
60
|
+
if pi > EPS:
|
|
61
|
+
h -= pi * math.log(pi)
|
|
62
|
+
return max(0.0, h)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Policy:
|
|
67
|
+
"""A planned sequence of actions over a future horizon."""
|
|
68
|
+
policy_id: str
|
|
69
|
+
actions: List[str]
|
|
70
|
+
label: str = ""
|
|
71
|
+
description: str = ""
|
|
72
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
73
|
+
|
|
74
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
75
|
+
return asdict(self)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Policy":
|
|
79
|
+
return cls(**data)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class PolicyEvaluation:
|
|
84
|
+
"""Breakdown of Expected Free Energy G(pi) for policy selection."""
|
|
85
|
+
policy_id: str
|
|
86
|
+
actions: List[str]
|
|
87
|
+
expected_free_energy_g: float
|
|
88
|
+
risk_pragmatic_divergence: float # D_KL(q(o|pi) || P(o in C)) - Divergence from prior preferences
|
|
89
|
+
ambiguity_expected_entropy: float # E_q(s)[ H(P(o|s)) ] - Expected observation ambiguity
|
|
90
|
+
epistemic_information_gain: float # Mutual Information I(s; o | pi) (Exploration Value)
|
|
91
|
+
pragmatic_goal_utility: float # Expected Log-Preference E[ ln C(o) ] (Exploitation Value)
|
|
92
|
+
probability: float # Softmax posterior probability P(pi)
|
|
93
|
+
is_optimal: bool = False
|
|
94
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
95
|
+
|
|
96
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
97
|
+
return asdict(self)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def from_dict(cls, data: Dict[str, Any]) -> "PolicyEvaluation":
|
|
101
|
+
return cls(**data)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass
|
|
105
|
+
class GenerativeModel:
|
|
106
|
+
"""
|
|
107
|
+
Active Inference Generative Model:
|
|
108
|
+
- S: Hidden states {s_1, ..., s_N}
|
|
109
|
+
- O: Observations {o_1, ..., o_M}
|
|
110
|
+
- U: Control actions {u_1, ..., u_K}
|
|
111
|
+
- A: Observation likelihood matrix P(o_m | s_n) [M x N]
|
|
112
|
+
- B: State transition matrices P(s_{t+1} | s_t, u) [K x N x N]
|
|
113
|
+
- C: Prior preference distribution over observations P(o) [M]
|
|
114
|
+
- D: Prior beliefs over initial hidden states P(s_0) [N]
|
|
115
|
+
"""
|
|
116
|
+
states: List[str]
|
|
117
|
+
observations: List[str]
|
|
118
|
+
actions: List[str]
|
|
119
|
+
a_matrix: List[List[float]] # Shape: (len(observations), len(states))
|
|
120
|
+
b_matrices: Dict[str, List[List[float]]] # action -> Matrix of shape (len(states), len(states))
|
|
121
|
+
c_preferences: List[float] # Length: len(observations)
|
|
122
|
+
d_prior: List[float] # Length: len(states)
|
|
123
|
+
|
|
124
|
+
def __post_init__(self):
|
|
125
|
+
num_s = len(self.states)
|
|
126
|
+
num_o = len(self.observations)
|
|
127
|
+
if len(self.d_prior) != num_s:
|
|
128
|
+
raise ValueError(f"D prior length {len(self.d_prior)} != number of states {num_s}")
|
|
129
|
+
if len(self.c_preferences) != num_o:
|
|
130
|
+
raise ValueError(f"C preferences length {len(self.c_preferences)} != number of observations {num_o}")
|
|
131
|
+
if len(self.a_matrix) != num_o or any(len(row) != num_s for row in self.a_matrix):
|
|
132
|
+
raise ValueError(f"A matrix must have shape ({num_o}, {num_s})")
|
|
133
|
+
# Normalize columns of A
|
|
134
|
+
norm_a = [[0.0] * num_s for _ in range(num_o)]
|
|
135
|
+
for s_idx in range(num_s):
|
|
136
|
+
col = [self.a_matrix[o_idx][s_idx] for o_idx in range(num_o)]
|
|
137
|
+
norm_col = _normalize(col)
|
|
138
|
+
for o_idx in range(num_o):
|
|
139
|
+
norm_a[o_idx][s_idx] = norm_col[o_idx]
|
|
140
|
+
self.a_matrix = norm_a
|
|
141
|
+
self.c_preferences = _normalize(self.c_preferences)
|
|
142
|
+
self.d_prior = _normalize(self.d_prior)
|
|
143
|
+
|
|
144
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
145
|
+
return asdict(self)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_dict(cls, data: Dict[str, Any]) -> "GenerativeModel":
|
|
149
|
+
return cls(**data)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass
|
|
153
|
+
class FreeEnergyReport:
|
|
154
|
+
"""Comprehensive Active Inference Free Energy state and policy telemetry."""
|
|
155
|
+
step: int
|
|
156
|
+
current_observation: str
|
|
157
|
+
belief_state: Dict[str, float]
|
|
158
|
+
variational_free_energy_f: float
|
|
159
|
+
complexity_kl: float
|
|
160
|
+
accuracy_log_likelihood: float
|
|
161
|
+
surprisal_bound: float
|
|
162
|
+
evaluated_policies: List[PolicyEvaluation]
|
|
163
|
+
selected_policy: PolicyEvaluation
|
|
164
|
+
selected_action: str
|
|
165
|
+
telemetry: Dict[str, Any] = field(default_factory=dict)
|
|
166
|
+
|
|
167
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
168
|
+
return {
|
|
169
|
+
"step": self.step,
|
|
170
|
+
"current_observation": self.current_observation,
|
|
171
|
+
"belief_state": self.belief_state,
|
|
172
|
+
"variational_free_energy_f": self.variational_free_energy_f,
|
|
173
|
+
"complexity_kl": self.complexity_kl,
|
|
174
|
+
"accuracy_log_likelihood": self.accuracy_log_likelihood,
|
|
175
|
+
"surprisal_bound": self.surprisal_bound,
|
|
176
|
+
"evaluated_policies": [p.to_dict() for p in self.evaluated_policies],
|
|
177
|
+
"selected_policy": self.selected_policy.to_dict(),
|
|
178
|
+
"selected_action": self.selected_action,
|
|
179
|
+
"telemetry": self.telemetry,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def from_dict(cls, data: Dict[str, Any]) -> "FreeEnergyReport":
|
|
184
|
+
policies = [PolicyEvaluation.from_dict(p) for p in data.get("evaluated_policies", [])]
|
|
185
|
+
sel_pol = PolicyEvaluation.from_dict(data["selected_policy"])
|
|
186
|
+
return cls(
|
|
187
|
+
step=data["step"],
|
|
188
|
+
current_observation=data["current_observation"],
|
|
189
|
+
belief_state=data["belief_state"],
|
|
190
|
+
variational_free_energy_f=data["variational_free_energy_f"],
|
|
191
|
+
complexity_kl=data["complexity_kl"],
|
|
192
|
+
accuracy_log_likelihood=data["accuracy_log_likelihood"],
|
|
193
|
+
surprisal_bound=data["surprisal_bound"],
|
|
194
|
+
evaluated_policies=policies,
|
|
195
|
+
selected_policy=sel_pol,
|
|
196
|
+
selected_action=data["selected_action"],
|
|
197
|
+
telemetry=data.get("telemetry", {}),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ActiveInferenceEngine:
|
|
202
|
+
"""
|
|
203
|
+
Friston Active Inference Engine:
|
|
204
|
+
Minimizes Variational Free Energy F w.r.t beliefs (Perception)
|
|
205
|
+
and minimizes Expected Free Energy G w.r.t policies (Action).
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
def __init__(
|
|
209
|
+
self,
|
|
210
|
+
generative_model: GenerativeModel,
|
|
211
|
+
policy_precision_gamma: float = 16.0,
|
|
212
|
+
):
|
|
213
|
+
self.model = generative_model
|
|
214
|
+
self.gamma = policy_precision_gamma
|
|
215
|
+
self.current_beliefs: List[float] = list(self.model.d_prior)
|
|
216
|
+
self.step_count: int = 0
|
|
217
|
+
self.history: List[Dict[str, Any]] = []
|
|
218
|
+
|
|
219
|
+
def update_beliefs(self, observation: str) -> Tuple[float, float, float]:
|
|
220
|
+
"""
|
|
221
|
+
Perception step: Update posterior state beliefs q(s) given observation o:
|
|
222
|
+
ln q*(s) = ln p(s) + ln p(o | s) - ln Z
|
|
223
|
+
Returns (Free_Energy_F, Complexity_KL, Accuracy_Log_Likelihood).
|
|
224
|
+
"""
|
|
225
|
+
if observation not in self.model.observations:
|
|
226
|
+
raise ValueError(f"Unknown observation '{observation}'. Available: {self.model.observations}")
|
|
227
|
+
|
|
228
|
+
obs_idx = self.model.observations.index(observation)
|
|
229
|
+
num_s = len(self.model.states)
|
|
230
|
+
|
|
231
|
+
# Unnormalized log posterior: ln d_i + ln A[obs_idx][i]
|
|
232
|
+
log_joint = []
|
|
233
|
+
for s_idx in range(num_s):
|
|
234
|
+
prior_s = max(EPS, self.current_beliefs[s_idx])
|
|
235
|
+
like_s = max(EPS, self.model.a_matrix[obs_idx][s_idx])
|
|
236
|
+
log_joint.append(math.log(prior_s) + math.log(like_s))
|
|
237
|
+
|
|
238
|
+
# Posterior beliefs via softmax
|
|
239
|
+
self.current_beliefs = _softmax(log_joint)
|
|
240
|
+
|
|
241
|
+
# Calculate Variational Free Energy F = Complexity - Accuracy
|
|
242
|
+
# Complexity = D_KL(q(s) || p(s))
|
|
243
|
+
complexity = _kl_divergence(self.current_beliefs, self.model.d_prior)
|
|
244
|
+
|
|
245
|
+
# Accuracy = E_q(s)[ ln p(o | s) ]
|
|
246
|
+
accuracy = 0.0
|
|
247
|
+
for s_idx in range(num_s):
|
|
248
|
+
like_s = max(EPS, self.model.a_matrix[obs_idx][s_idx])
|
|
249
|
+
accuracy += self.current_beliefs[s_idx] * math.log(like_s)
|
|
250
|
+
|
|
251
|
+
f_total = complexity - accuracy
|
|
252
|
+
|
|
253
|
+
return f_total, complexity, accuracy
|
|
254
|
+
|
|
255
|
+
def evaluate_policy(self, policy: Policy) -> PolicyEvaluation:
|
|
256
|
+
"""
|
|
257
|
+
Evaluate Expected Free Energy G(pi) for candidate policy pi:
|
|
258
|
+
G(pi) = Risk (Pragmatic Divergence) + Ambiguity (Expected Uncertainty)
|
|
259
|
+
"""
|
|
260
|
+
num_s = len(self.model.states)
|
|
261
|
+
num_o = len(self.model.observations)
|
|
262
|
+
|
|
263
|
+
# Forward simulate trajectory of beliefs under policy
|
|
264
|
+
pred_state = list(self.current_beliefs)
|
|
265
|
+
total_g = 0.0
|
|
266
|
+
total_risk = 0.0
|
|
267
|
+
total_ambiguity = 0.0
|
|
268
|
+
total_info_gain = 0.0
|
|
269
|
+
total_utility = 0.0
|
|
270
|
+
|
|
271
|
+
for action in policy.actions:
|
|
272
|
+
if action not in self.model.b_matrices:
|
|
273
|
+
raise ValueError(f"Action '{action}' does not have a B transition matrix.")
|
|
274
|
+
|
|
275
|
+
b_mat = self.model.b_matrices[action]
|
|
276
|
+
# Next state prediction: pred_next[i] = sum_j B[i][j] * pred_state[j]
|
|
277
|
+
next_state = [0.0] * num_s
|
|
278
|
+
for i in range(num_s):
|
|
279
|
+
for j in range(num_s):
|
|
280
|
+
next_state[i] += b_mat[i][j] * pred_state[j]
|
|
281
|
+
pred_state = _normalize(next_state)
|
|
282
|
+
|
|
283
|
+
# Predicted observation distribution: pred_obs[m] = sum_n A[m][n] * pred_state[n]
|
|
284
|
+
pred_obs = [0.0] * num_o
|
|
285
|
+
for m in range(num_o):
|
|
286
|
+
for n in range(num_s):
|
|
287
|
+
pred_obs[m] += self.model.a_matrix[m][n] * pred_state[n]
|
|
288
|
+
pred_obs = _normalize(pred_obs)
|
|
289
|
+
|
|
290
|
+
# 1. Risk: D_KL( q(o | pi) || C )
|
|
291
|
+
risk = _kl_divergence(pred_obs, self.model.c_preferences)
|
|
292
|
+
|
|
293
|
+
# 2. Ambiguity: E_q(s)[ H( A[:, s] ) ]
|
|
294
|
+
ambiguity = 0.0
|
|
295
|
+
for s_idx in range(num_s):
|
|
296
|
+
col = [self.model.a_matrix[o_idx][s_idx] for o_idx in range(num_o)]
|
|
297
|
+
ambiguity += pred_state[s_idx] * _entropy(col)
|
|
298
|
+
|
|
299
|
+
# 3. Epistemic Information Gain: H(q(o | pi)) - Ambiguity (Mutual Information I(s; o))
|
|
300
|
+
entropy_obs = _entropy(pred_obs)
|
|
301
|
+
info_gain = max(0.0, entropy_obs - ambiguity)
|
|
302
|
+
|
|
303
|
+
# 4. Pragmatic Utility: E_q(o)[ ln C(o) ]
|
|
304
|
+
utility = 0.0
|
|
305
|
+
for o_idx in range(num_o):
|
|
306
|
+
utility += pred_obs[o_idx] * math.log(max(EPS, self.model.c_preferences[o_idx]))
|
|
307
|
+
|
|
308
|
+
step_g = risk + ambiguity
|
|
309
|
+
|
|
310
|
+
total_g += step_g
|
|
311
|
+
total_risk += risk
|
|
312
|
+
total_ambiguity += ambiguity
|
|
313
|
+
total_info_gain += info_gain
|
|
314
|
+
total_utility += utility
|
|
315
|
+
|
|
316
|
+
return PolicyEvaluation(
|
|
317
|
+
policy_id=policy.policy_id,
|
|
318
|
+
actions=list(policy.actions),
|
|
319
|
+
expected_free_energy_g=total_g,
|
|
320
|
+
risk_pragmatic_divergence=total_risk,
|
|
321
|
+
ambiguity_expected_entropy=total_ambiguity,
|
|
322
|
+
epistemic_information_gain=total_info_gain,
|
|
323
|
+
pragmatic_goal_utility=total_utility,
|
|
324
|
+
probability=0.0, # Will be normalized across policies
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def select_action(
|
|
328
|
+
self,
|
|
329
|
+
observation: str,
|
|
330
|
+
candidate_policies: List[Policy],
|
|
331
|
+
) -> FreeEnergyReport:
|
|
332
|
+
"""
|
|
333
|
+
Execute one complete Active Inference reasoning cycle:
|
|
334
|
+
1. Ingest observation and update state beliefs (Perception).
|
|
335
|
+
2. Evaluate candidate policies across Epistemic & Pragmatic value (Planning).
|
|
336
|
+
3. Compute policy posterior distribution P(pi) via softmax(-gamma * G).
|
|
337
|
+
4. Select optimal policy and action (Action).
|
|
338
|
+
"""
|
|
339
|
+
self.step_count += 1
|
|
340
|
+
|
|
341
|
+
# 1. Perception
|
|
342
|
+
f_val, comp_kl, acc_ll = self.update_beliefs(observation)
|
|
343
|
+
|
|
344
|
+
if not candidate_policies:
|
|
345
|
+
# Generate default 1-step policies for all actions
|
|
346
|
+
candidate_policies = [
|
|
347
|
+
Policy(policy_id=f"policy_{act}", actions=[act], label=f"Execute {act}")
|
|
348
|
+
for act in self.model.actions
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
# 2. Policy Planning
|
|
352
|
+
evaluations = [self.evaluate_policy(p) for p in candidate_policies]
|
|
353
|
+
|
|
354
|
+
# 3. Policy Posterior P(pi) = softmax(-gamma * G)
|
|
355
|
+
neg_g_values = [-self.gamma * e.expected_free_energy_g for e in evaluations]
|
|
356
|
+
probs = _softmax(neg_g_values)
|
|
357
|
+
for e, p in zip(evaluations, probs):
|
|
358
|
+
e.probability = p
|
|
359
|
+
|
|
360
|
+
# Find best policy (minimum G)
|
|
361
|
+
best_eval = min(evaluations, key=lambda e: e.expected_free_energy_g)
|
|
362
|
+
best_eval.is_optimal = True
|
|
363
|
+
selected_action = best_eval.actions[0] if best_eval.actions else self.model.actions[0]
|
|
364
|
+
|
|
365
|
+
belief_dict = {
|
|
366
|
+
self.model.states[i]: self.current_beliefs[i]
|
|
367
|
+
for i in range(len(self.model.states))
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
report = FreeEnergyReport(
|
|
371
|
+
step=self.step_count,
|
|
372
|
+
current_observation=observation,
|
|
373
|
+
belief_state=belief_dict,
|
|
374
|
+
variational_free_energy_f=f_val,
|
|
375
|
+
complexity_kl=comp_kl,
|
|
376
|
+
accuracy_log_likelihood=acc_ll,
|
|
377
|
+
surprisal_bound=f_val,
|
|
378
|
+
evaluated_policies=evaluations,
|
|
379
|
+
selected_policy=best_eval,
|
|
380
|
+
selected_action=selected_action,
|
|
381
|
+
telemetry={
|
|
382
|
+
"policy_precision_gamma": self.gamma,
|
|
383
|
+
"states_count": len(self.model.states),
|
|
384
|
+
"observations_count": len(self.model.observations),
|
|
385
|
+
"entropy_of_beliefs": _entropy(self.current_beliefs),
|
|
386
|
+
},
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
self.history.append(report.to_dict())
|
|
390
|
+
return report
|
|
391
|
+
|
|
392
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
393
|
+
return {
|
|
394
|
+
"model": self.model.to_dict(),
|
|
395
|
+
"gamma": self.gamma,
|
|
396
|
+
"current_beliefs": self.current_beliefs,
|
|
397
|
+
"step_count": self.step_count,
|
|
398
|
+
"history": self.history,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@classmethod
|
|
402
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ActiveInferenceEngine":
|
|
403
|
+
model = GenerativeModel.from_dict(data["model"])
|
|
404
|
+
engine = cls(generative_model=model, policy_precision_gamma=data.get("gamma", 16.0))
|
|
405
|
+
engine.current_beliefs = list(data.get("current_beliefs", model.d_prior))
|
|
406
|
+
engine.step_count = int(data.get("step_count", 0))
|
|
407
|
+
engine.history = list(data.get("history", []))
|
|
408
|
+
return engine
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def create_default_architecture_pomdp() -> GenerativeModel:
|
|
412
|
+
"""
|
|
413
|
+
Factory for a standard Software Architecture Active Inference POMDP:
|
|
414
|
+
States: {OPTIMAL_DECOUPLED, CONTENTION_BOTTLENECK, MEMORY_LEAK, INTEGRITY_FAULT}
|
|
415
|
+
Observations: {HIGH_THROUGHPUT_CLEAN, LOCK_CONTENTION_WARN, MEMORY_GROWTH_WARN, CHECKSUM_FAIL}
|
|
416
|
+
Actions: {APPLY_CAS_ISOLATION, SHARD_WORKERS, REFACTOR_MUTEX, RUN_BENCHMARK}
|
|
417
|
+
"""
|
|
418
|
+
states = ["OPTIMAL_DECOUPLED", "CONTENTION_BOTTLENECK", "MEMORY_LEAK", "INTEGRITY_FAULT"]
|
|
419
|
+
observations = ["HIGH_THROUGHPUT_CLEAN", "LOCK_CONTENTION_WARN", "MEMORY_GROWTH_WARN", "CHECKSUM_FAIL"]
|
|
420
|
+
actions = ["APPLY_CAS_ISOLATION", "SHARD_WORKERS", "REFACTOR_MUTEX", "RUN_BENCHMARK"]
|
|
421
|
+
|
|
422
|
+
# A matrix [O x S]: observation likelihoods
|
|
423
|
+
a_mat = [
|
|
424
|
+
[0.85, 0.05, 0.05, 0.05], # HIGH_THROUGHPUT_CLEAN
|
|
425
|
+
[0.05, 0.80, 0.10, 0.05], # LOCK_CONTENTION_WARN
|
|
426
|
+
[0.05, 0.10, 0.80, 0.05], # MEMORY_GROWTH_WARN
|
|
427
|
+
[0.05, 0.05, 0.05, 0.85], # CHECKSUM_FAIL
|
|
428
|
+
]
|
|
429
|
+
|
|
430
|
+
# B matrices [S x S] for each action
|
|
431
|
+
b_mats: Dict[str, List[List[float]]] = {}
|
|
432
|
+
|
|
433
|
+
# APPLY_CAS_ISOLATION transitions towards OPTIMAL_DECOUPLED
|
|
434
|
+
b_mats["APPLY_CAS_ISOLATION"] = [
|
|
435
|
+
[0.90, 0.70, 0.60, 0.30],
|
|
436
|
+
[0.05, 0.20, 0.10, 0.10],
|
|
437
|
+
[0.03, 0.05, 0.25, 0.10],
|
|
438
|
+
[0.02, 0.05, 0.05, 0.50],
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
# SHARD_WORKERS reduces contention
|
|
442
|
+
b_mats["SHARD_WORKERS"] = [
|
|
443
|
+
[0.80, 0.65, 0.10, 0.10],
|
|
444
|
+
[0.10, 0.25, 0.10, 0.10],
|
|
445
|
+
[0.05, 0.05, 0.70, 0.10],
|
|
446
|
+
[0.05, 0.05, 0.10, 0.70],
|
|
447
|
+
]
|
|
448
|
+
|
|
449
|
+
# REFACTOR_MUTEX targets lock contention
|
|
450
|
+
b_mats["REFACTOR_MUTEX"] = [
|
|
451
|
+
[0.85, 0.75, 0.10, 0.10],
|
|
452
|
+
[0.05, 0.15, 0.10, 0.10],
|
|
453
|
+
[0.05, 0.05, 0.70, 0.10],
|
|
454
|
+
[0.05, 0.05, 0.10, 0.70],
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
# RUN_BENCHMARK maintains state (pure diagnostic/epistemic probe)
|
|
458
|
+
b_mats["RUN_BENCHMARK"] = [
|
|
459
|
+
[0.95, 0.05, 0.05, 0.05],
|
|
460
|
+
[0.02, 0.90, 0.02, 0.02],
|
|
461
|
+
[0.02, 0.03, 0.90, 0.03],
|
|
462
|
+
[0.01, 0.02, 0.03, 0.90],
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
# C preferences: agent strongly desires clean high throughput
|
|
466
|
+
c_pref = [0.85, 0.05, 0.05, 0.05]
|
|
467
|
+
|
|
468
|
+
# D prior: initial uniform/slightly optimistic state belief
|
|
469
|
+
d_prior = [0.50, 0.20, 0.15, 0.15]
|
|
470
|
+
|
|
471
|
+
return GenerativeModel(
|
|
472
|
+
states=states,
|
|
473
|
+
observations=observations,
|
|
474
|
+
actions=actions,
|
|
475
|
+
a_matrix=a_mat,
|
|
476
|
+
b_matrices=b_mats,
|
|
477
|
+
c_preferences=c_pref,
|
|
478
|
+
d_prior=d_prior,
|
|
479
|
+
)
|