feat: AI生图功能丰富

This commit is contained in:
2026-09-27 22:41:33 +08:00
parent 1c4fe1a836
commit 7f1503294f
20 changed files with 627 additions and 336 deletions
+2 -1
View File
@@ -202,7 +202,8 @@ async def create_materials(
await db.commit()
await db.refresh(product)
if queued:
# 多批上报的最后一批可能全被去重;仍要扫描此前批次留下的 pending 素材。
if req.process_assets:
background.add_task(process_product_assets, str(product.id))
return MaterialsResponse(
product_id=str(product.id),
+23 -5
View File
@@ -316,12 +316,15 @@ async def download_suite_zip(suite_id: str):
async def suite_image_edit(req: ImageEditSingleRequest) -> ImageEditTaskResponse:
"""提交单张图生图任务;立即返回任务 ID,实际生成在统一队列异步执行。"""
settings = get_settings()
provider_name = resolve_provider(req.model, None, settings.image_provider)
structured_edit = bool(req.main_image_url and req.main_image_url.strip())
if structured_edit and req.model not in {"gpt-image-2", "gpt-image-2@pcore"}:
raise HTTPException(status_code=400, detail="单张 AI 编辑仅支持 Pcore gpt-image-2")
provider_name = "pcore" if structured_edit else resolve_provider(req.model, None, settings.image_provider)
if provider_name not in GENERATORS:
raise HTTPException(status_code=400, detail=f"未知 provider: {provider_name}")
validate_model(provider_name, req.model)
spec = {"lang": "ru", "ratio": "3:4"} # 试算页固定 Ozon 规格(俄文图内文案 · 3:4)
spec = {"lang": req.target_language if structured_edit else "ru", "ratio": req.ratio if structured_edit else "3:4"}
model = api_model(req.model) # 剥 @provider 后缀,供应商侧只认裸模型名
product_id = (req.product_id or "").strip() or None
if product_id:
@@ -331,7 +334,12 @@ async def suite_image_edit(req: ImageEditSingleRequest) -> ImageEditTaskResponse
raise HTTPException(status_code=400, detail="product_id 不是合法 UUID") from exc
# 单图编辑优先级高于套图;支持多张参考底图,保持传入顺序并去重。
ref_urls = list(dict.fromkeys(url for url in [*req.image_urls, req.image_url] if url))
if structured_edit:
ref_urls = list(dict.fromkeys(url.strip() for url in [req.main_image_url, *req.reference_image_urls] if url and url.strip()))
if len(ref_urls) > 3:
raise HTTPException(status_code=400, detail="辅助参考图最多两张")
else:
ref_urls = list(dict.fromkeys(url for url in [*req.image_urls, req.image_url] if url))
if not ref_urls:
raise HTTPException(status_code=400, detail="请至少选择一张参考底图")
if req.mode == "outpaint":
@@ -341,6 +349,10 @@ async def suite_image_edit(req: ImageEditSingleRequest) -> ImageEditTaskResponse
raise HTTPException(status_code=400, detail="仅扩图只能选择一张参考底图")
if req.generate_size_chart:
raise HTTPException(status_code=400, detail="仅扩图不能同时生成尺寸标注图")
if structured_edit and req.ratio != "3:4":
raise HTTPException(status_code=400, detail="仅扩图只支持 3:4")
if structured_edit and req.mode == "edit" and req.background == "reference" and len(ref_urls) < 2:
raise HTTPException(status_code=400, detail="参考图背景需要选择一张辅助参考图")
# prompt_extra 清空展示文案,避免中文 UI 文案进入模型 prompt。
is_size_chart = bool(req.generate_size_chart)
@@ -354,10 +366,16 @@ async def suite_image_edit(req: ImageEditSingleRequest) -> ImageEditTaskResponse
lang=spec["lang"],
ratio=spec["ratio"],
# 仅扩图与用户明确填写的 AI 编辑要求都不得继承默认「纯净棚拍」风格。
style_set=0 if req.mode == "outpaint" or bool(req.prompt.strip()) else 5,
style_set=req.style_set if structured_edit and req.mode == "edit" else (0 if req.mode == "outpaint" or bool(req.prompt.strip()) else 5),
request_payload={
"context": _with_dimensions({"title": "AI 精修"}, req.dimensions, required=is_size_chart),
"style_prompt": None,
"style_prompt": req.style_prompt if structured_edit else None,
"single_edit": {
"translate_text": req.translate_text,
"target_language": req.target_language,
"background": req.background,
"background_description": req.background_description,
} if structured_edit and req.mode == "edit" else None,
"requirements": req.prompt,
"operation": req.mode,
# 用户写了要求时,这一轮只执行其要求,不再拼营销图、默认风格或默认俄文文案。