Spaces:
Running
Running
Case Zero - initial public release (fully local: Qwen2.5-1.5B via llama.cpp + Supertonic, custom pixel-noir SPA via gradio.Server)
414dc55 | """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 | |