""" Pydantic models for request/response validation and OpenAPI schema generation """ from typing import Optional, Dict from pydantic import BaseModel, Field from datetime import datetime class RenderRequest(BaseModel): """Request model for rendering Manim animations""" code: str = Field( ..., description="Python code containing Manim Scene class definition", json_schema_extra={ "example": ( "from manim import *\n\n" "class SquareToCircle(Scene):\n" " def construct(self):\n" " square = Square()\n" " circle = Circle()\n" " self.play(Transform(square, circle))\n" " self.wait()" ) } ) quality: str = Field( default="720p", description="Quality preset for rendering", json_schema_extra={ "enum": ["4k", "2k", "1080p", "720p", "480p"] } ) fps: Optional[int] = Field( None, description="Custom frames per second (overrides quality preset default)", ge=1, le=120, json_schema_extra={ "example": 30 } ) output_format: str = Field( default="mp4", description="Output video format(s). Use 'all' for MP4, GIF, and WebM", json_schema_extra={ "enum": ["mp4", "gif", "webm", "all"] } ) scene_name: Optional[str] = Field( None, description="Specific Scene class name to render (required if code contains multiple scenes)", json_schema_extra={ "example": "SquareToCircle" } ) class Config: json_schema_extra = { "example": { "code": "from manim import *\n\nclass MyScene(Scene):\n def construct(self):\n text = Text('Hello, Manim!')\n self.play(Write(text))\n self.wait()", "quality": "720p", "fps": 30, "output_format": "mp4", "scene_name": "MyScene" } } class RenderResponse(BaseModel): """Response model for render requests""" request_id: str = Field( ..., description="Unique identifier for this render request" ) status: str = Field( ..., description="Status of the render: 'success', 'error', or 'processing'", json_schema_extra={ "enum": ["success", "error", "processing"] } ) message: str = Field( ..., description="Human-readable status message" ) urls: Optional[Dict[str, str]] = Field( None, description="URLs to download rendered video files (available when status is 'success')", json_schema_extra={ "example": { "mp4": "https://your-space.hf.space/media/abc123-def456/output.mp4", "gif": "https://your-space.hf.space/media/abc123-def456/output.gif", "webm": "https://your-space.hf.space/media/abc123-def456/output.webm" } } ) expires_at: Optional[str] = Field( None, description="ISO 8601 timestamp when files will be automatically deleted (1 hour from creation)", json_schema_extra={ "example": "2025-12-19T15:30:00Z" } ) error_details: Optional[str] = Field( None, description="Detailed error message and traceback (available when status is 'error')" ) class Config: json_schema_extra = { "example": { "request_id": "abc123-def456-789", "status": "success", "message": "Animation rendered successfully", "urls": { "mp4": "https://your-space.hf.space/media/abc123-def456-789/output.mp4" }, "expires_at": "2025-12-19T15:30:00Z", "error_details": None } } class HealthResponse(BaseModel): """Response model for health check endpoint""" status: str = Field( ..., description="Overall system health status", json_schema_extra={ "enum": ["healthy", "degraded", "unhealthy"] } ) manim_version: str = Field( ..., description="Installed Manim version" ) ffmpeg_available: bool = Field( ..., description="Whether FFmpeg is available for video conversion" ) disk_space_mb: Optional[float] = Field( None, description="Available disk space in megabytes" ) active_renders: Optional[int] = Field( None, description="Number of currently active render processes" ) class Config: json_schema_extra = { "example": { "status": "healthy", "manim_version": "0.18.1", "ffmpeg_available": True, "disk_space_mb": 5120.5, "active_renders": 0 } } class StatusResponse(BaseModel): """Response model for checking render status""" request_id: str = Field( ..., description="Request ID being queried" ) status: str = Field( ..., description="Current status: 'ready', 'processing', 'not_found', or 'expired'", json_schema_extra={ "enum": ["ready", "processing", "not_found", "expired"] } ) files_available: Optional[list[str]] = Field( None, description="List of available file formats (e.g., ['mp4', 'gif'])" ) urls: Optional[Dict[str, str]] = Field( None, description="Download URLs for available files" ) expires_at: Optional[str] = Field( None, description="ISO 8601 timestamp when files will be deleted" ) log_tail: Optional[str] = Field( None, description="Recent log output for in-progress renders" )