Base code
This commit is contained in:
18
backend/services/linkedin/image_prompts/__init__.py
Normal file
18
backend/services/linkedin/image_prompts/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
LinkedIn Image Prompts Package
|
||||
|
||||
This package provides AI-powered image prompt generation for LinkedIn content
|
||||
using Google's Gemini API. It creates three distinct prompt styles optimized
|
||||
for professional business image generation.
|
||||
"""
|
||||
|
||||
from .linkedin_prompt_generator import LinkedInPromptGenerator
|
||||
|
||||
__all__ = [
|
||||
'LinkedInPromptGenerator'
|
||||
]
|
||||
|
||||
# Version information
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "Alwrity Team"
|
||||
__description__ = "LinkedIn AI Image Prompt Generation Services"
|
||||
@@ -0,0 +1,812 @@
|
||||
"""
|
||||
LinkedIn Image Prompt Generator Service
|
||||
|
||||
This service generates AI-optimized image prompts for LinkedIn content using Gemini's
|
||||
capabilities. It creates three distinct prompt styles (professional, creative, industry-specific)
|
||||
following best practices for image generation.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Dict, Any, List, Optional
|
||||
from datetime import datetime
|
||||
from loguru import logger
|
||||
|
||||
# Import existing infrastructure
|
||||
from ...onboarding.api_key_manager import APIKeyManager
|
||||
from ...llm_providers.gemini_provider import gemini_text_response
|
||||
|
||||
|
||||
class LinkedInPromptGenerator:
|
||||
"""
|
||||
Generates AI-optimized image prompts for LinkedIn content.
|
||||
|
||||
This service creates three distinct prompt styles following Gemini API best practices:
|
||||
1. Professional Style - Corporate aesthetics, clean lines, business colors
|
||||
2. Creative Style - Engaging visuals, vibrant colors, social media appeal
|
||||
3. Industry-Specific Style - Tailored to specific business sectors
|
||||
"""
|
||||
|
||||
def __init__(self, api_key_manager: Optional[APIKeyManager] = None):
|
||||
"""
|
||||
Initialize the LinkedIn Prompt Generator.
|
||||
|
||||
Args:
|
||||
api_key_manager: API key manager for Gemini authentication
|
||||
"""
|
||||
self.api_key_manager = api_key_manager or APIKeyManager()
|
||||
self.model = "gemini-2.0-flash-exp"
|
||||
|
||||
# Prompt generation configuration
|
||||
self.max_prompt_length = 500
|
||||
self.style_variations = {
|
||||
'professional': 'corporate, clean, business, professional',
|
||||
'creative': 'engaging, vibrant, creative, social media',
|
||||
'industry_specific': 'industry-tailored, specialized, contextual'
|
||||
}
|
||||
|
||||
logger.info("LinkedIn Prompt Generator initialized")
|
||||
|
||||
async def generate_three_prompts(
|
||||
self,
|
||||
linkedin_content: Dict[str, Any],
|
||||
aspect_ratio: str = "1:1"
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Generate three AI-optimized image prompts for LinkedIn content.
|
||||
|
||||
Args:
|
||||
linkedin_content: LinkedIn content context (topic, industry, content_type, content)
|
||||
aspect_ratio: Desired image aspect ratio
|
||||
|
||||
Returns:
|
||||
List of three prompt objects with style, prompt, and description
|
||||
"""
|
||||
try:
|
||||
start_time = datetime.now()
|
||||
logger.info(f"Generating image prompts for LinkedIn content: {linkedin_content.get('topic', 'Unknown')}")
|
||||
|
||||
# Generate prompts using Gemini
|
||||
prompts = await self._generate_prompts_with_gemini(linkedin_content, aspect_ratio)
|
||||
|
||||
if not prompts or len(prompts) < 3:
|
||||
logger.warning("Gemini prompt generation failed, using fallback prompts")
|
||||
prompts = self._get_fallback_prompts(linkedin_content, aspect_ratio)
|
||||
|
||||
# Ensure exactly 3 prompts
|
||||
prompts = prompts[:3]
|
||||
|
||||
# Validate and enhance prompts
|
||||
enhanced_prompts = []
|
||||
for i, prompt in enumerate(prompts):
|
||||
enhanced_prompt = self._enhance_prompt_for_linkedin(
|
||||
prompt, linkedin_content, aspect_ratio, i
|
||||
)
|
||||
enhanced_prompts.append(enhanced_prompt)
|
||||
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Generated {len(enhanced_prompts)} image prompts in {generation_time:.2f}s")
|
||||
|
||||
return enhanced_prompts
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating LinkedIn image prompts: {str(e)}")
|
||||
return self._get_fallback_prompts(linkedin_content, aspect_ratio)
|
||||
|
||||
async def _generate_prompts_with_gemini(
|
||||
self,
|
||||
linkedin_content: Dict[str, Any],
|
||||
aspect_ratio: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Generate image prompts using Gemini AI.
|
||||
|
||||
Args:
|
||||
linkedin_content: LinkedIn content context
|
||||
aspect_ratio: Image aspect ratio
|
||||
|
||||
Returns:
|
||||
List of generated prompts
|
||||
"""
|
||||
try:
|
||||
# Build the prompt for Gemini
|
||||
gemini_prompt = self._build_gemini_prompt(linkedin_content, aspect_ratio)
|
||||
|
||||
# Generate response using Gemini
|
||||
response = gemini_text_response(
|
||||
prompt=gemini_prompt,
|
||||
temperature=0.7,
|
||||
top_p=0.8,
|
||||
n=1,
|
||||
max_tokens=1000,
|
||||
system_prompt="You are an expert AI image prompt engineer specializing in LinkedIn content optimization."
|
||||
)
|
||||
|
||||
if not response:
|
||||
logger.warning("No response from Gemini prompt generation")
|
||||
return []
|
||||
|
||||
# Parse Gemini response into structured prompts
|
||||
prompts = self._parse_gemini_response(response, linkedin_content)
|
||||
|
||||
return prompts
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in Gemini prompt generation: {str(e)}")
|
||||
return []
|
||||
|
||||
def _build_gemini_prompt(
|
||||
self,
|
||||
linkedin_content: Dict[str, Any],
|
||||
aspect_ratio: str
|
||||
) -> str:
|
||||
"""
|
||||
Build comprehensive prompt for Gemini to generate image prompts.
|
||||
|
||||
Args:
|
||||
linkedin_content: LinkedIn content context
|
||||
aspect_ratio: Image aspect ratio
|
||||
|
||||
Returns:
|
||||
Formatted prompt for Gemini
|
||||
"""
|
||||
topic = linkedin_content.get('topic', 'business')
|
||||
industry = linkedin_content.get('industry', 'business')
|
||||
content_type = linkedin_content.get('content_type', 'post')
|
||||
content = linkedin_content.get('content', '')
|
||||
|
||||
# Extract key content elements for better context
|
||||
content_analysis = self._analyze_content_for_image_context(content, content_type)
|
||||
|
||||
prompt = f"""
|
||||
As an expert AI image prompt engineer specializing in LinkedIn content, generate 3 distinct image generation prompts for the following LinkedIn {content_type}:
|
||||
|
||||
TOPIC: {topic}
|
||||
INDUSTRY: {industry}
|
||||
CONTENT TYPE: {content_type}
|
||||
ASPECT RATIO: {aspect_ratio}
|
||||
|
||||
GENERATED CONTENT:
|
||||
{content}
|
||||
|
||||
CONTENT ANALYSIS:
|
||||
- Key Themes: {content_analysis['key_themes']}
|
||||
- Tone: {content_analysis['tone']}
|
||||
- Visual Elements: {content_analysis['visual_elements']}
|
||||
- Target Audience: {content_analysis['target_audience']}
|
||||
- Content Purpose: {content_analysis['content_purpose']}
|
||||
|
||||
Generate exactly 3 image prompts that directly relate to and enhance the generated content above:
|
||||
|
||||
1. PROFESSIONAL STYLE:
|
||||
- Corporate aesthetics with clean lines and geometric shapes
|
||||
- Professional color palette (blues, grays, whites)
|
||||
- Modern business environment or abstract business concepts
|
||||
- Clean, minimalist design approach
|
||||
- Suitable for B2B and professional networking
|
||||
- MUST directly relate to the specific content themes and industry context above
|
||||
|
||||
2. CREATIVE STYLE:
|
||||
- Eye-catching and engaging visual style
|
||||
- Vibrant colors while maintaining professional appeal
|
||||
- Creative composition that encourages social media engagement
|
||||
- Modern design elements with business context
|
||||
- Optimized for LinkedIn feed visibility
|
||||
- MUST visually represent the key themes and messages from the content above
|
||||
|
||||
3. INDUSTRY-SPECIFIC STYLE:
|
||||
- Tailored specifically to the {industry} industry
|
||||
- Industry-relevant imagery, colors, and visual elements
|
||||
- Professional yet creative approach
|
||||
- Balanced design suitable for business audience
|
||||
- Industry-specific symbolism and aesthetics
|
||||
- MUST incorporate visual elements that directly support the content's industry context
|
||||
|
||||
Each prompt should:
|
||||
- Be specific and detailed (50-100 words)
|
||||
- Include visual composition guidance
|
||||
- Specify color schemes and lighting
|
||||
- Mention LinkedIn optimization
|
||||
- Follow image generation best practices
|
||||
- Be suitable for the {aspect_ratio} aspect ratio
|
||||
- DIRECTLY reference and visualize the key themes, messages, and context from the generated content above
|
||||
- Create images that would naturally accompany and enhance the specific LinkedIn content provided
|
||||
|
||||
Return the prompts in this exact JSON format:
|
||||
[
|
||||
{{
|
||||
"style": "Professional",
|
||||
"prompt": "Detailed prompt description that directly relates to the content above...",
|
||||
"description": "Brief description of the visual style and how it relates to the content"
|
||||
}},
|
||||
{{
|
||||
"style": "Creative",
|
||||
"prompt": "Detailed prompt description that directly relates to the content above...",
|
||||
"description": "Brief description of the visual style and how it relates to the content"
|
||||
}},
|
||||
{{
|
||||
"style": "Industry-Specific",
|
||||
"prompt": "Detailed prompt description that directly relates to the content above...",
|
||||
"description": "Brief description of the visual style and how it relates to the content"
|
||||
}}
|
||||
]
|
||||
|
||||
Focus on creating prompts that will generate high-quality, LinkedIn-optimized images that directly enhance and complement the specific content provided above.
|
||||
"""
|
||||
|
||||
return prompt.strip()
|
||||
|
||||
def _analyze_content_for_image_context(self, content: str, content_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze the generated LinkedIn content to extract key elements for image context.
|
||||
|
||||
Args:
|
||||
content: The generated LinkedIn content
|
||||
content_type: Type of content (post, article, carousel, etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary containing content analysis for image generation
|
||||
"""
|
||||
try:
|
||||
# Basic content analysis
|
||||
content_lower = content.lower()
|
||||
word_count = len(content.split())
|
||||
|
||||
# Extract key themes based on content analysis
|
||||
key_themes = self._extract_key_themes(content_lower, content_type)
|
||||
|
||||
# Determine tone based on content analysis
|
||||
tone = self._determine_content_tone(content_lower)
|
||||
|
||||
# Identify visual elements that could be represented
|
||||
visual_elements = self._identify_visual_elements(content_lower, content_type)
|
||||
|
||||
# Determine target audience
|
||||
target_audience = self._determine_target_audience(content_lower, content_type)
|
||||
|
||||
# Determine content purpose
|
||||
content_purpose = self._determine_content_purpose(content_lower, content_type)
|
||||
|
||||
return {
|
||||
'key_themes': ', '.join(key_themes),
|
||||
'tone': tone,
|
||||
'visual_elements': ', '.join(visual_elements),
|
||||
'target_audience': target_audience,
|
||||
'content_purpose': content_purpose,
|
||||
'word_count': word_count,
|
||||
'content_type': content_type
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error analyzing content for image context: {str(e)}")
|
||||
return {
|
||||
'key_themes': 'business, professional',
|
||||
'tone': 'professional',
|
||||
'visual_elements': 'business concepts',
|
||||
'target_audience': 'professionals',
|
||||
'content_purpose': 'informational',
|
||||
'word_count': len(content.split()) if content else 0,
|
||||
'content_type': content_type
|
||||
}
|
||||
|
||||
def _extract_key_themes(self, content_lower: str, content_type: str) -> List[str]:
|
||||
"""Extract key themes from the content for image generation context."""
|
||||
themes = []
|
||||
|
||||
# Industry and business themes
|
||||
if any(word in content_lower for word in ['ai', 'artificial intelligence', 'machine learning']):
|
||||
themes.append('AI & Technology')
|
||||
if any(word in content_lower for word in ['marketing', 'branding', 'advertising']):
|
||||
themes.append('Marketing & Branding')
|
||||
if any(word in content_lower for word in ['leadership', 'management', 'strategy']):
|
||||
themes.append('Leadership & Strategy')
|
||||
if any(word in content_lower for word in ['innovation', 'growth', 'transformation']):
|
||||
themes.append('Innovation & Growth')
|
||||
if any(word in content_lower for word in ['data', 'analytics', 'insights']):
|
||||
themes.append('Data & Analytics')
|
||||
if any(word in content_lower for word in ['customer', 'user experience', 'engagement']):
|
||||
themes.append('Customer Experience')
|
||||
if any(word in content_lower for word in ['team', 'collaboration', 'workplace']):
|
||||
themes.append('Team & Collaboration')
|
||||
if any(word in content_lower for word in ['sustainability', 'environmental', 'green']):
|
||||
themes.append('Sustainability')
|
||||
if any(word in content_lower for word in ['finance', 'investment', 'economy']):
|
||||
themes.append('Finance & Economy')
|
||||
if any(word in content_lower for word in ['healthcare', 'medical', 'wellness']):
|
||||
themes.append('Healthcare & Wellness')
|
||||
|
||||
# Content type specific themes
|
||||
if content_type == 'post':
|
||||
if any(word in content_lower for word in ['tip', 'advice', 'insight']):
|
||||
themes.append('Tips & Advice')
|
||||
if any(word in content_lower for word in ['story', 'experience', 'journey']):
|
||||
themes.append('Personal Story')
|
||||
if any(word in content_lower for word in ['trend', 'future', 'prediction']):
|
||||
themes.append('Trends & Future')
|
||||
|
||||
elif content_type == 'article':
|
||||
if any(word in content_lower for word in ['research', 'study', 'analysis']):
|
||||
themes.append('Research & Analysis')
|
||||
if any(word in content_lower for word in ['case study', 'example', 'success']):
|
||||
themes.append('Case Studies')
|
||||
if any(word in content_lower for word in ['guide', 'tutorial', 'how-to']):
|
||||
themes.append('Educational Content')
|
||||
|
||||
elif content_type == 'carousel':
|
||||
if any(word in content_lower for word in ['steps', 'process', 'framework']):
|
||||
themes.append('Process & Framework')
|
||||
if any(word in content_lower for word in ['comparison', 'vs', 'difference']):
|
||||
themes.append('Comparison & Analysis')
|
||||
if any(word in content_lower for word in ['checklist', 'tips', 'best practices']):
|
||||
themes.append('Checklists & Best Practices')
|
||||
|
||||
# Default theme if none identified
|
||||
if not themes:
|
||||
themes.append('Business & Professional')
|
||||
|
||||
return themes[:3] # Limit to top 3 themes
|
||||
|
||||
def _determine_content_tone(self, content_lower: str) -> str:
|
||||
"""Determine the tone of the content for appropriate image styling."""
|
||||
if any(word in content_lower for word in ['excited', 'amazing', 'incredible', 'revolutionary']):
|
||||
return 'Enthusiastic & Dynamic'
|
||||
elif any(word in content_lower for word in ['challenge', 'problem', 'issue', 'difficult']):
|
||||
return 'Thoughtful & Analytical'
|
||||
elif any(word in content_lower for word in ['success', 'achievement', 'win', 'victory']):
|
||||
return 'Celebratory & Positive'
|
||||
elif any(word in content_lower for word in ['guide', 'tutorial', 'how-to', 'steps']):
|
||||
return 'Educational & Helpful'
|
||||
elif any(word in content_lower for word in ['trend', 'future', 'prediction', 'forecast']):
|
||||
return 'Forward-looking & Innovative'
|
||||
else:
|
||||
return 'Professional & Informative'
|
||||
|
||||
def _identify_visual_elements(self, content_lower: str, content_type: str) -> List[str]:
|
||||
"""Identify visual elements that could be represented in images."""
|
||||
visual_elements = []
|
||||
|
||||
# Technology and digital elements
|
||||
if any(word in content_lower for word in ['ai', 'robot', 'computer', 'digital']):
|
||||
visual_elements.extend(['Digital interfaces', 'Technology symbols', 'Abstract tech patterns'])
|
||||
|
||||
# Business and professional elements
|
||||
if any(word in content_lower for word in ['business', 'corporate', 'office', 'meeting']):
|
||||
visual_elements.extend(['Business environments', 'Professional settings', 'Corporate aesthetics'])
|
||||
|
||||
# Growth and progress elements
|
||||
if any(word in content_lower for word in ['growth', 'progress', 'improvement', 'success']):
|
||||
visual_elements.extend(['Growth charts', 'Progress indicators', 'Success symbols'])
|
||||
|
||||
# Data and analytics elements
|
||||
if any(word in content_lower for word in ['data', 'analytics', 'charts', 'metrics']):
|
||||
visual_elements.extend(['Data visualizations', 'Charts and graphs', 'Analytics dashboards'])
|
||||
|
||||
# Team and collaboration elements
|
||||
if any(word in content_lower for word in ['team', 'collaboration', 'partnership', 'network']):
|
||||
visual_elements.extend(['Team dynamics', 'Collaboration symbols', 'Network connections'])
|
||||
|
||||
# Industry-specific elements
|
||||
if 'healthcare' in content_lower:
|
||||
visual_elements.extend(['Medical symbols', 'Healthcare imagery', 'Wellness elements'])
|
||||
elif 'finance' in content_lower:
|
||||
visual_elements.extend(['Financial symbols', 'Money concepts', 'Investment imagery'])
|
||||
elif 'education' in content_lower:
|
||||
visual_elements.extend(['Learning symbols', 'Educational elements', 'Knowledge imagery'])
|
||||
|
||||
# Default visual elements
|
||||
if not visual_elements:
|
||||
visual_elements = ['Professional business concepts', 'Modern design elements', 'Corporate aesthetics']
|
||||
|
||||
return visual_elements[:4] # Limit to top 4 elements
|
||||
|
||||
def _determine_target_audience(self, content_lower: str, content_type: str) -> str:
|
||||
"""Determine the target audience for the content."""
|
||||
if any(word in content_lower for word in ['ceo', 'executive', 'leader', 'manager']):
|
||||
return 'C-Suite & Executives'
|
||||
elif any(word in content_lower for word in ['entrepreneur', 'startup', 'founder', 'business owner']):
|
||||
return 'Entrepreneurs & Business Owners'
|
||||
elif any(word in content_lower for word in ['marketer', 'sales', 'business development']):
|
||||
return 'Marketing & Sales Professionals'
|
||||
elif any(word in content_lower for word in ['developer', 'engineer', 'technical', 'it']):
|
||||
return 'Technical Professionals'
|
||||
elif any(word in content_lower for word in ['student', 'learner', 'aspiring', 'career']):
|
||||
return 'Students & Career Changers'
|
||||
else:
|
||||
return 'General Business Professionals'
|
||||
|
||||
def _determine_content_purpose(self, content_lower: str, content_type: str) -> str:
|
||||
"""Determine the primary purpose of the content."""
|
||||
if any(word in content_lower for word in ['tip', 'advice', 'how-to', 'guide']):
|
||||
return 'Educational & Instructional'
|
||||
elif any(word in content_lower for word in ['story', 'experience', 'journey', 'case study']):
|
||||
return 'Storytelling & Experience Sharing'
|
||||
elif any(word in content_lower for word in ['trend', 'prediction', 'future', 'insight']):
|
||||
return 'Trend Analysis & Forecasting'
|
||||
elif any(word in content_lower for word in ['challenge', 'problem', 'solution', 'strategy']):
|
||||
return 'Problem Solving & Strategy'
|
||||
elif any(word in content_lower for word in ['success', 'achievement', 'result', 'outcome']):
|
||||
return 'Success Showcase & Results'
|
||||
else:
|
||||
return 'Informational & Awareness'
|
||||
|
||||
def _parse_gemini_response(
|
||||
self,
|
||||
response: str,
|
||||
linkedin_content: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Parse Gemini response into structured prompt objects.
|
||||
|
||||
Args:
|
||||
response: Raw response from Gemini
|
||||
linkedin_content: LinkedIn content context
|
||||
|
||||
Returns:
|
||||
List of parsed prompt objects
|
||||
"""
|
||||
try:
|
||||
# Try to extract JSON from response
|
||||
import json
|
||||
import re
|
||||
|
||||
# Look for JSON array in the response
|
||||
json_match = re.search(r'\[.*\]', response, re.DOTALL)
|
||||
if json_match:
|
||||
json_str = json_match.group(0)
|
||||
prompts = json.loads(json_str)
|
||||
|
||||
# Validate prompt structure
|
||||
if isinstance(prompts, list) and len(prompts) >= 3:
|
||||
return prompts[:3]
|
||||
|
||||
# Fallback: parse response manually
|
||||
return self._parse_response_manually(response, linkedin_content)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error parsing Gemini response: {str(e)}")
|
||||
return self._parse_response_manually(response, linkedin_content)
|
||||
|
||||
def _parse_response_manually(
|
||||
self,
|
||||
response: str,
|
||||
linkedin_content: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Manually parse response if JSON parsing fails.
|
||||
|
||||
Args:
|
||||
response: Raw response from Gemini
|
||||
linkedin_content: LinkedIn content context
|
||||
|
||||
Returns:
|
||||
List of parsed prompt objects
|
||||
"""
|
||||
try:
|
||||
prompts = []
|
||||
lines = response.split('\n')
|
||||
|
||||
current_style = None
|
||||
current_prompt = []
|
||||
current_description = None
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if 'professional' in line.lower() and 'style' in line.lower():
|
||||
if current_style and current_prompt:
|
||||
prompts.append({
|
||||
'style': current_style,
|
||||
'prompt': ' '.join(current_prompt),
|
||||
'description': current_description or f'{current_style} style for LinkedIn'
|
||||
})
|
||||
current_style = 'Professional'
|
||||
current_prompt = []
|
||||
current_description = None
|
||||
|
||||
elif 'creative' in line.lower() and 'style' in line.lower():
|
||||
if current_style and current_prompt:
|
||||
prompts.append({
|
||||
'style': current_style,
|
||||
'prompt': ' '.join(current_prompt),
|
||||
'description': current_description or f'{current_style} style for LinkedIn'
|
||||
})
|
||||
current_style = 'Creative'
|
||||
current_prompt = []
|
||||
current_description = None
|
||||
|
||||
elif 'industry' in line.lower() and 'specific' in line.lower():
|
||||
if current_style and current_prompt:
|
||||
prompts.append({
|
||||
'style': current_style,
|
||||
'prompt': ' '.join(current_prompt),
|
||||
'description': current_description or f'{current_style} style for LinkedIn'
|
||||
})
|
||||
current_style = 'Industry-Specific'
|
||||
current_prompt = []
|
||||
current_description = None
|
||||
|
||||
elif line and not line.startswith('-') and current_style:
|
||||
current_prompt.append(line)
|
||||
|
||||
elif line.startswith('description:') and current_style:
|
||||
current_description = line.replace('description:', '').strip()
|
||||
|
||||
# Add the last prompt
|
||||
if current_style and current_prompt:
|
||||
prompts.append({
|
||||
'style': current_style,
|
||||
'prompt': ' '.join(current_prompt),
|
||||
'description': current_description or f'{current_style} style for LinkedIn'
|
||||
})
|
||||
|
||||
# Ensure we have exactly 3 prompts
|
||||
while len(prompts) < 3:
|
||||
style_name = ['Professional', 'Creative', 'Industry-Specific'][len(prompts)]
|
||||
prompts.append({
|
||||
'style': style_name,
|
||||
'prompt': f"Create a {style_name.lower()} LinkedIn image for {linkedin_content.get('topic', 'business')}",
|
||||
'description': f'{style_name} style for LinkedIn content'
|
||||
})
|
||||
|
||||
return prompts[:3]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in manual response parsing: {str(e)}")
|
||||
return self._get_fallback_prompts(linkedin_content, "1:1")
|
||||
|
||||
def _enhance_prompt_for_linkedin(
|
||||
self,
|
||||
prompt: Dict[str, Any],
|
||||
linkedin_content: Dict[str, Any],
|
||||
aspect_ratio: str,
|
||||
prompt_index: int
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Enhance individual prompt with LinkedIn-specific optimizations.
|
||||
|
||||
Args:
|
||||
prompt: Individual prompt object
|
||||
linkedin_content: LinkedIn content context
|
||||
aspect_ratio: Image aspect ratio
|
||||
prompt_index: Index of the prompt (0-2)
|
||||
|
||||
Returns:
|
||||
Enhanced prompt object
|
||||
"""
|
||||
try:
|
||||
topic = linkedin_content.get('topic', 'business')
|
||||
industry = linkedin_content.get('industry', 'business')
|
||||
content_type = linkedin_content.get('content_type', 'post')
|
||||
|
||||
# Get the base prompt text
|
||||
base_prompt = prompt.get('prompt', '')
|
||||
style = prompt.get('style', 'Professional')
|
||||
|
||||
# LinkedIn-specific enhancements based on style
|
||||
if style == 'Professional':
|
||||
enhancements = [
|
||||
f"Professional LinkedIn {content_type} image for {topic}",
|
||||
"Corporate aesthetics with clean lines and geometric shapes",
|
||||
"Professional color palette (blues, grays, whites)",
|
||||
"Modern business environment or abstract business concepts",
|
||||
f"Aspect ratio: {aspect_ratio}",
|
||||
"Mobile-optimized for LinkedIn feed viewing",
|
||||
"High-quality, professional business aesthetic"
|
||||
]
|
||||
elif style == 'Creative':
|
||||
enhancements = [
|
||||
f"Creative LinkedIn {content_type} image for {topic}",
|
||||
"Eye-catching and engaging visual style",
|
||||
"Vibrant colors while maintaining professional appeal",
|
||||
"Creative composition that encourages social media engagement",
|
||||
f"Aspect ratio: {aspect_ratio}",
|
||||
"Optimized for LinkedIn feed visibility and sharing",
|
||||
"Modern design elements with business context"
|
||||
]
|
||||
else: # Industry-Specific
|
||||
enhancements = [
|
||||
f"{industry} industry-specific LinkedIn {content_type} image for {topic}",
|
||||
f"Industry-relevant imagery and colors for {industry}",
|
||||
"Professional yet creative approach",
|
||||
"Balanced design suitable for business audience",
|
||||
f"Aspect ratio: {aspect_ratio}",
|
||||
f"Industry-specific symbolism and {industry} aesthetics",
|
||||
"Professional business appeal for LinkedIn"
|
||||
]
|
||||
|
||||
# Combine base prompt with enhancements
|
||||
enhanced_prompt_text = f"{base_prompt}\n\n"
|
||||
enhanced_prompt_text += "\n".join(enhancements)
|
||||
|
||||
# Ensure prompt length is within limits
|
||||
if len(enhanced_prompt_text) > self.max_prompt_length:
|
||||
enhanced_prompt_text = enhanced_prompt_text[:self.max_prompt_length] + "..."
|
||||
|
||||
return {
|
||||
'style': style,
|
||||
'prompt': enhanced_prompt_text,
|
||||
'description': prompt.get('description', f'{style} style for LinkedIn'),
|
||||
'prompt_index': prompt_index,
|
||||
'enhanced_at': datetime.now().isoformat(),
|
||||
'linkedin_optimized': True
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error enhancing prompt: {str(e)}")
|
||||
return prompt
|
||||
|
||||
def _get_fallback_prompts(
|
||||
self,
|
||||
linkedin_content: Dict[str, Any],
|
||||
aspect_ratio: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Generate fallback prompts if AI generation fails.
|
||||
|
||||
Args:
|
||||
linkedin_content: LinkedIn content context
|
||||
aspect_ratio: Image aspect ratio
|
||||
|
||||
Returns:
|
||||
List of fallback prompt objects
|
||||
"""
|
||||
topic = linkedin_content.get('topic', 'business')
|
||||
industry = linkedin_content.get('industry', 'business')
|
||||
content_type = linkedin_content.get('content_type', 'post')
|
||||
content = linkedin_content.get('content', '')
|
||||
|
||||
# Analyze content for better context
|
||||
content_analysis = self._analyze_content_for_image_context(content, content_type)
|
||||
|
||||
# Create context-aware fallback prompts
|
||||
fallback_prompts = [
|
||||
{
|
||||
'style': 'Professional',
|
||||
'prompt': f"""Create a professional LinkedIn {content_type} image for {topic} in the {industry} industry.
|
||||
|
||||
Key Content Themes: {content_analysis['key_themes']}
|
||||
Content Tone: {content_analysis['tone']}
|
||||
Visual Elements: {content_analysis['visual_elements']}
|
||||
|
||||
Corporate aesthetics with clean lines and geometric shapes
|
||||
Professional color palette (blues, grays, whites)
|
||||
Modern business environment or abstract business concepts
|
||||
Aspect ratio: {aspect_ratio}
|
||||
Mobile-optimized for LinkedIn feed viewing
|
||||
High-quality, professional business aesthetic
|
||||
Directly represents the content themes: {content_analysis['key_themes']}""",
|
||||
'description': f'Clean, business-appropriate visual for LinkedIn {content_type} about {topic}',
|
||||
'prompt_index': 0,
|
||||
'fallback': True,
|
||||
'content_context': content_analysis
|
||||
},
|
||||
{
|
||||
'style': 'Creative',
|
||||
'prompt': f"""Generate a creative LinkedIn {content_type} image for {topic} in {industry}.
|
||||
|
||||
Key Content Themes: {content_analysis['key_themes']}
|
||||
Content Purpose: {content_analysis['content_purpose']}
|
||||
Target Audience: {content_analysis['target_audience']}
|
||||
|
||||
Eye-catching and engaging visual style
|
||||
Vibrant colors while maintaining professional appeal
|
||||
Creative composition that encourages social media engagement
|
||||
Aspect ratio: {aspect_ratio}
|
||||
Optimized for LinkedIn feed visibility and sharing
|
||||
Modern design elements with business context
|
||||
Visually represents: {content_analysis['visual_elements']}""",
|
||||
'description': f'Eye-catching, shareable design for LinkedIn {content_type} about {topic}',
|
||||
'prompt_index': 1,
|
||||
'fallback': True,
|
||||
'content_context': content_analysis
|
||||
},
|
||||
{
|
||||
'style': 'Industry-Specific',
|
||||
'prompt': f"""Design a {industry} industry-specific LinkedIn {content_type} image for {topic}.
|
||||
|
||||
Key Content Themes: {content_analysis['key_themes']}
|
||||
Content Tone: {content_analysis['tone']}
|
||||
Visual Elements: {content_analysis['visual_elements']}
|
||||
|
||||
Industry-relevant imagery and colors for {industry}
|
||||
Professional yet creative approach
|
||||
Balanced design suitable for business audience
|
||||
Aspect ratio: {aspect_ratio}
|
||||
Industry-specific symbolism and {industry} aesthetics
|
||||
Professional business appeal for LinkedIn
|
||||
Incorporates visual elements: {content_analysis['visual_elements']}""",
|
||||
'description': f'Industry-tailored professional design for {industry} {content_type} about {topic}',
|
||||
'prompt_index': 2,
|
||||
'fallback': True,
|
||||
'content_context': content_analysis
|
||||
}
|
||||
]
|
||||
|
||||
logger.info(f"Using context-aware fallback prompts for LinkedIn {content_type} about {topic}")
|
||||
return fallback_prompts
|
||||
|
||||
async def validate_prompt_quality(
|
||||
self,
|
||||
prompt: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate the quality of a generated prompt.
|
||||
|
||||
Args:
|
||||
prompt: Prompt object to validate
|
||||
|
||||
Returns:
|
||||
Validation results
|
||||
"""
|
||||
try:
|
||||
prompt_text = prompt.get('prompt', '')
|
||||
style = prompt.get('style', '')
|
||||
|
||||
# Quality metrics
|
||||
length_score = min(len(prompt_text) / 100, 1.0) # Optimal length around 100 words
|
||||
specificity_score = self._calculate_specificity_score(prompt_text)
|
||||
linkedin_optimization_score = self._calculate_linkedin_optimization_score(prompt_text)
|
||||
|
||||
# Overall quality score
|
||||
overall_score = (length_score + specificity_score + linkedin_optimization_score) / 3
|
||||
|
||||
return {
|
||||
'valid': overall_score >= 0.7,
|
||||
'overall_score': round(overall_score, 2),
|
||||
'metrics': {
|
||||
'length_score': round(length_score, 2),
|
||||
'specificity_score': round(specificity_score, 2),
|
||||
'linkedin_optimization_score': round(linkedin_optimization_score, 2)
|
||||
},
|
||||
'recommendations': self._get_quality_recommendations(overall_score, prompt_text)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error validating prompt quality: {str(e)}")
|
||||
return {
|
||||
'valid': False,
|
||||
'overall_score': 0.0,
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
def _calculate_specificity_score(self, prompt_text: str) -> float:
|
||||
"""Calculate how specific and detailed the prompt is."""
|
||||
# Count specific visual elements, colors, styles mentioned
|
||||
specific_elements = [
|
||||
'wide-angle', 'close-up', 'low-angle', 'aerial',
|
||||
'blue', 'gray', 'white', 'red', 'green', 'yellow',
|
||||
'modern', 'minimalist', 'corporate', 'professional',
|
||||
'geometric', 'clean lines', 'sharp focus', 'soft lighting'
|
||||
]
|
||||
|
||||
element_count = sum(1 for element in specific_elements if element.lower() in prompt_text.lower())
|
||||
return min(element_count / 8, 1.0) # Normalize to 0-1
|
||||
|
||||
def _calculate_linkedin_optimization_score(self, prompt_text: str) -> float:
|
||||
"""Calculate how well the prompt is optimized for LinkedIn."""
|
||||
linkedin_keywords = [
|
||||
'linkedin', 'professional', 'business', 'corporate',
|
||||
'mobile', 'feed', 'social media', 'engagement',
|
||||
'networking', 'professional audience'
|
||||
]
|
||||
|
||||
keyword_count = sum(1 for keyword in linkedin_keywords if keyword.lower() in prompt_text.lower())
|
||||
return min(keyword_count / 5, 1.0) # Normalize to 0-1
|
||||
|
||||
def _get_quality_recommendations(self, score: float, prompt_text: str) -> List[str]:
|
||||
"""Get recommendations for improving prompt quality."""
|
||||
recommendations = []
|
||||
|
||||
if score < 0.7:
|
||||
if len(prompt_text) < 100:
|
||||
recommendations.append("Add more specific visual details and composition guidance")
|
||||
|
||||
if 'linkedin' not in prompt_text.lower():
|
||||
recommendations.append("Include LinkedIn-specific optimization terms")
|
||||
|
||||
if 'aspect ratio' not in prompt_text.lower():
|
||||
recommendations.append("Specify the desired aspect ratio")
|
||||
|
||||
if 'professional' not in prompt_text.lower() and 'business' not in prompt_text.lower():
|
||||
recommendations.append("Include professional business aesthetic guidance")
|
||||
|
||||
return recommendations
|
||||
Reference in New Issue
Block a user