crexs commited on
Commit
0841fff
·
verified ·
1 Parent(s): e440672

sync: update core/cognitive_snapshot.py

Browse files
Files changed (1) hide show
  1. core/cognitive_snapshot.py +158 -0
core/cognitive_snapshot.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """cognitive_snapshot.py — Observability and diagnostics for the comonadic pipeline.
2
+
3
+ Provides:
4
+ - SnapshotLogger: captures full ContextWorker state at each step
5
+ - TransitionComparator: evaluates predictor accuracy against actual transitions
6
+ """
7
+
8
+ import json
9
+ from dataclasses import dataclass, field
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from typing import Callable, Optional
13
+
14
+ from infj_bot.core.context_engine import CognitiveState, ContextWorker, CognitivePayload
15
+
16
+
17
+ @dataclass
18
+ class CognitiveSnapshot:
19
+ """A frozen frame of the cognitive pipeline at a single moment."""
20
+
21
+ step_index: int
22
+ timestamp: str
23
+ state: CognitiveState
24
+ user_input: str
25
+ internal_log: str
26
+ response: str
27
+ history_depth: int
28
+ metadata: dict = field(default_factory=dict)
29
+
30
+ def to_dict(self) -> dict:
31
+ return {
32
+ "step_index": self.step_index,
33
+ "timestamp": self.timestamp,
34
+ "state": self.state.model_dump(),
35
+ "user_input": self.user_input,
36
+ "internal_log": self.internal_log,
37
+ "response": self.response,
38
+ "history_depth": self.history_depth,
39
+ "metadata": self.metadata,
40
+ }
41
+
42
+
43
+ class SnapshotLogger:
44
+ """Logs full cognitive snapshots for later analysis.
45
+
46
+ Usage:
47
+ logger = SnapshotLogger(max_snapshots=50)
48
+ logger.capture(pipeline, step=0)
49
+ pipeline = pipeline.extend(some_op)
50
+ logger.capture(pipeline, step=1)
51
+ logger.write(Path("snapshots.jsonl"))
52
+ """
53
+
54
+ def __init__(self, max_snapshots: int = 50):
55
+ self.snapshots: list[CognitiveSnapshot] = []
56
+ self.max_snapshots = max_snapshots
57
+
58
+ def capture(
59
+ self,
60
+ worker: ContextWorker[CognitivePayload],
61
+ step: int,
62
+ extra_metadata: Optional[dict] = None,
63
+ ) -> None:
64
+ """Extract a snapshot from the current worker."""
65
+ payload = worker.current()
66
+ snap = CognitiveSnapshot(
67
+ step_index=step,
68
+ timestamp=datetime.now().isoformat(),
69
+ state=worker.state,
70
+ user_input=payload.user_input,
71
+ internal_log=payload.internal_log,
72
+ response=payload.response,
73
+ history_depth=len(worker.history),
74
+ metadata=extra_metadata or {},
75
+ )
76
+ self.snapshots.append(snap)
77
+ if len(self.snapshots) > self.max_snapshots:
78
+ self.snapshots = self.snapshots[-self.max_snapshots :]
79
+
80
+ def write(self, path: Path) -> None:
81
+ """Append snapshots to a newline-delimited JSON file."""
82
+ path.parent.mkdir(parents=True, exist_ok=True)
83
+ with open(path, "a", encoding="utf-8") as f:
84
+ for snap in self.snapshots:
85
+ f.write(json.dumps(snap.to_dict(), default=str) + "\n")
86
+ self.snapshots.clear()
87
+
88
+ def clear(self) -> None:
89
+ self.snapshots.clear()
90
+
91
+
92
+ @dataclass
93
+ class TransitionReport:
94
+ """Result of comparing a predicted state transition against actual."""
95
+
96
+ predicted: CognitiveState
97
+ actual: CognitiveState
98
+ delta_error: dict # per-field difference between predicted and actual
99
+ accuracy_score: float # 0.0 (worst) → 1.0 (perfect)
100
+
101
+ def to_dict(self) -> dict:
102
+ return {
103
+ "predicted": self.predicted.model_dump(),
104
+ "actual": self.actual.model_dump(),
105
+ "delta_error": self.delta_error,
106
+ "accuracy_score": round(self.accuracy_score, 4),
107
+ }
108
+
109
+
110
+ class TransitionComparator:
111
+ """Evaluates how well a predictor function models real state transitions.
112
+
113
+ Usage:
114
+ comp = TransitionComparator()
115
+ report = comp.compare(
116
+ before_state,
117
+ after_state,
118
+ predictor=lambda s: s.model_copy(update={"tension": s.tension - 0.2})
119
+ )
120
+ print(report.accuracy_score)
121
+ """
122
+
123
+ def compare(
124
+ self,
125
+ before: CognitiveState,
126
+ after: CognitiveState,
127
+ predictor: Callable[[CognitiveState], CognitiveState],
128
+ ) -> TransitionReport:
129
+ predicted = predictor(before)
130
+
131
+ delta_error = {
132
+ "coherence": round(predicted.coherence - after.coherence, 4),
133
+ "resonance": round(predicted.resonance - after.resonance, 4),
134
+ "tension": round(predicted.tension - after.tension, 4),
135
+ "shadow_depth": round(predicted.shadow_depth - after.shadow_depth, 4),
136
+ }
137
+
138
+ # Accuracy = 1 - normalised mean absolute error
139
+ total_error = sum(abs(v) for v in delta_error.values())
140
+ accuracy_score = max(0.0, 1.0 - total_error / 4.0)
141
+
142
+ return TransitionReport(
143
+ predicted=predicted,
144
+ actual=after,
145
+ delta_error=delta_error,
146
+ accuracy_score=accuracy_score,
147
+ )
148
+
149
+ def evaluate_on_history(
150
+ self,
151
+ history: list[CognitiveState],
152
+ predictor: Callable[[CognitiveState], CognitiveState],
153
+ ) -> list[TransitionReport]:
154
+ """Run comparison across an entire history chain."""
155
+ reports = []
156
+ for i in range(len(history) - 1):
157
+ reports.append(self.compare(history[i], history[i + 1], predictor))
158
+ return reports