"""Story-related Pydantic schemas.""" from datetime import datetime from typing import Literal from pydantic import BaseModel, Field MAX_DATA_LENGTH = 2000 MAX_EDU_THEME_LENGTH = 200 MAX_TTS_LENGTH = 4000 class StoryStatusMixin(BaseModel): """Shared generation status fields returned by story APIs.""" generation_status: str image_status: str audio_status: str last_error: str | None = None class GenerateRequest(BaseModel): """Story generation request.""" type: Literal["keywords", "full_story"] data: str = Field(..., min_length=1, max_length=MAX_DATA_LENGTH) education_theme: str | None = Field(default=None, max_length=MAX_EDU_THEME_LENGTH) child_profile_id: str | None = None universe_id: str | None = None class StoryResponse(StoryStatusMixin): """Story generation response.""" id: int title: str story_text: str cover_prompt: str | None image_url: str | None mode: str child_profile_id: str | None = None universe_id: str | None = None class StoryListItem(StoryStatusMixin): """Story list item.""" id: int title: str image_url: str | None created_at: datetime mode: str class FullStoryResponse(StoryStatusMixin): """Full story response with asset status.""" id: int title: str story_text: str cover_prompt: str | None image_url: str | None audio_ready: bool mode: str errors: dict[str, str | None] = Field(default_factory=dict) child_profile_id: str | None = None universe_id: str | None = None class StorybookRequest(BaseModel): """Storybook generation request.""" keywords: str = Field(..., min_length=1, max_length=200) page_count: int = Field(default=6, ge=4, le=12) education_theme: str | None = Field(default=None, max_length=MAX_EDU_THEME_LENGTH) generate_images: bool = Field(default=False, description="Whether to generate images too.") child_profile_id: str | None = None universe_id: str | None = None class StorybookPageResponse(BaseModel): """One storybook page.""" page_number: int text: str image_prompt: str image_url: str | None = None class StorybookResponse(StoryStatusMixin): """Storybook generation response.""" id: int | None = None title: str main_character: str art_style: str pages: list[StorybookPageResponse] cover_prompt: str cover_url: str | None = None class StoryDetailResponse(StoryStatusMixin): """Story detail response for both stories and storybooks.""" id: int title: str story_text: str | None = None pages: list[StorybookPageResponse] | None = None cover_prompt: str | None image_url: str | None mode: str child_profile_id: str | None = None universe_id: str | None = None class StoryImageResponse(StoryStatusMixin): """Cover image generation response.""" image_url: str | None class AchievementItem(BaseModel): """Achievement item returned for a story.""" type: str description: str obtained_at: str | None = None