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 | """Session - the stateful controller the UI drives. | |
| Holds the case, the backend, the cached suspect briefs, and the latest immutable | |
| GameState snapshot. Each action advances the snapshot; the snapshots themselves are | |
| never mutated. | |
| """ | |
| from __future__ import annotations | |
| from collections.abc import Iterator | |
| from ..llm.backend import LLMBackend | |
| from ..projections.player_view import PlayerCaseView, build_player_view | |
| from ..projections.suspect_brief import SuspectBrief, build_suspect_brief | |
| from ..schemas.accusation import Accusation, Verdict | |
| from ..schemas.case import CaseFile | |
| from ..schemas.clue import Clue | |
| from .accusation import finalize_accusation | |
| from .evidence_board import available_evidence, search_location | |
| from .game_state import GameState, new_game_state | |
| from .interrogation_loop import InterrogationEvent, interrogate | |
| class Session: | |
| def __init__(self, case: CaseFile, backend: LLMBackend) -> None: | |
| self.case = case | |
| self.backend = backend | |
| self._briefs: dict[str, SuspectBrief] = { | |
| s.sus_id: build_suspect_brief(case, s) for s in case.suspects | |
| } | |
| self.player_view: PlayerCaseView = build_player_view(case) | |
| self.state: GameState = new_game_state( | |
| case.case_id, tuple(s.sus_id for s in case.suspects) | |
| ) | |
| def brief(self, sus_id: str) -> SuspectBrief: | |
| return self._briefs[sus_id] | |
| def interrogate( | |
| self, | |
| sus_id: str, | |
| question: str, | |
| presented_clue_id: str | None = None, | |
| seed: int | None = None, | |
| ) -> Iterator[InterrogationEvent]: | |
| for event in interrogate( | |
| self.backend, self.case, self._briefs[sus_id], self.state, sus_id, question, | |
| presented_clue_id, seed, | |
| ): | |
| if event.final is not None: | |
| self.state = event.final.state | |
| yield event | |
| def search(self, loc_id: str) -> tuple[Clue, ...]: | |
| self.state, found = search_location(self.state, self.case, loc_id) | |
| return found | |
| def add_note(self, text: str) -> None: | |
| from .state_update import add_player_note | |
| self.state = add_player_note(self.state, text) | |
| def evidence(self) -> tuple[Clue, ...]: | |
| return available_evidence(self.state, self.case) | |
| def accuse(self, accusation: Accusation) -> Verdict: | |
| self.state, verdict = finalize_accusation(self.state, self.case, accusation) | |
| return verdict | |