Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts

This commit is contained in:
ajaysi
2026-02-08 13:56:57 +05:30
parent 1db10ccd0f
commit e404a86502
333 changed files with 42223 additions and 10875 deletions

View File

@@ -6,8 +6,8 @@ Normalizes persona data and onboarding information into reusable brand tokens.
from typing import Dict, Any, Optional
from loguru import logger
from services.onboarding import OnboardingDatabaseService
from services.database import SessionLocal
from api.content_planning.services.content_strategy.onboarding import OnboardingDataIntegrationService
class BrandDNASyncService:
@@ -16,6 +16,7 @@ class BrandDNASyncService:
def __init__(self):
"""Initialize Brand DNA Sync Service."""
self.logger = logger
self.integration_service = OnboardingDataIntegrationService()
logger.info("[Brand DNA Sync] Service initialized")
def get_brand_dna_tokens(self, user_id: str) -> Dict[str, Any]:
@@ -31,10 +32,16 @@ class BrandDNASyncService:
try:
db = SessionLocal()
try:
onboarding_db = OnboardingDatabaseService(db)
website_analysis = onboarding_db.get_website_analysis(user_id, db)
persona_data = onboarding_db.get_persona_data(user_id, db)
competitor_analyses = onboarding_db.get_competitor_analysis(user_id, db)
# Use SSOT Integration Service
integrated_data = self.integration_service.get_integrated_data_sync(user_id, db)
# Get canonical profile as primary source
canonical_profile = integrated_data.get('canonical_profile', {})
# Get raw data for deep fields
website_analysis = integrated_data.get('website_analysis', {})
persona_data = integrated_data.get('persona_data', {})
competitor_analyses = integrated_data.get('competitor_analysis', [])
finally:
db.close()
@@ -46,42 +53,43 @@ class BrandDNASyncService:
"competitive_positioning": {},
}
# Extract writing style from website analysis
# Layer 1: Canonical Profile (Priority)
brand_tokens["writing_style"] = {
"tone": canonical_profile.get('writing_tone', 'professional'),
"voice": canonical_profile.get('writing_voice', 'authoritative'),
"complexity": canonical_profile.get('writing_complexity', 'intermediate'),
"engagement_level": canonical_profile.get('writing_engagement', 'moderate'),
}
target_audience_raw = canonical_profile.get('target_audience')
if isinstance(target_audience_raw, dict):
brand_tokens["target_audience"] = {
"demographics": target_audience_raw.get('demographics', []),
"industry_focus": canonical_profile.get('industry', 'general'),
"expertise_level": target_audience_raw.get('expertise_level', 'intermediate'),
}
brand_tokens["visual_identity"] = {
"color_palette": canonical_profile.get('brand_colors', []),
"brand_values": canonical_profile.get('brand_values', []),
"positioning": "", # To be filled from website analysis
}
# Layer 2: Raw Website Analysis (Fallback/Enrichment)
if website_analysis:
writing_style = website_analysis.get('writing_style') or {}
target_audience = website_analysis.get('target_audience') or {}
brand_analysis = website_analysis.get('brand_analysis') or {}
style_guidelines = website_analysis.get('style_guidelines') or {}
# Ensure writing_style is a dict before accessing
if isinstance(writing_style, dict):
brand_tokens["writing_style"] = {
"tone": writing_style.get('tone', 'professional'),
"voice": writing_style.get('voice', 'authoritative'),
"complexity": writing_style.get('complexity', 'intermediate'),
"engagement_level": writing_style.get('engagement_level', 'moderate'),
}
# Enrich visual identity if missing
if not brand_tokens["visual_identity"]["color_palette"] and isinstance(brand_analysis, dict):
brand_tokens["visual_identity"]["color_palette"] = brand_analysis.get('color_palette', [])
brand_tokens["visual_identity"]["brand_values"] = brand_analysis.get('brand_values', [])
brand_tokens["visual_identity"]["positioning"] = brand_analysis.get('positioning', '')
# Ensure target_audience is a dict before accessing
if isinstance(target_audience, dict):
brand_tokens["target_audience"] = {
"demographics": target_audience.get('demographics', []),
"industry_focus": target_audience.get('industry_focus', 'general'),
"expertise_level": target_audience.get('expertise_level', 'intermediate'),
}
# Ensure brand_analysis is a dict before accessing
if isinstance(brand_analysis, dict) and brand_analysis:
brand_tokens["visual_identity"] = {
"color_palette": brand_analysis.get('color_palette', []),
"brand_values": brand_analysis.get('brand_values', []),
"positioning": brand_analysis.get('positioning', ''),
}
# Add style_guidelines if available and visual_identity exists
# Add style_guidelines if available
if style_guidelines and isinstance(style_guidelines, dict):
if "visual_identity" not in brand_tokens:
brand_tokens["visual_identity"] = {}
brand_tokens["visual_identity"]["style_guidelines"] = style_guidelines
# Extract persona data
@@ -112,21 +120,48 @@ class BrandDNASyncService:
brand_tokens["competitive_positioning"] = {
"differentiators": [],
"unique_value_props": [],
"market_position": "",
"competitor_insights": []
}
# Enrich with SSOT competitor analysis data
for competitor in competitor_analyses[:3]: # Top 3 competitors
if not isinstance(competitor, dict):
continue
analysis_data = competitor.get('analysis_data') or {}
if isinstance(analysis_data, dict) and analysis_data:
# Extract insights
competitive_insights = analysis_data.get('competitive_analysis') or {}
if isinstance(competitive_insights, dict) and competitive_insights:
# Differentiators
differentiators = competitive_insights.get('differentiators', [])
if isinstance(differentiators, list) and differentiators:
brand_tokens["competitive_positioning"]["differentiators"].extend(
differentiators[:2]
)
# Value Props
uvp = competitive_insights.get('unique_value_propositions', [])
if isinstance(uvp, list) and uvp:
brand_tokens["competitive_positioning"]["unique_value_props"].extend(
uvp[:2]
)
# Market Position (take from first valid competitor or aggregate)
if not brand_tokens["competitive_positioning"]["market_position"]:
brand_tokens["competitive_positioning"]["market_position"] = competitive_insights.get('market_position', '')
# Store simplified competitor insight
brand_tokens["competitive_positioning"]["competitor_insights"].append({
"name": competitor.get('competitor_url', 'Unknown'),
"strengths": analysis_data.get('strengths', [])[:3],
"weaknesses": analysis_data.get('weaknesses', [])[:3]
})
# Deduplicate lists
brand_tokens["competitive_positioning"]["differentiators"] = list(set(brand_tokens["competitive_positioning"]["differentiators"]))
brand_tokens["competitive_positioning"]["unique_value_props"] = list(set(brand_tokens["competitive_positioning"]["unique_value_props"]))
logger.info(f"[Brand DNA Sync] Extracted brand tokens for user {user_id}")
return brand_tokens

