import gradio as gr import httpx import os import json from dotenv import load_dotenv from typing import List, Dict, Tuple import asyncio # Load environment variables load_dotenv() # API Keys and Configuration SERPAPI_KEY = os.getenv("SERPAPI_KEY") GROQ_API_KEY = os.getenv("GROQ_API_KEY") MAX_SEARCH_RESULTS = int(os.getenv("MAX_SEARCH_RESULTS", "7")) GROQ_MODEL = os.getenv("GROQ_MODEL", "meta-llama/llama-4-maverick-17b-128e-instruct") # SerpAPI integration async def search_topic(topic: str) -> List[Dict[str, str]]: """ Search for a topic using SerpAPI and return structured search results. Args: topic: The topic to search for Returns: A list of dictionaries containing title and snippet for each search result """ if not SERPAPI_KEY: raise ValueError("SerpAPI key is not configured") params = { 'api_key': SERPAPI_KEY, 'q': topic, 'google_domain': 'google.com', 'gl': 'us', 'hl': 'en', 'num': MAX_SEARCH_RESULTS } # Make the request to SerpAPI async with httpx.AsyncClient(timeout=30.0) as client: response = await client.get('https://serpapi.com/search', params=params) if response.status_code != 200: raise Exception(f"SerpAPI request failed with status code {response.status_code}: {response.text}") data = response.json() # Extract organic search results search_results = [] if 'organic_results' in data: for result in data['organic_results'][:MAX_SEARCH_RESULTS]: search_result = { 'title': result.get('title', ''), 'snippet': result.get('snippet', '') } search_results.append(search_result) if not search_results: raise Exception("No search results found") return search_results # GroqCloud API integration async def _call_groq_api(prompt: str) -> str: """ Helper function to call GroqCloud API. Args: prompt: The prompt to send to GroqCloud Returns: The generated text response """ if not GROQ_API_KEY: raise ValueError("GroqCloud API key is not configured") headers = { "Authorization": f"Bearer {GROQ_API_KEY}", "Content-Type": "application/json" } payload = { "model": GROQ_MODEL, "messages": [ {"role": "user", "content": prompt} ], "temperature": 0.7, "max_tokens": 1024 } async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post( "https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload ) if response.status_code != 200: raise Exception(f"GroqCloud API request failed with status code {response.status_code}: {response.text}") response_data = response.json() # Extract the assistant's message content return response_data["choices"][0]["message"]["content"].strip() async def generate_linkedin_post(topic: str, search_results: List[Dict[str, str]]) -> Tuple[str, str]: """ Generate a LinkedIn post based on search results using GroqCloud API. Args: topic: The original search topic search_results: List of search results with title and snippet Returns: A tuple containing (summary, linkedin_post) """ # Format the search results into a single context string context = "\n\n".join([ f"Title: {result['title']}\nSnippet: {result['snippet']}" for result in search_results ]) # Step 1: Create a summary of the search results summary_prompt = f""" You are a helpful research assistant. Summarize the following search results about "{topic}" in a clear, comprehensive way that captures the key information. Focus on recent trends, statistics, expert opinions, and noteworthy developments. SEARCH RESULTS: {context} Summary: """ summary = await _call_groq_api(summary_prompt) # Step 2: Generate a LinkedIn post based on the summary post_prompt = f""" You're an expert LinkedIn content writer. Write an engaging LinkedIn post about "{topic}" based on the following research summary: RESEARCH SUMMARY: {summary} Follow these style guidelines: 🧠 You are Prerit Singh — a creative AI enthusiast, builder, and storyteller. Your posts feel like: Talking to a sharp, chilled-out friend A human behind the tech, not a robot explaining tech Sharing real-world experiments with excitement, not just dry facts ✍️ Writing Style Rules Tone Friendly, approachable, and relatable Confident but grounded (not boasting) Curious and playful (celebrating discoveries) Slightly witty and humorous where it fits naturally Honest reactions ("even I was shocked", "felt like magic", "saved me weeks") Sentence Behavior Mix short punchy sentences and slightly longer story sentences Avoid complex or heavy words — talk in everyday English Use occasional slang and desi-English flavor naturally ("bhai", "bro", "full time-waste", "no kidding") Speak like you're narrating an interesting story to a friend over chai Active voice always: NOT "It was built by me" YES "I built it" Emotional Behavior Wonder, excitement, playfulness Mild self-deprecating humor sometimes ("pizza didn't even show up yet, bro") Human imperfection is okay (showing surprise, struggle, trial and error) Flow and Formatting 1. Hook: 1 or 2 lines Must grab attention instantly Methods: Surprising statement Teasing curiosity Personal excitement Example Hooks: "Built a small AI tool — but it feels like magic." "So I was doing some market research... and AI just blew my mind." 2. Story/Body: Tell what you built/tested/discovered Keep paras max 1-3 sentences long Use arrows (→), bullets (•), or short lists to break information Include "how you did it" in simple steps Highlight the “magic moment” (the wow factor) Examples of transitional words you use: "So I thought —" "Here’s what happened —" "The process? Surprisingly simple!" 3. Key Outcomes: After explaining, list what the audience will get or learn Make it visual with arrows (→) or bullets Example: → Upload your meal photo → Instantly get calories and macros → Works even for Indian dishes 4. Personal Reflection: Always include your honest reaction Examples: "Works surprisingly well (even I was shocked)" "AI didn’t just help — it crushed it." "Honestly, this saved me weeks." 5. Call-to-Action (CTA): Invite conversation or opinions, NOT hard selling Example CTAs: "Would you use something like this?" "Curious to know your thoughts." "Hit me up in the comments if you want the prompt!" ✅ CTA tone must be casual and welcoming, not salesy. Visual Style Break paragraphs after every 1-2 sentences Make it breathable and easy to skim Use emojis occasionally (🍕🚀🔥), but only if it adds personality No heavy decoration. Keep it clean and airy. Hashtags Only at the end 5–7 natural hashtags based on post topic Examples: #AI #TechInnovation #OpenSource #BrandStrategy #CreativeTech #Innovation 🎯 Content Topics That Fit Prerit’s Style: Real AI experiments (even small ones) Discovering or comparing AI models/tools How AI made everyday work faster/easier/more fun Bridging personal life moments (pizza, Zoom chaos) with tech learnings Storytelling about solving problems with creativity + AI Friendly how-to guides (light style, not heavy teaching) 🔥 Personality Extras (Optional Flavors to Add) ✅ Use small reactions: "felt like magic" "no kidding" "bam — it’s done" "blew me away" ✅ Use cultural metaphors: "full time-waste, bhai" "while my chai was still brewing" "before the pizza even arrived" ✅ Occasional casual audience references: "bro," "bhai," "you know the vibe," "trust me," "hands down" ✅ Fun closing lines: "Chalo, now back to building!" "Ready to see the magic?" "This AI thing’s just getting started!" ✅ Reminder for AI: The post must feel human, fun, inspiring, and useful. It must sound like Prerit Singh talking — not a formal LinkedIn MBA consultant. LinkedIn Post: """ linkedin_post = await _call_groq_api(post_prompt) return summary, linkedin_post # Gradio interface function async def process_topic(topic: str, progress=gr.Progress()): """ Process a topic to generate a LinkedIn post. Args: topic: The topic to generate content for progress: Gradio progress tracker Returns: The generated LinkedIn post """ if not topic.strip(): return "Please enter a topic to generate a LinkedIn post." try: progress(0.1, desc="Starting search...") search_results = await search_topic(topic) progress(0.4, desc="Analyzing search results...") summary, post = await generate_linkedin_post(topic, search_results) progress(0.9, desc="Finalizing post...") return post except Exception as e: return f"Error: {str(e)}" # Create Gradio UI with gr.Blocks(title="LinkedIn Post Generator", theme=gr.themes.Soft()) as app: gr.Markdown("# LinkedIn Post Generator") gr.Markdown("Enter a topic and get a ready-to-post LinkedIn update based on latest information.") with gr.Row(): topic_input = gr.Textbox( label="Topic", placeholder="Enter a topic (e.g., AI trends 2025, remote work benefits, climate innovation)", lines=1 ) generate_button = gr.Button("Generate LinkedIn Post", variant="primary") with gr.Row(): output = gr.Textbox( label="Your LinkedIn Post", placeholder="Your generated post will appear here...", lines=12 ) generate_button.click( fn=process_topic, inputs=topic_input, outputs=output ) gr.Markdown("### How it works") gr.Markdown(""" 1. We search the web for real-time information about your topic 2. An AI summarizes the most relevant information 3. Another AI crafts a LinkedIn post in a friendly, engaging style """) # Launch the app app.launch()