"""Pydantic response/request models for the Prometheus API. Kept deliberately thin and explicit so the TypeScript types in ``dashboard/src/types`` can mirror them one-to-one. """ from __future__ import annotations from pydantic import BaseModel, Field class BBox(BaseModel): x1: float y1: float x2: float y2: float class DetectionItem(BaseModel): species: str class_id: int confidence: float bbox: BBox class DetectionResponse(BaseModel): """Result of an image detection request. ``annotated_image`` is a base64 data URL (PNG) so the frontend can render it without a second round-trip; ``heatmap_image`` is optional. """ model: str elapsed_ms: float image_width: int image_height: int total: int counts: dict[str, int] detections: list[DetectionItem] annotated_image: str heatmap_image: str | None = None class VideoJobCreated(BaseModel): job_id: str class VideoJobStatus(BaseModel): """Polled state of a video detection job. ``unique_counts`` are distinct BoT-SORT track IDs per species (estimated individuals); ``peak_counts`` are the most simultaneous detections in any one frame. ``video_url`` is set once status == "done". """ job_id: str status: str = Field(description="queued | processing | done | error") progress: float error: str | None = None filename: str | None = None model: str | None = None frames_total: int | None = None frames_processed: int | None = None elapsed_s: float | None = None unique_counts: dict[str, int] | None = None peak_counts: dict[str, int] | None = None total_unique: int | None = None video_url: str | None = None population: dict | None = None # distance-sampling estimate when survey params given class PopulationEstimate(BaseModel): """Distance-sampling density estimate + the data to draw the classic plot. Every field is a statistical ESTIMATE under standard assumptions — on a prototype with assumed camera parameters it is illustrative, not a census. """ n_detections: int model: str density: float density_ci: list[float] abundance: float | None = None abundance_ci: list[float] | None = None esw: float p_detect: float encounter_rate: float cv_density: float truncation_w: float transect_length: float area: float | None = None curve: list[dict] = Field(default_factory=list) # fitted detection function g(y) histogram: list[dict] = Field(default_factory=list) # observed distance histogram detections: list[dict] = Field(default_factory=list) # {lat, lon, species} for the map truth: dict | None = None # present in synthetic demo class ModelInfo(BaseModel): name: str path: str size_mb: float is_default: bool = False recommended_conf: float = 0.3 # suggested confidence for this checkpoint viewpoint: str = "general" # nadir | oblique | general note: str = "" # one-line guidance shown in the UI class SystemInfo(BaseModel): status: str = "ok" version: str device: str torch_cuda: bool models_available: int project: str class ClassInfo(BaseModel): id: int name: str hex: str sources: list[str] class Capability(BaseModel): id: str title: str summary: str status: str = Field(description="live | beta | planned") category: str icon: str