models.py

"""
Pydantic models for API request/response schemas
"""

from datetime import date, datetime
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


class SystemSummary(BaseModel):
    """Top bar metrics"""

    snapshot_date: date
    net_delta_roi: float = Field(description="Weighted average ΔROI across all paths")
    mean_velocity_ms: float = Field(
        description="Average decision velocity in milliseconds"
    )
    system_pcr: float = Field(description="System-wide path confidence ratio")
    delta_roi_change: Optional[float] = Field(
        default=None, description="Change from yesterday"
    )
    velocity_change: Optional[float] = Field(
        default=None, description="Change from yesterday"
    )
    pcr_change: Optional[float] = Field(
        default=None, description="Change from yesterday"
    )


class PathLeaderboard(BaseModel):
    """Single row in path leaderboard"""

    path_id: str
    delta_roi_7d: float = Field(description="7-day average ΔROI")
    velocity_ms: float = Field(description="Decision velocity")
    pcr: float = Field(description="Path confidence ratio")
    total_actions: int
    total_outcomes: int
    stability_score: float = Field(description="1 / (1 + stddev)")
    rank: int


class EdgeMetric(BaseModel):
    """Edge details for graph visualization"""

    edge_id: str
    src_node_id: str
    dst_node_id: str
    src_label: str
    dst_label: str
    edge_type: str
    latest_delta_roi: float
    latest_latency_ms: int
    decay_weight: float
    avg_delta_roi_7d: Optional[float] = None
    total_spend_7d: Optional[float] = None


class TimeseriesPoint(BaseModel):
    """Single point in path timeseries"""

    date: date
    weighted_roi: float
    daily_spend: float
    avg_latency: float
    roi_7d_ma: float = Field(description="7-day moving average")


class PathDetail(BaseModel):
    """Complete path details with graph and timeseries"""

    path_id: str
    metrics: Dict[str, Any]
    edges: List[EdgeMetric]
    timeseries: List[TimeseriesPoint]


class EdgeHistoryPoint(BaseModel):
    """Single point in edge sparkline"""

    date: date
    delta_roi: float
    n_obs: int
    spend: float
    roi_3d_ma: float
    roi_7d_stddev: Optional[float] = None


class EdgeHistory(BaseModel):
    """Edge sparkline data"""

    edge_id: str
    history: List[EdgeHistoryPoint]


class DiagnosticDay(BaseModel):
    """Daily diagnostic stats"""

    date: date
    forecasts_logged: int
    actions_logged: int
    outcomes_logged: int
    last_data_insert: datetime
    attributed_paths: Optional[int] = None
    avg_attribution_weight: Optional[float] = None
    n_controls: Optional[int] = None
    control_quality_score: Optional[float] = None


class DiagnosticsResponse(BaseModel):
    """System health diagnostics"""

    daily_stats: List[DiagnosticDay]
    data_age_hours: Optional[float] = Field(description="Hours since last data insert")
    status: str = Field(description="healthy | stale | error")


class JobResponse(BaseModel):
    """Job trigger response"""

    job_id: str
    status: str = Field(description="started | running | completed | failed")
    run_date: str
    message: Optional[str] = None
    error: Optional[str] = None
← All docsView source on GitHub →