"""提示词公共层:与模型家族无关的商品上下文、风格模板、图类型名与文案组件。 各家族模块(alibaba / doubao / gpt / google)只负责"如何对模型说话", 商品信息提炼与风格体系统一在这里维护,避免多处漂移。 """ from __future__ import annotations import re # ── 风格模板(与插件端 STYLE_SET_OPTIONS 对应;提示词可被用户在插件里改写覆盖)─── STYLE_SETS: dict[int, dict] = { 1: { "name": "北欧极简", "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": "Fresh and bright: white-to-pale-blue gradient background, high-key lighting, clear luminous colors, airy and uncluttered composition", "bg": "", }, 3: { "name": "高级感深色", "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": "Warm lifestyle: warm beige background, cozy warm lighting, inviting home-living texture, approachable and natural atmosphere", "bg": "", }, 5: { "name": "纯净棚拍", "tone": "Clean e-commerce studio: plain light background, even frontal softbox lighting, no distracting props, product centered and prominent", "bg": "", }, } # ── 图类型中文名(导出文件名用)─────────────────────────────────────────── TYPE_NAMES_ZH: dict[str, str] = { "white_bg": "白底主图", "key_features": "核心卖点图", "selling_pt": "卖点图", "material": "材质图", "lifestyle": "场景展示图", "multi_scene": "多场景拼图", "ecommerce_detail": "电商详情图", "size_chart": "尺寸标注图", "sku_collection": "SKU合集图", "custom": "创意图", } # ── 图内营销文案渲染规范(各家族共用;语言由平台决定)────────────────────── TEXT_RENDER = { "zh": ( "Render concise Chinese marketing text inside the image: main headline max 8 Chinese characters, " "sub-lines max 12 characters each, font is modern clean sans-serif (Source Han Sans style), " "high legibility, tasteful typography layout, colors harmonized with the composition. " "No spelling errors, no garbled characters." ), "en": ( "Render concise English marketing text inside the image: headline max 5 words, " "sub-lines max 8 words each, Helvetica Neue style sans-serif, high legibility, " "tasteful typography layout, colors harmonized with the composition. No spelling errors." ), "ru": ( "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." ), } def resolve_style(style_set: int, style_prompt: str | None = None) -> dict: """用户改写的风格提示词整体覆盖内置模板(tone/bg 整体替换)。""" if style_prompt and style_prompt.strip(): return {"name": "custom", "tone": style_prompt.strip(), "bg": ""} return STYLE_SETS.get(style_set, STYLE_SETS[1]) def requirements_block(requirements: str | None) -> str: """用户强制要求块:最高优先级、置于提示词最前、覆盖冲突指令(原文保留不翻译)。""" if requirements and requirements.strip(): return ( "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(".") + "." ) return "" # ── 商品上下文提炼 ──────────────────────────────────────────────────────── def _shorten(text: str, n: int) -> str: text = re.sub(r"\s+", " ", (text or "")).strip() return text[:n] def _clean_title(title: str) -> str: """去掉常见堆砌词,让标题更可读。""" t = _shorten(title, 60) return re.sub(r"[【【】】\\[\\]|/]", " ", t).strip() def build_context(raw: dict, fallback_name: str = "", fallback_desc: str = "") -> dict: """从采集数据提炼生图上下文:标题、描述行、卖点列表、参数行。 raw: {title, desc, price, params: [{key, value}], sellingPoints} """ # 试算页会同时上传中文采集标题和已回填的俄文标题;后者用于图内可见文案。 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) # 卖点:优先显式卖点文本;否则从参数表里挑短而有信息量的键值对 selling_points: list[dict] = [] sp_text = raw.get("sellingPoints") or "" if sp_text: for chunk in re.split(r"[;;\n·]+|(?= 5: break dimensions = raw.get("dimensions") or {} known_dimensions: list[tuple[str, object]] = [] if isinstance(dimensions, dict): known_dimensions = [ (label, dimensions[key]) for key, label in (("length", "length"), ("width", "width"), ("height", "height")) if dimensions.get(key) is not None ] unit = (dimensions.get("unit") or "mm") if isinstance(dimensions, dict) else "mm" dimensions_line = "; ".join(f"{label}: {value} {unit}" for label, value in known_dimensions) known_labels = [label for label, _ in known_dimensions] missing_labels = [label for label in ("length", "width", "height") if label not in known_labels] dimension_annotation_instruction = ( "STRICT MEASUREMENT OUTPUT RULE: render exactly " + str(len(known_labels)) + " measurement arrows and labels, only for: " + ", ".join(known_labels) + ". The only permitted dimension values are: " + dimensions_line + ". Do not infer product measurements. Do not draw, label, or show an arrow for: " + ", ".join(missing_labels or ["any additional direction"]) + ", depth, thickness, diameter, or any other unlisted dimension." if known_dimensions else "" ) params_line = "; ".join( f"{p.get('key')}: {p.get('value')}" for p in (raw.get("params") or [])[:8] ) return { "title": title, "title_en": title, # 采集源多为中文标题,英文场景直接用原词避免乱翻译 "desc": desc, "selling_points": selling_points[:3], "params_line": "; ".join(filter(None, (params_line, dimensions_line))), # 俄文图片里不能直接渲染中文参数;保留不含汉字的规格(型号、数字、单位)供尺寸图使用。 "params_line_non_cjk": "; ".join(filter(None, ( "; ".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')}") ), dimensions_line, ))), "price": raw.get("price") or "", "dimension_annotation_instruction": dimension_annotation_instruction, } def selling_point_lines(ctx: dict, lang: str, max_n: int = 3) -> str: """卖点列表 → 单行文案(图内 callout/标题用),无卖点返回空串。""" sps = ctx["selling_points"][:max_n] if not sps: return "" key = "zh" if lang == "zh" else "en" 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: return TYPE_NAMES_ZH.get(type_id, type_id)