o2o-ado2-short-form/server/app/schemas.py

82 lines
3.1 KiB
Python

"""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 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