"""Google 图像模型(nano-banana / nano-banana-2 / nano-banana-2-lite / nano-banana-pro)提示词。 语义:Gemini 图像编辑 —— 原生主体保持能力强,输入图即"主体 + 底图", 对自然语言指令遵循好。不套用 GPT 的编辑契约(冗长的拒绝条款反而稀释指令), 也不用负面清单(无 negative_prompt 参数)。要点: - 开头一句话钉死"主体 = 第一张图里的商品,逐像素保持"; - 指令自然语言描述目标画面(场景/排版/文案),不重述商品外观; - 标题/参数仅作识别背景并声明以图为准。 """ from __future__ import annotations from .common import TEXT_RENDER, requirements_block, resolve_style, selling_point_lines _SUBJECT_LOCK = ( "SUBJECT LOCK (highest priority): the product in the first image is the subject. " "Keep it exactly as photographed — same shape, proportions, colors, print/pattern, " "logo, label and every detail — and place that very product into the result. " "A second image, when present, is another view of the same product for reference only." ) _QUALITY = ( "OUTPUT: photorealistic commercial e-commerce photography, ultra-detailed, " "natural light and shadow, professional retouching." ) _REMINDER = ( "Reminder: keep the product exactly as in the first image; change only its surroundings, " "composition, lighting and overlay graphics." ) 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']})" return line + ". The image, not this text, defines the product's appearance." # ── 各图类型指令(自然语言编辑口吻)──────────────────────────────────────── def _task_white_bg(ctx: dict, lang: str) -> str: return ( "Replace the background of this product photo with seamless pure white (RGB 255,255,255): " "product centered in front view filling about 85% of the frame, even studio lighting with only " "a faint natural contact shadow. No props, no added text, no background elements." ) def _task_key_features(ctx: dict, lang: str) -> str: sp = selling_point_lines(ctx, lang) or ctx["title"] return ( "Create a square key-features infographic: the unchanged product on the left two-thirds, " "a clean right-hand panel with 3 feature callouts using minimal line icons and thin leader " f"lines pointing at the product. Callout copy: {sp}." ) def _task_selling_pt(ctx: dict, lang: str) -> str: sp = selling_point_lines(ctx, lang, 1) or ctx["title"] return ( "Turn the photo into a single-selling-point poster: hero close-up of the unchanged product at " f"a dynamic angle, one large bold headline about \"{sp}\", generous negative space, and a small " "magnified circle zooming into an existing detail of the product." ) def _task_material(ctx: dict, lang: str) -> str: return ( "Create an extreme macro close-up of an existing area of the product's surface, showing its " "true fabric weave / texture / stitching exactly as in the photo; shallow depth of field, " "raking light, small caption in a corner." ) def _task_lifestyle(ctx: dict, lang: str) -> str: return ( "Place the unchanged product into a realistic everyday scene where it would naturally be used: " "human-scale surroundings, soft daylight, authentic candid mood, the product as the clear visual focus." ) def _task_multi_scene(ctx: dict, lang: str) -> str: sp = selling_point_lines(ctx, lang) task = ( "Build a triptych of three vertical panels separated by thin gutters: each panel shows an " "identical copy of the product in a different usage scene (home interior / outdoor street / " "office desk), with consistent color grading across panels." ) if sp: task += f" Panel captions: {sp}." return task def _task_ecommerce_detail(ctx: dict, lang: str) -> str: sp = selling_point_lines(ctx, lang) or ctx["title"] params = ctx["params_line"] 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" + (f" (specs: {params})" if params else "") + f", one highlighted row: {sp}." ) def _task_size_chart(ctx: dict, lang: str) -> str: dims = ctx["params_line"] 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 " "each line" + (f" (known specs: {dims})" if dims else "") + ", small caption row, precise technical-drawing aesthetic." ) def _task_sku_collection(ctx: dict, lang: str) -> str: # 不展开"全部配色":会凭空造出新商品;只排列同一件的多个副本 return ( "Arrange several identical copies of the product in a neat equal grid (2-4 per row) with a small " "label chip below each copy; identical lighting and scale across copies. Every copy shows this " "exact product — do not invent other colorways or variants." ) _TASK_BUILDERS = { "white_bg": (_task_white_bg, False), "key_features": (_task_key_features, True), "selling_pt": (_task_selling_pt, True), "material": (_task_material, True), "lifestyle": (_task_lifestyle, True), "multi_scene": (_task_multi_scene, True), "ecommerce_detail": (_task_ecommerce_detail, True), "size_chart": (_task_size_chart, True), "sku_collection": (_task_sku_collection, True), } def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict | None = None, style_prompt: str | None = None, requirements: str | None = None) -> str: """构造指定图类型的 prompt:要求块 → 指令 → 主体锁 → 锚定 → 风格 → 文案 → 画质 → 提醒。""" style = resolve_style(style_set, style_prompt) extra = extra or {} 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: entry = _TASK_BUILDERS.get(type_id) if entry is None: raise ValueError(f"未知图类型: {type_id}") builder, wants_text = entry task = builder(ctx, lang) if hint: task += f" Composition guidance: {hint}." parts = [p for p in (requirements_block(requirements),) if p] parts.append(task) parts.append(_SUBJECT_LOCK) parts.append(_anchor(ctx)) parts.append(f"Scene style (scene and background only, never the product): {style['tone']}.") if wants_text: parts.append(f"Text overlay (a graphic layer, never printed on the product): {TEXT_RENDER[lang]}") parts.append(_QUALITY) parts.append(_REMINDER) return "\n\n".join(parts)