""" Defines structured JSON schema for video scripts. """ from typing import List, Dict, Any, Optional from pydantic import BaseModel, Field class VideoScriptSection(BaseModel): """ Represents a section of a video script. """ section_type: str = Field(..., description="Type of section: hook, key_points, explanation, conclusion, call_to_action") content: str = Field(..., description="Content text of this section") display_title: Optional[str] = Field(None, description="Display title for this section (for UI)") class VideoScript(BaseModel): """ Structured schema for video scripts. """ title: str = Field(..., description="Title of the video") target_audience: Optional[str] = Field(None, description="Target audience for this video") tone: Optional[str] = Field(None, description="Tone of the video: professional, casual, etc.") sections: List[VideoScriptSection] = Field(..., description="Ordered sections of the script") call_to_action: Optional[str] = Field(None, description="Call to action for the video") key_summary_points: Optional[List[str]] = Field(default_factory=list, description="Key summary points") sources: Optional[List[str]] = Field(default_factory=list, description="Sources used for the script") fact_check_confirmation: Optional[str] = Field(None, description="Confirmation that facts have been checked") def get_full_script_text(self) -> str: """ Generate the full script text suitable for narration. Returns: Combined script text from all sections """ return "\n\n".join(section.content for section in self.sections) def get_formatted_display(self) -> str: """ Generate formatted script for display in chat UI. Returns: Markdown formatted script for display """ result = [f"## 🎬 {self.title}"] if self.target_audience: result.append(f"**Target Audience:** {self.target_audience}") if self.tone: result.append(f"**Tone:** {self.tone}") result.append("") # Empty line # Add each section with its display title for section in self.sections: if section.display_title: emoji = self._get_emoji_for_section(section.section_type) result.append(f"### {emoji} {section.display_title.upper()}") # Add content, maintaining paragraph breaks result.append(section.content) result.append("") # Empty line # Add summary points if available if self.key_summary_points and len(self.key_summary_points) > 0: result.append("### 📋 KEY SUMMARY POINTS") for point in self.key_summary_points: result.append(f"- {point}") result.append("") # Add sources if available if self.sources and len(self.sources) > 0: result.append("### 📚 SOURCES") for source in self.sources: result.append(f"- {source}") return "\n".join(result) def _get_emoji_for_section(self, section_type: str) -> str: """ Get appropriate emoji for section type. Args: section_type: Type of section Returns: Emoji character """ emoji_map = { "hook": "🎯", "key_points": "📊", "explanation": "💡", "detail": "📋", "context": "🔍", "conclusion": "🏁", "call_to_action": "⚡" } return emoji_map.get(section_type, "✨") def get_legacy_format(self) -> Dict[str, Any]: """ Get script in the legacy format for backward compatibility. Returns: Dictionary in the original format used by the system """ # Extract full script text script_text = self.get_full_script_text() # Find call_to_action text cta_text = self.call_to_action or "" for section in self.sections: if section.section_type == "call_to_action": cta_text = section.content break # Build legacy format return { "title": self.title, "script": script_text, "callToAction": cta_text, "factCheckConfirmation": self.fact_check_confirmation or "", "keySummaryPoints": self.key_summary_points or [], "sources": self.sources or [] } def convert_legacy_to_script(legacy_data: Dict[str, Any]) -> VideoScript: """ Convert legacy script data to VideoScript format. Args: legacy_data: Legacy script data dictionary Returns: VideoScript object """ sections = [] # Extract script content and try to split into logical sections script_content = legacy_data.get("script", "") # Try to find a hook (first sentence or paragraph) hook = "" remaining_script = script_content if "." in script_content: hook_parts = script_content.split(".", 1) if len(hook_parts) > 0: hook = hook_parts[0].strip() + "." remaining_script = hook_parts[1].strip() # Add hook if found if hook: sections.append( VideoScriptSection( section_type="hook", content=hook, display_title="Hook" ) ) # Add main content if remaining_script: sections.append( VideoScriptSection( section_type="explanation", content=remaining_script, display_title="Main Content" ) ) # Add call to action if present call_to_action = legacy_data.get("callToAction", "") if call_to_action: sections.append( VideoScriptSection( section_type="call_to_action", content=call_to_action, display_title="Call To Action" ) ) # Create VideoScript object return VideoScript( title=legacy_data.get("title", "Video Script"), sections=sections, key_summary_points=legacy_data.get("keySummaryPoints", []), sources=legacy_data.get("sources", []), call_to_action=call_to_action, fact_check_confirmation=legacy_data.get("factCheckConfirmation", "") )