Unterminated string starting at: line 158 column 25 (char 22024)
```json
{
"plan_name": "AI Creative Video Analysis for SubSun Sunglasses",
"estimated_duration_minutes": 45,
"methodology_notes": "Multi-phase analysis focusing on video creative identification, competitor video strategy patterns, and AI-friendly creative frameworks. Limited by inability to analyze video content directly, but can correlate video usage with ad metadata, performance proxies, and timing patterns to identify successful video creative strategies.",
"steps": [
{
"id": "step_1",
"type": "data_fetch",
"name": "Fetch all competitor ads data",
"depends_on": [],
"params": {
"table": "competitor_ads",
"columns": ["ad_archive_id", "page_name", "primary_text", "headline", "description", "creative_type", "cta_type", "ad_delivery_start_time", "ad_delivery_stop_time", "is_currently_active", "eu_total_reach", "target_ages", "target_gender"],
"filters": {},
"limit": 25000
}
},
{
"id": "step_2",
"type": "data_fetch",
"name": "Fetch competitor pages for context",
"depends_on": [],
"params": {
"table": "competitor_pages",
"columns": ["page_id", "page_name", "category", "country_code"],
"filters": {},
"limit": 100
}
},
{
"id": "step_3",
"type": "data_fetch",
"name": "Fetch ad daily performance data",
"depends_on": [],
"params": {
"table": "competitor_ad_daily",
"columns": ["ad_archive_id", "check_date", "is_active", "eu_total_reach"],
"filters": {},
"limit": 300000
}
},
{
"id": "step_4",
"type": "storage_list",
"name": "List all video creative files",
"d
```json
{
"plan_name": "AI Creative Video Analysis for SubSun Sunglasses",
"estimated_duration_minutes": 45,
"methodology_notes": "Multi-phase analysis focusing on video creative identification, competitor video strategy patterns, and AI-friendly creative frameworks. Limited by inability to analyze video content directly, but can correlate video usage with ad metadata, performance proxies, and timing patterns to identify successful video creative strategies.",
"steps": [
{
"id": "step_1",
"type": "data_fetch",
"name": "Fetch all competitor ads data",
"depends_on": [],
"params": {
"table": "competitor_ads",
"columns": ["ad_archive_id", "page_name", "primary_text", "headline", "description", "creative_type", "cta_type", "ad_delivery_start_time", "ad_delivery_stop_time", "is_currently_active", "eu_total_reach", "target_ages", "target_gender"],
"filters": {},
"limit": 25000
}
},
{
"id": "step_2",
"type": "data_fetch",
"name": "Fetch competitor pages for context",
"depends_on": [],
"params": {
"table": "competitor_pages",
"columns": ["page_id", "page_name", "category", "country_code"],
"filters": {},
"limit": 100
}
},
{
"id": "step_3",
"type": "data_fetch",
"name": "Fetch ad daily performance data",
"depends_on": [],
"params": {
"table": "competitor_ad_daily",
"columns": ["ad_archive_id", "check_date", "is_active", "eu_total_reach"],
"filters": {},
"limit": 300000
}
},
{
"id": "step_4",
"type": "storage_list",
"name": "List all video creative files",
"depends_on": [],
"params": {
"all_competitors": true,
"video_only": true
}
},
{
"id": "step_5",
"type": "python_compute",
"name": "Identify video ads and merge with metadata",
"depends_on": ["step_1", "step_4"],
"params": {
"description": "Cross-reference video files with ad metadata to identify which ads have video creative",
"code": "import pandas as pd\nimport re\nfrom collections import Counter\n\n# Extract ad_archive_id from video files\nvideo_ads = competitor_media[competitor_media['is_video'] == True].copy()\nvideo_ads['extracted_id'] = video_ads['ad_archive_id']\n\n# Merge video file data with ad metadata\nvideo_ad_data = competitor_ads.merge(video_ads[['ad_archive_id', 'public_url', 'size_bytes', 'folder']], \n left_on='ad_archive_id', right_on='ad_archive_id', how='inner')\n\nprint(f\"Total ads in database: {len(competitor_ads)}\")\nprint(f\"Video files found: {len(video_ads)}\")\nprint(f\"Ads with video creative matched: {len(video_ad_data)}\")\nprint(f\"\\nTop competitors by video ad count:\")\nvideo_counts = video_ad_data['page_name'].value_counts().head(10)\nprint(video_counts.to_string())\n\nvideo_percentage = len(video_ad_data) / len(competitor_ads) * 100\nprint(f\"\\nVideo adoption rate: {video_percentage:.1f}% of all ads\")"
}
},
{
"id": "step_6",
"type": "python_compute",
"name": "Analyze video vs static ad performance proxies",
"depends_on": ["step_1", "step_3", "step_5"],
"params": {
"description": "Compare longevity and reach patterns between video and static ads",
"code": "import pandas as pd\nimport numpy as np\nfrom datetime import datetime\n\n# Calculate ad longevity for all ads\ncompetitor_ads['start_date'] = pd.to_datetime(competitor_ads['ad_delivery_start_time'])\ncompetitor_ads['stop_date'] = pd.to_datetime(competitor_ads['ad_delivery_stop_time'])\ncompetitor_ads['longevity_days'] = (competitor_ads['stop_date'] - competitor_ads['start_date']).dt.days\n\n# Mark ads as video or static\ncompetitor_ads['has_video'] = competitor_ads['ad_archive_id'].isin(video_ad_data['ad_archive_id'])\n\n# Compare performance proxies\nperformance_comparison = competitor_ads.groupby('has_video').agg({\n 'longevity_days': ['mean', 'median', 'count'],\n 'eu_total_reach': ['mean', 'median'],\n 'ad_archive_id': 'count'\n}).round(2)\n\nprint(\"Performance Comparison - Video vs Static Ads:\")\nprint(performance_comparison.to_string())\n\n# Look at current active status\nactive_comparison = competitor_ads.groupby(['has_video', 'is_currently_active']).size().unstack(fill_value=0)\nprint(\"\\nActive Status Distribution:\")\nprint(active_comparison.to_string())\n\nvideo_stats = competitor_ads[competitor_ads['has_video'] == True]\nstatic_stats = competitor_ads[competitor_ads['has_video'] == False]\n\nprint(f\"\\nVideo ads average longevity: {video_stats['longevity_days'].mean():.1f} days\")\nprint(f\"Static ads average longevity: {static_stats['longevity_days'].mean():.1f} days\")"
}
},
{
"id": "step_7",
"type": "chart",
"name": "Video adoption by competitor visualization",
"depends_on": ["step_5"],
"params": {
"description": "Create bar chart showing video ad volume by top competitors",
"code": "import matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Get top 12 competitors by video count\ntop_video_competitors = video_ad_data['page_name'].value_counts().head(12)\n\nfig, ax = plt.subplots(figsize=(12, 8))\nbars = ax.bar(range(len(top_video_competitors)), top_video_competitors.values, \n color='#ff6b35', alpha=0.8)\n\n# Customize the chart\nax.set_xlabel('Competitors', fontsize=12, color='white')\nax.set_ylabel('Number of Video Ads', fontsize=12, color='white')\nax.set_title('Video Ad Volume by Competitor\\n(Eyewear/Fashion Brands)', fontsize=14, color='white', pad=20)\n\n# Set x-axis labels\nax.set_xticks(range(len(top_video_competitors)))\nax.set_xticklabels(top_video_competitors.index, rotation=45, ha='right', color='white')\nax.tick_params(colors='white')\n\n# Add value labels on bars\nfor i, bar in enumerate(bars):\n height = bar.get_height()\n ax.text(bar.get_x() + bar.get_width()/2., height + 1,\n f'{int(height)}', ha='center', va='bottom', color='white', fontsize=10)\n\n# Style the chart\nax.spines['top'].set_visible(False)\nax.spines['right'].set_visible(False)\nax.spines['bottom'].set_color('white')\nax.spines['left'].set_color('white')\nax.grid(True, alpha=0.3, color='white')\n\nplt.tight_layout()\nsave_chart(fig, 'video_adoption_by_competitor.png')\nplt.close()"
}
},
{
"id": "step_8",
"type": "python_compute",
"name": "Analyze video ad copy patterns and themes",
"depends_on": ["step_5"],
"params": {
"description": "Extract patterns from video ad headlines and primary text to identify 'fun AI creative' themes",
"code": "import pandas as pd\nimport re\nfrom collections import Counter\n\n# Analyze headlines for patterns\nvideo_headlines = video_ad_data['headline'].dropna().str.lower()\nvideo_primary_text = video_ad_data['primary_text'].dropna().str.lower()\n\n# Keywords that suggest 'fun' or creative content\nfun_keywords = ['new', 'style', 'collection', 'fresh', 'cool', 'perfect', 'amazing', 'stunning', \n 'love', 'discover', 'explore', 'magic', 'transform', 'elevate', 'vibe', 'mood']\n\nai_creative_keywords = ['personalized', 'custom', 'unique', 'individual', 'personal', 'tailored',\n 'smart', 'intelligent', 'adaptive', 'dynamic', 'interactive']\n\n# Count keyword occurrences in headlines\nheadline_keywords = []\nfor headline in video_headlines:\n if pd.notna(headline):\n words = re.findall(r'\\b\\w+\\b', str(headline))\n headline_keywords.extend(words)\n\nheadline_counter = Counter(headline_keywords)\nprint(\"Top words in video ad headlines:\")\nprint(dict(headline_counter.most_common(20)))\n\n# Analyze fun keywords\nfun_matches = sum(1 for keyword in fun_keywords if keyword in ' '.join(headline_keywords))\nprint(f\"\\nFun-related keywords found: {fun_matches}\")\n\n# Look at CTAs\ncta_analysis = video_ad_data['cta_type'].value_counts()\nprint(\"\\nCall-to-Action types in video ads:\")\nprint(cta_analysis.to_string())\n\n# Sample some actual headlines for context\nprint(\"\\nSample video ad headlines:\")\nsample_headlines = video_ad_data['headline'].dropna().head(10)\nfor i, headline in enumerate(sample_headlines, 1):\n print(f\"{i}. {headline}\")"
}
},
{
"id": "step_9",
"type": "llm_synthesis",
"name": "Synthesize video creative insights",
"depends_on": ["step_5", "step_6", "step_8"],
"params": {
"prompt_template": "Based on the video ad analysis data, provide insights for SubSun about video creative opportunities:\n\nVideo Adoption Data:\n{{step_5.stdout}}\n\nPerformance Comparison:\n{{step_6.stdout}}\n\nContent Analysis:\n{{step_8.stdout}}\n\nWhat are the key takeaways about video creative strategy in the eyewear/sunglasses market? Focus on practical recommendations for SubSun's 'fun AI creative' initiative."
}
},
{
"id": "step_10",
"type": "python_compute",
"name": "Identify seasonal and timing patterns in video ads",
"depends_on": ["step_5"],
"params": {
"description": "Analyze when video ads are launched and their seasonal patterns",
"code": "import pandas as pd\nimport matplotlib.pyplot as plt\nfrom datetime import datetime\n\n# Convert dates and extract timing info\nvideo_ad_data['launch_date'] = pd.to_datetime(video_ad_data['ad_delivery_start_time'])\nvideo_ad_data['launch_month'] = video_ad_data['launch_date'].dt.month\nvideo_ad_data['launch_year'] = video_ad_data['launch_date'].dt.year\nvideo_ad_data['launch_quarter'] = video_ad_data['launch_date'].dt.quarter\n\n# Monthly distribution\nmonthly_launches = video_ad_data['launch_month'].value_counts().sort_index()\nprint(\"Video ad launches by month:\")\nmonth_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\nfor month, count in monthly_launches.items():\n print(f\"{month_names[month-1]}: {count}\")\n\n# Quarterly patterns\nquarterly_launches = video_ad_data['launch_quarter'].value_counts().sort_index()\nprint(\"\\nVideo ad launches by quarter:\")\nfor quarter, count in quarterly_launches.items():\n print(f\"Q{quarter}: {count}\")\n\n# Recent trends (2023-2024)\nrecent_video_ads = video_ad_data[video_ad_data['launch_year'] >= 2023]\nrecent_monthly = recent_video_ads['launch_month'].value_counts().sort_index()\nprint(\"\\nRecent video ad activity (2023+):\")\nfor month, count in recent_monthly.items():\n print(f\"{month_names[month-1]} 2023+: {count}\")\n\n# Peak seasons identification\nspring_summer = video_ad_data[video_ad_data['launch_month'].isin([3,4,5,6,7,8])]\nfall_winter = video_ad_data[video_ad_data['launch_month'].isin([9,10,11,12,1,2])]\n\nprint(f\"\\nSpring/Summer launches: {len(spring_summer)} ({len(spring_summer)/len(video_ad_data)*100:.1f}%)\")\nprint(f\"Fall/Winter launches: {len(fall_winter)} ({len(fall_winter)/len(video_ad_data)*100:.1f}%)\")"
}
},
{
"id": "step_11",
"type": "chart",
"name": "Seasonal video launch patterns",
"depends_on": ["step_10"],
"params": {
"description": "Create visualization of video ad seasonal launch patterns",
"code": "import matplotlib.pyplot as plt\nimport numpy as np\n\n# Create the seasonal pattern chart\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))\n\n# Monthly distribution\nmonthly_data = video_ad_data['launch_month'].value_counts().sort_index()\nmonth_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n\nax1.bar(range(1, 13), [monthly_data.get(i, 0) for i in range(1, 13)], \n color='#35a8ff', alpha=0.8)\nax1.set_xlabel('Month', color='white')\nax1.set_ylabel('Video Ad Launches', color='white')\nax1.set_title('Video Ad Launches by Month', color='white', fontsize=12)\nax1.set_xticks(range(1, 13))\nax1.set_xticklabels([m[:3] for m in month_names], color='white')\nax1.tick_params(colors='white')\nax1.grid(True, alpha=0.3, color='white')\n\n# Quarterly distribution\nquarterly_data = video_ad_data['launch_quarter'].value_counts().sort_index()\ncolors = ['#ff6b35', '#35a8ff', '#4ecdc4', '#45b7d1']\nax2.pie([quarterly_data.get(i, 0) for i in range(1, 5)], \n labels=['Q1', 'Q2', 'Q3', 'Q4'],\n colors=colors, autopct='%1.1f%%', startangle=90,\n textprops={'color': 'white'})\nax2.set_title('Video Ad Distribution by Quarter', color='white', fontsize=12)\n\n# Style both subplots\nfor ax in [ax1, ax2]:\n ax.spines['top'].set_visible(False)\n ax.spines['right'].set_visible(False)\n ax.spines['bottom'].set_color('white')\n ax.spines['left'].set_color('white')\n\nplt.tight_layout()\nsave_chart(fig, 'seasonal_video_patterns.png')\nplt.close()"
}
},
{
"id": "step_12",
"type": "python_compute",
"name": "Analyze top performing video competitors in detail",
"depends_on": ["step_5", "step_6"],
"params": {
"description": "Deep dive into top 5 video-using competitors and their strategies",
"code": "import pandas as pd\nfrom collections import Counter\n\n# Get top 5 video competitors\ntop_5_video = video_ad_data['page_name'].value_counts().head(5).index.tolist()\n\nprint(\"DETAILED ANALYSIS - TOP 5 VIDEO COMPETITORS:\")\nprint(\"=\" * 50)\n\nfor competitor in top_5_video:\n comp_videos = video_ad_data[video_ad_data['page_name'] == competitor]\n \n print(f\"\\n{competitor.upper()}:\")\n print(f\"- Total video ads: {len(comp_videos)}\")\n print(f\"- Average longevity: {comp_videos['longevity_days'].mean():.1f} days\")\n print(f\"- Average EU reach: {comp_videos['eu_total_reach'].mean():.0f}\")\n print(f\"- Currently active: {comp_videos['is_currently_active'].sum()}\")\n \n # CTA analysis\n ctas = comp_videos['cta_type'].value_counts().head(3)\n print(f\"- Top CTAs: {', '.join([f'{cta} ({count})' for cta, count in ctas.items()])}\")\n \n # Sample headlines\n sample_headlines = comp_videos['headline'].dropna().head(3).tolist()\n print(f\"- Sample headlines:\")\n for i, headline in enumerate(sample_headlines, 1):\n print(f\" {i}. {headline}\")\n \n # File size analysis (proxy for video complexity)\n avg_size_mb = comp_videos['size_bytes'].mean() / (1024*1024)\n print(f\"- Average video size: {avg_size_mb:.1f} MB\")\n \n print(\"-\" * 30)\n\n# Generate public URLs for top examples\nprint(\"\\nTOP VIDEO EXAMPLES FOR REVIEW:\")\nprint(\"=\" * 40)\n\n# Get best performing videos (by longevity * reach proxy)\nvideo_ad_data['performance_score'] = video_ad_data['longevity_days'] * (video_ad_data['eu_total_reach'].fillna(1))\ntop_videos = video_ad_data.nlargest(10, 'performance_score')\n\nfor idx, row in top_videos.iterrows():\n print(f\"\\n{row['page_name']} - Performance Score: {row['performance_score']:.0f}\")\n print(f\"Headline: {row['headline']}\")\n print(f\"Video URL: {row['public_url']}\")\n print(f\"Duration: {row['longevity_days']} days | Reach: {row['eu_total_reach']}\")"
}
},
{
"id": "step_13",
"type": "python_compute",
"name": "Create AI creative framework recommendations",
"depends_on": ["step_8", "step_12"],
"params": {
"description": "Synthesize findings into actionable AI creative framework for SubSun",
"code": "import pandas as pd\nfrom collections import Counter\n\n# Analyze successful patterns for AI creative framework\nprint(\"AI CREATIVE FRAMEWORK FOR SUBSUN\")\nprint(\"=\" * 40)\n\n# 1. Format Strategy\nvideo_adoption_rate = len(video_ad_data) / len(competitor_ads) * 100\nprint(f\"\\n1. VIDEO OPPORTUNITY ASSESSMENT:\")\nprint(f\"- Current market video adoption: {video_adoption_rate:.1f}%\")\nprint(f\"- This represents a differentiation opportunity for SubSun\")\nprint(f\"- Video ads show {video_ad_data['longevity_days'].mean():.1f} day average lifespan\")\n\n# 2. Content Themes\nprint(f\"\\n2. SUCCESSFUL CONTENT THEMES:\")\nall_headlines = ' '.join(video_ad_data['headline'].dropna().str.lower())\ntheme_keywords = {\n 'lifestyle': ['style', 'look', 'vibe', 'mood', 'feel'],\n 'product_focus': ['sunglasses', 'eyewear', 'glasses', 'frames'],\n 'emotional': ['love', 'perfect', 'amazing', 'stunning', 'beautiful'],\n 'action': ['discover', 'explore', 'find', 'get', 'shop', 'try'],\n 'seasonal': ['summer', 'sun', 'beach', 'vacation', 'outdoor']\n}\n\nfor theme, keywords in theme_keywords.items():\n count = sum(all_headlines.count(keyword) for keyword in keywords)\n print(f\"- {theme.replace('_', ' ').title()}: {count} mentions\")\n\n# 3. CTA Strategy\nprint(f\"\\n3. CALL-TO-ACTION STRATEGY:\")\ntop_ctas = video_ad_data['cta_type'].value_counts().head(5)\nfor cta, count in top_ctas.items():\n percentage = (count / len(video_ad_data)) * 100\n print(f\"- {cta}: {count} uses ({percentage:.1f}%)\")\n\n# 4. Timing Strategy\nprint(f\"\\n4. OPTIMAL TIMING PATTERNS:\")\nspring_summer_count = len(video_ad_data[video_ad_data['launch_month'].isin([3,4,5,6,7,8])])\nfall_winter_count = len(video_ad_data[video_ad_data['launch_month'].isin([9,10,11,12,1,2])])\nprint(f\"- Spring/Summer launches: {spring_summer_count} ({spring_summer_count/len(video_ad_data)*100:.1f}%)\")\nprint(f\"- Fall/Winter launches: {fall_winter_count} ({fall_winter_count/len(video_ad_data)*100:.1f}%)\")\n\n# 5. AI Implementation Suggestions\nprint(f\"\\n5. AI CREATIVE IMPLEMENTATION IDEAS:\")\nprint(f\"- Personalized frame try-on experiences\")\nprint(f\"- Dynamic lifestyle scene matching (beach, city, etc.)\")\nprint(f\"- Seasonal color/style adaptation\")\nprint(f\"- User-generated content amplification\")\nprint(f\"- Virtual styling assistant demos\")\n\n# 6. Competitor Benchmarks\nprint(f\"\\n6. KEY COMPETITORS TO WATCH:\")\ntop_performers = video_ad_data.groupby('page_name').agg({\n 'performance_score': 'mean',\n 'ad_archive_id': 'count'\n}).sort_values('performance_score', ascending=False).head(5)\n\nfor competitor, data in top_performers.iterrows():\n print(f\"- {competitor}: Avg Performance {data['performance_score']:.0f}, {data['ad_archive_id']} videos\")"
}
},
{
"id": "step_14",
"type": "chart",
"name": "AI creative opportunity matrix",
"depends_on": ["step_13"],
"params": {
"description": "Create visual framework showing video adoption vs performance for competitor positioning",
"code": "import matplotlib.pyplot as plt\nimport numpy as np\n\n# Create competitor performance matrix\ncompetitor_stats = video_ad_data.groupby('page_name').agg({\n 'longevity_days': 'mean',\n 'eu_total_reach': 'mean',\n 'ad_archive_id': 'count',\n 'size_bytes': 'mean'\n}).fillna(0)\n\n# Filter to competitors with at least 5 videos\nsignificant_competitors = competitor_stats[competitor_stats['ad_archive_id'] >= 5]\n\nfig, ax = plt.subplots(figsize=(12, 8))\n\n# Create bubble chart\nfor idx, (competitor, data) in enumerate(significant_competitors.iterrows()):\n x = data['longevity_days']\n y = data['eu_total_reach']\n size = data['ad_archive_id'] * 20 # Scale bubble size\n \n ax.scatter(x, y, s=size, alpha=0.6, \n color=plt.cm.viridis(idx / len(significant_competitors)))\n \n # Label points\n ax.annotate(competitor, (x, y), xytext=(5, 5), \n textcoords='offset points', fontsize=9, color='white')\n\nax.set_xlabel('Average Ad Longevity (Days)', fontsize=12, color='white')\nax.set_ylabel('Average EU Reach', fontsize=12, color='white')\nax.set_title('Video Creative Performance Matrix\\n(Bubble size = Number of Videos)', \n fontsize=14, color='white', pad=20)\n\n# Add quadrant lines\nmedian_longevity = significant_competitors['longevity_days'].median()\nmedian_reach = significant_competitors['eu_total_reach'].median()\n\nax.axvline(median_longevity, color='white', alpha=0.3, linestyle='--')\nax.axhline(median_reach, color='white', alpha=0.3, linestyle='--')\n\n# Add quadrant labels\nax.text(ax.get_xlim()[1]*0.95, ax.get_ylim()[1]*0.95, 'High Reach\\nLong Duration', \n ha='right', va='top', color='white', alpha=0.7)\nax.text(ax.get_xlim()[0]*1.05, ax.get_ylim()[0]*1.05, 'Low Reach\\nShort Duration', \n ha='left', va='bottom', color='white', alpha=0.7)\n\nax.tick_params(colors='white')\nax.spines['top'].set_visible(False)\nax.spines['right'].set_visible(False)\nax.spines['bottom'].set_color('white')\nax.spines['left'].set_color('white')\nax.grid(True, alpha=0.3, color='white')\n\nplt.tight_layout()\nsave_chart(fig, 'video_performance_matrix.png')\nplt.close()"
}
},
{
"id": "step_15",
"type": "python_compute",
"name": "Generate specific video examples for SubSun",
"depends_on": ["step_12", "step_13"],
"params": {
"description": "Create specific video URLs and examples SubSun should review for inspiration",
"code": "import pandas as pd\n\nprint(\"CURATED VIDEO EXAMPLES FOR SUBSUN REVIEW\")\nprint(\"=\" * 45)\n\n# Categories of videos to showcase\ncategories = {\n 'High Performance': video_ad_data.nlargest(5, 'performance_score'),\n 'Long Running': video_ad_data.nlargest(5, 'longevity_days'),\n 'Recent Launches': video_ad_data[video_ad_data['launch_date'] >= '2024-01-01'].head(5),\n 'Fun/Lifestyle Focus': video_ad_data[video_ad_data['headline'].str.contains('style|vibe|look|cool|amazing', case=False, na=False)].head(5)\n}\n\nfor category, videos in categories.items():\n if len(videos) > 0:\n print(f\"\\n{category.upper()} EXAMPLES:\")\n print(\"-\" * 30)\n \n for idx, (_, video) in enumerate(videos.iterrows(), 1):\n print(f\"{idx}. {video['page_name']}\")\n print(f\" Headline: {video['headline']}\")\n print(f\" Duration: {video['longevity_days']} days | Reach: {video['eu_total_reach']}\")\n print(f\" Video URL: {video['public_url']}\")\n print(f\" File Size: {video['size_bytes']/(1024*1024):.1f} MB\")\n \n # Add AI creative suggestions based on this example\n if 'style' in str(video['headline']).lower():\n print(f\" 💡 AI Idea: Personalized style matching\")\n elif 'new' in str(video['headline']).lower():\n print(f\" 💡 AI Idea: Dynamic product reveals\")\n elif 'perfect' in str(video['headline']).lower():\n print(f\" 💡 AI Idea: Face-shape matching algorithm\")\n else:\n print(f\" 💡 AI Idea: Context-aware lifestyle scenarios\")\n print()\n\n# Summary recommendations\nprint(\"\\nSTARTER VIDEO CONCEPTS FOR SUBSUN:\")\nprint(\"=\" * 35)\nprint(\"1. 'Perfect Frame Finder' - AI-powered face shape analysis\")\nprint(\"2. 'Style Chameleon' - Dynamic background/lifestyle matching\")\nprint(\"3. 'Seasonal Adaptive' - Auto-adjusting color palettes\")\nprint(\"4. 'Virtual Try-On Journey' - Progressive style exploration\")\nprint(\"5. 'Sun Story Generator' - Personalized adventure narratives\")\n\n# Technical specs based on successful videos\navg_size = video_ad_data['size_bytes'].mean() / (1024*1024)\nprint(f\"\\nTECHNICAL RECOMMENDATIONS:\")\nprint(f\"- Target video size: {avg_size:.1f} MB average
You are the research director for a digital marketing analytics agency (beseen.studio).
You must plan a rigorous, multi-step analytical sprint. This is serious analytical work — the output should be something a human analyst would spend a full day producing.
SPRINT BRIEF:
- Objective: subsun want to get into fun ai creative, look at some of the videos their competitors have in the database which are similar vibe, and make some examples for subsun of where to start
- Client: SubSun
- Client background: SubSun is a sunglasses brand. DTC ecommerce via Shopify. Runs Meta (Facebook/Instagram) ads. Key metrics: ROAS, CPA, AOV.
- Vertical: fashion / sunglasses / DTC ecommerce
- Context: None
AVAILABLE DATA (with schemas):
{
"competitor_ads": {
"table": "competitor_ads",
"columns": [
"id",
"ad_archive_id",
"competitor_page_id",
"page_name",
"primary_text",
"headline",
"description",
"creative_type",
"platform_list",
"ad_delivery_start_time",
"ad_delivery_stop_time",
"first_seen_date",
"last_seen_date",
"is_currently_active",
"languages",
"created_at",
"updated_at",
"eu_total_reach",
"age_country_gender_reach",
"target_ages",
"target_gender",
"target_locations",
"ad_snapshot_url",
"preview_url",
"cta_type",
"publisher_platforms"
],
"row_count": 22869
},
"competitor_ad_daily": {
"table": "competitor_ad_daily",
"columns": [
"id",
"ad_archive_id",
"check_date",
"is_active",
"created_at",
"eu_total_reach",
"age_country_gender_reach"
],
"row_count": 270946
},
"competitor_pages": {
"table": "competitor_pages",
"columns": [
"id",
"page_id",
"page_name",
"category",
"is_political",
"country_code",
"notes",
"created_at",
"updated_at"
],
"row_count": 25
},
"image_classifications": {
"table": "image_classifications",
"columns": [
"filename",
"shot_type",
"excluded",
"created_at",
"updated_at"
],
"row_count": 517
}
}
DATA CONTEXT — what each table/source actually contains:
TABLES:
- competitor_ads: Meta Ad Library scrape. Each row = one competitor ad with page_name, headline, primary_text (copy), description, creative_type (static/carousel/image/video), CTA type, start/stop dates, active status, ad_archive_id, ad_snapshot_url, preview_url, eu_total_reach, target_ages, target_gender, target_locations. ~1000 rows.
- competitor_ad_daily: Daily check of each ad's status and EU reach estimate. Tracks ad longevity and reach over time.
- competitor_pages: List of competitor Facebook pages we track (page_name, category, country_code). ~25 pages in eyewear/fashion.
- competitor_copy_analysis: Pre-analysed copy patterns from competitor ads (may be empty).
- image_classifications: Classifications of the CLIENT's own product photography (shot_type: Product Angle, Packaging, Product Hero, Model Editorial, etc). This is SubSun's own image library, NOT competitors. ~517 rows.
- creative_analysis_feedback: Human feedback on creative analysis quality (may be empty).
MEDIA STORAGE (Supabase Storage bucket: "competitor-previews"):
- ~3,400 actual video files (.mp4) and ~6,400 images (.jpg) of competitor ad creative.
- Organised by competitor folder (lowercase page name). Files named by ad_archive_id.
- Joins to competitor_ads table via ad_archive_id column.
- Accessible via public URL: {supabase_url}/storage/v1/object/public/competitor-previews/{folder}/{ad_archive_id}.mp4 (or .jpg)
- Top competitors by video count: bound (658), the kooples (349), ace (297), ace & tate (266), chimi (206), le specs (194), mutimer (172), jmm (155), toast (154), vuarnet (124)
- The sprint engine can list files in these folders and generate public URLs for viewing/download.
- IMPORTANT: We cannot run computer vision on videos in the pipeline, but we CAN: list what exists, correlate file counts with ad metadata, generate viewing links, analyse ad metadata of ads that HAVE video creative, and compare patterns between video vs static ads.
WHAT YOU CAN DEEPLY ANALYSE:
- Copy patterns (headlines, primary text, descriptions, CTAs, messaging themes, tone)
- Creative format strategy (who uses video vs static vs carousel, and when)
- Ad longevity (which ads run longest, which get pulled quickly)
- Timing patterns (launch dates, seasonal trends, campaign cadence)
- Competitive share of voice (ad volume by competitor over time)
- Targeting patterns (ages, genders, locations where available)
- Reach estimates (EU reach from ad_daily data)
- Performance proxies (longevity × reach as engagement proxy)
- Cross-reference metadata with actual stored creative files
WHAT YOU CANNOT DO IN THE PIPELINE:
- Computer vision / video analysis (no frame extraction or visual classification)
- Access video/image pixel content programmatically
- But you CAN reference which files exist and generate viewable URLs for the team to review.
PRIOR KNOWLEDGE FROM PREVIOUS SPRINTS:
- [analysis] # Strategic Video Advertising Analysis for SubSun
## Competitor Creative Intelligence & AI Implementation Roadmap
---
## 1. EXECUTIVE SUMMARY
The eyewear market shows surprisingly low video adoption with only **1.6% of competitor ads using video format** (16 out of 1,000 total ads analyzed). **Me
- [analysis] ### EXECUTIVE SUMMARY
SubSun can enhance its marketing strategy by adopting creative elements seen in successful campaigns of competitors like Moscot, Jimmy Fairly, and Etudes Studio. By integrating these strategies, SubSun can increase engagement and align more closely with current trends.
### KE
- [analysis] ### EXECUTIVE SUMMARY
SubSun aims to enhance their marketing strategy with fun AI-driven creative content. By analyzing competitors' similar vibe videos, this deliverable identifies key elements SubSun can adopt or adapt for their campaigns.
### KEY FINDINGS
- **Competitor Trend Analysis**: Compe
- [analysis] ### EXECUTIVE SUMMARY
SubSun aims to explore fun AI-driven creative content for their marketing campaigns. After analyzing competitor data, this deliverable outlines a strategic approach for SubSun to create engaging videos that align with current trends in the market.
### KEY FINDINGS
- **Compet
PYTHON LIBRARIES AVAILABLE: pandas, numpy, scipy, sklearn, statsmodels, xgboost, lightgbm, matplotlib, seaborn, plotly, collections, re, datetime, math
Your job: create a structured execution plan. Each step must be one of these types:
1. **data_fetch** — Fetch specific data from a database table.
Parameters: table (str), columns (list), filters (dict), limit (int)
The variable name in code will be the table name (e.g. "competitor_ads" → DataFrame called `competitor_ads`)
1b. **storage_list** — List actual media files (videos/images) from the competitor-previews storage bucket.
Parameters: all_competitors (bool — list ALL competitors), folder (str — specific competitor folder e.g. "chimi/"), video_only (bool)
Returns a DataFrame called `competitor_media` (or `media_{folder}`) with columns: filename, folder, mimetype, size_bytes, is_video, is_image, ad_archive_id, public_url, created_at.
The ad_archive_id column joins to competitor_ads table for cross-referencing metadata with actual creative files.
USE THIS to discover what actual video/image creative exists. This is the real data — ~3,400 videos and ~6,400 images.
2. **python_compute** — Run Python code for analysis/transformation.
Parameters: code (str — valid Python), description (str)
The code has access to: DataFrames from ALL previous steps (by table name), pd, np, json, re, datetime, collections, Counter, defaultdict, math, statistics.
Use `save_chart(fig, "filename.png")` to save matplotlib/plotly figures. CHART_DIR variable has the save path.
Use `print()` for key findings — stdout is captured and passed to the final synthesis.
Assign results to named variables — they'll be captured automatically.
IMPORTANT: Write COMPLETE, WORKING Python code. Import what you need. Don't use `...` or pseudocode.
3. **llm_synthesis** — Ask a LOCAL LLM (Ollama) to interpret/narrate computed results.
Parameters: prompt_template (str with {{step_id.stdout}} or {{step_id.variables.name}} placeholders for prior step outputs)
NOTE: This runs on a local 7B model — keep prompts focused and provide structured data to interpret.
Do NOT specify a model parameter — the system uses the default local model.
4. **chart** — Generate a specific visualization.
Parameters: code (str — Python code using matplotlib/plotly), description (str)
matplotlib and seaborn are pre-imported. Dark theme is pre-configured (bg=#0a0a0a).
You MUST call `save_chart(fig, "filename.png")` at the end or the chart won't be saved.
Use `plt.close()` after saving to free memory.
PLANNING RULES:
- This sprint should have **15-30 steps** and represent 30-60 minutes of compute time.
- Start by fetching ALL relevant data tables — cast a wide net.
- Then do MULTIPLE rounds of analysis: aggregation → statistical testing → pattern detection → segmentation → trend analysis.
- Each python_compute step should do ONE focused computation. Do not cram everything into one step.
- After every 3-5 compute steps, include an llm_synthesis step to interpret intermediate findings — this feeds into later analysis.
- Include at least 3-5 chart steps throughout (not all at the end).
- Build analysis progressively: basic stats → cross-tabulation → statistical tests → clustering/segmentation → temporal patterns → synthesis.
- Make the analysis genuinely useful — not a summary a human could do in a spreadsheet.
- Be specific: reference exact column names from the schemas above.
- If the brief asks about something the data can't directly answer, plan the CLOSEST useful analysis and note the limitation.
- The FINAL step must be an llm_synthesis that creates a comprehensive written deliverable from all prior findings.
Return ONLY valid JSON:
{
"plan_name": "Short descriptive name",
"estimated_duration_minutes": 30,
"methodology_notes": "Brief explanation of analytical approach and any data limitations",
"steps": [
{
"id": "step_1",
"type": "data_fetch",
"name": "Fetch all competitor ads",
"depends_on": [],
"params": {
"table": "competitor_ads",
"columns": ["page_name", "headline", "primary_text", "creative_type", "cta_type", "ad_delivery_start_time", "ad_delivery_stop_time", "is_currently_active"],
"filters": {},
"limit": 5000
}
},
{
"id": "step_2",
"type": "python_compute",
"name": "Compute creative format distribution by competitor",
"depends_on": ["step_1"],
"params": {
"description": "Group ads by page_name and creative_type, compute counts and percentages",
"code": "import pandas as pd\ncounts = competitor_ads.groupby(['page_name', 'creative_type']).size().reset_index(name='count')\ntotal_by_page = competitor_ads.groupby('page_name').size().reset_index(name='total')\ncounts = counts.merge(total_by_page, on='page_name')\ncounts['pct'] = (counts['count'] / counts['total'] * 100).round(1)\nprint(counts.to_string())"
}
}
]
}