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