Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from langchain_core.messages import HumanMessage | |
| import os | |
| import random | |
| import json # <<< THE FIX IS HERE | |
| # --- Configuration --- | |
| GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") | |
| # --- Stage 1: Data Ingestion (Simulation) --- | |
| def fetch_market_chatter(niche_description: str) -> list[str]: | |
| """Simulates scraping Reddit, Hacker News, etc., for a given niche.""" | |
| print(f"Simulating scraping for: {niche_description}") | |
| base_comments = [ | |
| "Ugh, another project management tool that charges per user. I'm a solo founder, this kills me.", | |
| "I love the idea of Notion but it's just too slow and bloated now. I need something faster.", | |
| "Why can't any of these tools just have a simple, reliable integration with Google Calendar?", | |
| "I'm a writer, not a project manager. I just need a clean way to organize my chapters and research.", | |
| "Asana is too complex. Trello is too simple. Is there anything in between?", | |
| "The real magic would be a tool that automatically generates a weekly summary of my progress.", | |
| "Their customer support is a joke. Took three days to get a reply on a critical bug.", | |
| "The 'all-in-one' promise is a lie. It does 10 things poorly instead of one thing well.", | |
| "If someone built a beautiful, minimalist PM tool for visual artists, I'd pay $50/month in a heartbeat." | |
| ] | |
| return random.sample(base_comments, k=random.randint(5, len(base_comments))) | |
| # --- Stage 2 & 3: AI Analysis Pipeline --- | |
| def analyze_resonance(niche_description: str, comments: list[str]) -> dict: | |
| """Uses an LLM to perform topic modeling, pain point extraction, and sentiment analysis.""" | |
| if not GEMINI_API_KEY: | |
| return {"error": "GEMINI_API_KEY not set in Space secrets."} | |
| try: | |
| llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", google_api_key=GEMINI_API_KEY) | |
| comments_formatted = "\n".join([f"- \"{c}\"" for c in comments]) | |
| prompt = f""" | |
| You are a market research analyst with superhuman insight. Analyze the following raw market chatter (comments from Reddit, Hacker News, etc.) for a proposed product idea. | |
| **Proposed Product Idea:** "{niche_description}" | |
| **Raw Market Chatter:** | |
| {comments_formatted} | |
| **Your Task:** | |
| Analyze the chatter and return a valid JSON object with the following structure. Do not include any text, code block markers, or explanations outside the single JSON object. | |
| {{ | |
| "pain_points": [ | |
| "A summary of the most common complaint or frustration.", | |
| "A summary of the second most common complaint.", | |
| "A summary of a third, more niche complaint." | |
| ], | |
| "magic_words": [ | |
| "A positive, benefit-oriented word or phrase people use.", | |
| "Another evocative word people use to describe their ideal solution.", | |
| "A third powerful, emotional word." | |
| ], | |
| "target_villain": "The name of a competitor or a type of product that people frequently complain about.", | |
| "unserved_tribe": "A description of a specific user subgroup whose needs are not being met.", | |
| "resonance_score": A number between 1 and 100 representing how well the proposed product idea fits the market chatter, | |
| "resonance_justification": "A one-sentence explanation for the score you gave." | |
| }} | |
| """ | |
| response = llm.invoke([HumanMessage(content=prompt)]) | |
| # Clean up the response to ensure it's valid JSON | |
| json_str = response.content.strip().replace("```json", "").replace("```", "") | |
| return json.loads(json_str) # This line now works because `json` is imported | |
| except Exception as e: | |
| # Catch JSON decoding errors specifically | |
| return {"error": f"Failed to analyze resonance. The AI model may have returned an invalid format. Details: {e}"} | |
| # --- Main Orchestrator --- | |
| def run_precog_analysis(niche_description: str): | |
| # This orchestrates the entire pipeline | |
| yield "Initializing Pre-Cog Engine...", None, None, gr.Button("Analyzing...", interactive=False) | |
| # Stage 1 | |
| comments = fetch_market_chatter(niche_description) | |
| yield f"Scanning Market Chatter... ({len(comments)} data points found)", None, None, gr.Button("Analyzing...", interactive=False) | |
| # Stage 2 & 3 | |
| analysis_result = analyze_resonance(niche_description, comments) | |
| if "error" in analysis_result: | |
| # Display the error in the main report area for visibility | |
| yield f"## Analysis Error\n\n**Details:** {analysis_result['error']}", "", "Error", gr.Button("Analyze Again", interactive=True) | |
| return | |
| # --- Prepare Outputs --- | |
| score = analysis_result.get('resonance_score', 0) | |
| color = "green" if score > 65 else "orange" if score > 40 else "red" | |
| report_md = f""" | |
| <div style="text-align:center; border: 2px solid {color}; border-radius:10px; padding:20px;"> | |
| <h2 style="margin:0;">Market Resonance Score</h2> | |
| <p style="font-size: 80px; font-weight:bold; margin:0; color:{color};">{score}/100</p> | |
| <p style="margin:0; font-style:italic;">"{analysis_result.get('resonance_justification', 'No justification provided.')}"</p> | |
| </div> | |
| ### ๐ฅ Top 3 Unspoken Pain Points | |
| 1. **{analysis_result.get('pain_points', ['N/A'])[0]}** | |
| 2. **{analysis_result.get('pain_points', ['N/A'])[1]}** | |
| 3. **{analysis_result.get('pain_points', ['N/A'])[2]}** | |
| ### โจ Your "Magic Words" for Marketing | |
| Use these exact words in your landing page copy. This is how your customers talk. | |
| - `{analysis_result.get('magic_words', ['N/A'])[0]}` | |
| - `{analysis_result.get('magic_words', ['N/A'])[1]}` | |
| - `{analysis_result.get('magic_words', ['N/A'])[2]}` | |
| """ | |
| strategy_md = f""" | |
| ### ๐ฏ Your Go-to-Market Strategy | |
| **Your "Villain":** | |
| Position your product as the direct antidote to **{analysis_result.get('target_villain', 'existing complex tools')}**. Your marketing should say, "Tired of [Villain's problem]? We fixed it." | |
| **Your Unserved "Tribe":** | |
| Focus your initial launch on this niche group: **{analysis_result.get('unserved_tribe', 'no specific tribe identified')}**. They are desperately looking for a solution and will become your first evangelists. | |
| """ | |
| yield report_md, strategy_md, "Analysis Complete", gr.Button("Analyze Resonance", interactive=True) | |
| # --- Gradio UI --- | |
| with gr.Blocks(theme=gr.themes.Glass(), css=".gradio-container{max-width: 800px !important}") as demo: | |
| gr.Markdown("# ๐ฎ Pre-Cog: The Market Resonance Engine") | |
| gr.Markdown("Stop guessing. Analyze real-time market chatter to see if your idea will succeed *before* you build it.") | |
| with gr.Column(): | |
| niche_input = gr.Textbox(label="Describe Your Product Idea or Niche", placeholder="e.g., 'An AI-powered project management tool for solopreneurs' or 'A better way to learn guitar online'") | |
| submit_btn = gr.Button("Analyze Resonance", variant="primary") | |
| gr.Markdown("---") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("## Resonance Report") | |
| report_output = gr.Markdown() | |
| with gr.Column(scale=1): | |
| gr.Markdown("## Strategy") | |
| strategy_output = gr.Markdown() | |
| status_output = gr.Markdown() | |
| submit_btn.click( | |
| fn=run_precog_analysis, | |
| inputs=[niche_input], | |
| outputs=[report_output, strategy_output, status_output, submit_btn] | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("### This is a demo of the Pre-Cog Engine. \n The Pro version provides access to live data streams, deeper analysis, and continuous market monitoring. \n **[๐ Inquire About Early Access on Gumroad](https://gumroad.com/)**") | |
| if __name__ == "__main__": | |
| demo.launch() |