from proteus.game.runtime.trace import SessionTrace def test_session_trace_memory_ref_defaults_none_and_round_trips(): t = SessionTrace( scenario="template", motive_category="survival", seed=42, difficulty="easy", model="demo", outcome="survived", ) assert t.memory_ref is None t2 = SessionTrace.model_validate_json(t.model_dump_json()) assert t2.memory_ref is None t3 = SessionTrace( scenario="template", motive_category="survival", seed=42, difficulty="easy", model="demo", outcome="survived", memory_ref="demo@FIXED", ) assert SessionTrace.model_validate_json(t3.model_dump_json()).memory_ref == "demo@FIXED" from proteus.game.agents import VanillaAgent from proteus.game.engine.difficulty import Difficulty from proteus.providers import FakeProvider from proteus.game.runtime import SessionRunner from proteus.game.runtime.memory import MemoryCheckpoint, MemoryTurn def _memory() -> MemoryCheckpoint: return MemoryCheckpoint( model="demo", scenario="template", difficulty="easy", seed=42, created_at="FIXED", memory_turns=[ MemoryTurn(turn_idx=1, frame_ascii="MEMFRAME-1", action="up", reasoning="", focal_pos=(5, 3), predator_pos=(7, 3)), MemoryTurn(turn_idx=2, frame_ascii="MEMFRAME-2", action="down", reasoning="", focal_pos=(5, 2), predator_pos=(6, 3)), ], outcome="survived", transparent_prompt="brief", ) def _runner(memory=None, memory_ref=None) -> SessionRunner: prov = FakeProvider(["ACTION: stay"] * 20, model_name="demo") return SessionRunner( "template", VanillaAgent(prov), difficulty=Difficulty.EASY, seed=42, play_turns=3, use_probe=False, memory=memory, memory_ref=memory_ref, ) def test_memory_injection_shows_explicit_memory_and_preserves_measurement(): # NOTE: template provides a default (persona) memory, so the no-explicit-memory # baseline already carries a MEMORY block. The contrast here is therefore # "explicit fixture memory" vs "default memory", not "memory" vs "no memory". base = _runner().run() withmem = _runner(memory=_memory(), memory_ref="demo@FIXED").run() obs1 = withmem.turns[0].observation # the explicit memory block is present at turn 1 assert "MEMORY" in obs1 assert "MEMFRAME-1" in obs1 and "MEMFRAME-2" in obs1 assert "you chose: up" in obs1 # the explicit memory overrides the scenario default, so the observation differs assert obs1 != base.turns[0].observation # the scored game is identical: same answer keys, diagnostic, metrics assert [t.motive_action for t in withmem.turns] == [t.motive_action for t in base.turns] assert [t.habit_action for t in withmem.turns] == [t.habit_action for t in base.turns] assert [t.is_diagnostic for t in withmem.turns] == [t.is_diagnostic for t in base.turns] assert withmem.metrics == base.metrics # memory_ref recorded with explicit memory, None when falling back to default assert withmem.memory_ref == "demo@FIXED" assert base.memory_ref is None def test_memory_is_shown_every_turn_for_auto_regressive_play(): withmem = _runner(memory=_memory(), memory_ref="x").run() # Auto-regressive play: the handover memory is carried on turn 2+ as well # (not only turn 1), so a stateless agent never loses it mid-episode. assert "MEMFRAME-1" in withmem.turns[0].observation assert "MEMFRAME-1" in withmem.turns[1].observation