Spaces:
Sleeping
Sleeping
| from typing import List, Literal, Optional | |
| from pydantic import BaseModel, Field | |
| from openai import OpenAI | |
| from dotenv import load_dotenv | |
| import os | |
| load_dotenv() | |
| client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) | |
| class VisualHierarchy(BaseModel): | |
| flow_summary: str = Field(..., description="Brief of top-to-bottom reading flow and funneling.") | |
| key_elements_order: List[str] = Field(..., description="Ordered list of elements as the eye flows.") | |
| class TypographySpec(BaseModel): | |
| casing: Literal["all-caps","title-case","sentence-case","mixed"] | |
| weight: Literal["light","regular","medium","bold","extra-bold"] | |
| color: str | |
| notes: str | |
| class CTAInfo(BaseModel): | |
| text: str | |
| shape_or_style: str | |
| bg_color: str | |
| text_color: str | |
| contrast_comment: str | |
| class VisualStructureLayout(BaseModel): | |
| background_color: str | |
| psychological_association: str | |
| fit_to_theme_comment: str | |
| typography: TypographySpec | |
| minimalism_level: Literal["none","low","medium","high"] | |
| cta: CTAInfo | |
| hierarchy: VisualHierarchy | |
| class CopywritingBreakdown(BaseModel): | |
| headline: str | |
| device_used: List[Literal["curiosity_gap","authority","fear_avoidance","education","urgency","social_proof","other"]] | |
| supporting_text: str | |
| compliance_comment: str | |
| cta_text: str | |
| funnel_feel: Literal["content_recommendation","direct_response","hybrid"] | |
| class PsychologicalBehavioralTriggers(BaseModel): | |
| curiosity: str | |
| fear_or_pain_point: str | |
| authority_appeal: str | |
| ease_of_action: str | |
| class StoryCreativeTells(BaseModel): | |
| problem: str | |
| hope: str | |
| next_step: str | |
| narrative_summary: str | |
| class StrengthWeaknessItem(BaseModel): | |
| point: str | |
| why_it_matters: str | |
| class RisksArbitrageContext(BaseModel): | |
| bounce_risk: str | |
| fatigue_risk: str | |
| compliance_risk: str | |
| class CreativeVariant(BaseModel): | |
| hypothesis: str | |
| change: str | |
| expected_effect: str | |
| metric_to_watch: Literal["CTR","CPC","CPI","CVR","RPM","BounceRate","TimeOnPage","QualityRanking","Other"] | |
| class FunnelSynergy(BaseModel): | |
| landing_page_angle: str | |
| affiliate_or_offer_fit: Optional[str] = None | |
| class TestingPlan(BaseModel): | |
| primary_metrics: List[Literal["CTR","CPC","CVR","RPM","BounceRate","TimeOnPage","QualityRanking","Other"]] | |
| sample_size_or_duration_hint: str | |
| stop_loss_rules: str | |
| class OptimizationNextSteps(BaseModel): | |
| creative_variants: List[CreativeVariant] | |
| funnel_synergy: FunnelSynergy | |
| testing_plan: TestingPlan | |
| class MetaDetect(BaseModel): | |
| language: str | |
| detected_topic: str | |
| confidence_0to1: float = Field(ge=0, le=1) | |
| red_flags: List[str] = Field(..., description="Potential policy or brand-safety flags, if any.") | |
| class AdAnalysis(BaseModel): | |
| visual_structure_layout: VisualStructureLayout | |
| copywriting_breakdown: CopywritingBreakdown | |
| psychological_behavioral_triggers: PsychologicalBehavioralTriggers | |
| story_creative_tells: StoryCreativeTells | |
| strengths: List[StrengthWeaknessItem] | |
| weaknesses: List[StrengthWeaknessItem] | |
| risks_in_arbitrage_context: RisksArbitrageContext | |
| optimization_next_steps: OptimizationNextSteps | |
| meta: MetaDetect | |
| SYSTEM_PROMPT = """ | |
| You are a senior performance-creative analyst who analyze a SINGLE image ad. Do not include explanations, markdown, or extra keys. | |
| Rules: | |
| - Be precise and concise. Avoid generic fluff. | |
| - When unsure, infer cautiously and note uncertainty. | |
| - Explain “why it matters” in business terms (CTR, CPC, CVR, RPM, bounce, fatigue, policy). | |
| - Do not invent medical claims or promises; flag policy risks instead. | |
| - Keep color names human-readable (e.g., “pure orange”, “purple”, “yellow”). | |
| """ | |
| USER_PROMPT_TEMPLATE = """ | |
| Task: Analyze the attached image ad and produce a structured breakdown: | |
| 1) Visual structure & layout | |
| 2) Copywriting breakdown | |
| 3) Psychological & behavioral triggers | |
| 4) Story the creative tells | |
| 5) Strengths | |
| 6) Weaknesses | |
| 7) Risks in arbitrage context | |
| 8) Optimization / next steps (variants, funnel synergy, testing plan) | |
| Constraints: | |
| - Use crisp, decision-ready language. | |
| - If text is in Spanish or another language, keep your summaries in English but preserve original CTA text. | |
| """ | |
| def get_analysis(img_url): | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": [ | |
| { | |
| "type": "text", | |
| "text": SYSTEM_PROMPT | |
| } | |
| ] | |
| }, | |
| { | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "text", | |
| "text": USER_PROMPT_TEMPLATE | |
| }, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": img_url | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| completion = client.chat.completions.parse( | |
| model="gpt-4o", | |
| messages=messages, | |
| response_format=AdAnalysis, | |
| ) | |
| ad_analysis = completion.choices[0].message.parsed | |
| return ad_analysis | |