AgentnessBench / tests /runtime /test_director_errand_runner.py
irregular6612's picture
feat(errand): handover memory carries courier position + behaviour flags on the text path
ef62102
Raw
History Blame Contribute Delete
4.87 kB
# tests/runtime/test_director_errand_runner.py
"""author_errand_runner: single-agent persona route on MEMORY_LAYOUT + 3 variants."""
from proteus.game.runtime.multiagent_director import (
author_errand_runner, author_errand_runner_variants)
from proteus.game.runtime.memory import memory_frames
from proteus.game.scenarios.base import get_scenario
from proteus.game.scenarios import errand_world as w
def test_single_agent_deterministic():
a = author_errand_runner(seed=7, persona="civic")
b = author_errand_runner(seed=7, persona="civic")
assert a.model_dump_json() == b.model_dump_json()
t0 = a.memory_turns[0]
couriers = [ag for ag in t0.agents if ag.kind == "agent"]
assert len(couriers) == 1 and couriers[0].is_chosen # exactly ONE moving agent
assert "npc_down" in {ag.kind for ag in t0.agents} # pedestrian present
idxs = {i for (_x, _y, i) in t0.cells}
assert {w.C_ROAD, w.C_GRASS, w.C_HOUSE} <= idxs # overlay carried (road+grass+house)
assert a.wall_rects # walls carried for replay
def test_persona_reaches_home():
for pid in ("civic", "warm_outlaw", "opportunist"):
ck = author_errand_runner(seed=7, persona=pid)
assert ck.memory_turns[-1].events == ["arrived home"], pid
assert ck.persona_weight_id == pid
def test_opportunist_grabs_wallet_civic_does_not():
civic = author_errand_runner(seed=7, persona="civic")
opp = author_errand_runner(seed=7, persona="opportunist")
def wallet_ever_gone(ck):
return any(all(i != w.C_WALLET for (_x, _y, i) in t.cells) for t in ck.memory_turns)
assert wallet_ever_gone(opp) and not wallet_ever_gone(civic)
def test_variants_returns_three_labelled():
v = author_errand_runner_variants(seed=7)
assert set(v) == {"civic", "warm_outlaw", "opportunist"}
assert all(ck.scenario == "errand_runner" for ck in v.values())
def test_frames_render_and_have_single_courier():
ck = author_errand_runner(seed=7, persona="civic")
scen = get_scenario("errand_runner")()
frames = memory_frames(ck, legend=scen.legend(), grid_size=scen.grid_size)
assert frames and "grid" in frames[0]
def test_default_memory_attached():
scen = get_scenario("errand_runner")()
ck = scen.default_memory(7, None)
assert ck is not None and ck.scenario == "errand_runner"
def test_memory_paints_walls_grass_house_and_one_courier():
ck = author_errand_runner(seed=7, persona="civic")
scen = get_scenario("errand_runner")()
g = memory_frames(ck, legend=scen.legend(), grid_size=scen.grid_size)[0]["grid"]
flat = [v for row in g for v in row]
# walls/grass/house/road painted
assert w.C_WALL in flat and w.C_HOUSE in flat and w.C_GRASS in flat and w.C_ROAD in flat
assert flat.count(w.C_COURIER) == w.AGENT * w.AGENT # exactly ONE 2x2 courier
def test_memory_text_path_carries_position_and_decision_context():
"""The LLM-visible memory (render_memory_block) must convey WHERE the persona
was each turn and the salient decision context, not a bare 'errand tN'
placeholder — otherwise the text handover memory has no spatial grounding and
the promised "watch how you handled the crosswalk/wallet/pedestrian" is empty.
"""
from proteus.game.runtime.memory import render_memory_block
opp = author_errand_runner(seed=7, persona="opportunist")
block = render_memory_block(opp)
# placeholder gone; every turn carries the courier's coordinates.
assert "errand t" not in block
assert block.count("you@(") == len(opp.memory_turns)
# the opportunist grabs the wallet -> the wallet context surfaces in memory;
# the civic citizen never stands on it, so its memory shows no wallet pickup.
assert "wallet" in block.lower()
civic = author_errand_runner(seed=7, persona="civic")
assert "wallet" not in render_memory_block(civic).lower()
def test_civic_avoids_grass_cut_personas_step_on_it():
from proteus.game.scenarios import errand_world as w
lay = w.MEMORY_LAYOUT
grass = set(w.grass_cells(lay))
def grass_cells_walked(ck):
# any tick where the chosen agent's 2x2 footprint overlaps grass
hits = 0
for t in ck.memory_turns:
ag = next((a for a in t.agents if a.kind == "agent"), None)
if ag is None:
continue
fp = {(ag.pos[0]+i, ag.pos[1]+j) for i in range(ag.size) for j in range(ag.size)}
if fp & grass:
hits += 1
return hits
civic = author_errand_runner(seed=7, persona="civic")
outlaw = author_errand_runner(seed=7, persona="warm_outlaw")
assert grass_cells_walked(civic) == 0 # civic keeps off the lawn
assert grass_cells_walked(outlaw) > 0 # warm_outlaw cuts across the grass