""" Configuration schema with Pydantic models Single source of truth for all configuration defaults and validation. """ from typing import Optional from pydantic import BaseModel, Field class LLMConfig(BaseModel): """LLM configuration""" api_key: str = Field(default="", description="LLM API Key") base_url: str = Field(default="", description="LLM API Base URL") model: str = Field(default="", description="LLM Model Name") class TTSLocalConfig(BaseModel): """Local TTS configuration (Edge TTS)""" voice: str = Field(default="zh-CN-YunjianNeural", description="Edge TTS voice ID") speed: float = Field(default=1.2, ge=0.5, le=2.0, description="Speech speed multiplier (0.5-2.0)") class TTSComfyUIConfig(BaseModel): """ComfyUI TTS configuration""" default_workflow: Optional[str] = Field(default=None, description="Default TTS workflow (optional)") class TTSSubConfig(BaseModel): """TTS-specific configuration (under comfyui.tts)""" inference_mode: str = Field(default="local", description="TTS inference mode: 'local' or 'comfyui'") local: TTSLocalConfig = Field(default_factory=TTSLocalConfig, description="Local TTS (Edge TTS) configuration") comfyui: TTSComfyUIConfig = Field(default_factory=TTSComfyUIConfig, description="ComfyUI TTS configuration") # Backward compatibility: keep default_workflow at top level @property def default_workflow(self) -> Optional[str]: """Get default workflow (for backward compatibility)""" return self.comfyui.default_workflow class ImageSubConfig(BaseModel): """Image-specific configuration (under comfyui.image)""" default_workflow: Optional[str] = Field(default=None, description="Default image workflow (optional)") prompt_prefix: str = Field( default="Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style", description="Prompt prefix for all image generation" ) class ComfyUIConfig(BaseModel): """ComfyUI configuration (includes global settings and service-specific configs)""" comfyui_url: str = Field(default="http://127.0.0.1:8188", description="ComfyUI Server URL") runninghub_api_key: str = Field(default="", description="RunningHub API Key (optional)") tts: TTSSubConfig = Field(default_factory=TTSSubConfig, description="TTS-specific configuration") image: ImageSubConfig = Field(default_factory=ImageSubConfig, description="Image-specific configuration") class PixelleVideoConfig(BaseModel): """Pixelle-Video main configuration""" project_name: str = Field(default="Pixelle-Video", description="Project name") llm: LLMConfig = Field(default_factory=LLMConfig) comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig) def is_llm_configured(self) -> bool: """Check if LLM is properly configured""" return bool( self.llm.api_key and self.llm.api_key.strip() and self.llm.base_url and self.llm.base_url.strip() and self.llm.model and self.llm.model.strip() ) def validate_required(self) -> bool: """Validate required configuration""" return self.is_llm_configured() def to_dict(self) -> dict: """Convert to dictionary (for backward compatibility)""" return self.model_dump()