File size: 8,311 Bytes
026f283 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | """
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
# ---------------------------------------------------------------------------
# Output models: what the inventor produces
# ---------------------------------------------------------------------------
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] = [] # Optional scroll-stopping headlines
visual_styles: List[str] = [] # Optional scene/prompt-style descriptions
target_audience: str = "" # Hyper-specific audience for this essential (AI-decided, different per essential)
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)
# Ensure model returns target_audience; fallback for older parsed outputs
# (InventedEssential now has target_audience with default "")
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)
# Singleton for use from third_flow / generator
creative_inventor_service = CreativeInventorService()
|