View File

@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session
from sqlalchemy import desc
from models.product_marketing_models import Campaign, CampaignProposal, CampaignAsset, CampaignStatus
from services.database import SessionLocal
from services.database import get_session_for_user
class CampaignStorageService:
@@ -35,7 +35,7 @@ class CampaignStorageService:
Returns:
Saved Campaign object
"""
db = SessionLocal()
db = get_session_for_user(user_id)
try:
campaign_id = campaign_data.get('campaign_id')
@@ -91,7 +91,11 @@ class CampaignStorageService:
campaign_id: str
) -> Optional[Campaign]:
"""Get campaign by ID."""
db = SessionLocal()
db = get_session_for_user(user_id)
if not db:
logger.error(f"Could not create database session for user {user_id}")
return None
try:
campaign = db.query(Campaign).filter(
Campaign.campaign_id == campaign_id,
@@ -111,7 +115,7 @@ class CampaignStorageService:
limit: int = 50
) -> List[Campaign]:
"""List campaigns for user."""
db = SessionLocal()
db = get_session_for_user(user_id)
try:
query = db.query(Campaign).filter(Campaign.user_id == user_id)
@@ -200,7 +204,7 @@ class CampaignStorageService:
status: str
) -> bool:
"""Update campaign status."""
db = SessionLocal()
db = get_session_for_user(user_id)
try:
campaign = db.query(Campaign).filter(
Campaign.campaign_id == campaign_id,

View File

@@ -7,9 +7,9 @@ from typing import Dict, Any, Optional, List
from loguru import logger
import json
from services.onboarding.database_service import OnboardingDatabaseService
from services.database import SessionLocal
from services.llm_providers.main_text_generation import llm_text_gen
from api.content_planning.services.content_strategy.onboarding import OnboardingDataIntegrationService
from .product_marketing_templates import (
ProductMarketingTemplates,
TemplateCategory,
@@ -77,6 +77,7 @@ class IntelligentPromptBuilder:
def _parse_user_input(
self,
user_input: str,
user_id: str,
asset_type: Optional[str] = None
) -> Dict[str, Any]:
"""
@@ -138,7 +139,7 @@ Output: {"product_name": "luxury watch", "product_type": "watch", "use_case": "m
prompt=prompt,
system_prompt=system_prompt,
json_struct=json_struct,
user_id=None # No user_id needed for parsing
user_id=user_id # Pass user_id for subscription checking
)
# Parse JSON response
@@ -185,26 +186,21 @@ Output: {"product_name": "luxury watch", "product_type": "watch", "use_case": "m
Get all onboarding data for user.
Returns:
Dictionary with website_analysis, persona_data, competitor_analyses
Dictionary with canonical_profile only (Single Source of Truth)
"""
db = SessionLocal()
try:
onboarding_db = OnboardingDatabaseService(db)
website_analysis = onboarding_db.get_website_analysis(user_id, db)
persona_data = onboarding_db.get_persona_data(user_id, db)
competitor_analyses = onboarding_db.get_competitor_analysis(user_id, db)
integration_service = OnboardingDataIntegrationService()
integrated_data = integration_service.get_integrated_data_sync(user_id, db)
canonical_profile = integrated_data.get('canonical_profile', {})
return {
"website_analysis": website_analysis or {},
"persona_data": persona_data or {},
"competitor_analyses": competitor_analyses or [],
"canonical_profile": canonical_profile,
}
except Exception as e:
logger.error(f"[Intelligent Prompt Builder] Error getting onboarding data: {str(e)}")
return {
"website_analysis": {},
"persona_data": {},
"competitor_analyses": [],
"canonical_profile": {},
}
finally:
db.close()
@@ -218,49 +214,49 @@ Output: {"product_name": "luxury watch", "product_type": "watch", "use_case": "m
"""
Infer requirements from parsed input and onboarding context.
Uses onboarding data to fill in missing information:
- Platform from onboarding (if user has e-commerce setup)
- Style from brand DNA
- Target audience from onboarding
Uses canonical profile for:
- Style (aesthetic)
- Target audience
- Brand colors
- Tone/Voice
"""
requirements = parsed_input.copy()
website_analysis = onboarding_data.get("website_analysis", {})
persona_data = onboarding_data.get("persona_data", {})
# We rely strictly on canonical_profile now
canonical_profile = onboarding_data.get("canonical_profile", {}) or {}
# Infer platform from onboarding
if not requirements.get("platform_hints"):
# Check if user has e-commerce setup (from website analysis)
brand_analysis = website_analysis.get("brand_analysis", {})
# Try to infer platform from website URL or other hints
# For now, default to e-commerce if no hints
# This logic was: if use_case == ecommerce -> shopify.
# We can keep this simple inference or check if industry is ecommerce.
if requirements.get("use_case") == "ecommerce":
requirements["platform_hints"] = ["shopify"] # Default e-commerce platform
# Infer style from brand DNA
# Infer style from brand DNA (canonical)
if not requirements.get("style_hints"):
if brand_analysis:
style_guidelines = brand_analysis.get("style_guidelines", {})
aesthetic = style_guidelines.get("aesthetic", "")
if aesthetic:
requirements["style_hints"] = [aesthetic.lower()]
visual_style = canonical_profile.get("visual_style", {})
aesthetic = visual_style.get("aesthetic")
if aesthetic:
requirements["style_hints"] = [aesthetic.lower()]
# Infer target audience from onboarding
target_audience = website_analysis.get("target_audience", {})
# Target Audience (canonical)
target_audience = canonical_profile.get("target_audience")
if target_audience:
requirements["target_audience"] = target_audience
# Infer brand colors
if brand_analysis:
color_palette = brand_analysis.get("color_palette", [])
if color_palette:
requirements["brand_colors"] = color_palette[:5] # Top 5 colors
# Brand colors (canonical)
brand_colors = canonical_profile.get("brand_colors", [])
if brand_colors:
requirements["brand_colors"] = brand_colors[:5] # Top 5 colors
# Infer writing style
writing_style = website_analysis.get("writing_style", {})
if writing_style:
requirements["tone"] = writing_style.get("tone", "professional")
requirements["voice"] = writing_style.get("voice", "authoritative")
# Tone/Voice (canonical)
tone = canonical_profile.get("writing_tone") or "professional"
requirements["tone"] = tone
voice = canonical_profile.get("writing_voice")
if voice:
requirements["voice"] = voice
return requirements
@@ -423,6 +419,16 @@ Output: {"product_name": "luxury watch", "product_type": "watch", "use_case": "m
# Brand colors from onboarding
if requirements.get("brand_colors"):
defaults["brand_colors"] = requirements["brand_colors"]
# Pass through other inferred context
if requirements.get("tone"):
defaults["tone"] = requirements["tone"]
if requirements.get("voice"):
defaults["voice"] = requirements["voice"]
if requirements.get("target_audience"):
defaults["target_audience"] = requirements["target_audience"]
if requirements.get("industry"):
defaults["industry"] = requirements["industry"]
# Additional context
defaults["additional_context"] = requirements.get("additional_context", "")

View File

@@ -6,8 +6,8 @@ Extracts ALL onboarding data and provides personalized defaults for forms and re
from typing import Dict, Any, Optional, List
from loguru import logger
from services.onboarding.database_service import OnboardingDatabaseService
from services.database import SessionLocal
from api.content_planning.services.content_strategy.onboarding import OnboardingDataIntegrationService
class PersonalizationService:
@@ -38,87 +38,37 @@ class PersonalizationService:
"""
db = SessionLocal()
try:
onboarding_db = OnboardingDatabaseService(db)
website_analysis = onboarding_db.get_website_analysis(user_id, db)
persona_data = onboarding_db.get_persona_data(user_id, db)
competitor_analyses = onboarding_db.get_competitor_analysis(user_id, db)
integration_service = OnboardingDataIntegrationService()
integrated_data = integration_service.get_integrated_data_sync(user_id, db)
canonical_profile = integrated_data.get('canonical_profile', {})
# Map strictly from Canonical Profile
preferences = {
"industry": None,
"target_audience": {},
"platform_preferences": [],
"content_preferences": [],
"style_preferences": {},
"brand_colors": [],
"industry": canonical_profile.get("industry"),
"target_audience": canonical_profile.get("target_audience", {}),
"platform_preferences": canonical_profile.get("platform_preferences", []),
"content_preferences": canonical_profile.get("content_types", []),
"style_preferences": canonical_profile.get("visual_style", {}),
"brand_colors": canonical_profile.get("brand_colors", []),
"recommended_templates": [],
"recommended_channels": [],
"writing_style": {},
"brand_values": [],
"writing_style": {
"tone": canonical_profile.get("writing_tone", "professional"),
"voice": canonical_profile.get("writing_voice", "authoritative"),
"complexity": canonical_profile.get("writing_complexity", "intermediate"),
"engagement_level": canonical_profile.get("writing_engagement", "moderate"),
},
"brand_values": canonical_profile.get("brand_values", []),
}
# Extract from website_analysis
if website_analysis:
# Industry
target_audience = website_analysis.get("target_audience", {})
preferences["industry"] = target_audience.get("industry_focus")
# Target audience
preferences["target_audience"] = {
"demographics": target_audience.get("demographics", []),
"expertise_level": target_audience.get("expertise_level", "intermediate"),
"industry_focus": target_audience.get("industry_focus"),
}
# Writing style
writing_style = website_analysis.get("writing_style", {})
preferences["writing_style"] = {
"tone": writing_style.get("tone", "professional"),
"voice": writing_style.get("voice", "authoritative"),
"complexity": writing_style.get("complexity", "intermediate"),
"engagement_level": writing_style.get("engagement_level", "moderate"),
}
# Brand colors
brand_analysis = website_analysis.get("brand_analysis", {})
if brand_analysis:
preferences["brand_colors"] = brand_analysis.get("color_palette", [])
preferences["brand_values"] = brand_analysis.get("brand_values", [])
# Style preferences
style_guidelines = website_analysis.get("style_guidelines", {})
if style_guidelines:
preferences["style_preferences"] = {
"aesthetic": style_guidelines.get("aesthetic", "modern"),
"visual_style": style_guidelines.get("visual_style", "clean"),
}
# Extract from persona_data
if persona_data:
core_persona = persona_data.get("corePersona", {})
platform_personas = persona_data.get("platformPersonas", {})
selected_platforms = persona_data.get("selectedPlatforms", [])
# Platform preferences from selected platforms
if selected_platforms:
preferences["platform_preferences"] = selected_platforms
elif platform_personas:
# Extract platforms from platform personas
preferences["platform_preferences"] = list(platform_personas.keys())
# Recommended channels based on platform personas
if platform_personas:
# Prioritize platforms with active personas
preferences["recommended_channels"] = list(platform_personas.keys())[:5] # Top 5
# Content preferences from persona
if core_persona:
content_format_rules = core_persona.get("content_format_rules", {})
if content_format_rules:
preferred_formats = content_format_rules.get("preferred_formats", [])
preferences["content_preferences"] = preferred_formats
# Infer content preferences from industry
if preferences["industry"]:
# Ensure target_audience structure
if isinstance(preferences["target_audience"], dict):
ta = preferences["target_audience"]
if "industry_focus" not in ta and preferences["industry"]:
ta["industry_focus"] = preferences["industry"]
# Infer content preferences from industry if missing (Business Rule)
if not preferences["content_preferences"] and preferences["industry"]:
industry_content_map = {
"ecommerce": ["product_images", "product_videos", "lifestyle_content"],
"saas": ["feature_highlights", "tutorials", "demo_videos"],

View File

@@ -7,9 +7,7 @@ from typing import Dict, Any, Optional
from loguru import logger
from services.ai_prompt_optimizer import AIPromptOptimizer
from services.onboarding import OnboardingDataService
from services.onboarding.database_service import OnboardingDatabaseService
from services.persona_data_service import PersonaDataService
from api.content_planning.services.content_strategy.onboarding import OnboardingDataIntegrationService
from services.database import SessionLocal
@@ -19,9 +17,9 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
def __init__(self):
"""Initialize Product Marketing Prompt Builder."""
super().__init__()
self.onboarding_data_service = OnboardingDataService()
self.onboarding_integration_service = OnboardingDataIntegrationService()
self.logger = logger
logger.info("[Product Marketing Prompt Builder] Initialized")
self.logger.info("[Product Marketing Prompt Builder] Initialized")
def build_marketing_image_prompt(
self,
@@ -45,66 +43,61 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
Enhanced prompt with brand DNA, persona style, and marketing context
"""
try:
# Get onboarding data
# Use Canonical Profile (SSOT)
db = SessionLocal()
try:
onboarding_db = OnboardingDatabaseService(db)
website_analysis = onboarding_db.get_website_analysis(user_id, db)
persona_data = onboarding_db.get_persona_data(user_id, db)
competitor_analyses = onboarding_db.get_competitor_analysis(user_id, db)
onboarding_data = self.onboarding_integration_service._build_canonical_from_db(user_id, db)
except Exception as e:
self.logger.error(f"Error fetching onboarding data: {e}")
onboarding_data = {}
finally:
db.close()
canonical_profile = onboarding_data or {}
# Build prompt layers
enhanced_prompt = base_prompt
# Layer 1: Brand DNA (from website_analysis)
if website_analysis:
writing_style = website_analysis.get('writing_style', {})
target_audience = website_analysis.get('target_audience', {})
brand_analysis = website_analysis.get('brand_analysis', {})
style_guidelines = website_analysis.get('style_guidelines', {})
# Add brand tone and style
tone = writing_style.get('tone', 'professional')
voice = writing_style.get('voice', 'authoritative')
brand_enhancement = f", {tone} tone, {voice} voice"
# Add target audience context
# 1. Brand Voice & Tone (Canonical)
tone = canonical_profile.get('writing_tone', 'professional')
voice = canonical_profile.get('writing_voice', 'authoritative')
brand_enhancement = f", {tone} tone, {voice} voice"
enhanced_prompt += brand_enhancement
# 2. Target Audience (Canonical)
target_audience = canonical_profile.get('target_audience')
demographics = []
if isinstance(target_audience, dict):
demographics = target_audience.get('demographics', [])
if demographics:
audience_context = f", targeting {', '.join(demographics[:2])}"
enhanced_prompt += audience_context
# Add brand visual identity if available
if brand_analysis:
color_palette = brand_analysis.get('color_palette', [])
if color_palette:
colors = ', '.join(color_palette[:3])
enhanced_prompt += f", brand colors: {colors}"
if not demographics:
# fallback to checking keys if demographics key is missing but dict acts as demographics
pass
elif isinstance(target_audience, list):
demographics = target_audience
elif isinstance(target_audience, str):
demographics = [target_audience]
if demographics:
audience_str = ', '.join([str(d) for d in demographics[:2]])
enhanced_prompt += f", targeting {audience_str}"
# Layer 2: Persona Visual Style (from persona_data)
if persona_data:
core_persona = persona_data.get('corePersona', {})
platform_personas = persona_data.get('platformPersonas', {})
# 3. Brand Identity (Canonical)
brand_colors = canonical_profile.get('brand_colors', [])
if brand_colors:
colors = ', '.join([str(c) for c in brand_colors[:3]])
enhanced_prompt += f", brand colors: {colors}"
if core_persona:
persona_name = core_persona.get('persona_name', '')
archetype = core_persona.get('archetype', '')
if persona_name:
enhanced_prompt += f", {persona_name} style"
# Channel-specific persona adaptation
if channel and platform_personas:
platform_persona = platform_personas.get(channel, {})
if platform_persona:
visual_identity = platform_persona.get('visual_identity', {})
if visual_identity:
aesthetic = visual_identity.get('aesthetic_preferences', '')
if aesthetic:
enhanced_prompt += f", {aesthetic} aesthetic"
visual_style = canonical_profile.get('visual_style', {})
aesthetic = visual_style.get('aesthetic')
if aesthetic:
enhanced_prompt += f", {aesthetic} aesthetic"
# 4. Persona Style (Canonical - derived from Persona Data if available)
# Note: Canonical profile already merges persona data into tone/voice/style.
# If we need specific persona name, we might need to check if it's stored in canonical.
# Currently canonical stores aggregated traits.
# Layer 3: Channel Optimization
# Channel-specific optimization
channel_enhancements = {
'instagram': ', Instagram-optimized composition, vibrant colors, engaging visual',
'linkedin': ', professional photography, clean composition, business-focused',
@@ -117,7 +110,6 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
if channel and channel.lower() in channel_enhancements:
enhanced_prompt += channel_enhancements[channel.lower()]
# Layer 4: Asset Type Specific
asset_type_enhancements = {
'hero_image': ', hero image style, prominent product placement, professional photography',
'product_photo': ', product photography, clean background, detailed product showcase',
@@ -128,11 +120,6 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
if asset_type in asset_type_enhancements:
enhanced_prompt += asset_type_enhancements[asset_type]
# Layer 5: Competitive Differentiation
if competitor_analyses and len(competitor_analyses) > 0:
# Extract unique positioning from competitor analysis
enhanced_prompt += ", unique positioning, differentiated visual style"
# Layer 6: Quality Descriptors
enhanced_prompt += ", professional photography, high quality, detailed, sharp focus, natural lighting"
@@ -142,11 +129,11 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
if marketing_goal:
enhanced_prompt += f", {marketing_goal} focused"
logger.info(f"[Marketing Prompt] Enhanced prompt for user {user_id}: {enhanced_prompt[:200]}...")
self.logger.info(f"[Marketing Prompt] Enhanced prompt for user {user_id}: {enhanced_prompt[:200]}...")
return enhanced_prompt
except Exception as e:
logger.error(f"[Marketing Prompt] Error building prompt: {str(e)}")
self.logger.error(f"[Marketing Prompt] Error building prompt: {str(e)}")
# Return base prompt with minimal enhancement if error
return f"{base_prompt}, professional photography, high quality"
@@ -172,97 +159,62 @@ class ProductMarketingPromptBuilder(AIPromptOptimizer):
Enhanced prompt with persona style, brand voice, and marketing context
"""
try:
# Get onboarding data
# Use Canonical Profile (SSOT)
db = SessionLocal()
try:
onboarding_db = OnboardingDatabaseService(db)
website_analysis = onboarding_db.get_website_analysis(user_id, db)
persona_data = onboarding_db.get_persona_data(user_id, db)
competitor_analyses = onboarding_db.get_competitor_analysis(user_id, db)
onboarding_data = self.onboarding_integration_service._build_canonical_from_db(user_id, db)
except Exception as e:
self.logger.error(f"Error fetching onboarding data: {e}")
onboarding_data = {}
finally:
db.close()
canonical_profile = onboarding_data or {}
# Build enhanced prompt
enhanced_prompt = base_request
# Add persona linguistic fingerprint
if persona_data:
core_persona = persona_data.get('corePersona', {})
platform_personas = persona_data.get('platformPersonas', {})
if core_persona:
persona_name = core_persona.get('persona_name', '')
linguistic_fingerprint = core_persona.get('linguistic_fingerprint', {})
if persona_name:
enhanced_prompt += f"\n\nFollow {persona_name} persona style:"
if linguistic_fingerprint:
sentence_metrics = linguistic_fingerprint.get('sentence_metrics', {})
lexical_features = linguistic_fingerprint.get('lexical_features', {})
if sentence_metrics:
avg_length = sentence_metrics.get('average_sentence_length_words', '')
if avg_length:
enhanced_prompt += f"\n- Average sentence length: {avg_length} words"
if lexical_features:
go_to_words = lexical_features.get('go_to_words', [])
avoid_words = lexical_features.get('avoid_words', [])
vocabulary_level = lexical_features.get('vocabulary_level', '')
if go_to_words:
enhanced_prompt += f"\n- Use these words: {', '.join(go_to_words[:5])}"
if avoid_words:
enhanced_prompt += f"\n- Avoid these words: {', '.join(avoid_words[:5])}"
if vocabulary_level:
enhanced_prompt += f"\n- Vocabulary level: {vocabulary_level}"
# Channel-specific persona adaptation
if channel and platform_personas:
platform_persona = platform_personas.get(channel, {})
if platform_persona:
content_format_rules = platform_persona.get('content_format_rules', {})
engagement_patterns = platform_persona.get('engagement_patterns', {})
if content_format_rules:
char_limit = content_format_rules.get('character_limit', '')
hashtag_strategy = content_format_rules.get('hashtag_strategy', '')
if char_limit:
enhanced_prompt += f"\n- Character limit: {char_limit}"
if hashtag_strategy:
enhanced_prompt += f"\n- Hashtag strategy: {hashtag_strategy}"
# 1. Brand Voice & Tone (Canonical)
tone = canonical_profile.get('writing_tone', 'professional')
voice = canonical_profile.get('writing_voice', 'authoritative')
complexity = canonical_profile.get('writing_complexity', 'intermediate')
# Add brand voice
if website_analysis:
writing_style = website_analysis.get('writing_style', {})
target_audience = website_analysis.get('target_audience', {})
tone = writing_style.get('tone', 'professional')
voice = writing_style.get('voice', 'authoritative')
enhanced_prompt += f"\n- Brand tone: {tone}, Brand voice: {voice}"
enhanced_prompt += f"\n\nBrand Voice & Tone:\n- Tone: {tone}\n- Voice: {voice}\n- Complexity: {complexity}"
# 2. Target Audience (Canonical)
target_audience = canonical_profile.get('target_audience')
demographics = []
if isinstance(target_audience, dict):
demographics = target_audience.get('demographics', [])
expertise_level = target_audience.get('expertise_level', 'intermediate')
if demographics:
enhanced_prompt += f"\n- Target audience: {', '.join(demographics[:2])}, {expertise_level} level"
elif isinstance(target_audience, list):
demographics = target_audience
elif isinstance(target_audience, str):
demographics = [target_audience]
if demographics:
enhanced_prompt += f"\n- Target Audience: {', '.join([str(d) for d in demographics[:3]])}"
# 3. Industry (Canonical)
business_info = canonical_profile.get('business_info', {})
industry = business_info.get('industry')
if industry:
enhanced_prompt += f"\n- Industry Context: {industry}"
# 4. Platform Preferences / Context
if channel:
enhanced_prompt += f"\n- Platform: {channel}"
# Add channel specific constraints if needed, but usually base model handles it well with just platform name
# Add competitive positioning
if competitor_analyses and len(competitor_analyses) > 0:
enhanced_prompt += "\n- Differentiate from competitors, highlight unique value propositions"
# Add marketing context
# 5. Marketing Context
if product_context:
marketing_goal = product_context.get('marketing_goal', '')
if marketing_goal:
enhanced_prompt += f"\n- Marketing goal: {marketing_goal}"
enhanced_prompt += f"\n- Goal: {marketing_goal}"
logger.info(f"[Marketing Copy Prompt] Enhanced for user {user_id}: {enhanced_prompt[:200]}...")
self.logger.info(f"[Marketing Copy Prompt] Enhanced for user {user_id}: {enhanced_prompt[:200]}...")
return enhanced_prompt
except Exception as e:
logger.error(f"[Marketing Copy Prompt] Error building prompt: {str(e)}")
self.logger.error(f"[Marketing Copy Prompt] Error building prompt: {str(e)}")
return base_request
def optimize_marketing_prompt(