Spaces:
Running
Running
File size: 2,204 Bytes
414dc55 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | """Compact memory for a suspect prompt.
Two cheap, bounded sources keep a small model consistent without a vector store:
a structured ledger (what has been established) and a rolling transcript buffer.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from ..constants import ROLLING_BUFFER_TURNS
from ..schemas.case import CaseFile
from ..schemas.suspect import Suspect
if TYPE_CHECKING: # avoid a runtime cycle: engine imports the suspects layer
from ..engine.game_state import SuspectState
def _fact_lookup(case: CaseFile) -> dict[str, str]:
return {f.fact_id: f.statement for f in case.facts}
def ledger_text(case: CaseFile, suspect: Suspect, state: SuspectState) -> str:
facts = _fact_lookup(case)
lines: list[str] = []
if state.revealed_fact_ids:
admitted = "; ".join(sorted(facts.get(fid, fid) for fid in state.revealed_fact_ids))
lines.append(f"You have already admitted: {admitted}")
if state.evidence_shown:
shown = "; ".join(
sorted(_safe_clue_name(case, cid) for cid in state.evidence_shown)
)
lines.append(f"The detective has already shown you: {shown}")
if state.broken_lie_ids:
topics = "; ".join(
sorted(
lie.topic
for lie in suspect.anchored_lies
if lie.lie_id in state.broken_lie_ids
)
)
lines.append(f"You have already been caught lying about: {topics}. Do not repeat that lie.")
if state.stress >= 0.7:
lines.append("You are visibly rattled and close to breaking.")
elif state.stress >= 0.4:
lines.append("You are tense and guarded.")
return "\n".join(lines) if lines else "Nothing has been established yet in this interrogation."
def buffer_text(state: SuspectState, n: int = ROLLING_BUFFER_TURNS) -> str:
recent = state.transcript[-n:]
if not recent:
return "(no questions asked yet)"
return "\n".join(f'Detective: "{e.question}"\nYou: "{e.answer}"' for e in recent)
def _safe_clue_name(case: CaseFile, clue_id: str) -> str:
try:
return case.clue(clue_id).name
except KeyError:
return clue_id
|