Files
ALwrity/backend/services/story_writer/service_components/outline.py

173 lines
7.1 KiB
Python

"""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