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 | """Spatial and temporal primitives: locations, time windows, and whereabouts. | |
| Times are integer minutes since midnight to keep alibi reasoning exact and cheap. | |
| """ | |
| from __future__ import annotations | |
| from pydantic import BaseModel, ConfigDict, Field, model_validator | |
| from ..constants import DAY_MINUTES | |
| class Location(BaseModel): | |
| model_config = ConfigDict(frozen=True) | |
| loc_id: str | |
| name: str | |
| description: str = "" | |
| adjacent_to: tuple[str, ...] = () | |
| class TimeWindow(BaseModel): | |
| """A closed interval [start_min, end_min] within a single day.""" | |
| model_config = ConfigDict(frozen=True) | |
| start_min: int = Field(ge=0, le=DAY_MINUTES) | |
| end_min: int = Field(ge=0, le=DAY_MINUTES) | |
| def _ordered(self) -> TimeWindow: | |
| if self.end_min < self.start_min: | |
| raise ValueError(f"end_min {self.end_min} precedes start_min {self.start_min}") | |
| return self | |
| def contains(self, minute: int) -> bool: | |
| return self.start_min <= minute <= self.end_min | |
| def covers(self, other: TimeWindow) -> bool: | |
| return self.start_min <= other.start_min and other.end_min <= self.end_min | |
| def overlaps(self, other: TimeWindow) -> bool: | |
| return self.start_min <= other.end_min and other.start_min <= self.end_min | |
| class WhereaboutsSegment(BaseModel): | |
| """Where a person actually was during one slice of the murder window.""" | |
| model_config = ConfigDict(frozen=True) | |
| window: TimeWindow | |
| loc_id: str | |
| activity: str = "" | |
| co_present_sus_ids: tuple[str, ...] = () | |
| class AlibiSegment(BaseModel): | |
| """Where a suspect *claims* to have been, with any claimed witnesses.""" | |
| model_config = ConfigDict(frozen=True) | |
| window: TimeWindow | |
| loc_id: str | |
| witness_sus_ids: tuple[str, ...] = () | |
| class StatedAlibi(BaseModel): | |
| model_config = ConfigDict(frozen=True) | |
| claim_text: str | |
| claimed_segments: tuple[AlibiSegment, ...] = () | |