from __future__ import annotations from typing import Any from pydantic import BaseModel, ConfigDict, Field, field_validator class ModuleGenerationVideoConfigV2(BaseModel): model_config = ConfigDict(extra="ignore") engine_id: str = Field(..., min_length=1, max_length=32, description="视频引擎ID") duration: int = Field(..., ge=1, le=120, description="视频时长,单位秒") aspect_ratio: str = Field(..., min_length=1, max_length=16, description="视频比例") resolution: str = Field(..., min_length=1, max_length=16, description="目标分辨率") @field_validator("engine_id", "aspect_ratio", "resolution", mode="before") @classmethod def _strip_required(cls, value: str) -> str: value = str(value or "").strip() if not value: raise ValueError("字段不能为空") return value class HotOpeningTaskCreateV2(BaseModel): model_config = ConfigDict(extra="ignore") material_video_url: str = Field(..., min_length=1, description="爆款参考视频,步骤2分析使用,步骤3绝不携带") material_video_resource_id: str | None = Field(None, max_length=32) material_video_duration_seconds: float | None = Field(None, gt=0) material_image_url: str | None = Field(None, description="可选参考图片;仅支持本地上传/历史素材") material_image_resource_id: str | None = Field(None, max_length=32) source_project_name: str | None = Field(None, max_length=40) target_project_name: str | None = Field(None, max_length=40) core_content_point: str | None = Field(None, max_length=200) project_description: str | None = Field(None, max_length=1000) target_platform: str | None = Field(None, max_length=64) video_config: ModuleGenerationVideoConfigV2 idempotency_key: str | None = Field(None, max_length=64) @field_validator( "material_video_url", "material_image_url", "source_project_name", "target_project_name", "core_content_point", "project_description", "target_platform", "idempotency_key", mode="before", ) @classmethod def _strip_text(cls, value: str | None) -> str | None: if value is None: return None value = str(value).strip() return value or None class ShotReplicateProjectCreateV2(BaseModel): model_config = ConfigDict(extra="ignore") material_image_url: str | None = Field(None, description="可选参考图片;仅支持本地上传/历史素材") material_image_resource_id: str | None = Field(None, max_length=32) target_project_name: str | None = Field(None, max_length=40) core_content_point: str | None = Field(None, max_length=200) project_description: str | None = Field(None, max_length=1000) target_platform: str | None = Field(None, max_length=64) video_config: ModuleGenerationVideoConfigV2 idempotency_key: str | None = Field(None, max_length=64) @field_validator( "material_image_url", "target_project_name", "core_content_point", "project_description", "target_platform", "idempotency_key", mode="before", ) @classmethod def _strip_text(cls, value: str | None) -> str | None: if value is None: return None value = str(value).strip() return value or None class ModuleVideoPromptRetryV2(BaseModel): model_config = ConfigDict(extra="forbid") video_config: ModuleGenerationVideoConfigV2 class ModuleVideoPromptSchemaUpdateV2(BaseModel): model_config = ConfigDict(extra="ignore") prompt_schema: dict[str, Any] = Field(..., description="允许编辑字段的 JSON patch") @field_validator("prompt_schema") @classmethod def _non_empty_schema(cls, value: dict[str, Any]) -> dict[str, Any]: if not isinstance(value, dict) or not value: raise ValueError("prompt_schema 必须为非空对象") return value class ModuleGenerationV2ActionOut(BaseModel): message: str project_id: str step_id: str | None = None flow_version: str = "v2" detail: Any | None = None