mgbam commited on
Commit
04b88f3
Β·
verified Β·
1 Parent(s): 0e8510e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -46
app.py CHANGED
@@ -5,6 +5,7 @@ from langchain_google_genai import ChatGoogleGenerativeAI
5
  from langchain_core.messages import HumanMessage
6
  import os
7
  import random
 
8
 
9
  # --- Configuration ---
10
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
@@ -13,8 +14,6 @@ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
13
  def fetch_market_chatter(niche_description: str) -> list[str]:
14
  """Simulates scraping Reddit, Hacker News, etc., for a given niche."""
15
  print(f"Simulating scraping for: {niche_description}")
16
- # In a real app, this would use PRAW, snscrape, etc.
17
- # We'll generate realistic-sounding comments.
18
  base_comments = [
19
  "Ugh, another project management tool that charges per user. I'm a solo founder, this kills me.",
20
  "I love the idea of Notion but it's just too slow and bloated now. I need something faster.",
@@ -32,46 +31,46 @@ def fetch_market_chatter(niche_description: str) -> list[str]:
32
  def analyze_resonance(niche_description: str, comments: list[str]) -> dict:
33
  """Uses an LLM to perform topic modeling, pain point extraction, and sentiment analysis."""
34
  if not GEMINI_API_KEY:
35
- return {"error": "GEMINI_API_KEY not set."}
36
 
37
- llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", google_api_key=GEMINI_API_KEY)
38
-
39
- # Create a single, powerful prompt for the analysis
40
- comments_formatted = "\n".join([f"- \"{c}\"" for c in comments])
41
- prompt = f"""
42
- 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.
43
-
44
- **Proposed Product Idea:** "{niche_description}"
45
-
46
- **Raw Market Chatter:**
47
- {comments_formatted}
48
-
49
- **Your Task:**
50
- Analyze the chatter and return a JSON object with the following structure. Do not include any text outside the JSON object.
51
- {{
52
- "pain_points": [
53
- "A summary of the most common complaint or frustration.",
54
- "A summary of the second most common complaint.",
55
- "A summary of a third, more niche complaint."
56
- ],
57
- "magic_words": [
58
- "A positive, benefit-oriented word or phrase people use.",
59
- "Another evocative word people use to describe their ideal solution.",
60
- "A third powerful, emotional word."
61
- ],
62
- "target_villain": "The name of a competitor or a type of product that people frequently complain about.",
63
- "unserved_tribe": "A description of a specific user subgroup whose needs are not being met.",
64
- "resonance_score": A number between 1 and 100 representing how well the proposed product idea fits the market chatter,
65
- "resonance_justification": "A one-sentence explanation for the score you gave."
66
- }}
67
- """
68
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  response = llm.invoke([HumanMessage(content=prompt)])
70
  # Clean up the response to ensure it's valid JSON
71
  json_str = response.content.strip().replace("```json", "").replace("```", "")
72
- return json.loads(json_str)
73
  except Exception as e:
74
- return {"error": f"Failed to analyze resonance: {e}"}
 
75
 
76
  # --- Main Orchestrator ---
77
  def run_precog_analysis(niche_description: str):
@@ -85,19 +84,18 @@ def run_precog_analysis(niche_description: str):
85
  # Stage 2 & 3
86
  analysis_result = analyze_resonance(niche_description, comments)
87
  if "error" in analysis_result:
88
- yield f"Error: {analysis_result['error']}", None, None, gr.Button("Analyze", interactive=True)
 
89
  return
90
 
91
  # --- Prepare Outputs ---
92
-
93
- # The main report
94
  score = analysis_result.get('resonance_score', 0)
95
  color = "green" if score > 65 else "orange" if score > 40 else "red"
96
  report_md = f"""
97
  <div style="text-align:center; border: 2px solid {color}; border-radius:10px; padding:20px;">
98
  <h2 style="margin:0;">Market Resonance Score</h2>
99
  <p style="font-size: 80px; font-weight:bold; margin:0; color:{color};">{score}/100</p>
100
- <p style="margin:0; font-style:italic;">"{analysis_result.get('resonance_justification', '')}"</p>
101
  </div>
102
 
103
  ### πŸ’₯ Top 3 Unspoken Pain Points
@@ -112,18 +110,17 @@ def run_precog_analysis(niche_description: str):
112
  - `{analysis_result.get('magic_words', ['N/A'])[2]}`
113
  """
114
 
115
- # The strategic insights
116
  strategy_md = f"""
117
  ### 🎯 Your Go-to-Market Strategy
118
 
119
  **Your "Villain":**
120
- Position your product as the direct antidote to **{analysis_result.get('target_villain', 'N/A')}**. Your marketing should say, "Tired of [Villain's problem]? We fixed it."
121
 
122
  **Your Unserved "Tribe":**
123
- Focus your initial launch on this niche group: **{analysis_result.get('unserved_tribe', 'N/A')}**. They are desperately looking for a solution and will become your first evangelists.
124
  """
125
 
126
- yield report_md, strategy_md, "Analysis Complete", gr.Button("Analyze", interactive=True)
127
 
128
  # --- Gradio UI ---
129
  with gr.Blocks(theme=gr.themes.Glass(), css=".gradio-container{max-width: 800px !important}") as demo:
@@ -144,7 +141,7 @@ with gr.Blocks(theme=gr.themes.Glass(), css=".gradio-container{max-width: 800px
144
  gr.Markdown("## Strategy")
145
  strategy_output = gr.Markdown()
146
 
147
- status_output = gr.Markdown() # A simple status bar
148
 
149
  submit_btn.click(
150
  fn=run_precog_analysis,
@@ -153,7 +150,7 @@ with gr.Blocks(theme=gr.themes.Glass(), css=".gradio-container{max-width: 800px
153
  )
154
 
155
  gr.Markdown("---")
156
- 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 **[πŸš€ Launching Soon - Inquire About Early Access](https://gumroad.com/)**")
157
 
158
  if __name__ == "__main__":
159
  demo.launch()
 
5
  from langchain_core.messages import HumanMessage
6
  import os
7
  import random
8
+ import json # <<< THE FIX IS HERE
9
 
10
  # --- Configuration ---
11
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
 
14
  def fetch_market_chatter(niche_description: str) -> list[str]:
15
  """Simulates scraping Reddit, Hacker News, etc., for a given niche."""
16
  print(f"Simulating scraping for: {niche_description}")
 
 
17
  base_comments = [
18
  "Ugh, another project management tool that charges per user. I'm a solo founder, this kills me.",
19
  "I love the idea of Notion but it's just too slow and bloated now. I need something faster.",
 
31
  def analyze_resonance(niche_description: str, comments: list[str]) -> dict:
32
  """Uses an LLM to perform topic modeling, pain point extraction, and sentiment analysis."""
33
  if not GEMINI_API_KEY:
34
+ return {"error": "GEMINI_API_KEY not set in Space secrets."}
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  try:
37
+ llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", google_api_key=GEMINI_API_KEY)
38
+
39
+ comments_formatted = "\n".join([f"- \"{c}\"" for c in comments])
40
+ prompt = f"""
41
+ 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.
42
+
43
+ **Proposed Product Idea:** "{niche_description}"
44
+
45
+ **Raw Market Chatter:**
46
+ {comments_formatted}
47
+
48
+ **Your Task:**
49
+ 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.
50
+ {{
51
+ "pain_points": [
52
+ "A summary of the most common complaint or frustration.",
53
+ "A summary of the second most common complaint.",
54
+ "A summary of a third, more niche complaint."
55
+ ],
56
+ "magic_words": [
57
+ "A positive, benefit-oriented word or phrase people use.",
58
+ "Another evocative word people use to describe their ideal solution.",
59
+ "A third powerful, emotional word."
60
+ ],
61
+ "target_villain": "The name of a competitor or a type of product that people frequently complain about.",
62
+ "unserved_tribe": "A description of a specific user subgroup whose needs are not being met.",
63
+ "resonance_score": A number between 1 and 100 representing how well the proposed product idea fits the market chatter,
64
+ "resonance_justification": "A one-sentence explanation for the score you gave."
65
+ }}
66
+ """
67
  response = llm.invoke([HumanMessage(content=prompt)])
68
  # Clean up the response to ensure it's valid JSON
69
  json_str = response.content.strip().replace("```json", "").replace("```", "")
70
+ return json.loads(json_str) # This line now works because `json` is imported
71
  except Exception as e:
72
+ # Catch JSON decoding errors specifically
73
+ return {"error": f"Failed to analyze resonance. The AI model may have returned an invalid format. Details: {e}"}
74
 
75
  # --- Main Orchestrator ---
76
  def run_precog_analysis(niche_description: str):
 
84
  # Stage 2 & 3
85
  analysis_result = analyze_resonance(niche_description, comments)
86
  if "error" in analysis_result:
87
+ # Display the error in the main report area for visibility
88
+ yield f"## Analysis Error\n\n**Details:** {analysis_result['error']}", "", "Error", gr.Button("Analyze Again", interactive=True)
89
  return
90
 
91
  # --- Prepare Outputs ---
 
 
92
  score = analysis_result.get('resonance_score', 0)
93
  color = "green" if score > 65 else "orange" if score > 40 else "red"
94
  report_md = f"""
95
  <div style="text-align:center; border: 2px solid {color}; border-radius:10px; padding:20px;">
96
  <h2 style="margin:0;">Market Resonance Score</h2>
97
  <p style="font-size: 80px; font-weight:bold; margin:0; color:{color};">{score}/100</p>
98
+ <p style="margin:0; font-style:italic;">"{analysis_result.get('resonance_justification', 'No justification provided.')}"</p>
99
  </div>
100
 
101
  ### πŸ’₯ Top 3 Unspoken Pain Points
 
110
  - `{analysis_result.get('magic_words', ['N/A'])[2]}`
111
  """
112
 
 
113
  strategy_md = f"""
114
  ### 🎯 Your Go-to-Market Strategy
115
 
116
  **Your "Villain":**
117
+ 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."
118
 
119
  **Your Unserved "Tribe":**
120
+ 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.
121
  """
122
 
123
+ yield report_md, strategy_md, "Analysis Complete", gr.Button("Analyze Resonance", interactive=True)
124
 
125
  # --- Gradio UI ---
126
  with gr.Blocks(theme=gr.themes.Glass(), css=".gradio-container{max-width: 800px !important}") as demo:
 
141
  gr.Markdown("## Strategy")
142
  strategy_output = gr.Markdown()
143
 
144
+ status_output = gr.Markdown()
145
 
146
  submit_btn.click(
147
  fn=run_precog_analysis,
 
150
  )
151
 
152
  gr.Markdown("---")
153
+ 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/)**")
154
 
155
  if __name__ == "__main__":
156
  demo.launch()