"""AI engine — Gemini for copy, Nano Banana / Nano Banana Pro for imagery. Models used: Text: gemini-2.5-flash — all copy generation Image: gemini-2.5-flash-image — Nano Banana (fast lifestyle shots) Image: gemini-3-pro-image-preview — Nano Banana Pro (premium hero/product shots) Powers: Demo A: generate_asset_pack() — 1 product → 12 marketing assets Demo B: competitor_xray() — competitor URL → analysis + JV upgrade Demo C: pdp_surgeon() — existing copy → style variants PDP: optimise_pdp_copy() — full PDP rewrite Images: generate_all_images() — Nano Banana product imagery """ import os, json, hashlib, re from pathlib import Path from google import genai from google.genai import types GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "") client = genai.Client(api_key=GEMINI_KEY) if GEMINI_KEY else None GEN_DIR = Path(__file__).parent / "generated" GEN_DIR.mkdir(exist_ok=True) TEXT_MODEL = "gemini-2.5-flash" IMG_FAST = "gemini-2.5-flash-image" # Nano Banana IMG_PRO = "gemini-3-pro-image-preview" # Nano Banana Pro def _call_gemini(prompt: str, temperature: float = 0.7) -> dict: """Call Gemini text model, return parsed JSON.""" if not client: return {"error": "GEMINI_API_KEY not configured"} response = client.models.generate_content( model=TEXT_MODEL, contents=prompt, config=types.GenerateContentConfig( temperature=temperature, response_mime_type="application/json", ), ) try: return json.loads(response.text) except json.JSONDecodeError: match = re.search(r'\{.*\}', response.text, re.DOTALL) if match: return json.loads(match.group()) return {"error": "Failed to parse AI response", "raw": response.text[:500]} def _generate_image(prompt: str, model: str = IMG_PRO) -> tuple: """Generate image via Nano Banana. Returns (filename, mime_type) or ('','').""" if not client: return ("", "") cache_key = hashlib.md5(f"{model}:{prompt}".encode()).hexdigest()[:14] # Check cache for ext in ("png", "jpg", "jpeg"): cached = GEN_DIR / f"{cache_key}.{ext}" if cached.exists(): return (cached.name, f"image/{ext}") try: response = client.models.generate_content( model=model, contents=prompt, config=types.GenerateContentConfig( response_modalities=["IMAGE", "TEXT"], ), ) for part in response.candidates[0].content.parts: if part.inline_data and part.inline_data.data: mime = part.inline_data.mime_type or "image/png" ext = "jpg" if "jpeg" in mime or "jpg" in mime else "png" filename = f"{cache_key}.{ext}" (GEN_DIR / filename).write_bytes(part.inline_data.data) return (filename, mime) except Exception as e: print(f"[img-gen] {model} error: {e}") return ("", "") # ═══════════════════════════════════════════════════════════════ # DEMO A — One Product → 12 Assets # ═══════════════════════════════════════════════════════════════ def generate_asset_pack(product: dict) -> dict: prompt = f"""You are a world-class ecommerce copywriter and marketing strategist for Just Vitamins (justvitamins.co.uk), a trusted UK vitamin brand — 4.8★ Trustpilot, 230,000+ customers, 20 years trading. PRODUCT DATA: - Title: {product.get('title','')} - Subtitle: {product.get('subtitle','')} - Price: {product.get('price','')} for {product.get('quantity','')} - Per unit: {product.get('per_unit_cost','')} - Benefits: {json.dumps(product.get('benefits',[]))} - Description: {product.get('description','')[:1500]} - EFSA Health Claims: {json.dumps(product.get('health_claims',[]))} - Category: {product.get('category','')} Generate a COMPLETE 12-asset marketing pack. Be specific to THIS product. Return JSON: {{ "hero_angles": [ {{"headline":"…","target_desire":"…","best_for":"…"}}, {{"headline":"…","target_desire":"…","best_for":"…"}}, {{"headline":"…","target_desire":"…","best_for":"…"}} ], "pdp_copy": {{ "headline":"…", "bullets":["…","…","…","…","…"], "faq":[{{"q":"…","a":"…"}},{{"q":"…","a":"…"}},{{"q":"…","a":"…"}}] }}, "ad_hooks":["…","…","…","…","…"], "email_subjects":[ {{"subject":"…","preview":"…"}}, {{"subject":"…","preview":"…"}}, {{"subject":"…","preview":"…"}} ], "tiktok_script":{{ "title":"…", "hook_0_3s":"…", "body_3_12s":"…", "cta_12_15s":"…", "why_it_works":"…" }}, "blog_outline":{{ "title":"…", "sections":["…","…","…","…","…"], "seo_keyword":"…", "monthly_searches":"…" }}, "meta_seo":{{ "title":"…","description":"…","title_chars":0,"desc_chars":0 }}, "alt_text":[ {{"image_type":"Hero product shot","alt":"…","filename":"…"}}, {{"image_type":"Lifestyle image","alt":"…","filename":"…"}} ], "ab_variants":[ {{"label":"Rational","copy":"…"}}, {{"label":"Emotional","copy":"…"}}, {{"label":"Social Proof","copy":"…"}} ] }} RULES: EFSA claims must be accurate. Email subjects <50 chars. Meta title <60 chars, description <160 chars. UK English.""" return _call_gemini(prompt, 0.75) # ═══════════════════════════════════════════════════════════════ # DEMO B — Competitor X-Ray # ═══════════════════════════════════════════════════════════════ def competitor_xray(competitor_data: dict) -> dict: prompt = f"""You are a competitive intelligence analyst for Just Vitamins (justvitamins.co.uk) — trusted UK supplement brand, 20 yrs, 4.8★ Trustpilot, 230K+ customers, eco bio-pouch packaging. COMPETITOR PAGE: - URL: {competitor_data.get('url','')} - Title: {competitor_data.get('title','')} - Brand: {competitor_data.get('brand','')} - Price: {competitor_data.get('price','')} - Meta: {competitor_data.get('meta_description','')} - Description: {competitor_data.get('description','')[:2000]} - Bullets: {json.dumps(competitor_data.get('bullets',[])[:10])} - Page extract: {competitor_data.get('raw_text','')[:2000]} Perform a deep competitive analysis. Output JSON: {{ "competitor_name":"…", "what_theyre_selling":"One sentence — what they're REALLY selling (emotional promise, not product)", "top_5_tactics":[ {{"tactic":"…","explanation":"…"}}, {{"tactic":"…","explanation":"…"}}, {{"tactic":"…","explanation":"…"}}, {{"tactic":"…","explanation":"…"}}, {{"tactic":"…","explanation":"…"}} ], "weakest_claim":"Their most vulnerable claim / biggest gap", "jv_hero_section":{{ "headline":"Killer headline positioning JV as better", "body":"2-3 sentences of copy that beats them without naming them", "value_prop":"Single most powerful reason to choose JV" }}, "differentiators":[ {{"point":"…","proof_idea":"Specific content or test idea to prove it"}}, {{"point":"…","proof_idea":"…"}}, {{"point":"…","proof_idea":"…"}} ], "do_not_say":["Compliance note 1","…","…","…"] }} RULES: No false claims. EFSA/ASA compliant. Strategic, not aggressive.""" return _call_gemini(prompt, 0.7) # ═══════════════════════════════════════════════════════════════ # DEMO C — PDP Surgeon # ═══════════════════════════════════════════════════════════════ STYLE_INSTRUCTIONS = { "balanced": "Balanced, trustworthy DTC supplement voice. Mix emotional hooks with rational proof.", "premium": "Premium aspirational voice. Sophisticated language, formulation science, target affluent buyers.", "dr": "Direct-response style. Pattern interrupts, urgency, specific numbers, scarcity, stacked bonuses.", "medical": "Clinical, medically-safe tone. Proper nomenclature, structure/function claims only, FDA disclaimer.", } def pdp_surgeon(product: dict, style: str = "balanced") -> dict: instruction = STYLE_INSTRUCTIONS.get(style, STYLE_INSTRUCTIONS["balanced"]) prompt = f"""You are a PDP conversion specialist rewriting a product page for Just Vitamins. PRODUCT: - Title: {product.get('title','')} - Subtitle: {product.get('subtitle','')} - Price: {product.get('price','')} for {product.get('quantity','')} - Benefits: {json.dumps(product.get('benefits',[]))} - Description: {product.get('description','')[:1500]} - EFSA Claims: {json.dumps(product.get('health_claims',[]))} STYLE: {style.upper()} — {instruction} Rewrite the PDP. For EVERY change add a conversion annotation with estimated % impact. Output JSON: {{ "style":"{style}", "title":"…", "subtitle":"…", "hero_copy":"Main persuasion paragraph", "hero_annotation":"Why this works — conversion impact", "bullets":[ {{"text":"…","annotation":"Why — e.g. +22% add-to-cart"}}, {{"text":"…","annotation":"…"}}, {{"text":"…","annotation":"…"}}, {{"text":"…","annotation":"…"}} ], "social_proof":"Line using 4.8★ Trustpilot, 230K customers", "social_proof_annotation":"…", "price_reframe":"Reframe price as no-brainer", "price_annotation":"…", "cta_text":"CTA button text", "usage_instruction":"How to take — written to remove friction", "usage_annotation":"…" }} RULES: EFSA claims accurate. Realistic % lifts (5-40%). UK English.""" return _call_gemini(prompt, 0.8) # ═══════════════════════════════════════════════════════════════ # FULL PDP OPTIMISATION # ═══════════════════════════════════════════════════════════════ def optimise_pdp_copy(product: dict) -> dict: prompt = f"""You are an expert ecommerce copywriter for Just Vitamins (justvitamins.co.uk) — 4.8★ Trustpilot, 230K+ customers, 20 years. PRODUCT: - Title: {product['title']} - Subtitle: {product.get('subtitle','')} - Price: {product.get('price','')} for {product.get('quantity','')} - Per unit: {product.get('per_unit_cost','')} - Benefits: {json.dumps(product.get('benefits',[]))} - Description: {product.get('description','')[:1500]} - EFSA Claims: {json.dumps(product.get('health_claims',[]))} - Category: {product.get('category','')} Rewrite everything. Output JSON: {{ "seo_title":"…", "subtitle":"…", "benefit_bullets":["…","…","…","…","…"], "why_section":"Para 1\\n\\nPara 2\\n\\nPara 3", "who_for":["…","…","…"], "social_proof":"…", "meta_description":"…", "faqs":[{{"q":"…","a":"…"}},{{"q":"…","a":"…"}},{{"q":"…","a":"…"}}] }} Keep EFSA claims accurate. UK English.""" return _call_gemini(prompt, 0.7) # ═══════════════════════════════════════════════════════════════ # IMAGE GENERATION — Nano Banana / Pro # ═══════════════════════════════════════════════════════════════ def generate_product_images(product: dict) -> dict: """Generate product images using Nano Banana (fast) + Nano Banana Pro (hero).""" title = product.get("title", "vitamin supplement") category = product.get("category", "vitamins") results = {} # Hero — use Nano Banana Pro for best quality hero_prompt = ( f"Professional product hero photograph for an ecommerce page. " f"A premium eco-friendly kraft bio-pouch supplement packaging for '{title}' " f"by Just Vitamins UK. Clean cream/white background, soft natural shadows, " f"minimalist staging with a small ceramic dish of capsules/tablets beside it " f"and a sprig of green herb. Premium, trustworthy, modern. " f"Commercial product photography, sharp focus, studio lighting." ) fname, mime = _generate_image(hero_prompt, IMG_PRO) results["hero"] = {"filename": fname, "mime": mime, "model": "Nano Banana Pro"} # Lifestyle — Nano Banana (fast) lifestyle_prompt = ( f"Lifestyle product photograph: '{title}' by Just Vitamins UK in an eco " f"bio-pouch sitting on a marble kitchen counter with morning sunlight " f"streaming through a window. Fresh fruit, a glass of water, and green " f"leaves nearby. Warm, healthy, inviting mood. Shallow depth of field. " f"Photorealistic, 4K quality." ) fname, mime = _generate_image(lifestyle_prompt, IMG_FAST) results["lifestyle"] = {"filename": fname, "mime": mime, "model": "Nano Banana"} # Benefits — Nano Banana Pro benefits_prompt = ( f"Clean infographic-style illustration showing health benefits of " f"{category} supplements: strong bones, immune support, energy, vitality. " f"Modern flat design, warm gold/green/cream palette. " f"Professional, suitable for a premium UK health brand website. No text." ) fname, mime = _generate_image(benefits_prompt, IMG_PRO) results["benefits"] = {"filename": fname, "mime": mime, "model": "Nano Banana Pro"} return results