"""Pydantic schemas for the Higgsfield Shorts wrapper. VideoSpec mirrors the Remotion data contract (remotion/src/data/mumum.ts) 1:1. The LLM (spec_builder) fills VideoSpec; Higgsfield consumes higgsfield_prompt; Remotion consumes the rest (hook / selling_point / brand_lines / end_card). """ from __future__ import annotations from typing import Literal, Optional from pydantic import BaseModel, Field # ---------- Inbound: interview answers (no complex analysis) ---------- class GenerateRequest(BaseModel): kind: Literal["place", "product", "message"] biz_name: str = Field(..., description="업체명·상품명·메시지 제목") addr: Optional[str] = Field(None, description="주소 또는 판매 사이트 URL") price: Optional[str] = Field(None, description="가격 정보") selling: str = Field(..., description="주인/마케터가 생각하는 강력한 한방 셀링포인트") # ---------- VideoSpec sub-objects (mirror mumum.ts) ---------- class Hook(BaseModel): eyebrow: str title: str class SellingPoint(BaseModel): items: list[str] = Field(..., description="3개 독립 배지 카피") class EndCard(BaseModel): brand: str location: str disclosure: str = "실제 사진 기반, AI 카메라 효과를 적용한 영상입니다." class VideoSpec(BaseModel): # 에너지 프로파일 → Remotion 리듬/트랜지션 기본값 결정 profile: Literal["Still Cinema", "Rhythm Reveal", "Maximum Viral"] # Higgsfield marketing_studio_video 프롬프트 (유형별 톤) higgsfield_prompt: str hook: Hook selling_point: SellingPoint brand_lines: list[str] = Field(..., description="감성 카피 2줄") end_card: EndCard caption: str = Field(..., description="업로드용 캡션+해시태그") # ---------- Outbound: 자막 스크립트 4블록 ---------- class ScriptResult(BaseModel): intro: str = Field(..., description="인트로 (후킹)") selling: str = Field(..., description="셀링포인트") story: str = Field(..., description="감성 스토리") cta: str = Field(..., description="CTA") # ---------- Outbound: final result ---------- class GenerateResult(BaseModel): video_url: str caption: str profile: str cost_credits: float = 0.0 job_id: Optional[str] = None # ---------- Outbound: 갤러리용 완료 영상 메타데이터 ---------- class VideoMeta(BaseModel): job_id: str video_url: str biz_name: Optional[str] = None caption: Optional[str] = None profile: Optional[str] = None created_at: float = 0.0 # Unix timestamp # ---------- Outbound: 비동기 작업 상태 (폴링) ---------- class JobStatus(BaseModel): job_id: str # queued: 큐 대기 / running: 생성 중 / done: 완료 / error: 실패 status: Literal["queued", "running", "done", "error"] stage: Optional[str] = Field(None, description="현재 단계: spec | higgsfield | remotion") stage_index: int = Field(0, description="단계 인덱스 0..2 (프론트 진행바 매핑)") progress: int = Field(0, description="전체 진행률 0..100 (대략)") # 완료 시 채워지는 결과 필드 (GenerateResult 와 동일 의미) video_url: Optional[str] = None caption: Optional[str] = None profile: Optional[str] = None cost_credits: float = 0.0 # 실패 시 사용자에게 보여줄 메시지 error: Optional[str] = None