""" Detection Pipeline Schema Definitions Source of truth for all detection SSE events and wire-format models. Generates: Pydantic (detect/sse_contract.py), TypeScript (ui/detection-app/src/types/sse-contract.ts) Pipeline-internal models that never cross the wire (e.g. Frame with np.ndarray) live in detect/models.py and are NOT generated. """ from dataclasses import dataclass, field from typing import List, Literal, Optional # --- Enums as Literal unions (wire format, not Python Enum) --- NodeStatus = Literal["idle", "processing", "completed", "error"] DetectionSource = Literal["ocr", "local_vlm", "cloud_llm", "logo_match", "auxiliary"] LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR"] # --- Nested components --- @dataclass class GraphNode: """A pipeline stage node.""" id: str status: str = "idle" # NodeStatus items_in: int = 0 items_out: int = 0 @dataclass class GraphEdge: """An edge between pipeline stages.""" source: str target: str throughput: int = 0 @dataclass class BoundingBoxEvent: """Bounding box in SSE event payloads.""" x: int y: int w: int h: int confidence: float label: str resolved_brand: Optional[str] = None source: Optional[str] = None stage: Optional[str] = None @dataclass class BrandSummary: """Per-brand stats in the final report.""" brand: str total_appearances: int = 0 total_screen_time: float = 0.0 avg_confidence: float = 0.0 first_seen: float = 0.0 last_seen: float = 0.0 # --- SSE event payloads --- @dataclass class GraphUpdate: """Pipeline node state transition. SSE event: graph_update""" nodes: List[GraphNode] = field(default_factory=list) edges: List[GraphEdge] = field(default_factory=list) active_path: List[str] = field(default_factory=list) @dataclass class StatsUpdate: """Funnel statistics snapshot. SSE event: stats_update""" frames_extracted: int = 0 frames_after_scene_filter: int = 0 cv_regions_detected: int = 0 regions_detected: int = 0 regions_resolved_by_ocr: int = 0 regions_escalated_to_local_vlm: int = 0 regions_escalated_to_cloud_llm: int = 0 cloud_llm_calls: int = 0 processing_time_seconds: float = 0.0 estimated_cloud_cost_usd: float = 0.0 @dataclass class FrameUpdate: """Current frame being processed. SSE event: frame_update""" frame_ref: int timestamp: float jpeg_b64: str boxes: List[BoundingBoxEvent] = field(default_factory=list) @dataclass class Detection: """A confirmed brand detection. SSE event: detection""" brand: str timestamp: float duration: float confidence: float source: str # DetectionSource content_type: str bbox: Optional[BoundingBoxEvent] = None frame_ref: Optional[int] = None @dataclass class LogEvent: """Pipeline log line. SSE event: log""" level: str # LogLevel stage: str msg: str ts: str trace_id: Optional[str] = None @dataclass class DetectionReportSummary: """Final detection report summary.""" video_source: str content_type: str duration_seconds: float total_detections: int = 0 brands: List[BrandSummary] = field(default_factory=list) stats: Optional[StatsUpdate] = None @dataclass class JobComplete: """Final report when pipeline finishes. SSE event: job_complete""" job_id: str report: Optional[DetectionReportSummary] = None @dataclass class RunContext: """Run context injected into all SSE events for grouping.""" run_id: str parent_job_id: str run_type: str = "initial" # initial | replay | retry # --- Checkpoint API types --- @dataclass class CheckpointInfo: """Available checkpoint for a stage.""" stage: str is_scenario: bool = False scenario_label: str = "" @dataclass class ReplayRequest: """Request to replay pipeline from a specific stage.""" job_id: str start_stage: str config_overrides: Optional[dict] = None @dataclass class ReplayResponse: """Result of a replay invocation.""" status: str job_id: str start_stage: str detections: int = 0 brands_found: int = 0 @dataclass class RetryRequest: """Request to queue async retry with different config.""" job_id: str config_overrides: Optional[dict] = None start_stage: str = "escalate_vlm" schedule_seconds: Optional[float] = None @dataclass class RetryResponse: """Result of queueing a retry task.""" status: str task_id: str job_id: str # --- Export lists for modelgen --- DETECT_VIEWS = [ GraphNode, GraphEdge, BoundingBoxEvent, BrandSummary, GraphUpdate, StatsUpdate, FrameUpdate, Detection, LogEvent, DetectionReportSummary, JobComplete, RunContext, CheckpointInfo, ReplayRequest, ReplayResponse, RetryRequest, RetryResponse, ]