Fix Issue 1: Make Trajectory None guards thorough (matching SRE patch quality)
Browse files- purpose_agent/types.py +22 -12
purpose_agent/types.py
CHANGED
|
@@ -3,6 +3,10 @@ Core data types for the Purpose Agent framework.
|
|
| 3 |
|
| 4 |
All modules exchange these types — this keeps the architecture modular
|
| 5 |
and lets you swap out any component without touching the others.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
|
| 8 |
from __future__ import annotations
|
|
@@ -105,6 +109,9 @@ class Trajectory:
|
|
| 105 |
|
| 106 |
The Experience Replay module stores these and the Optimizer extracts
|
| 107 |
heuristics from high-reward trajectories.
|
|
|
|
|
|
|
|
|
|
| 108 |
"""
|
| 109 |
task_description: str
|
| 110 |
purpose: str
|
|
@@ -114,30 +121,33 @@ class Trajectory:
|
|
| 114 |
|
| 115 |
@property
|
| 116 |
def cumulative_reward(self) -> float:
|
| 117 |
-
"""Sum of all positive deltas in the trajectory."""
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
if s.score is not None and s.score.delta > 0
|
| 121 |
-
|
|
|
|
| 122 |
|
| 123 |
@property
|
| 124 |
def total_delta(self) -> float:
|
| 125 |
-
"""Net state improvement across the entire trajectory."""
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
|
|
|
|
|
|
| 129 |
|
| 130 |
@property
|
| 131 |
def success_rate(self) -> float:
|
| 132 |
-
"""Fraction of steps that improved state."""
|
| 133 |
-
scored = [s for s in self.steps if s.score is not None]
|
| 134 |
if not scored:
|
| 135 |
return 0.0
|
| 136 |
return sum(1 for s in scored if s.score.improved) / len(scored)
|
| 137 |
|
| 138 |
@property
|
| 139 |
def final_phi(self) -> float | None:
|
| 140 |
-
"""Final Φ value (state-distance-to-goal) at end of trajectory."""
|
| 141 |
scored = [s for s in self.steps if s.score is not None]
|
| 142 |
if not scored:
|
| 143 |
return None
|
|
|
|
| 3 |
|
| 4 |
All modules exchange these types — this keeps the architecture modular
|
| 5 |
and lets you swap out any component without touching the others.
|
| 6 |
+
|
| 7 |
+
NOTE: sre_patches.py monkey-patches Trajectory properties at import time
|
| 8 |
+
with even more robust None guards. The versions here are the baseline;
|
| 9 |
+
SRE patches are the authoritative runtime versions.
|
| 10 |
"""
|
| 11 |
|
| 12 |
from __future__ import annotations
|
|
|
|
| 109 |
|
| 110 |
The Experience Replay module stores these and the Optimizer extracts
|
| 111 |
heuristics from high-reward trajectories.
|
| 112 |
+
|
| 113 |
+
NOTE: sre_patches.py replaces these properties with more thorough None guards.
|
| 114 |
+
The patched versions check both `s.score is not None` AND `s.score.delta is not None`.
|
| 115 |
"""
|
| 116 |
task_description: str
|
| 117 |
purpose: str
|
|
|
|
| 121 |
|
| 122 |
@property
|
| 123 |
def cumulative_reward(self) -> float:
|
| 124 |
+
"""Sum of all positive deltas in the trajectory (None-safe)."""
|
| 125 |
+
total = 0.0
|
| 126 |
+
for s in self.steps:
|
| 127 |
+
if s.score is not None and s.score.delta is not None and s.score.delta > 0:
|
| 128 |
+
total += s.score.delta
|
| 129 |
+
return total
|
| 130 |
|
| 131 |
@property
|
| 132 |
def total_delta(self) -> float:
|
| 133 |
+
"""Net state improvement across the entire trajectory (None-safe)."""
|
| 134 |
+
total = 0.0
|
| 135 |
+
for s in self.steps:
|
| 136 |
+
if s.score is not None and s.score.delta is not None:
|
| 137 |
+
total += s.score.delta
|
| 138 |
+
return total
|
| 139 |
|
| 140 |
@property
|
| 141 |
def success_rate(self) -> float:
|
| 142 |
+
"""Fraction of steps that improved state (None-safe)."""
|
| 143 |
+
scored = [s for s in self.steps if s.score is not None and s.score.delta is not None]
|
| 144 |
if not scored:
|
| 145 |
return 0.0
|
| 146 |
return sum(1 for s in scored if s.score.improved) / len(scored)
|
| 147 |
|
| 148 |
@property
|
| 149 |
def final_phi(self) -> float | None:
|
| 150 |
+
"""Final Φ value (state-distance-to-goal) at end of trajectory (None-safe)."""
|
| 151 |
scored = [s for s in self.steps if s.score is not None]
|
| 152 |
if not scored:
|
| 153 |
return None
|