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 | """The PUBLIC wire contract: the exact case shape the Preact client receives. | |
| This mirrors the prototype's ``prototype/js/case.jsx`` (the data-model-by-example) in | |
| camelCase. It is the ONLY case representation that ever reaches the browser before the | |
| verdict. The sealed solution - killer, true motive, key-evidence chain - and every | |
| per-question/per-evidence suspicion *delta* and scripted *answer* are deliberately absent: | |
| those are produced live, server-side (the deltas alone would reveal the killer). | |
| """ | |
| from __future__ import annotations | |
| from pydantic import BaseModel, ConfigDict, Field | |
| from pydantic.alias_generators import to_camel | |
| class _Wire(BaseModel): | |
| """camelCase on the wire; snake_case in Python. Frozen - a view is never mutated.""" | |
| model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True, frozen=True) | |
| class PublicVictim(_Wire): | |
| name: str | |
| role: str | |
| age: int | |
| sprite: str | |
| bio: str | |
| class SuggestedQuestion(_Wire): | |
| id: str | |
| q: str | |
| class PublicSuspect(_Wire): | |
| id: str | |
| name: str | |
| role: str | |
| age: int | |
| sprite: str | |
| gender: str = "male" | |
| tag: str | |
| baseline_suspicion: int | |
| motive: str # apparent motive shown in the dossier - NOT proof | |
| alibi: str | |
| quote: str | |
| greet: str | |
| suggested_questions: tuple[SuggestedQuestion, ...] | |
| class ThreadMessage(_Wire): | |
| from_: str = Field(alias="from") | |
| who: str | |
| t: str | |
| m: str | |
| class PublicEvidence(_Wire): | |
| id: str | |
| name: str | |
| type: str # PHONE | PAPER | IMAGE | AUDIO | DATA | |
| icon: str | |
| time: str | |
| found: str | |
| summary: str | |
| # Exactly one of these display payloads is populated, per type. | |
| thread: tuple[ThreadMessage, ...] | None = None | |
| detail: str | None = None | |
| transcript: str | None = None | |
| dur: str | None = None | |
| rows: tuple[tuple[str, ...], ...] | None = None | |
| class TimelineBeat(_Wire): | |
| time: str | |
| label: str | |
| locked: bool = False | |
| ev: str | None = None | |
| conflict: bool = False | |
| class FlashbackAccount(_Wire): | |
| who: str | |
| scene: str | |
| lines: tuple[str, ...] | |
| flags: tuple[int, ...] = () | |
| class PublicFlashback(_Wire): | |
| title: str | |
| a: FlashbackAccount | |
| b: FlashbackAccount | |
| class PublicMotive(_Wire): | |
| id: str | |
| text: str | |
| class StoryBeat(_Wire): | |
| scene: str | |
| kicker: str | |
| title: str | |
| text: str | |
| class PublicCase(_Wire): | |
| id: str | |
| city: str | |
| district: str | |
| title: str | |
| tagline: str | |
| weather: str | |
| victim: PublicVictim | |
| scene: str | |
| tod: str | |
| found: str | |
| cause: str | |
| facts: tuple[tuple[str, str], ...] | |
| boot_lines: tuple[str, ...] | |
| story_beats: tuple[StoryBeat, ...] | |
| suspects: tuple[PublicSuspect, ...] | |
| evidence: tuple[PublicEvidence, ...] | |
| timeline: tuple[TimelineBeat, ...] | |
| flashback: PublicFlashback | |
| motives: tuple[PublicMotive, ...] | |
| def golden_to_public(data: dict) -> PublicCase: | |
| """Project a sealed golden/stored case dict into the PUBLIC view. | |
| Strips the ``sealed`` block and, per suspect, the scripted ``answer``/``delta`` and | |
| the ``present``/``default`` reply tables - leaving only question text the player sees. | |
| """ | |
| suspects = tuple( | |
| PublicSuspect( | |
| id=s["id"], | |
| name=s["name"], | |
| role=s["role"], | |
| age=s["age"], | |
| sprite=s["sprite"], | |
| gender=s.get("gender", "male"), | |
| tag=s["tag"], | |
| baseline_suspicion=s["suspicion"], | |
| motive=s["motive"], | |
| alibi=s["alibi"], | |
| quote=s["quote"], | |
| greet=s["greet"], | |
| suggested_questions=tuple( | |
| SuggestedQuestion(id=q["id"], q=q["q"]) for q in s["questions"] | |
| ), | |
| ) | |
| for s in data["suspects"] | |
| ) | |
| return PublicCase( | |
| id=data["id"], | |
| city=data["city"], | |
| district=data["district"], | |
| title=data["title"], | |
| tagline=data["tagline"], | |
| weather=data["weather"], | |
| victim=PublicVictim(**data["victim"]), | |
| scene=data["scene"], | |
| tod=data["tod"], | |
| found=data["found"], | |
| cause=data["cause"], | |
| facts=tuple(tuple(pair) for pair in data["facts"]), | |
| boot_lines=tuple(data["bootLines"]), | |
| story_beats=tuple(StoryBeat(**b) for b in data.get("storyBeats", [])), | |
| suspects=suspects, | |
| evidence=tuple(PublicEvidence(**e) for e in data["evidence"]), | |
| timeline=tuple(TimelineBeat(**b) for b in data["timeline"]), | |
| flashback=PublicFlashback( | |
| title=data["flashback"]["title"], | |
| a=FlashbackAccount(**data["flashback"]["a"]), | |
| b=FlashbackAccount(**data["flashback"]["b"]), | |
| ), | |
| motives=tuple(PublicMotive(**m) for m in data["motives"]), | |
| ) | |