""" Story Writer Models Pydantic models for story generation API requests and responses. """ from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any, Union class StoryGenerationRequest(BaseModel): """Request model for story generation.""" persona: str = Field(..., description="The persona statement for the author") story_setting: str = Field(..., description="The setting of the story") character_input: str = Field(..., description="The characters in the story") plot_elements: str = Field(..., description="The plot elements of the story") writing_style: str = Field(..., description="The writing style (e.g., Formal, Casual, Poetic, Humorous)") story_tone: str = Field(..., description="The tone of the story (e.g., Dark, Uplifting, Suspenseful, Whimsical)") narrative_pov: str = Field(..., description="The narrative point of view (e.g., First Person, Third Person Limited, Third Person Omniscient)") audience_age_group: str = Field(..., description="The target audience age group (e.g., Children, Young Adults, Adults)") content_rating: str = Field(..., description="The content rating (e.g., G, PG, PG-13, R)") ending_preference: str = Field(..., description="The preferred ending (e.g., Happy, Tragic, Cliffhanger, Twist)") story_length: str = Field(default="Medium", description="Target story length (Short: >1000 words, Medium: >5000 words, Long: >10000 words)") enable_explainer: bool = Field(default=True, description="Enable explainer features") enable_illustration: bool = Field(default=True, description="Enable illustration features") enable_video_narration: bool = Field(default=True, description="Enable story video and narration features") # Image generation settings image_provider: Optional[str] = Field(default=None, description="Image generation provider (gemini, huggingface, stability)") image_width: int = Field(default=1024, description="Image width in pixels") image_height: int = Field(default=1024, description="Image height in pixels") image_model: Optional[str] = Field(default=None, description="Image generation model") # Video generation settings video_fps: int = Field(default=24, description="Frames per second for video") video_transition_duration: float = Field(default=0.5, description="Duration of transitions between scenes in seconds") # Audio generation settings audio_provider: Optional[str] = Field(default="gtts", description="TTS provider (gtts, pyttsx3)") audio_lang: str = Field(default="en", description="Language code for TTS") audio_slow: bool = Field(default=False, description="Whether to speak slowly (gTTS only)") audio_rate: int = Field(default=150, description="Speech rate (pyttsx3 only)") class StorySetupGenerationRequest(BaseModel): """Request model for AI story setup generation.""" story_idea: str = Field(..., description="Basic story idea or information from the user") class StorySetupOption(BaseModel): """A single story setup option.""" persona: str = Field(..., description="The persona statement for the author") story_setting: str = Field(..., description="The setting of the story") character_input: str = Field(..., description="The characters in the story") plot_elements: str = Field(..., description="The plot elements of the story") writing_style: str = Field(..., description="The writing style") story_tone: str = Field(..., description="The tone of the story") narrative_pov: str = Field(..., description="The narrative point of view") audience_age_group: str = Field(..., description="The target audience age group") content_rating: str = Field(..., description="The content rating") ending_preference: str = Field(..., description="The preferred ending") story_length: str = Field(default="Medium", description="Target story length (Short: >1000 words, Medium: >5000 words, Long: >10000 words)") premise: str = Field(..., description="The story premise (1-2 sentences)") reasoning: str = Field(..., description="Brief reasoning for this setup option") # Image generation settings image_provider: Optional[str] = Field(default=None, description="Image generation provider (gemini, huggingface, stability)") image_width: int = Field(default=1024, description="Image width in pixels") image_height: int = Field(default=1024, description="Image height in pixels") image_model: Optional[str] = Field(default=None, description="Image generation model") # Video generation settings video_fps: int = Field(default=24, description="Frames per second for video") video_transition_duration: float = Field(default=0.5, description="Duration of transitions between scenes in seconds") # Audio generation settings audio_provider: Optional[str] = Field(default="gtts", description="TTS provider (gtts, pyttsx3)") audio_lang: str = Field(default="en", description="Language code for TTS") audio_slow: bool = Field(default=False, description="Whether to speak slowly (gTTS only)") audio_rate: int = Field(default=150, description="Speech rate (pyttsx3 only)") class StorySetupGenerationResponse(BaseModel): """Response model for story setup generation.""" options: List[StorySetupOption] = Field(..., description="Three story setup options") success: bool = Field(default=True, description="Whether the generation was successful") class StoryScene(BaseModel): """Model for a story scene.""" scene_number: int = Field(..., description="Scene number") title: str = Field(..., description="Scene title") description: str = Field(..., description="Scene description") image_prompt: str = Field(..., description="Image prompt for scene visualization") audio_narration: str = Field(..., description="Audio narration text for the scene") character_descriptions: List[str] = Field(default_factory=list, description="Character descriptions in the scene") key_events: List[str] = Field(default_factory=list, description="Key events in the scene") class StoryStartRequest(StoryGenerationRequest): """Request model for story start generation.""" premise: str = Field(..., description="The story premise") outline: Union[str, List[StoryScene], List[Dict[str, Any]]] = Field(..., description="The story outline (text or structured scenes)") class StoryPremiseResponse(BaseModel): """Response model for premise generation.""" premise: str = Field(..., description="Generated story premise") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class StoryOutlineResponse(BaseModel): """Response model for outline generation.""" outline: Union[str, List[StoryScene]] = Field(..., description="Generated story outline (text or structured scenes)") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") is_structured: bool = Field(default=False, description="Whether the outline is structured (scenes) or plain text") class StoryContentResponse(BaseModel): """Response model for story content generation.""" story: str = Field(..., description="Generated story content") premise: Optional[str] = Field(None, description="Story premise") outline: Optional[str] = Field(None, description="Story outline") is_complete: bool = Field(default=False, description="Whether the story is complete") iterations: int = Field(default=0, description="Number of continuation iterations") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class StoryFullGenerationResponse(BaseModel): """Response model for full story generation.""" premise: str = Field(..., description="Generated story premise") outline: str = Field(..., description="Generated story outline") story: str = Field(..., description="Generated complete story") is_complete: bool = Field(default=False, description="Whether the story is complete") iterations: int = Field(default=0, description="Number of continuation iterations") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class StoryContinueRequest(BaseModel): """Request model for continuing story generation.""" premise: str = Field(..., description="The story premise") outline: Union[str, List[StoryScene], List[Dict[str, Any]]] = Field(..., description="The story outline (text or structured scenes)") story_text: str = Field(..., description="Current story text to continue from") persona: str = Field(..., description="The persona statement for the author") story_setting: str = Field(..., description="The setting of the story") character_input: str = Field(..., description="The characters in the story") plot_elements: str = Field(..., description="The plot elements of the story") writing_style: str = Field(..., description="The writing style") story_tone: str = Field(..., description="The tone of the story") narrative_pov: str = Field(..., description="The narrative point of view") audience_age_group: str = Field(..., description="The target audience age group") content_rating: str = Field(..., description="The content rating") ending_preference: str = Field(..., description="The preferred ending") story_length: str = Field(default="Medium", description="Target story length (Short: >1000 words, Medium: >5000 words, Long: >10000 words)") class StoryContinueResponse(BaseModel): """Response model for story continuation.""" continuation: str = Field(..., description="Generated story continuation") is_complete: bool = Field(default=False, description="Whether the story is complete (contains IAMDONE)") success: bool = Field(default=True, description="Whether the generation was successful") class TaskStatus(BaseModel): """Task status model.""" task_id: str = Field(..., description="Task ID") status: str = Field(..., description="Task status (pending, processing, completed, failed)") progress: Optional[float] = Field(None, description="Progress percentage (0-100)") message: Optional[str] = Field(None, description="Progress message") result: Optional[Dict[str, Any]] = Field(None, description="Task result when completed") error: Optional[str] = Field(None, description="Error message if failed") created_at: Optional[str] = Field(None, description="Task creation timestamp") updated_at: Optional[str] = Field(None, description="Task last update timestamp") class StoryImageGenerationRequest(BaseModel): """Request model for image generation.""" scenes: List[StoryScene] = Field(..., description="List of scenes to generate images for") provider: Optional[str] = Field(None, description="Image generation provider (gemini, huggingface, stability)") width: Optional[int] = Field(default=1024, description="Image width") height: Optional[int] = Field(default=1024, description="Image height") model: Optional[str] = Field(None, description="Image generation model") class StoryImageResult(BaseModel): """Model for a generated image result.""" scene_number: int = Field(..., description="Scene number") scene_title: str = Field(..., description="Scene title") image_filename: str = Field(..., description="Image filename") image_url: str = Field(..., description="Image URL") width: int = Field(..., description="Image width") height: int = Field(..., description="Image height") provider: str = Field(..., description="Image generation provider") model: Optional[str] = Field(None, description="Image generation model") seed: Optional[int] = Field(None, description="Image generation seed") error: Optional[str] = Field(None, description="Error message if generation failed") class StoryImageGenerationResponse(BaseModel): """Response model for image generation.""" images: List[StoryImageResult] = Field(..., description="List of generated images") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class RegenerateImageRequest(BaseModel): """Request model for regenerating a single scene image with a direct prompt.""" scene_number: int = Field(..., description="Scene number to regenerate image for") scene_title: str = Field(..., description="Scene title") prompt: str = Field(..., description="Direct prompt to use for image generation (no AI prompt generation)") provider: Optional[str] = Field(None, description="Image generation provider (gemini, huggingface, stability)") width: Optional[int] = Field(1024, description="Image width") height: Optional[int] = Field(1024, description="Image height") model: Optional[str] = Field(None, description="Model to use for image generation") class RegenerateImageResponse(BaseModel): """Response model for regenerated image.""" scene_number: int = Field(..., description="Scene number") scene_title: str = Field(..., description="Scene title") image_filename: str = Field(..., description="Generated image filename") image_url: str = Field(..., description="Image URL") width: int = Field(..., description="Image width") height: int = Field(..., description="Image height") provider: str = Field(..., description="Provider used") model: Optional[str] = Field(None, description="Model used") seed: Optional[int] = Field(None, description="Seed used") success: bool = Field(default=True, description="Whether the generation was successful") error: Optional[str] = Field(None, description="Error message if generation failed") class StoryAudioGenerationRequest(BaseModel): """Request model for audio generation.""" scenes: List[StoryScene] = Field(..., description="List of scenes to generate audio for") provider: Optional[str] = Field(default="gtts", description="TTS provider (gtts, pyttsx3)") lang: Optional[str] = Field(default="en", description="Language code for TTS") slow: Optional[bool] = Field(default=False, description="Whether to speak slowly (gTTS only)") rate: Optional[int] = Field(default=150, description="Speech rate (pyttsx3 only)") class StoryAudioResult(BaseModel): """Model for a generated audio result.""" scene_number: int = Field(..., description="Scene number") scene_title: str = Field(..., description="Scene title") audio_filename: str = Field(..., description="Audio filename") audio_url: str = Field(..., description="Audio URL") provider: str = Field(..., description="TTS provider") file_size: int = Field(..., description="Audio file size in bytes") error: Optional[str] = Field(None, description="Error message if generation failed") class StoryAudioGenerationResponse(BaseModel): """Response model for audio generation.""" audio_files: List[StoryAudioResult] = Field(..., description="List of generated audio files") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class GenerateAIAudioRequest(BaseModel): """Request model for generating AI audio for a single scene.""" scene_number: int = Field(..., description="Scene number to generate audio for") scene_title: str = Field(..., description="Scene title") text: str = Field(..., description="Text to convert to speech") voice_id: Optional[str] = Field("Wise_Woman", description="Voice ID for AI audio generation") speed: Optional[float] = Field(1.0, description="Speech speed (0.5-2.0)") volume: Optional[float] = Field(1.0, description="Speech volume (0.1-10.0)") pitch: Optional[float] = Field(0.0, description="Speech pitch (-12 to 12)") emotion: Optional[str] = Field("happy", description="Emotion for speech") class GenerateAIAudioResponse(BaseModel): """Response model for AI audio generation.""" scene_number: int = Field(..., description="Scene number") scene_title: str = Field(..., description="Scene title") audio_filename: str = Field(..., description="Generated audio filename") audio_url: str = Field(..., description="Audio URL") provider: str = Field(..., description="Provider used (wavespeed)") model: str = Field(..., description="Model used (minimax/speech-02-hd)") voice_id: str = Field(..., description="Voice ID used") text_length: int = Field(..., description="Number of characters in text") file_size: int = Field(..., description="Audio file size in bytes") cost: float = Field(..., description="Cost of generation") success: bool = Field(default=True, description="Whether the generation was successful") error: Optional[str] = Field(None, description="Error message if generation failed") class StoryVideoGenerationRequest(BaseModel): """Request model for video generation.""" scenes: List[StoryScene] = Field(..., description="List of scenes to generate video for") image_urls: List[str] = Field(..., description="List of image URLs for each scene") audio_urls: List[str] = Field(..., description="List of audio URLs for each scene") video_urls: Optional[List[Optional[str]]] = Field(None, description="Optional list of animated video URLs (preferred over images)") ai_audio_urls: Optional[List[Optional[str]]] = Field(None, description="Optional list of AI audio URLs (preferred over free audio)") story_title: Optional[str] = Field(default="Story", description="Title of the story") fps: Optional[int] = Field(default=24, description="Frames per second for video") transition_duration: Optional[float] = Field(default=0.5, description="Duration of transitions between scenes") class StoryVideoResult(BaseModel): """Model for a generated video result.""" video_filename: str = Field(..., description="Video filename") video_url: str = Field(..., description="Video URL") duration: float = Field(..., description="Video duration in seconds") fps: int = Field(..., description="Frames per second") file_size: int = Field(..., description="Video file size in bytes") num_scenes: int = Field(..., description="Number of scenes in the video") error: Optional[str] = Field(None, description="Error message if generation failed") class StoryVideoGenerationResponse(BaseModel): """Response model for video generation.""" video: StoryVideoResult = Field(..., description="Generated video") success: bool = Field(default=True, description="Whether the generation was successful") task_id: Optional[str] = Field(None, description="Task ID for async operations") class AnimateSceneRequest(BaseModel): """Request model for per-scene animation preview.""" scene_number: int = Field(..., description="Scene number to animate") scene_data: Dict[str, Any] = Field(..., description="Scene data payload") story_context: Dict[str, Any] = Field(..., description="Story-wide context used for prompts") image_url: str = Field(..., description="Relative URL to the generated scene image") duration: int = Field(default=5, description="Animation duration (5 or 10 seconds)") class AnimateSceneVoiceoverRequest(AnimateSceneRequest): """Request model for WaveSpeed InfiniteTalk animation.""" audio_url: str = Field(..., description="Relative URL to the generated scene audio") resolution: Optional[str] = Field("720p", description="Output resolution ('480p' or '720p')") prompt: Optional[str] = Field(None, description="Optional positive prompt override") class AnimateSceneResponse(BaseModel): """Response model for scene animation preview.""" success: bool = Field(default=True, description="Whether the animation succeeded") scene_number: int = Field(..., description="Scene number animated") video_filename: str = Field(..., description="Stored video filename") video_url: str = Field(..., description="API URL to access the animated video") duration: int = Field(..., description="Duration of the animation") cost: float = Field(..., description="Cost billed for the animation") prompt_used: str = Field(..., description="Animation prompt passed to the model") provider: str = Field(default="wavespeed", description="Underlying provider used") prediction_id: Optional[str] = Field(None, description="WaveSpeed prediction ID for resume operations") class ResumeSceneAnimationRequest(BaseModel): """Request model to resume scene animation download.""" prediction_id: str = Field(..., description="WaveSpeed prediction ID to resume from") scene_number: int = Field(..., description="Scene number being resumed") duration: int = Field(default=5, description="Animation duration (5 or 10 seconds)")