Spaces:
Running
Running
| # !pip install --upgrade langchain langchain-core langchain-community langchain-groq gradio python-dotenv | |
| # !pip install langchain-groq | |
| import os | |
| import gradio as gr | |
| import re | |
| from datetime import datetime, timedelta | |
| import json | |
| import random | |
| # from google.colab import userdata | |
| # Import LangChain components | |
| try: | |
| from langchain_groq import ChatGroq | |
| from langchain_core.prompts import PromptTemplate | |
| from langchain_core.output_parsers import StrOutputParser | |
| print("β All packages imported successfully!") | |
| except ImportError as e: | |
| print(f"β Import Error: {e}") | |
| print("\nβ οΈ Please run the installation command first:") | |
| print("!pip install --upgrade langchain langchain-core langchain-community langchain-groq gradio python-dotenv") | |
| raise | |
| # ========================= | |
| # API Key Configuration | |
| # ========================= | |
| #GROQ_API_KEY = "add your api key or load from google secerets" | |
| GROQ_API_KEY = os.getenv('GROQ_API_KEY') | |
| # ========================= | |
| # Initialize LLM | |
| # ========================= | |
| try: | |
| llm = ChatGroq( | |
| temperature=0.7, | |
| groq_api_key=GROQ_API_KEY, | |
| model_name="llama-3.1-8b-instant" | |
| ) | |
| print("β LLM initialized successfully!") | |
| except Exception as e: | |
| print(f"β LLM initialization error: {e}") | |
| raise | |
| # ========================= | |
| # Enhanced Prompt Templates | |
| # ========================= | |
| linkedin_template = """ | |
| You are an expert LinkedIn content writer with 10+ years of experience. | |
| Create a highly engaging LinkedIn post that maximizes engagement. | |
| Requirements: | |
| - Start with a powerful hook that stops scrolling | |
| - Use short, punchy paragraphs (2-3 lines max) | |
| - Include storytelling elements or data points | |
| - Add 2-4 relevant emojis naturally throughout | |
| - Use line breaks for visual appeal | |
| - End with an engaging CTA | |
| - Word count: {word_count} words | |
| Topic: {topic} | |
| Tone: {tone} | |
| Target Audience: {audience} | |
| Post Type: {post_type} | |
| {hashtags_instruction} | |
| Write the post now with natural emoji placement. | |
| """ | |
| carousel_template = """ | |
| Create a LinkedIn carousel post with {slides} slides. | |
| Each slide should have: | |
| - A catchy title (5-8 words) | |
| - 2-3 bullet points of content | |
| - Relevant emoji | |
| Topic: {topic} | |
| Tone: {tone} | |
| Format each slide as: | |
| SLIDE [number]: | |
| Title: [title] | |
| β’ [point 1] | |
| β’ [point 2] | |
| β’ [point 3] | |
| """ | |
| story_template = """ | |
| Create a compelling LinkedIn story post about {topic}. | |
| Use the following structure: | |
| 1. Opening hook (2-3 lines) | |
| 2. The challenge/situation | |
| 3. The turning point | |
| 4. The lesson/insight | |
| 5. Call to action | |
| Tone: {tone} | |
| Include emojis naturally. | |
| Target length: {word_count} words | |
| """ | |
| thread_template = """ | |
| Create a LinkedIn thread with {posts} posts on {topic}. | |
| Each post should: | |
| - Be standalone valuable | |
| - Connect to the overall narrative | |
| - Be 100-150 words | |
| - Include relevant emojis | |
| Tone: {tone} | |
| Format as: | |
| POST 1/[total]: | |
| [content] | |
| POST 2/[total]: | |
| [content] | |
| """ | |
| # ========================= | |
| # Post History Storage | |
| # ========================= | |
| post_history = [] | |
| def save_to_history(post, topic, tone, audience): | |
| """Save generated post to history""" | |
| entry = { | |
| "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "post": post, | |
| "topic": topic, | |
| "tone": tone, | |
| "audience": audience | |
| } | |
| post_history.insert(0, entry) | |
| if len(post_history) > 50: | |
| post_history.pop() | |
| return format_history() | |
| def format_history(): | |
| """Format history for display""" | |
| if not post_history: | |
| return "No posts in history yet. Generate some posts to see them here!" | |
| formatted = [] | |
| for idx, entry in enumerate(post_history[:10], 1): | |
| formatted.append(f""" | |
| **Post #{idx}** - {entry['timestamp']} | |
| π Topic: {entry['topic']} | |
| π Tone: {entry['tone']} | π― Audience: {entry['audience']} | |
| {entry['post'][:200]}... | |
| --- | |
| """) | |
| return "\n".join(formatted) | |
| def get_post_from_history(post_number): | |
| """Retrieve specific post from history""" | |
| try: | |
| idx = int(post_number) - 1 | |
| if 0 <= idx < len(post_history): | |
| return post_history[idx]['post'] | |
| return "Invalid post number" | |
| except: | |
| return "Please enter a valid post number" | |
| # ========================= | |
| # Schedule Optimizer | |
| # ========================= | |
| def get_best_posting_times(industry, audience_location, goal): | |
| """AI-powered posting time recommendations""" | |
| template = """ | |
| You are a LinkedIn marketing expert specializing in optimal posting times. | |
| Analyze and recommend the best 5 posting times for: | |
| Industry: {industry} | |
| Audience Location: {audience_location} | |
| Goal: {goal} | |
| Provide specific day and time recommendations with reasoning. | |
| Format as: | |
| 1. [Day] at [Time] - [Reason] | |
| 2. [Day] at [Time] - [Reason] | |
| ... | |
| Also include: | |
| - Best day of week overall | |
| - Times to avoid | |
| - Frequency recommendation | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["industry", "audience_location", "goal"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "industry": industry, | |
| "audience_location": audience_location, | |
| "goal": goal | |
| }) | |
| return result | |
| # ========================= | |
| # Competitor Analysis | |
| # ========================= | |
| def analyze_competitor_post(competitor_post, your_niche): | |
| """Analyze what makes a post successful""" | |
| template = """ | |
| You are a LinkedIn growth expert. Analyze this successful post and extract insights. | |
| Niche/Industry: {niche} | |
| Post to analyze: | |
| {post} | |
| Provide detailed analysis: | |
| 1. **Hook Analysis**: Why the opening works | |
| 2. **Structure**: How it's organized | |
| 3. **Engagement Tactics**: What drives comments/shares | |
| 4. **Key Elements**: Specific techniques used | |
| 5. **Actionable Tips**: How to replicate success | |
| 6. **Improvement Ideas**: What could make it even better | |
| Be specific and tactical. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["niche", "post"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "niche": your_niche, | |
| "post": competitor_post | |
| }) | |
| return result | |
| # ========================= | |
| # Image Suggestion | |
| # ========================= | |
| def suggest_images(post_content, post_type): | |
| """AI suggests what images/visuals to use""" | |
| template = """ | |
| You are a visual content strategist for LinkedIn. | |
| Analyze this post and suggest the best visual content to accompany it. | |
| Post Type: {post_type} | |
| Post Content: | |
| {content} | |
| Suggest: | |
| 1. **Primary Image Type**: (e.g., infographic, photo, illustration, chart) | |
| 2. **Specific Visual Elements**: What should be shown | |
| 3. **Color Scheme**: Recommended colors | |
| 4. **Text Overlay**: What text (if any) should be on the image | |
| 5. **Style Guidelines**: Professional, casual, modern, etc. | |
| 6. **Alternative Options**: 2-3 other visual ideas | |
| 7. **Stock Photo Keywords**: Keywords to search for the right image | |
| 8. **Design Tools**: Recommended tools (Canva templates, etc.) | |
| Be specific and actionable. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["post_type", "content"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "post_type": post_type, | |
| "content": post_content | |
| }) | |
| return result | |
| # ========================= | |
| # A/B Testing | |
| # ========================= | |
| def generate_ab_versions(topic, tone, audience): | |
| """Generate two different versions for A/B testing""" | |
| template_a = """ | |
| Create Version A - Hook-focused approach: | |
| Topic: {topic} | |
| Tone: {tone} | |
| Audience: {audience} | |
| Start with an extremely powerful, curiosity-driven hook. | |
| Focus on immediate attention-grabbing. | |
| Use pattern interrupts. | |
| 150-200 words. | |
| """ | |
| template_b = """ | |
| Create Version B - Value-first approach: | |
| Topic: {topic} | |
| Tone: {tone} | |
| Audience: {audience} | |
| Start by immediately delivering value/insight. | |
| Focus on practical takeaways. | |
| Use data or specific examples. | |
| 150-200 words. | |
| """ | |
| prompt_a = PromptTemplate(input_variables=["topic", "tone", "audience"], template=template_a) | |
| prompt_b = PromptTemplate(input_variables=["topic", "tone", "audience"], template=template_b) | |
| chain_a = prompt_a | llm | StrOutputParser() | |
| chain_b = prompt_b | llm | StrOutputParser() | |
| version_a = chain_a.invoke({"topic": topic, "tone": tone, "audience": audience}) | |
| version_b = chain_b.invoke({"topic": topic, "tone": tone, "audience": audience}) | |
| version_a = fix_emoji_encoding(version_a) | |
| version_b = fix_emoji_encoding(version_b) | |
| analytics_a = analyze_post(version_a) | |
| analytics_b = analyze_post(version_b) | |
| comparison = f""" | |
| # π °οΈ VERSION A - Hook-Focused | |
| **Strategy**: Attention-grabbing hook, curiosity-driven | |
| {version_a} | |
| {format_analytics(analytics_a)} | |
| --- | |
| # π ±οΈ VERSION B - Value-First | |
| **Strategy**: Immediate value delivery, practical focus | |
| {version_b} | |
| {format_analytics(analytics_b)} | |
| --- | |
| ## π A/B Testing Recommendations: | |
| - Test both versions at similar times (Tuesday/Wednesday morning) | |
| - Track: Impressions, Engagement Rate, Comments, Shares | |
| - Run each for 24-48 hours | |
| - The version with higher engagement rate (not just likes) wins | |
| - Consider your audience: C-level prefers Version B, broader audience may prefer Version A | |
| """ | |
| return comparison | |
| # ========================= | |
| # Trend Analyzer | |
| # ========================= | |
| def analyze_linkedin_trends(industry, timeframe): | |
| """Get current LinkedIn trending topics""" | |
| template = """ | |
| You are a LinkedIn trends analyst with access to current platform data. | |
| Analyze current LinkedIn trends for: | |
| Industry: {industry} | |
| Timeframe: {timeframe} | |
| Provide: | |
| 1. **Top 5 Trending Topics**: What's getting traction now | |
| 2. **Rising Hashtags**: Trending hashtags to use | |
| 3. **Content Formats**: Which formats are performing best (text, carousel, video, etc.) | |
| 4. **Engagement Patterns**: What drives engagement now | |
| 5. **Topic Ideas**: 5 specific post ideas based on trends | |
| 6. **What to Avoid**: Topics that are oversaturated | |
| Be current, specific, and actionable. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["industry", "timeframe"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "industry": industry, | |
| "timeframe": timeframe | |
| }) | |
| return result | |
| # ========================= | |
| # Personal Branding | |
| # ========================= | |
| def create_brand_voice(name, industry, values, personality, expertise): | |
| """Create a consistent personal brand voice guide""" | |
| template = """ | |
| You are a personal branding expert. Create a comprehensive brand voice guide. | |
| Profile: | |
| - Name: {name} | |
| - Industry: {industry} | |
| - Core Values: {values} | |
| - Personality: {personality} | |
| - Key Expertise: {expertise} | |
| Create a detailed brand voice guide including: | |
| 1. **Voice Characteristics**: 3-5 key traits | |
| 2. **Tone Guidelines**: | |
| - Professional contexts | |
| - Casual contexts | |
| - Thought leadership | |
| 3. **Language Do's and Don'ts**: | |
| - Preferred words/phrases | |
| - Words to avoid | |
| 4. **Signature Elements**: | |
| - Opening styles | |
| - Closing CTAs | |
| - Emoji usage | |
| 5. **Content Pillars**: 5 main topic areas | |
| 6. **Example Phrases**: 10 on-brand phrases to use | |
| 7. **Differentiation**: What makes this voice unique | |
| Be specific and actionable for consistent LinkedIn presence. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["name", "industry", "values", "personality", "expertise"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "name": name, | |
| "industry": industry, | |
| "values": values, | |
| "personality": personality, | |
| "expertise": expertise | |
| }) | |
| return result | |
| def apply_brand_voice(post, brand_guide_summary): | |
| """Apply brand voice to a post""" | |
| template = """ | |
| Rewrite this post to match the brand voice guidelines. | |
| Brand Voice Guidelines: | |
| {brand_guide} | |
| Original Post: | |
| {post} | |
| Rewrite maintaining the core message but adapting tone, language, and style to match the brand voice perfectly. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["brand_guide", "post"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "brand_guide": brand_guide_summary, | |
| "post": post | |
| }) | |
| return fix_emoji_encoding(result) | |
| # ========================= | |
| # Export Functions - FIXED | |
| # ========================= | |
| def export_as_text(content, filename="linkedin_post"): | |
| """Export post as downloadable text file""" | |
| if not content or not content.strip(): | |
| return None | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| full_filename = f"{filename}_{timestamp}.txt" | |
| # Create file in current directory for proper download | |
| try: | |
| with open(full_filename, 'w', encoding='utf-8') as f: | |
| f.write(content) | |
| return full_filename | |
| except Exception as e: | |
| print(f"Export error: {e}") | |
| return None | |
| def create_post_document(posts_list): | |
| """Create a formatted document with multiple posts""" | |
| if not posts_list: | |
| return "" | |
| doc_content = f""" | |
| LinkedIn Content Package | |
| Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | |
| {'='*80} | |
| """ | |
| for idx, post in enumerate(posts_list, 1): | |
| doc_content += f""" | |
| POST #{idx} | |
| {'-'*80} | |
| {post} | |
| {'='*80} | |
| """ | |
| return doc_content | |
| # ========================= | |
| # Helper Functions | |
| # ========================= | |
| def suggest_tone(topic): | |
| topic_lower = topic.lower() | |
| if any(word in topic_lower for word in ["success", "growth", "motivation", "inspire"]): | |
| return "Inspirational" | |
| elif any(word in topic_lower for word in ["team", "colleague", "work", "project", "career"]): | |
| return "Professional" | |
| elif any(word in topic_lower for word in ["data", "research", "analysis", "tech"]): | |
| return "Analytical" | |
| else: | |
| return "Friendly" | |
| def suggest_hashtags(topic, count=5): | |
| topic_lower = topic.lower() | |
| hashtag_map = { | |
| "ai": ["#AI", "#MachineLearning", "#DeepLearning", "#Tech", "#Innovation"], | |
| "career": ["#Career", "#Growth", "#ProfessionalDevelopment", "#Leadership", "#Success"], | |
| "team": ["#Teamwork", "#Collaboration", "#Productivity", "#Management", "#Culture"], | |
| "marketing": ["#Marketing", "#DigitalMarketing", "#ContentMarketing", "#Strategy", "#Branding"], | |
| "sales": ["#Sales", "#Business", "#B2B", "#Revenue", "#Growth"], | |
| "leadership": ["#Leadership", "#Management", "#ExecutiveLeadership", "#Vision", "#Strategy"], | |
| "startup": ["#Startup", "#Entrepreneur", "#Innovation", "#Business", "#VC"], | |
| "data": ["#DataScience", "#Analytics", "#BigData", "#DataDriven", "#Insights"] | |
| } | |
| for key, hashtags in hashtag_map.items(): | |
| if key in topic_lower: | |
| return " ".join(hashtags[:count]) | |
| return "#Inspiration #Learning #Success #Innovation #Growth" | |
| def analyze_post(post): | |
| """Enhanced analytics""" | |
| words = len(post.split()) | |
| paragraphs = post.count("\n\n") + 1 | |
| emojis = len(re.findall(r'[^\w\s,.]', post)) | |
| hashtags = len(re.findall(r'#\w+', post)) | |
| lines = post.count("\n") + 1 | |
| # Engagement score calculation | |
| engagement_score = 0 | |
| if 150 <= words <= 250: | |
| engagement_score += 30 | |
| elif words < 150: | |
| engagement_score += 20 | |
| else: | |
| engagement_score += 10 | |
| if 3 <= paragraphs <= 5: | |
| engagement_score += 20 | |
| if 2 <= emojis <= 5: | |
| engagement_score += 20 | |
| if 3 <= hashtags <= 5: | |
| engagement_score += 15 | |
| if any(cta in post.lower() for cta in ["comment", "share", "thoughts", "agree", "experience"]): | |
| engagement_score += 15 | |
| return { | |
| "words": words, | |
| "paragraphs": paragraphs, | |
| "emojis": emojis, | |
| "hashtags": hashtags, | |
| "lines": lines, | |
| "engagement_score": min(engagement_score, 100) | |
| } | |
| def format_analytics(analytics): | |
| return f""" | |
| π **Post Analytics** | |
| β’ Words: {analytics['words']} | |
| β’ Paragraphs: {analytics['paragraphs']} | |
| β’ Emojis: {analytics['emojis']} | |
| β’ Hashtags: {analytics['hashtags']} | |
| β’ Engagement Score: {analytics['engagement_score']}/100 | |
| """ | |
| def fix_emoji_encoding(text): | |
| try: | |
| fixed = text.encode('latin-1').decode('utf-8') | |
| return fixed | |
| except (UnicodeDecodeError, UnicodeEncodeError): | |
| return text | |
| def generate_post(topic, tone, audience, post_type, word_count, num_variations, custom_hashtags): | |
| if not topic.strip(): | |
| return "Please enter a topic to generate a LinkedIn post." | |
| # Determine hashtags | |
| if custom_hashtags.strip(): | |
| hashtags = custom_hashtags | |
| hashtags_instruction = f"Include these hashtags at the end: {hashtags}" | |
| else: | |
| hashtags = suggest_hashtags(topic) | |
| hashtags_instruction = f"Suggest and include 3-5 relevant hashtags at the end" | |
| # Select template based on post type | |
| if post_type == "Standard Post": | |
| template = linkedin_template | |
| prompt = PromptTemplate( | |
| input_variables=["topic", "tone", "audience", "post_type", "word_count", "hashtags_instruction"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| elif post_type == "Story Post": | |
| template = story_template | |
| prompt = PromptTemplate( | |
| input_variables=["topic", "tone", "word_count"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| elif post_type == "Carousel (5 slides)": | |
| template = carousel_template | |
| prompt = PromptTemplate( | |
| input_variables=["slides", "topic", "tone"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| elif post_type == "Thread (3 posts)": | |
| template = thread_template | |
| prompt = PromptTemplate( | |
| input_variables=["posts", "topic", "tone"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| # Generate variations | |
| variations = [] | |
| for i in range(num_variations): | |
| if post_type == "Standard Post": | |
| result = chain.invoke({ | |
| "topic": topic, | |
| "tone": tone, | |
| "audience": audience, | |
| "post_type": post_type, | |
| "word_count": word_count, | |
| "hashtags_instruction": hashtags_instruction | |
| }) | |
| elif post_type == "Story Post": | |
| result = chain.invoke({ | |
| "topic": topic, | |
| "tone": tone, | |
| "word_count": word_count | |
| }) | |
| elif post_type == "Carousel (5 slides)": | |
| result = chain.invoke({ | |
| "slides": 5, | |
| "topic": topic, | |
| "tone": tone | |
| }) | |
| elif post_type == "Thread (3 posts)": | |
| result = chain.invoke({ | |
| "posts": 3, | |
| "topic": topic, | |
| "tone": tone | |
| }) | |
| result = fix_emoji_encoding(result) | |
| analytics = analyze_post(result) | |
| formatted = f"**Variation {i+1}**\n\n{result}\n\n{format_analytics(analytics)}" | |
| variations.append(formatted) | |
| # Save to history | |
| save_to_history(result, topic, tone, audience) | |
| return "\n\n" + "="*80 + "\n\n".join(variations) | |
| def rewrite_post(original_post, instruction): | |
| """Rewrite existing post based on user instruction""" | |
| if not original_post.strip(): | |
| return "Please paste a post to rewrite." | |
| rewrite_template = """ | |
| You are a LinkedIn content expert. Rewrite the following post based on this instruction: | |
| Instruction: {instruction} | |
| Original Post: | |
| {original_post} | |
| Provide the rewritten version maintaining LinkedIn best practices. | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["instruction", "original_post"], | |
| template=rewrite_template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({ | |
| "instruction": instruction, | |
| "original_post": original_post | |
| }) | |
| result = fix_emoji_encoding(result) | |
| analytics = analyze_post(result) | |
| return f"{result}\n\n{format_analytics(analytics)}" | |
| def generate_hashtag_suggestions(topic, count): | |
| """AI-powered hashtag suggestions""" | |
| template = """ | |
| Generate {count} highly relevant and trending LinkedIn hashtags for this topic: {topic} | |
| Provide hashtags that: | |
| - Are currently popular on LinkedIn | |
| - Mix broad and niche hashtags | |
| - Include industry-specific tags | |
| - Balance reach and relevance | |
| Return only the hashtags separated by spaces, starting with # | |
| """ | |
| prompt = PromptTemplate( | |
| input_variables=["topic", "count"], | |
| template=template | |
| ) | |
| chain = prompt | llm | StrOutputParser() | |
| result = chain.invoke({"topic": topic, "count": count}) | |
| return result.strip() | |
| def export_current_post(content): | |
| """Export the current post as a text file - FIXED""" | |
| if not content or not content.strip(): | |
| return None, "β Nothing to export! Generate a post first." | |
| # Remove analytics section if present | |
| if "π **Post Analytics**" in content: | |
| content = content.split("π **Post Analytics**")[0].strip() | |
| filepath = export_as_text(content, "linkedin_post") | |
| if filepath: | |
| return filepath, "β Post exported successfully!" | |
| else: | |
| return None, "β Export failed. Please try again." | |
| def export_history_package(count): | |
| """Export multiple posts from history - FIXED""" | |
| try: | |
| count = int(count) | |
| posts_to_export = [entry['post'] for entry in post_history[:count]] | |
| if not posts_to_export: | |
| return None, "No posts to export. Generate some posts first!" | |
| content = create_post_document(posts_to_export) | |
| filepath = export_as_text(content, "linkedin_posts_package") | |
| if filepath: | |
| return filepath, f"β Exported {len(posts_to_export)} posts successfully!" | |
| else: | |
| return None, "β Export failed. Please try again." | |
| except Exception as e: | |
| return None, f"β Export failed: {str(e)}" | |
| # ========================= | |
| # Gradio Interface | |
| # ========================= | |
| demo = gr.Blocks() | |
| with demo: | |
| gr.Markdown( | |
| """ | |
| # LinkedIn AI Writer | |
| ### The Complete LinkedIn Content Creation Suite with 15+ AI-Powered Features | |
| """ | |
| ) | |
| with gr.Tabs(): | |
| # Tab 1: Generate New Post | |
| with gr.Tab("βοΈ Generate Post"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| topic_input = gr.Textbox( | |
| label="π Topic", | |
| placeholder="e.g., How AI is transforming remote work...", | |
| lines=3 | |
| ) | |
| with gr.Row(): | |
| tone_input = gr.Dropdown( | |
| ["Professional", "Friendly", "Inspirational", "Analytical", "Humorous"], | |
| label="π Tone", | |
| value="Professional" | |
| ) | |
| audience_input = gr.Dropdown( | |
| ["General", "C-Level Executives", "Managers", "Individual Contributors", "Students", "Entrepreneurs"], | |
| label="π― Target Audience", | |
| value="General" | |
| ) | |
| with gr.Row(): | |
| post_type_input = gr.Dropdown( | |
| ["Standard Post", "Story Post", "Carousel (5 slides)", "Thread (3 posts)"], | |
| label="π Post Type", | |
| value="Standard Post" | |
| ) | |
| word_count_input = gr.Slider( | |
| minimum=100, | |
| maximum=300, | |
| step=25, | |
| value=200, | |
| label="π Word Count" | |
| ) | |
| num_variations_input = gr.Slider( | |
| minimum=1, | |
| maximum=3, | |
| step=1, | |
| value=1, | |
| label="π’ Number of Variations" | |
| ) | |
| custom_hashtags_input = gr.Textbox( | |
| label="π·οΈ Custom Hashtags (optional)", | |
| placeholder="#AI #Innovation #Tech" | |
| ) | |
| with gr.Row(): | |
| generate_button = gr.Button("β¨ Generate Post", variant="primary", size="lg") | |
| suggest_hashtags_button = gr.Button("π‘ Suggest Hashtags") | |
| with gr.Column(scale=2): | |
| output_box = gr.Textbox( | |
| label="π Generated Post", | |
| lines=25, | |
| interactive=True, | |
| elem_classes="output-text" | |
| ) | |
| with gr.Row(): | |
| copy_button = gr.Button("π Copy", size="sm") | |
| export_button = gr.Button("πΎ Export as .txt", size="sm") | |
| with gr.Row(): | |
| copy_status = gr.Textbox(label="", interactive=False, show_label=False, lines=1) | |
| export_file = gr.File(label="π₯ Download File", interactive=False, type="filepath") | |
| # Tab 2: A/B Testing | |
| with gr.Tab("π¬ A/B Testing"): | |
| gr.Markdown("### Generate two versions of your post optimized for different strategies") | |
| with gr.Row(): | |
| with gr.Column(): | |
| ab_topic = gr.Textbox(label="π Topic", placeholder="Enter your topic...", lines=3) | |
| with gr.Row(): | |
| ab_tone = gr.Dropdown( | |
| ["Professional", "Friendly", "Inspirational", "Analytical"], | |
| label="π Tone", | |
| value="Professional" | |
| ) | |
| ab_audience = gr.Dropdown( | |
| ["General", "C-Level Executives", "Managers", "Individual Contributors"], | |
| label="π― Audience", | |
| value="General" | |
| ) | |
| ab_button = gr.Button("π¬ Generate A/B Versions", variant="primary") | |
| with gr.Column(): | |
| ab_output = gr.Textbox(label="π A/B Test Results", lines=30, interactive=True) | |
| # Tab 3: Schedule Optimizer | |
| with gr.Tab("π Schedule Optimizer"): | |
| gr.Markdown("### Find the perfect time to post based on your industry and audience") | |
| with gr.Row(): | |
| with gr.Column(): | |
| schedule_industry = gr.Textbox( | |
| label="π’ Your Industry", | |
| placeholder="e.g., Technology, Marketing, Finance..." | |
| ) | |
| schedule_location = gr.Dropdown( | |
| ["North America (EST)", "Europe (CET)", "Asia Pacific", "Global/Mixed"], | |
| label="π Primary Audience Location", | |
| value="North America (EST)" | |
| ) | |
| schedule_goal = gr.Dropdown( | |
| ["Maximum Reach", "Engagement (Comments/Shares)", "Lead Generation", "Thought Leadership"], | |
| label="π― Primary Goal", | |
| value="Maximum Reach" | |
| ) | |
| schedule_button = gr.Button("β° Get Optimal Times", variant="primary") | |
| with gr.Column(): | |
| schedule_output = gr.Textbox(label="π Recommended Posting Schedule", lines=20) | |
| # Tab 4: Competitor Analysis | |
| with gr.Tab("π Competitor Analysis"): | |
| gr.Markdown("### Analyze successful posts to understand what works") | |
| with gr.Row(): | |
| with gr.Column(): | |
| competitor_post = gr.Textbox( | |
| label="π Paste Competitor's Post", | |
| placeholder="Paste the successful post you want to analyze...", | |
| lines=10 | |
| ) | |
| competitor_niche = gr.Textbox( | |
| label="π― Your Niche/Industry", | |
| placeholder="e.g., SaaS Marketing, Data Science..." | |
| ) | |
| analyze_button = gr.Button("π Analyze Post", variant="primary") | |
| with gr.Column(): | |
| competitor_output = gr.Textbox(label="π Analysis Results", lines=25) | |
| # Tab 5: Image Suggestions | |
| with gr.Tab("πΌοΈ Image Suggestions"): | |
| gr.Markdown("### Get AI-powered recommendations for visual content") | |
| with gr.Row(): | |
| with gr.Column(): | |
| image_post = gr.Textbox( | |
| label="π Your Post Content", | |
| placeholder="Paste your post here...", | |
| lines=10 | |
| ) | |
| image_type = gr.Dropdown( | |
| ["Standard Post", "Story Post", "Carousel", "Article Cover"], | |
| label="π Post Type", | |
| value="Standard Post" | |
| ) | |
| image_button = gr.Button("π¨ Get Image Suggestions", variant="primary") | |
| with gr.Column(): | |
| image_output = gr.Textbox(label="πΌοΈ Visual Recommendations", lines=25) | |
| # Tab 6: Trend Analyzer | |
| with gr.Tab("π Trend Analyzer"): | |
| gr.Markdown("### Discover what's trending in your industry on LinkedIn") | |
| with gr.Row(): | |
| with gr.Column(): | |
| trend_industry = gr.Textbox( | |
| label="π’ Industry", | |
| placeholder="e.g., AI, Marketing, Fintech..." | |
| ) | |
| trend_timeframe = gr.Dropdown( | |
| ["Last 7 days", "Last 30 days", "Last 3 months"], | |
| label="β° Timeframe", | |
| value="Last 30 days" | |
| ) | |
| trend_button = gr.Button("π Analyze Trends", variant="primary") | |
| with gr.Column(): | |
| trend_output = gr.Textbox(label="π Trending Topics & Insights", lines=25) | |
| # Tab 7: Personal Branding | |
| with gr.Tab("π Personal Branding"): | |
| gr.Markdown("### Create your unique brand voice for consistent LinkedIn presence") | |
| with gr.Tabs(): | |
| with gr.Tab("Create Brand Voice"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| brand_name = gr.Textbox(label="π€ Your Name", placeholder="John Doe") | |
| brand_industry = gr.Textbox(label="π’ Industry", placeholder="e.g., Digital Marketing") | |
| brand_values = gr.Textbox( | |
| label="π Core Values", | |
| placeholder="e.g., Innovation, Authenticity, Growth", | |
| lines=2 | |
| ) | |
| brand_personality = gr.Textbox( | |
| label="π Personality Traits", | |
| placeholder="e.g., Approachable, Data-driven, Creative", | |
| lines=2 | |
| ) | |
| brand_expertise = gr.Textbox( | |
| label="π Key Expertise", | |
| placeholder="e.g., Content Strategy, SEO, Brand Building", | |
| lines=2 | |
| ) | |
| brand_create_button = gr.Button("β¨ Create Brand Voice Guide", variant="primary") | |
| with gr.Column(): | |
| brand_output = gr.Textbox(label="π Your Brand Voice Guide", lines=30) | |
| with gr.Tab("Apply Brand Voice"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| apply_post = gr.Textbox( | |
| label="π Post to Brand", | |
| placeholder="Paste any post here to adapt it to your brand voice...", | |
| lines=10 | |
| ) | |
| apply_guide = gr.Textbox( | |
| label="π Brand Guidelines (summary)", | |
| placeholder="Paste key points from your brand guide...", | |
| lines=5 | |
| ) | |
| apply_button = gr.Button("π¨ Apply Brand Voice", variant="primary") | |
| with gr.Column(): | |
| apply_output = gr.Textbox(label="β¨ Branded Post", lines=20) | |
| # Tab 8: Rewrite Post | |
| with gr.Tab("π Rewrite Post"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| original_post_input = gr.Textbox( | |
| label="π Paste Your Post", | |
| placeholder="Paste your LinkedIn post here...", | |
| lines=10 | |
| ) | |
| rewrite_instruction = gr.Textbox( | |
| label="βοΈ Rewrite Instruction", | |
| placeholder="e.g., Make it more professional, Add storytelling, Shorten it...", | |
| lines=3 | |
| ) | |
| rewrite_button = gr.Button("π Rewrite Post", variant="primary") | |
| with gr.Column(): | |
| rewrite_output = gr.Textbox( | |
| label="β¨ Rewritten Post", | |
| lines=15, | |
| interactive=True | |
| ) | |
| # Tab 9: Post History | |
| with gr.Tab("π Post History"): | |
| gr.Markdown("### View and manage your generated posts") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| history_display = gr.Textbox( | |
| label="π Recent Posts (Last 10)", | |
| lines=30, | |
| value=format_history(), | |
| interactive=False | |
| ) | |
| refresh_history_button = gr.Button("π Refresh History") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Load Post from History") | |
| load_post_number = gr.Number( | |
| label="Post Number", | |
| value=1, | |
| precision=0 | |
| ) | |
| load_button = gr.Button("π₯ Load Post") | |
| loaded_post = gr.Textbox(label="Loaded Post", lines=15, interactive=True) | |
| gr.Markdown("### Export Multiple Posts") | |
| export_count = gr.Slider( | |
| minimum=1, | |
| maximum=10, | |
| step=1, | |
| value=5, | |
| label="Number of posts to export" | |
| ) | |
| export_all_button = gr.Button("πΎ Export Package") | |
| export_status = gr.Textbox(label="Export Status", lines=2) | |
| export_package_file = gr.File(label="π₯ Download Package", interactive=False, type="filepath") | |
| # Tab 10: Tips & Best Practices | |
| with gr.Tab("π‘ Tips & Best Practices"): | |
| gr.Markdown( | |
| """ | |
| ## π― LinkedIn Best Practices | |
| ### π Maximize Engagement: | |
| - **Hook in first 2 lines**: Grab attention immediately | |
| - **Use line breaks**: Make posts visually scannable | |
| - **Include data/numbers**: Specific metrics build credibility | |
| - **Add storytelling**: Personal experiences resonate | |
| - **End with CTA**: Ask questions, request opinions | |
| ### β Optimal Post Structure: | |
| 1. **Hook** (1-2 lines) - Stop the scroll | |
| 2. **Context/Story** (3-5 lines) - Build connection | |
| 3. **Key Points** (bullet points work well) - Deliver value | |
| 4. **Conclusion/Insight** (2-3 lines) - Tie it together | |
| 5. **CTA** (1 line + hashtags) - Drive action | |
| ### π·οΈ Hashtag Strategy: | |
| - Use 3-5 hashtags maximum | |
| - Mix popular (100K+ followers) and niche (10K-50K) hashtags | |
| - Place at the end of post | |
| - Research trending hashtags in your industry weekly | |
| ### π Best Times to Post: | |
| - **Tuesday-Thursday**: 9-11 AM (highest engagement) | |
| - **Wednesday**: Best overall day | |
| - **Avoid**: Weekends for B2B, early mornings, late evenings | |
| - **Test**: Your audience may have unique patterns | |
| ### π Content Types That Work: | |
| - **Personal stories**: Authentic experiences (highest engagement) | |
| - **Industry insights**: Thought leadership | |
| - **How-to guides**: Actionable advice | |
| - **Data-driven posts**: Research and statistics | |
| - **Controversial opinions**: Spark discussion (carefully!) | |
| - **Lists**: Easy to scan, high shareability | |
| - **Behind-the-scenes**: Show your process | |
| ### π¨ Visual Content Tips: | |
| - Posts with images get 2x more comments | |
| - Carousel posts get 1.5x more reach | |
| - Use high-quality, professional images | |
| - Infographics perform exceptionally well | |
| - Personal photos > stock photos | |
| ### π Growth Hacks: | |
| 1. **Comment within first hour**: Boost your own post | |
| 2. **Engage before posting**: Warm up the algorithm | |
| 3. **Tag relevant people**: (2-3 max, only when appropriate) | |
| 4. **Post consistently**: 3-5x per week minimum | |
| 5. **Respond to every comment**: Within first 2 hours | |
| 6. **Use "see more" strategically**: Hook in first 2 lines | |
| 7. **Write for mobile**: Short paragraphs, more line breaks | |
| ### π Optimal Lengths: | |
| - **Standard posts**: 150-250 words (sweet spot: 200) | |
| - **Long-form**: 1,300-2,000 words (for thought leadership) | |
| - **Carousels**: 10-15 slides maximum | |
| - **Threads**: 3-5 posts | |
| ### β οΈ What to Avoid: | |
| - External links (post in first comment instead) | |
| - Too many hashtags (looks spammy) | |
| - Generic content (be specific!) | |
| - Overly promotional content | |
| - Inconsistent posting (algorithm penalizes) | |
| - Ignoring comments (kills engagement) | |
| ### π Advanced Tips: | |
| - Use the **"3-3-3 Rule"**: 3 posts/week, 3 comments on others' posts/day, 3 meaningful connections/day | |
| - **Native video** gets 5x more engagement than YouTube links | |
| - **Ask questions** at the end - increases comments by 50% | |
| - **Use emojis strategically** - but not in excess | |
| - **Write for skimmers**: Bullets, bold, line breaks | |
| """ | |
| ) | |
| # ========================= | |
| # Event Handlers | |
| # ========================= | |
| # Generate Post | |
| generate_button.click( | |
| fn=generate_post, | |
| inputs=[ | |
| topic_input, | |
| tone_input, | |
| audience_input, | |
| post_type_input, | |
| word_count_input, | |
| num_variations_input, | |
| custom_hashtags_input | |
| ], | |
| outputs=output_box | |
| ) | |
| # Suggest Hashtags | |
| suggest_hashtags_button.click( | |
| fn=lambda topic: generate_hashtag_suggestions(topic, 5), | |
| inputs=topic_input, | |
| outputs=custom_hashtags_input | |
| ) | |
| # A/B Testing | |
| ab_button.click( | |
| fn=generate_ab_versions, | |
| inputs=[ab_topic, ab_tone, ab_audience], | |
| outputs=ab_output | |
| ) | |
| # Schedule Optimizer | |
| schedule_button.click( | |
| fn=get_best_posting_times, | |
| inputs=[schedule_industry, schedule_location, schedule_goal], | |
| outputs=schedule_output | |
| ) | |
| # Competitor Analysis | |
| analyze_button.click( | |
| fn=analyze_competitor_post, | |
| inputs=[competitor_post, competitor_niche], | |
| outputs=competitor_output | |
| ) | |
| # Image Suggestions | |
| image_button.click( | |
| fn=suggest_images, | |
| inputs=[image_post, image_type], | |
| outputs=image_output | |
| ) | |
| # Trend Analyzer | |
| trend_button.click( | |
| fn=analyze_linkedin_trends, | |
| inputs=[trend_industry, trend_timeframe], | |
| outputs=trend_output | |
| ) | |
| # Personal Branding | |
| brand_create_button.click( | |
| fn=create_brand_voice, | |
| inputs=[brand_name, brand_industry, brand_values, brand_personality, brand_expertise], | |
| outputs=brand_output | |
| ) | |
| apply_button.click( | |
| fn=apply_brand_voice, | |
| inputs=[apply_post, apply_guide], | |
| outputs=apply_output | |
| ) | |
| # Rewrite Post | |
| rewrite_button.click( | |
| fn=rewrite_post, | |
| inputs=[original_post_input, rewrite_instruction], | |
| outputs=rewrite_output | |
| ) | |
| # Post History | |
| refresh_history_button.click( | |
| fn=format_history, | |
| inputs=[], | |
| outputs=history_display | |
| ) | |
| load_button.click( | |
| fn=get_post_from_history, | |
| inputs=load_post_number, | |
| outputs=loaded_post | |
| ) | |
| # Export History Package - FIXED | |
| export_all_button.click( | |
| fn=export_history_package, | |
| inputs=export_count, | |
| outputs=[export_package_file, export_status] | |
| ) | |
| # Copy to Clipboard | |
| copy_js = """ | |
| async function() { | |
| const textArea = document.querySelector('.output-text textarea'); | |
| if (textArea && textArea.value) { | |
| try { | |
| await navigator.clipboard.writeText(textArea.value); | |
| return "β Copied successfully!"; | |
| } catch (err) { | |
| textArea.select(); | |
| document.execCommand('copy'); | |
| return "β Copied successfully!"; | |
| } | |
| } | |
| return "β Nothing to copy! Generate a post first."; | |
| } | |
| """ | |
| copy_button.click( | |
| fn=None, | |
| inputs=None, | |
| outputs=copy_status, | |
| js=copy_js | |
| ) | |
| # Export Current Post - FIXED | |
| export_button.click( | |
| fn=export_current_post, | |
| inputs=[output_box], | |
| outputs=[export_file, copy_status] | |
| ) | |
| # Launch the application | |
| print("\n" + "="*80) | |
| print("π LAUNCHING LINKEDIN AI WRITER PRO - ULTIMATE EDITION") | |
| print("="*80 + "\n") | |
| demo.launch(share=True, debug=True) |