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"""
{score}/100
"{analysis_result.get('resonance_justification', 'No justification provided.')}"