feat: 优化图片资源删除管理,生图任务列表管理

This commit is contained in:
2026-08-30 14:50:33 +08:00
parent 4ee667142b
commit 957289f717
46 changed files with 4180 additions and 524 deletions
+5 -11
View File
@@ -7,7 +7,7 @@
from __future__ import annotations
from .common import (
STYLE_SETS, TEXT_RENDER, requirements_block, resolve_style, selling_point_lines,
STYLE_SETS, TEXT_RENDER, params_line_for_output, requirements_block, resolve_style, selling_point_lines,
)
# ── 公共组件(主体参考语义专用)────────────────────────────────────────────
@@ -85,7 +85,7 @@ def _prompt_multi_scene(ctx: dict, style: dict, lang: str) -> str:
def _prompt_ecommerce_detail(ctx: dict, style: dict, lang: str) -> str:
sp = selling_point_lines(ctx, lang) or ctx["title"]
params = ctx["params_line"]
params = params_line_for_output(ctx, lang)
return (
f"E-commerce detail-page hero section for product \"{ctx['title']}\", square layout: "
f"top half is a hero banner with the product at a 3/4 angle ({style['bg']}); "
@@ -95,7 +95,7 @@ def _prompt_ecommerce_detail(ctx: dict, style: dict, lang: str) -> str:
)
def _prompt_size_chart(ctx: dict, style: dict, lang: str) -> str:
dims = ctx["params_line"]
dims = params_line_for_output(ctx, lang)
return (
f"Product size chart infographic for \"{ctx['title']}\": product shown in clean front and side views "
f"on light background, with thin measurement annotation lines (arrows) marking length, width and height, "
@@ -115,15 +115,9 @@ def _prompt_sku_collection(ctx: dict, style: dict, lang: str) -> str:
def _prompt_custom(ctx: dict, style: dict, lang: str, extra: dict) -> str:
hint = (extra.get("prompt_hint") or "").strip()
purpose = extra.get("title") or ""
detail = extra.get("detail") or ""
bg = f" {style['bg']} as environment." if style.get("bg") else ""
composed = (
f"E-commerce marketing image for product \"{ctx['title']}\""
+ (f" — {purpose}" if purpose else "")
+ (f": {detail}" if detail else "")
+ "."
)
# title/detail 是规划器给前端展示的中文文案;只使用约定为英文的 prompt_hint,避免它们变成图中文字。
composed = f"E-commerce marketing image for product \"{ctx['title']}\"."
if hint:
composed += f" Composition: {hint}."
return f"{composed}{bg} {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
+28 -9
View File
@@ -12,27 +12,27 @@ import re
STYLE_SETS: dict[int, dict] = {
1: {
"name": "北欧极简",
"tone": "北欧极简风:浅灰或米白背景,柔和漫射光,低饱和色调,画面留白充足,构图克制干净",
"tone": "Nordic minimalism: light gray or warm off-white background, soft diffused light, low-saturation palette, generous negative space, restrained and clean composition",
"bg": "",
},
2: {
"name": "清新明亮",
"tone": "清新明亮风:明亮的白色到浅蓝渐变背景,高调光线,色彩明快通透,整体轻盈干净",
"tone": "Fresh and bright: white-to-pale-blue gradient background, high-key lighting, clear luminous colors, airy and uncluttered composition",
"bg": "",
},
3: {
"name": "高级感深色",
"tone": "高级质感风:深灰或炭黑背景,戏剧性侧光打光,突出商品材质与光泽,沉稳高级",
"tone": "Premium dark aesthetic: deep gray or charcoal background, dramatic side lighting, emphasize material texture and sheen, refined and restrained mood",
"bg": "",
},
4: {
"name": "暖调生活",
"tone": "温暖生活风:暖米色背景,暖色灯光氛围,温馨的家居质感,亲和力强",
"tone": "Warm lifestyle: warm beige background, cozy warm lighting, inviting home-living texture, approachable and natural atmosphere",
"bg": "",
},
5: {
"name": "纯净棚拍",
"tone": "标准电商棚拍:纯色浅背景,均匀的正面柔光,无杂物干扰,商品居中突出",
"tone": "Clean e-commerce studio: plain light background, even frontal softbox lighting, no distracting props, product centered and prominent",
"bg": "",
},
}
@@ -70,6 +70,9 @@ TEXT_RENDER = {
"Render concise Russian marketing text inside the image: headline max 4 words, "
"sub-lines max 6 words each, modern clean sans-serif (Inter / PT Sans style), "
"proper Cyrillic typography, high legibility, tasteful layout, colors harmonized with the composition. "
"Translate any Chinese source facts into Russian before rendering. All newly generated visible text "
"must be Russian Cyrillic only: never render Chinese, Hanzi, CJK characters, or Chinese phrases. "
"The sole exception is Chinese text physically printed on the reference product itself, which must remain unchanged. "
"No spelling errors, no mixed latin/cyrillic gibberish."
),
}
@@ -86,8 +89,8 @@ def requirements_block(requirements: str | None) -> str:
"""用户强制要求块:最高优先级、置于提示词最前、覆盖冲突指令(原文保留不翻译)。"""
if requirements and requirements.strip():
return (
"STRICT REQUIREMENTS (highest priority, must be followed exactly, "
"override any conflicting instruction): "
"STRICT REQUIREMENTS (highest priority for visual intent; interpret any Chinese source instruction "
"by meaning only and never copy its Hanzi into newly generated text): "
+ requirements.strip().rstrip(".")
+ "."
)
@@ -110,7 +113,8 @@ def build_context(raw: dict, fallback_name: str = "", fallback_desc: str = "") -
raw: {title, desc, price, params: [{key, value}], sellingPoints}
"""
title = _clean_title(raw.get("title") or fallback_name or "product")
# 试算页会同时上传中文采集标题和已回填的俄文标题;后者用于图内可见文案。
title = _clean_title(raw.get("title_ru") or raw.get("title") or fallback_name or "product")
desc = _shorten(raw.get("desc") or fallback_desc or "", 200)
# 卖点:优先显式卖点文本;否则从参数表里挑短而有信息量的键值对
@@ -138,6 +142,12 @@ def build_context(raw: dict, fallback_name: str = "", fallback_desc: str = "") -
"desc": desc,
"selling_points": selling_points[:3],
"params_line": params_line,
# 俄文图片里不能直接渲染中文参数;保留不含汉字的规格(型号、数字、单位)供尺寸图使用。
"params_line_non_cjk": "; ".join(
f"{p.get('key')}: {p.get('value')}"
for p in (raw.get("params") or [])[:8]
if not re.search(r"[\u3400-\u9fff]", f"{p.get('key')} {p.get('value')}")
),
"price": raw.get("price") or "",
}
@@ -147,7 +157,16 @@ def selling_point_lines(ctx: dict, lang: str, max_n: int = 3) -> str:
if not sps:
return ""
key = "zh" if lang == "zh" else "en"
return "; ".join(s[key] for s in sps if s.get(key))
values = [s[key] for s in sps if s.get(key)]
# 中文卖点是模型理解商品的来源,不应成为俄文图片中的逐字文案。
if lang == "ru":
values = [v for v in values if not re.search(r"[\u3400-\u9fff]", v)]
return "; ".join(values)
def params_line_for_output(ctx: dict, lang: str) -> str:
"""返回可进入图内规格文案的参数;俄文模式剔除含汉字的原始采集参数。"""
return ctx.get("params_line") if lang == "zh" else ctx.get("params_line_non_cjk", "")
def type_name(type_id: str) -> str:
+5 -9
View File
@@ -9,7 +9,7 @@
"""
from __future__ import annotations
from .common import TEXT_RENDER, requirements_block, resolve_style, selling_point_lines
from .common import TEXT_RENDER, params_line_for_output, requirements_block, resolve_style, selling_point_lines
_SUBJECT_LOCK = (
"SUBJECT LOCK (highest priority): the product in the first image is the subject. "
@@ -32,8 +32,8 @@ _REMINDER = (
def _anchor(ctx: dict) -> str:
"""商品文字锚定:仅供识别,明确以图为准(同 gpt 模块,避免文字反噬商品)。"""
line = f"Context (identification only): the product is \"{ctx['title']}\""
if ctx.get("params_line"):
line += f" ({ctx['params_line']})"
if ctx.get("params_line_non_cjk"):
line += f" ({ctx['params_line_non_cjk']})"
return line + ". The image, not this text, defines the product's appearance."
@@ -88,7 +88,7 @@ def _task_multi_scene(ctx: dict, lang: str) -> str:
def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
sp = selling_point_lines(ctx, lang) or ctx["title"]
params = ctx["params_line"]
params = params_line_for_output(ctx, lang)
return (
"Compose a square detail-page hero section: top half a hero banner with the unchanged product "
"at a 3/4 angle; bottom half a clean spec card with 3 feature rows and line icons"
@@ -97,7 +97,7 @@ def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
)
def _task_size_chart(ctx: dict, lang: str) -> str:
dims = ctx["params_line"]
dims = params_line_for_output(ctx, lang)
return (
"Create a size chart: the unchanged product in clean front and side views on a light background, "
"thin measurement annotation lines (arrows) marking length, width and height with values beside "
@@ -135,11 +135,7 @@ def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict
hint = (extra.get("prompt_hint") or "").strip()
if type_id == "custom":
purpose = extra.get("title") or ""
detail = extra.get("detail") or ""
task = "Create an e-commerce marketing image featuring the product from the first image"
task += f" — {purpose}" if purpose else ""
task += f": {detail}" if detail else ""
task += "."
wants_text = True
else:
+5 -9
View File
@@ -15,7 +15,7 @@
"""
from __future__ import annotations
from .common import TEXT_RENDER, requirements_block, resolve_style, selling_point_lines
from .common import TEXT_RENDER, params_line_for_output, requirements_block, resolve_style, selling_point_lines
# 保真锁:商品由 Image 1 唯一定义,其余指令一律不得触碰商品本体
_PRESERVE = (
@@ -55,8 +55,8 @@ def _anchor(ctx: dict) -> str:
文字锚定用于帮模型认出"是哪件商品",而不是"长什么样"。
"""
line = f"CONTEXT (identification only): the product is \"{ctx['title']}\""
if ctx.get("params_line"):
line += f" ({ctx['params_line']})"
if ctx.get("params_line_non_cjk"):
line += f" ({ctx['params_line_non_cjk']})"
return (
line
+ ". Image 1 — not this text — defines the product's appearance; "
@@ -119,7 +119,7 @@ def _task_multi_scene(ctx: dict, lang: str) -> str:
def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
sp = selling_point_lines(ctx, lang) or ctx["title"]
params = ctx["params_line"]
params = params_line_for_output(ctx, lang)
return (
"TASK: Detail-page hero section on a square canvas. Top half: hero banner with the unchanged "
"product at a 3/4 angle. Bottom half: clean spec card with 3 feature rows and line icons"
@@ -128,7 +128,7 @@ def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
)
def _task_size_chart(ctx: dict, lang: str) -> str:
dims = ctx["params_line"]
dims = params_line_for_output(ctx, lang)
return (
"TASK: Measurement chart. Show the unchanged product in clean front and side views on a light "
"background; overlay thin technical annotation lines (arrows) marking length, width and height "
@@ -167,11 +167,7 @@ def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict
hint = (extra.get("prompt_hint") or "").strip()
if type_id == "custom":
purpose = extra.get("title") or ""
detail = extra.get("detail") or ""
task = "TASK: Create an e-commerce marketing image featuring the product from Image 1"
task += f" — {purpose}" if purpose else ""
task += f": {detail}" if detail else ""
task += "."
wants_text = True
else: