nniehaus's picture
Update app.py
e91311e verified
import streamlit as st
import os
import re
# Handle both old and new OpenAI library versions
try:
from openai import OpenAI
OPENAI_V1 = True
except ImportError:
import openai
OPENAI_V1 = False
# Initialize OpenAI client
def get_openai_client():
api_key = os.getenv("OPENAI_API_KEY") or st.secrets.get("OPENAI_API_KEY")
if not api_key:
st.error("OpenAI API key not found. Please set OPENAI_API_KEY in your environment variables or Streamlit secrets.")
return None
if OPENAI_V1:
return OpenAI(api_key=api_key)
else:
openai.api_key = api_key
return openai
# Subject line analysis functions
def analyze_subject_line(subject_line):
"""Analyze subject line for key metrics"""
analysis = {
'character_count': len(subject_line),
'word_count': len(subject_line.split()),
'mobile_preview': subject_line[:33] + "..." if len(subject_line) > 33 else subject_line,
'has_emoji': bool(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF\U00002600-\U000027BF]', subject_line)),
'has_numbers': bool(re.search(r'\d', subject_line)),
'exclamation_count': subject_line.count('!'),
}
# Simple scoring
score = 8 # Start with good score
# Length optimization (2-4 words ideal, 33 chars max for mobile)
if analysis['word_count'] <= 4:
score += 1
elif analysis['word_count'] > 7:
score -= 1
if analysis['character_count'] <= 33:
score += 1
elif analysis['character_count'] > 50:
score -= 2
# Bonuses
if analysis['has_numbers']:
score += 1
if analysis['has_emoji']:
score += 1
# Penalties
if analysis['exclamation_count'] > 1:
score -= 2
analysis['score'] = max(1, min(10, score))
return analysis
def generate_subject_lines_advanced(audience, message, tone, psychology_framework, industry, personalization, client):
"""Generate high-quality, compelling subject lines"""
try:
framework_strategies = {
"FOMO (Fear of Missing Out)": "Create genuine urgency and scarcity. Use time-sensitive language, countdown elements, or limited availability",
"Curiosity Gap": "Create compelling information gaps that demand closure. Hint at valuable secrets, surprising revelations, or counterintuitive insights",
"Social Proof": "Leverage what others are doing - specific user numbers, testimonials, trending status, or peer behavior",
"Loss Aversion": "Frame what they're currently missing or losing rather than what they could gain",
"Reciprocity": "Lead with valuable gifts, free resources, or helpful insights before asking for anything",
"Authority": "Reference credible experts, research data, proven methodologies, or industry leadership",
"Scarcity": "Emphasize genuine limited availability, exclusive access, or member-only benefits"
}
framework_strategy = framework_strategies.get(psychology_framework, '')
prompt_text = f"""You are a world-class email copywriter who has written subject lines for the highest-performing email campaigns. Your subject lines are known for being irresistibly compelling while staying authentic.
Campaign Context:
- Target Audience: {audience}
- Core Message: {message}
- Brand Tone: {tone}
- Psychological Strategy: {psychology_framework}
- Industry Context: {industry}
- Personalization Approach: {personalization}
Advanced Strategy: {framework_strategy}
Your Mission: Create 10 subject lines that would make someone instantly stop scrolling and click. Each should feel fresh, compelling, and impossible to ignore.
CRITICAL EMOJI REQUIREMENT: Include emojis in at least 6 out of 10 subject lines (60%+). Emojis increase open rates by 39% when used appropriately. Place them strategically at the beginning or end for maximum impact.
LENGTH VARIATION REQUIREMENT:
- 7 subject lines: Ultra-short (2-4 words, max 33 characters) for mobile optimization
- 2 subject lines: Medium length (5-7 words, 34-50 characters) for desktop impact
- 1 subject line: Longer format (8+ words, 51-65 characters) for storytelling appeal
Quality Requirements:
1. Apply {psychology_framework} psychology masterfully, not obviously
2. Include power words that trigger emotional response
3. Use numbers strategically (proven to increase opens by 45%)
4. Make each line feel unique and scroll-stopping
5. Avoid clichés, generic phrases, or obvious sales language
6. Ensure emojis enhance rather than distract from the message
Examples of compelling techniques:
- Specific numbers: "2-min trick", "73% faster", "$847 saved"
- Intriguing questions: "Still doing this?", "Why X?", "Ready for Y?"
- Surprising statements: "We're wrong", "This failed", "Oops..."
- Personal urgency: "Your deadline", "Behind schedule?", "Missing this?"
CRITICAL: Output format must be exactly:
**1.** [subject line] (Length: Short/Medium/Long)
[preheader text]
**2.** [subject line] (Length: Short/Medium/Long)
[preheader text]
**3.** [subject line] (Length: Short/Medium/Long)
[preheader text]
Continue for all 10 options. Make each subject line feel like it was crafted by a master copywriter, not an AI tool. Remember: 6+ must have emojis, vary the lengths as specified."""
if OPENAI_V1:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt_text}],
max_tokens=1000,
temperature=0.7
)
return response.choices[0].message.content
else:
response = client.ChatCompletion.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt_text}],
max_tokens=1000,
temperature=0.7
)
return response.choices[0].message.content
except Exception as e:
st.error(f"Error generating subject lines: {str(e)}")
return None
def optimize_existing_subject_line_clean(existing_subject, existing_preheader, audience, message, tone, psychology_framework, industry, personalization, client):
"""Advanced optimization with superior subject lines"""
try:
analysis = analyze_subject_line(existing_subject)
preheader_text = f"Current preheader: {existing_preheader}" if existing_preheader else "No preheader provided"
prompt_text = f"""You are an elite email optimization specialist who transforms mediocre subject lines into irresistible ones. Your optimizations consistently achieve 40%+ higher open rates.
CURRENT SUBJECT LINE ANALYSIS:
"{existing_subject}"
{preheader_text}
Length: {analysis['character_count']} characters, {analysis['word_count']} words
Mobile Preview: "{analysis['mobile_preview']}"
Current Effectiveness Score: {analysis['score']}/10
OPTIMIZATION BRIEF:
- Target Audience: {audience}
- Core Message: {message}
- Desired Tone: {tone}
- Psychology to Apply: {psychology_framework}
- Industry Context: {industry}
- Personalization Strategy: {personalization}
YOUR MISSION: Transform this into 5 compelling alternatives that would dramatically outperform the original.
CRITICAL EMOJI REQUIREMENT: Include emojis in at least 3 out of 5 optimized options (60%+). Emojis increase open rates by 39% when used strategically.
LENGTH VARIATION REQUIREMENT:
- 3 options: Ultra-short (2-4 words, max 33 characters) for mobile optimization
- 1 option: Medium length (5-7 words, 34-50 characters) for desktop impact
- 1 option: Longer format (8+ words, 51-65 characters) for storytelling appeal
Advanced Optimization Strategies:
1. Apply {psychology_framework} psychology masterfully
2. Maximize mobile impact while offering variety
3. Use power words that trigger immediate action
4. Include specific numbers or percentages when relevant
5. Create genuine intrigue without being misleading
6. Leverage industry-specific pain points and desires
7. Make each option feel scroll-stopping and urgent
Quality Standards:
- Each subject line should feel like it was written by the world's best copywriter
- Avoid generic, cliché, or obviously promotional language
- Create genuine curiosity and emotional pull
- Ensure mobile-first optimization with strategic longer options
- Focus on what makes people instantly want to click
EXACT OUTPUT FORMAT:
**Current:** {existing_subject} (Score: {analysis['score']}/10)
**Option 1:** [compelling subject line] (Length: Short/Medium/Long)
[irresistible preheader]
**Option 2:** [compelling subject line] (Length: Short/Medium/Long)
[irresistible preheader]
**Option 3:** [compelling subject line] (Length: Short/Medium/Long)
[irresistible preheader]
**Option 4:** [compelling subject line] (Length: Short/Medium/Long)
[irresistible preheader]
**Option 5:** [compelling subject line] (Length: Short/Medium/Long)
[irresistible preheader]
Make each option significantly more compelling than the original. Remember: 3+ must have emojis, vary lengths as specified."""
if OPENAI_V1:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt_text}],
max_tokens=1200,
temperature=0.7
)
return response.choices[0].message.content
else:
response = client.ChatCompletion.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt_text}],
max_tokens=1200,
temperature=0.7
)
return response.choices[0].message.content
except Exception as e:
st.error(f"Error optimizing subject line: {str(e)}")
return None
def main():
st.set_page_config(page_title="Email Subject Line Optimizer", layout="wide")
# Clean, minimal styling
st.markdown("""
<style>
.main-header { text-align: center; margin-bottom: 2rem; }
.score-box {
padding: 8px 12px;
border-radius: 6px;
text-align: center;
font-weight: bold;
margin: 8px 0;
}
.score-good { background-color: #d4edda; color: #155724; }
.score-okay { background-color: #fff3cd; color: #856404; }
.score-poor { background-color: #f8d7da; color: #721c24; }
.preview-mobile {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
font-family: monospace;
border-left: 3px solid #007bff;
}
.sidebar .stSelectbox label { font-size: 14px !important; }
.stExpander { margin: 0.5rem 0; }
</style>
""", unsafe_allow_html=True)
# Header
st.markdown('<div class="main-header">', unsafe_allow_html=True)
st.markdown("# 🚀 Email Subject Line Optimizer")
st.markdown("*Generate high-converting subject lines using psychological frameworks*")
st.markdown('</div>', unsafe_allow_html=True)
# Initialize OpenAI client
client = get_openai_client()
if not client:
return
# Mode selection
mode = st.radio(
"Choose your mode:",
["Generate New Subject Lines", "Optimize Existing Subject Line"],
horizontal=True
)
st.markdown("---")
# Main content in columns
col1, col2 = st.columns([2, 1])
with col1:
# Basic inputs
audience_desc = st.text_input(
"Target audience:",
placeholder="e.g., busy entrepreneurs, working mothers, SaaS buyers"
)
message_content = st.text_area(
"Main message or offer:",
placeholder="e.g., 50% off sale, free webinar, product launch",
height=80
)
# Framework selection in columns
fcol1, fcol2 = st.columns(2)
with fcol1:
tone = st.selectbox(
"Tone:",
['Friendly', 'Professional', 'Urgent', 'Curious', 'Playful', 'Authoritative']
)
psychology_framework = st.selectbox(
"Psychology:",
['Curiosity Gap', 'FOMO (Fear of Missing Out)', 'Social Proof', 'Scarcity', 'Reciprocity', 'Authority', 'Loss Aversion']
)
with fcol2:
industry = st.selectbox(
"Industry:",
['E-commerce', 'SaaS/Tech', 'B2B Services', 'Healthcare', 'Education', 'Finance', 'Marketing', 'Other']
)
personalization = st.selectbox(
"Personalization:",
['First Name', 'Location', 'Purchase History', 'Behavior', 'Industry', 'Seasonal', 'None']
)
with col2:
# Compact tips section
with st.expander("📱 Quick Tips", expanded=False):
st.markdown("""
**Mobile First:** 33 chars max
**Optimal:** 2-4 words
**Numbers:** +45% opens
**Emojis:** +39% (use in 60%+ of options)
**Personalization:** +26% opens
**Length Variety:** Mix short & medium options
""")
with st.expander("🧠 Psychology Guide", expanded=False):
st.markdown("""
**Curiosity:** Leave gaps, ask questions
**FOMO:** "Last chance", "Limited time"
**Social Proof:** "Join 1000+ users"
**Scarcity:** "Only 3 left"
**Reciprocity:** Free value first
**Authority:** Expert credibility
""")
# Mode-specific sections
if mode == "Optimize Existing Subject Line":
st.markdown("### Current Subject Line")
ocol1, ocol2 = st.columns([2, 1])
with ocol1:
existing_subject = st.text_input(
"Subject line to optimize:",
placeholder="Enter your current subject line"
)
existing_preheader = st.text_input(
"Current preheader (optional):",
placeholder="Preview text that appears after subject"
)
with ocol2:
if existing_subject:
analysis = analyze_subject_line(existing_subject)
score_class = "good" if analysis['score'] >= 7 else "okay" if analysis['score'] >= 5 else "poor"
st.markdown(f"""
<div class="score-box score-{score_class}">
Score: {analysis['score']}/10
</div>
""", unsafe_allow_html=True)
st.markdown(f"""
<div class="preview-mobile">
<strong>Mobile:</strong> {analysis['mobile_preview']}
</div>
""", unsafe_allow_html=True)
st.metric("Length", f"{analysis['character_count']} chars")
st.metric("Words", analysis['word_count'])
generate_button = st.button('🔧 Optimize Subject Line', use_container_width=True, type="primary")
if generate_button:
if not existing_subject.strip():
st.error("Please enter a subject line to optimize.")
return
if not audience_desc.strip() or not message_content.strip():
st.error("Please provide audience and message details.")
return
with st.spinner("Optimizing your subject line..."):
response = optimize_existing_subject_line_clean(
existing_subject, existing_preheader, audience_desc, message_content,
tone, psychology_framework, industry, personalization, client
)
if response:
st.markdown("### Optimized Results")
st.markdown(response)
else: # Generate New Subject Lines
generate_button = st.button('✨ Generate Subject Lines', use_container_width=True, type="primary")
if generate_button:
if not audience_desc.strip() or not message_content.strip():
st.error("Please provide audience and message details.")
return
with st.spinner("Generating optimized subject lines..."):
response = generate_subject_lines_advanced(
audience_desc, message_content, tone, psychology_framework,
industry, personalization, client
)
if response:
st.markdown("### Your Optimized Subject Lines")
st.markdown(response)
# Simple footer
st.markdown("---")
with st.expander("📊 Why These Practices Work", expanded=False):
st.markdown("""
**Research-backed optimization:**
- 64% of people decide to open based on subject line alone
- Mobile users see only 33 characters before truncation
- Subject lines with numbers get 45% higher open rates
- Personalized subject lines are 26% more likely to be opened
- Emojis can increase open rates by 39% when used appropriately
- 2-4 word subject lines achieve the highest engagement
- Length variety provides options for different audiences and contexts
""")
if __name__ == "__main__":
main()