"""Pydantic 契约(插件 ↔ 服务端)。""" from __future__ import annotations from pydantic import BaseModel, Field SUPPORTED_TYPES = [ "white_bg", "key_features", "selling_pt", "material", "lifestyle", "multi_scene", "ecommerce_detail", "size_chart", "sku_collection", "custom", ] # 通义(DashScope)生图模型白名单:插件下拉可选的模型 TONGYI_MODELS = ["qwen-image-3.0-pro", "wan2.7-image-pro"] # ── 采集上传 ── class SourceInfo(BaseModel): platform: str = Field(..., description="ozon | 1688 | taobao") itemId: str | None = None url: str = "" collectedAt: int | None = None # epoch 毫秒 class TextMaterial(BaseModel): kind: str = Field(..., description="title | params | selling_point | desc | price | brand") content: str = "" pairs: list[dict] | None = None # [{key, value}] class ImageMaterial(BaseModel): groupKey: str = Field(..., description="main | sku | detail | video") groupName: str = "" variantName: str | None = None url: str = Field(..., description="源站原图 URL") index: int = 0 type: str = "img" dedupeKey: str | None = None class MaterialsRequest(BaseModel): product_id: str | None = Field(default=None, description="传了=追加到已有商品") source: SourceInfo texts: list[TextMaterial] = Field(default_factory=list) images: list[ImageMaterial] = Field(default_factory=list) refererOrigin: str | None = None class MaterialsResponse(BaseModel): product_id: str assets_queued: int assets_skipped: int = 0 # ── 套图生成 ── # 目标平台 → 文案语言 + 图片比例(平台决定规格,不再单独选语言) PLATFORM_SPECS: dict[str, dict] = { "ozon": {"lang": "ru", "ratio": "3:4", "label": "Ozon"}, "wb": {"lang": "ru", "ratio": "3:4", "label": "Wildberries"}, "cn": {"lang": "zh", "ratio": "1:1", "label": "中文(国内平台)"}, } class SuiteCreateRequest(BaseModel): style_set: int = Field(default=1, ge=1, le=7, description="风格模板 1-7") types: list[str] = Field(default_factory=lambda: ["white_bg", "key_features", "lifestyle", "multi_scene"]) platform: str = Field(default="cn", description="目标平台:ozon | wb | cn") provider: str | None = Field(default=None, description="覆盖默认 provider(doubao | tongyi)") model: str | None = Field(default=None, description="覆盖默认生图模型(tongyi: qwen-image-3.0-pro / wan2.7-image-pro)") requirements: str | None = Field(default=None, description="生图要求(最高优先级,强制约束)") # ── 无状态套图生成(工具流程:请求自带采集数据)── class GenerateImageItem(BaseModel): url: str = Field(..., description="勾选的图片 URL(源站原图)") group_key: str = Field(default="main", description="main | sku | detail") variant_name: str | None = Field(default=None, description="SKU 规格名(方案绑定用)") class PlanItem(BaseModel): """出图方案项:一类图 × 数量,可绑定 SKU 规格。""" kind: str = Field(default="custom", description="图类型(SUPPORTED_TYPES 之一)") title: str = Field(..., description="方案标题,如「主图·粉色」") detail: str = Field(default="", description="这张图展示什么(中文)") prompt_hint: str = Field(default="", description="构图提示(英文,进生图 prompt)") count: int = Field(default=1, ge=0, le=5) variant_name: str | None = Field(default=None, description="绑定的 SKU 规格名") class GenerateRequest(BaseModel): texts: list[TextMaterial] = Field(default_factory=list, description="采集的文本素材") images: list[GenerateImageItem] = Field(default_factory=list, description="勾选的参考图") style_set: int = Field(default=1, ge=1, le=7) style_prompt: str | None = Field(default=None, description="用户改写的风格提示词(覆盖 style_set 模板)") requirements: str | None = Field(default=None, description="生图要求(最高优先级,强制约束,覆盖其他设定)") types: list[str] = Field(default_factory=list, description="旧参数:无方案时按类型生成") plan: list[PlanItem] | None = Field(default=None, description="出图方案(优先于 types)") platform: str = Field(default="cn", description="目标平台:ozon | wb | cn") provider: str | None = Field(default=None, description="覆盖默认 provider(doubao | tongyi)") model: str | None = Field(default=None, description="覆盖默认生图模型(tongyi: qwen-image-3.0-pro / wan2.7-image-pro)") # ── 出图方案规划(DeepSeek)── class PlanRequest(BaseModel): texts: list[TextMaterial] = Field(default_factory=list) sku_variants: list[str] = Field(default_factory=list, description="带图的 SKU 规格名") image_stats: dict = Field(default_factory=dict, description="分组图片数量统计") platform: str = Field(default="cn") requirements: str | None = Field(default=None, description="生图要求(最高优先级,规划方案必须遵循)") class PlanItemOut(BaseModel): kind: str title: str detail: str = "" prompt_hint: str = "" count: int = 1 variant_name: str | None = None class PlanResponse(BaseModel): summary: str = "" items: list[PlanItemOut] class SuiteCreateResponse(BaseModel): suite_id: str class SuiteImageOut(BaseModel): type_id: str name: str url: str status: str error: str | None = None class SuiteOut(BaseModel): id: str product_id: str status: str style_set: int platform: str lang: str ratio: str types: list[str] provider: str model: str | None = None images: list[SuiteImageOut] error: str | None = None # ── 商品 ── class AssetOut(BaseModel): id: str group_key: str variant_name: str | None = None type: str source_url: str url: str | None = None status: str class ProductOut(BaseModel): id: str stage: str source_platform: str | None = None source_item_id: str | None = None source_url: str | None = None name: str description: str price: float | None = None asset_counts: dict | None = None assets: list[AssetOut] = Field(default_factory=list) created_at: str | None = None class ProductListOut(BaseModel): total: int items: list[ProductOut]