Qiddiya-Smart-Guide / src /models.py
munals's picture
Upload 33 files
214f910 verified
from __future__ import annotations
from typing import Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
class UserRequest(BaseModel):
"""Validated user request for planning a Qiddiya visit."""
visit_date: str = Field(
...,
description="Date of visit in YYYY-MM-DD format.",
examples=["2026-02-15"],
)
start_time: str = Field(
...,
description="Start time in HH:MM 24h format.",
examples=["10:00"],
)
end_time: str = Field(
...,
description="End time in HH:MM 24h format.",
examples=["20:00"],
)
must_do_attractions: List[str] = Field(
default_factory=list,
description="List of must-do attraction names.",
)
intensity_preference: int = Field(
3,
ge=1,
le=5,
description="1=relaxed, 5=extreme thrills.",
)
walking_tolerance: int = Field(
3,
ge=1,
le=5,
description="1=min walking, 5=okay with longer walks.",
)
@field_validator("visit_date")
@classmethod
def validate_date(cls, v: str) -> str:
parts = v.split("-")
if len(parts) != 3:
raise ValueError("visit_date must be in YYYY-MM-DD format")
year, month, day = parts
if len(year) != 4 or len(month) != 2 or len(day) != 2:
raise ValueError("visit_date must be in YYYY-MM-DD format")
return v
@field_validator("start_time", "end_time")
@classmethod
def validate_time(cls, v: str) -> str:
parts = v.split(":")
if len(parts) != 2:
raise ValueError("time must be in HH:MM format")
hour, minute = parts
if not hour.isdigit() or not minute.isdigit():
raise ValueError("time must be numeric HH:MM")
h = int(hour)
m = int(minute)
if not (0 <= h <= 23 and 0 <= m <= 59):
raise ValueError("time must be a valid 24h time")
return v
class Attraction(BaseModel):
id: str
name: str
node_id: str
thrill_level: int = Field(ge=1, le=5)
family_friendly: bool
average_duration_minutes: int = Field(ge=5, le=240)
base_popularity: int = Field(ge=1, le=10)
class ItineraryStop(BaseModel):
attraction_id: str
attraction_name: str
node_id: str
start_time: str
end_time: str
estimated_wait_minutes: int = Field(ge=0)
walking_distance_m: int = Field(ge=0)
is_suggested: bool = Field(default=False, description="True if added by system to boost enjoyment, not selected by user.")
class ItineraryPlan(BaseModel):
visit_date: str
total_wait_minutes: int
total_walking_m: int
coverage_score: float = Field(ge=0.0)
enjoyment_score: float = Field(ge=0.0)
stops: List[ItineraryStop]
logs: List[str] = Field(default_factory=list)
class EvaluationResult(BaseModel):
visit_date: str
request_summary: str
total_wait_minutes: int
total_walking_m: int
estimated_fatigue_score: float = Field(ge=0.0)
must_do_coverage_ratio: float = Field(ge=0.0, le=1.0)
constraint_violations: List[str] = Field(default_factory=list)
notes: Optional[str] = None
class SystemResponse(BaseModel):
"""Debug view of the multi-agent run (logs, reflection, critique)."""
logs: List[str] = Field(default_factory=list)
reflection_round: int = 0
critique: str = ""
wait_time_forecast: Optional[Dict[str, int]] = None
class PlanResponse(BaseModel):
"""Plan plus system debug info for the UI."""
plan: ItineraryPlan
system: SystemResponse