irregular6612 commited on
Commit
b69ffd3
·
1 Parent(s): 64ce939

feat(cp7): MemoryTurn/MemoryCheckpoint models (JSON round-trip)

Browse files
proteus/runtime/memory.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Memory checkpoint models + persistence for the CP7 memory pre-roll.
2
+
3
+ A MemoryCheckpoint is one full-information self-play episode the playing model
4
+ ran *before* the scored game. It is persisted as a single-file JSON checkpoint
5
+ per (model, timestamp) and shown at the handover as the model's prior
6
+ experience. Like ``trace.py`` this module is a serialization boundary: it
7
+ imports only pydantic + stdlib and NO other runtime module.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ class MemoryTurn(BaseModel):
16
+ """One self-play turn of the memory episode.
17
+
18
+ Attributes:
19
+ turn_idx: 1-based index within the memory episode.
20
+ frame_ascii: The pre-move grid the model saw this memory turn.
21
+ action: The action the model committed.
22
+ reasoning: The model's reasoning excerpt (may be truncated).
23
+ focal_pos: Focal ``(x, y)`` BEFORE the move (JSON array on disk).
24
+ predator_pos: Predator ``(x, y)`` BEFORE the move.
25
+ """
26
+
27
+ turn_idx: int
28
+ frame_ascii: str
29
+ action: str
30
+ reasoning: str = ""
31
+ focal_pos: tuple[int, int]
32
+ predator_pos: tuple[int, int]
33
+
34
+
35
+ class MemoryCheckpoint(BaseModel):
36
+ """A persisted full-info self-play episode used as handover memory.
37
+
38
+ Attributes:
39
+ model: Provider model identifier that produced the episode.
40
+ scenario: Registered scenario name.
41
+ motive_category: Category label (default ``"survival"``).
42
+ difficulty: Difficulty band string.
43
+ seed: Seed of the memory world (same as the scored game).
44
+ created_at: ISO-ish stamp from an injectable clock (filesystem-safe).
45
+ memory_turns: The episode's per-turn records, in play order.
46
+ outcome: ``"survived"`` or ``"eliminated"``.
47
+ transparent_prompt: The full-info brief used to drive the episode.
48
+ """
49
+
50
+ model: str
51
+ scenario: str
52
+ motive_category: str = "survival"
53
+ difficulty: str
54
+ seed: int | None = None
55
+ created_at: str
56
+ memory_turns: list[MemoryTurn] = Field(default_factory=list)
57
+ outcome: str
58
+ transparent_prompt: str
tests/runtime/test_memory.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from proteus.runtime.memory import MemoryCheckpoint, MemoryTurn
2
+
3
+
4
+ def _checkpoint() -> MemoryCheckpoint:
5
+ return MemoryCheckpoint(
6
+ model="demo",
7
+ scenario="predator_evade",
8
+ difficulty="easy",
9
+ seed=42,
10
+ created_at="2026-06-02T10-40-56Z",
11
+ memory_turns=[
12
+ MemoryTurn(
13
+ turn_idx=1, frame_ascii="FRAME-A", action="up",
14
+ reasoning="r1", focal_pos=(5, 3), predator_pos=(7, 3),
15
+ ),
16
+ ],
17
+ outcome="survived",
18
+ transparent_prompt="brief",
19
+ )
20
+
21
+
22
+ def test_checkpoint_round_trips_through_json():
23
+ ck = _checkpoint()
24
+ restored = MemoryCheckpoint.model_validate_json(ck.model_dump_json())
25
+ assert restored.model == "demo"
26
+ assert restored.memory_turns[0].action == "up"
27
+ assert restored.outcome == "survived"
28
+ # positions come back as tuples (pydantic coerces the JSON arrays)
29
+ assert restored.memory_turns[0].focal_pos == (5, 3)
30
+
31
+
32
+ def test_checkpoint_defaults():
33
+ ck = MemoryCheckpoint(
34
+ model="m", scenario="s", difficulty="easy", seed=None,
35
+ created_at="t", memory_turns=[], outcome="survived",
36
+ transparent_prompt="b",
37
+ )
38
+ assert ck.motive_category == "survival"
39
+ assert ck.memory_turns == []