"""Pydantic request/response models for the discovery API.""" from datetime import datetime from typing import Optional from pydantic import BaseModel class StartResponse(BaseModel): conversation_id: str class RespondRequest(BaseModel): prompt_alive: str = "" prompt_friction: str = "" prompt_pull: str = "" prompt_recognition: str = "" prompt_future: str = "" prompt_goals_short: str = "" prompt_goals_long: str = "" class RespondResponse(BaseModel): conversation_id: str status: str class Confidence(BaseModel): triad: Optional[str] = None type: Optional[str] = None variant: Optional[str] = None ikigai: Optional[str] = None class ProfileResponse(BaseModel): id: str user_id: str conversation_id: str generated_at: datetime triad: Optional[str] = None probable_type: Optional[int] = None wing: Optional[int] = None instinctual_variant: Optional[str] = None instinctual_stack: Optional[str] = None love_summary: Optional[str] = None strength_summary: Optional[str] = None mission_summary: Optional[str] = None vocation_summary: Optional[str] = None overlap_narrative: Optional[str] = None short_term_goals: Optional[str] = None long_term_goals: Optional[str] = None confidence: Optional[Confidence] = None locked: bool = False extraction_notes: Optional[str] = None class ProfileUpdate(BaseModel): """Partial edit of a profile's prose. Only fields explicitly provided are updated (see exclude_unset in the router). The AI's structural inference (triad/type/wing/variant) and confidence are not editable here.""" love_summary: Optional[str] = None strength_summary: Optional[str] = None mission_summary: Optional[str] = None vocation_summary: Optional[str] = None overlap_narrative: Optional[str] = None short_term_goals: Optional[str] = None long_term_goals: Optional[str] = None class ConfirmResponse(BaseModel): status: str # -- Phase 2: AI coach reflection loop --------------------------------------- class ReflectRequest(BaseModel): # Empty/omitted starts the loop (the coach's opening reflection). message: str = "" class ReflectionMessageOut(BaseModel): role: str # "coach" or "person" content: str sequence: int created_at: datetime class ReflectTurnResponse(BaseModel): """One coach turn, plus the (possibly revised) profile.""" message: ReflectionMessageOut profile: ProfileResponse revised: bool = False revision_note: Optional[str] = None class ReflectionThreadResponse(BaseModel): messages: list[ReflectionMessageOut] profile: ProfileResponse class ProfileRevisionOut(BaseModel): id: str source: str # "extraction" | "reflection" | "manual_edit" fields: dict note: Optional[str] = None created_at: datetime class ConversationResponse(BaseModel): id: str user_id: str started_at: datetime completed_at: Optional[datetime] = None prompt_alive: Optional[str] = None prompt_friction: Optional[str] = None prompt_pull: Optional[str] = None prompt_recognition: Optional[str] = None prompt_future: Optional[str] = None prompt_goals_short: Optional[str] = None prompt_goals_long: Optional[str] = None model_config = {"from_attributes": True}