Spaces:
Runtime error
Runtime error
Commit ·
b69ffd3
1
Parent(s): 64ce939
feat(cp7): MemoryTurn/MemoryCheckpoint models (JSON round-trip)
Browse files- proteus/runtime/memory.py +58 -0
- tests/runtime/test_memory.py +39 -0
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 == []
|