from __future__ import annotations from typing import Optional from pydantic import BaseModel, Field, model_validator class PlannerConfigOverride(BaseModel): recency_weeks: Optional[int] = Field(default=None, ge=0) calorie_tolerance_pct: Optional[int] = Field(default=None, ge=0, le=100) max_total_minutes: Optional[int] = Field(default=None, ge=0) max_meal_cost: Optional[float] = Field(default=None, ge=0) w_savings: Optional[float] = Field(default=None, ge=0, le=1) w_coverage: Optional[float] = Field(default=None, ge=0, le=1) w_pantry: Optional[float] = Field(default=None, ge=0, le=1) w_time: Optional[float] = Field(default=None, ge=0, le=1) w_recency: Optional[float] = Field(default=None, ge=0, le=1) time_ideal_minutes: Optional[int] = Field(default=None, ge=0) time_full_minutes: Optional[int] = Field(default=None, ge=0) recency_full_weeks: Optional[int] = Field(default=None, ge=0) top_k: Optional[int] = Field(default=None, ge=1) set_size: Optional[int] = Field(default=None, ge=1) p_protein: Optional[float] = Field(default=None, ge=0) p_cuisine: Optional[float] = Field(default=None, ge=0) @model_validator(mode="after") def _check_weights_sum(self): weights = [self.w_savings, self.w_coverage, self.w_pantry, self.w_time, self.w_recency] if all(w is not None for w in weights): total = sum(weights) if not 0.999 <= total <= 1.001: raise ValueError(f"weights must sum to 1.0, got {total:.4f}") return self class PlannerConfigResponse(BaseModel): recency_weeks: int calorie_tolerance_pct: int max_total_minutes: int max_meal_cost: float w_savings: float w_coverage: float w_pantry: float w_time: float w_recency: float time_ideal_minutes: int time_full_minutes: int recency_full_weeks: int top_k: int set_size: int p_protein: float p_cuisine: float source: str = "default" class PlannerConfigUpdateRequest(BaseModel): planner_config: PlannerConfigOverride