|
|
""" |
|
|
Creative Inventor: generates new ad angles, concepts, visuals, and psychological triggers by itself. |
|
|
Can run standalone (ideas for review/export) or feed into the extensive flow. |
|
|
""" |
|
|
|
|
|
from typing import List, Optional |
|
|
|
|
|
from openai import OpenAI |
|
|
from pydantic import BaseModel |
|
|
|
|
|
from config import settings |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InventedEssential(BaseModel): |
|
|
"""One invented creative essential: trigger + angles + concepts + visual directions + hyper-specific audience.""" |
|
|
psychology_trigger: str |
|
|
angles: List[str] |
|
|
concepts: List[str] |
|
|
visual_directions: List[str] |
|
|
hooks: List[str] = [] |
|
|
visual_styles: List[str] = [] |
|
|
target_audience: str = "" |
|
|
|
|
|
|
|
|
class InventedCreativeOutput(BaseModel): |
|
|
"""Structured output from the creative inventor.""" |
|
|
output: List[InventedEssential] |
|
|
|
|
|
|
|
|
class CreativeInventorService: |
|
|
""" |
|
|
Invents new ad angles, concepts, visuals, and psychological triggers. |
|
|
Use standalone to generate ideas, or feed output into the extensive flow. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
self.client = OpenAI(api_key=settings.openai_api_key) |
|
|
self.model = getattr(settings, "third_flow_model", "gpt-4o") |
|
|
|
|
|
def invent( |
|
|
self, |
|
|
niche: str, |
|
|
offer: str, |
|
|
*, |
|
|
target_audience_hint: Optional[str] = None, |
|
|
existing_reference: Optional[str] = None, |
|
|
trend_context: Optional[str] = None, |
|
|
competitor_insights: Optional[str] = None, |
|
|
n: int = 5, |
|
|
) -> List[InventedEssential]: |
|
|
""" |
|
|
Invent new psychological triggers, angles, concepts, visual directions, |
|
|
hooks, visual styles, and a hyper-specific target_audience per essential. |
|
|
The AI decides all target audiences; they must be different types (demographic, psychographic, situation). |
|
|
|
|
|
Args: |
|
|
niche: e.g. "GLP-1", "Home Insurance" |
|
|
offer: what is being promoted |
|
|
target_audience_hint: optional broad hint (e.g. "weight loss"); AI still invents distinct hyper-specific audiences per essential |
|
|
existing_reference: optional summary of existing strategies (to diverge or avoid repeating) |
|
|
trend_context: optional trending topics or occasions |
|
|
competitor_insights: optional notes on what competitors are doing |
|
|
n: number of invented "essentials" to return (each with its own hyper-specific audience) |
|
|
|
|
|
Returns: |
|
|
List of InventedEssential for use in extensive flow or export. |
|
|
""" |
|
|
system_prompt = """You are a creative strategist who invents NEW ad angles, concepts, visuals, psychological triggers, and HYPER-SPECIFIC target audiences for performance and affiliate marketing. |
|
|
|
|
|
Your job is to CREATE novel ideas—not to recycle generic ones. For each "essential" you produce: |
|
|
- target_audience: ONE hyper-specific audience for this essential. You MUST invent a different type of audience for each essential. Examples of hyper-specific audiences: "Women 45–55 who tried Ozempic and stalled", "Men 35–45 with 30+ lbs to lose who hate gyms", "Busy moms who yo-yo diet and have given up on willpower", "Women 50+ who've tried everything and feel it's biology not laziness", "Professionals who travel constantly and want no-fuss delivery", "Skeptics who think GLP-1 is a scam and need proof". Mix demographics, psychographics, and situations. No generic "people interested in X". |
|
|
- Psychology trigger: the emotional or cognitive lever (e.g. validation before solution, future regret, justified scarcity, objection-first then flip). |
|
|
- Angles: the reasons this specific audience should care; reframe the problem or outcome in a fresh way. |
|
|
- Concepts: the creative execution or storyline (e.g. crossed-out negative labels, progress bar + social proof, two-path fork). |
|
|
- Visual directions: concrete visual ideas (e.g. "progress bar 87% full with 10k counter", "grid of crossed-out DAY 1s"). |
|
|
- Hooks: 2–4 scroll-stopping headline phrases for that essential and audience. |
|
|
- Visual styles: 2–4 short scene/prompt-style descriptions for image generation. |
|
|
|
|
|
Rules: |
|
|
- Each essential MUST have a different target_audience. Vary types: age/gender, life situation, mindset, past failures, skepticism, convenience-seekers, etc. |
|
|
- Prioritize novelty and specificity. Avoid vague angles or generic audiences. |
|
|
- Combine triggers in new ways. Invent visual metaphors that work for the chosen audience. |
|
|
- Output exactly the number of "essentials" requested (n). Each essential should feel distinct and speak to a distinctly different audience.""" |
|
|
|
|
|
user_parts = [ |
|
|
f"Niche: {niche}", |
|
|
f"Offer: {offer}", |
|
|
f"Invent exactly {n} different creative essentials. For EACH essential you MUST set a different hyper-specific target_audience (demographic, psychographic, or situation). Then set psychology_trigger, angles, concepts, visual_directions, hooks, visual_styles for that audience.", |
|
|
] |
|
|
if target_audience_hint: |
|
|
user_parts.append(f"Broad category hint (invent specific segments within or related to this): {target_audience_hint}") |
|
|
if existing_reference: |
|
|
user_parts.append(f"Existing strategies to diverge from (do not copy):\n{existing_reference}") |
|
|
if trend_context: |
|
|
user_parts.append(f"Trend / occasion context (use for relevance):\n{trend_context}") |
|
|
if competitor_insights: |
|
|
user_parts.append(f"Competitor / market insights (use to differentiate):\n{competitor_insights}") |
|
|
|
|
|
user_content = "\n\n".join(user_parts) |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
completion = self.client.beta.chat.completions.parse( |
|
|
model=self.model, |
|
|
messages=[ |
|
|
{"role": "system", "content": system_prompt}, |
|
|
{"role": "user", "content": user_content}, |
|
|
], |
|
|
response_format=InventedCreativeOutput, |
|
|
) |
|
|
response = completion.choices[0].message |
|
|
if response.parsed and response.parsed.output: |
|
|
return response.parsed.output[:n] |
|
|
return [] |
|
|
except Exception as e: |
|
|
print(f"CreativeInventor error: {e}") |
|
|
return [] |
|
|
|
|
|
def invent_and_export( |
|
|
self, |
|
|
niche: str, |
|
|
offer: str, |
|
|
essentials: Optional[List["InventedEssential"]] = None, |
|
|
target_audience_hint: Optional[str] = None, |
|
|
**kwargs, |
|
|
) -> str: |
|
|
""" |
|
|
Invent (if essentials not provided) and return a human-readable text summary for export or review. |
|
|
""" |
|
|
if essentials is None: |
|
|
essentials = self.invent(niche=niche, offer=offer, target_audience_hint=target_audience_hint, **kwargs) |
|
|
lines = [ |
|
|
f"# Invented creatives — {niche}" + (f" | {target_audience_hint}" if target_audience_hint else ""), |
|
|
"", |
|
|
] |
|
|
for i, e in enumerate(essentials, 1): |
|
|
aud = getattr(e, "target_audience", "") or "" |
|
|
lines.append(f"## Essential {i}: {e.psychology_trigger}" + (f" — {aud}" if aud else "")) |
|
|
lines.append("") |
|
|
if aud: |
|
|
lines.append("**Target audience:** " + aud) |
|
|
lines.append("") |
|
|
lines.append("**Angles:** " + " | ".join(e.angles)) |
|
|
lines.append("**Concepts:** " + " | ".join(e.concepts)) |
|
|
lines.append("**Visual directions:** " + " | ".join(e.visual_directions)) |
|
|
if e.hooks: |
|
|
lines.append("**Hooks:** " + " | ".join(e.hooks)) |
|
|
if e.visual_styles: |
|
|
lines.append("**Visual styles:** " + " | ".join(e.visual_styles)) |
|
|
lines.append("") |
|
|
return "\n".join(lines) |
|
|
|
|
|
|
|
|
|
|
|
creative_inventor_service = CreativeInventorService() |
|
|
|