"""Request/response schemas for Video Engine V1 (curriculum-aligned scripts). The response model is the engine's ``VideoScript`` (imported and re-exported so routes and OpenAPI share one source of truth). """ from __future__ import annotations from typing import Any from pydantic import BaseModel, Field from app.services.video_script_engine import ( # re-export for routes / OpenAPI CurriculumHints, ExamTimestampChapter, VideoScript, VideoSegment, VisualScene, ) __all__ = [ "CurriculumHints", "ExamTimestampChapter", "VideoScript", "VideoSegment", "VisualScene", "VideoScriptRequest", "SegmentAudioRequest", "SegmentAudioResponse", "VideoProgressResponse", ] class VideoScriptRequest(BaseModel): chapter_slug: str = Field(..., min_length=1, max_length=120) subject: str = Field(default="", max_length=80) level: str = Field(default="", max_length=20) mode: str = Field(default="teach", max_length=24) duration_minutes: int = Field(default=10, ge=3, le=15) language_preference: str | None = Field(default="manglish", max_length=24) curriculum_hints: CurriculumHints = Field(default_factory=CurriculumHints) student_progress: dict[str, Any] | None = None class SegmentAudioRequest(BaseModel): # The renderer sends the on-screen script text; never a file path. teacher_script: str = Field(..., min_length=1, max_length=4000) language: str = Field(default="manglish", max_length=24) voice: str | None = Field(default=None, max_length=40) class SegmentAudioResponse(BaseModel): segment_id: str # "spoken" when synthesized audio is available, "read_along" for silent mode. mode: str = "read_along" audio_ready: bool = False # Opaque retrieval token (hash), NEVER a file path. None in read-along mode. audio_token: str | None = None subtitle_text: str message: str = "Read-along mode: follow the on-screen script." class VideoProgressResponse(BaseModel): video_id: str current_segment_index: int = 0 completed_segment_ids: list[str] = Field(default_factory=list) percent_complete: float = 0.0 updated_at: str | None = None