"""Story outline generation helpers.""" from __future__ import annotations import json from typing import Any, Dict from fastapi import HTTPException from loguru import logger from services.llm_providers.main_text_generation import llm_text_gen from .base import StoryServiceBase class StoryOutlineMixin(StoryServiceBase): """Provides outline generation behaviour.""" def _get_outline_schema(self) -> Dict[str, Any]: """Return JSON schema for structured story outlines.""" return { "type": "object", "properties": { "scenes": { "type": "array", "items": { "type": "object", "properties": { "scene_number": {"type": "integer"}, "title": {"type": "string"}, "description": {"type": "string"}, "image_prompt": {"type": "string"}, "audio_narration": {"type": "string"}, "character_descriptions": {"type": "array", "items": {"type": "string"}}, "key_events": {"type": "array", "items": {"type": "string"}}, }, "required": ["scene_number", "title", "description", "image_prompt", "audio_narration"], }, } }, "required": ["scenes"], } def generate_outline( self, *, premise: str, persona: str, story_setting: str, character_input: str, plot_elements: str, writing_style: str, story_tone: str, narrative_pov: str, audience_age_group: str, content_rating: str, ending_preference: str, user_id: str, use_structured_output: bool = True, include_anime_bible: bool = False, ) -> Any: """Generate a story outline with optional structured JSON output.""" persona_prompt = self.build_persona_prompt( persona, story_setting, character_input, plot_elements, writing_style, story_tone, narrative_pov, audience_age_group, content_rating, ending_preference, ) parameter_guidance = self._get_parameter_interaction_guidance( writing_style, story_tone, audience_age_group, content_rating ) outline_prompt = f"""\ {persona_prompt} **PREMISE:** {premise} {parameter_guidance} **YOUR TASK:** Create a detailed story outline with multiple scenes that brings this premise to life. The outline must perfectly align with ALL of the story setup parameters provided above. **SCENE PROGRESSION STRUCTURE:** **Scene 1-2 (Opening):** - Introduce the setting ({story_setting}) and main characters ({character_input}) - Establish the {story_tone} tone from the beginning - Set up the main conflict or adventure based on the plot elements ({plot_elements}) - Hook the audience with an engaging opening that matches {writing_style} style - Use the {narrative_pov} perspective to establish the story world - Create intrigue and interest appropriate for {audience_age_group} - Respect the {content_rating} content rating from the start **Scene 3-7 (Development):** - Develop the plot elements ({plot_elements}) in detail - Build character relationships and growth using the specified characters ({character_input}) - Create tension, obstacles, or challenges that advance the story - Maintain the {writing_style} style consistently throughout - Progress toward the {ending_preference} ending - Explore the setting ({story_setting}) more deeply - Ensure all content is age-appropriate for {audience_age_group} - Maintain the {story_tone} tone while developing the plot - Respect the {content_rating} content rating in all scenes - Use the {narrative_pov} perspective consistently **Final Scenes (Resolution):** - Resolve the main conflict established in the plot elements ({plot_elements}) - Deliver the {ending_preference} ending - Tie together all plot elements and character arcs - Provide satisfying closure appropriate for {audience_age_group} - Maintain the {writing_style} style and {story_tone} tone until the end - Ensure the ending respects the {content_rating} content rating - Use the {narrative_pov} perspective to conclude the story **OUTLINE STRUCTURE:** For each scene, provide: 1. **Scene Number and Title** 2. **Description** (written in {writing_style}, maintaining {story_tone}, and age-appropriate for {audience_age_group}) 3. **Image Prompt** (vivid, visually descriptive, includes setting/characters, age-appropriate) 4. **Audio Narration** (2-3 sentences, engaging, maintains style/tone, suitable for narration) 5. **Character Descriptions** (for characters appearing in the scene) 6. **Key Events** (bullet list of important happenings) **CONTEXT INTEGRATION REQUIREMENTS:** - Ensure every scene reflects the setting ({story_setting}) - Keep characters consistent with ({character_input}) - Integrate plot elements ({plot_elements}) logically - Maintain persona voice ({persona}) - Respect audience age group ({audience_age_group}) and content rating ({content_rating}) Before finalizing, verify that every scene adheres to the writing style, tone, age appropriateness, content rating, and narrative POV. Create 5-10 scenes that tell a complete, engaging story with clear progression and satisfying resolution. """ try: if use_structured_output: outline_schema = self._get_outline_schema() try: response = self.load_json_response( llm_text_gen(prompt=outline_prompt, json_struct=outline_schema, user_id=user_id) ) scenes = response.get("scenes", []) if scenes: logger.info(f"[StoryWriter] Generated {len(scenes)} structured scenes for user {user_id}") logger.info( "[StoryWriter] Outline generated with parameters: " f"audience={audience_age_group}, style={writing_style}, tone={story_tone}" ) return scenes logger.warning("[StoryWriter] No scenes found in structured output, falling back to text parsing") raise ValueError("No scenes found in structured output") except (json.JSONDecodeError, ValueError, KeyError) as exc: logger.warning( f"[StoryWriter] Failed to parse structured JSON outline ({exc}), falling back to text parsing" ) return self._parse_text_outline(outline_prompt, user_id) outline = self.generate_with_retry(outline_prompt, user_id=user_id) return outline.strip() except HTTPException: raise except Exception as exc: logger.error(f"Outline Generation Error: {exc}") raise RuntimeError(f"Failed to generate outline: {exc}") from exc