Spaces:
Sleeping
Sleeping
| 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.", | |
| ) | |
| 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 | |
| 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 | |