import streamlit as st import json import os import numpy as np import plotly.graph_objs as go from groq import Groq from dotenv import load_dotenv load_dotenv() # load .env file GROQ_API_KEY = os.environ.get("GROQ_API_KEY") # --- CONFIG --- GROQ_MODEL = "llama3-70b-8192" groq_client = Groq(api_key=GROQ_API_KEY) PERSONA_PATH = os.getenv("PERSONA_PATH", "/tmp/personas.json") # --- THEME COLORS --- neon_blue = "#00fff7" neon_green = "#7CFC00" neon_pink = "#F72585" neon_cyan = "#0ffcff" neon_bg = "#181830" neon_orange = "#FFB347" neon_shadow = "#2dfdff44" font_main = "Inter, Segoe UI, Arial, sans-serif" st.set_page_config(page_title="🚀 New Launch Studio", layout="wide", initial_sidebar_state="collapsed") # Set dark theme programmatically st.markdown( """ """, unsafe_allow_html=True ) # --- STYLE --- st.markdown(f""" """, unsafe_allow_html=True) # --- TITLE & DESCRIPTION --- st.markdown(f"
🚀 New Launch Studio
", unsafe_allow_html=True) st.markdown(f"
Will your next product idea actually vibe with your audience? Pop your concept below and instantly see what your customer personas think—no fluff, just punchy, actionable feedback and a reality check on your launch.
", unsafe_allow_html=True) st.markdown(f"
⚡ Heads up: Our demo and market data is based on protein powder reviews—so for best results, enter a health, nutrition, or supplement product!
", unsafe_allow_html=True) # --- NAVIGATION BUTTONS --- st.markdown(f"""
🏠 Home 👤 Persona Analysis
""", unsafe_allow_html=True) # --- PRODUCT DESCRIPTION INPUT --- st.markdown(f"

1. Describe Your New Product

", unsafe_allow_html=True) product_desc = st.text_area( "", height=110, placeholder="E.g. Introducing VanillaWhey: zero sugar, 25g protein, added digestive enzymes, eco-packaging, smooth vanilla flavor, perfect for fitness and daily wellness." ) # --- LOAD PERSONAS --- if os.path.exists(PERSONA_PATH): with open(PERSONA_PATH, "r", encoding="utf-8") as f: personas = json.load(f) else: personas = [] st.warning("No personas found. Please generate personas first in the Persona Analysis page.") def clean_points(text, max_points=2): lines = [l for l in text.replace('\r', '\n').split('\n') if l.strip() and not l.strip().lower().startswith( ('here is', 'here are', 'persona:', 'this is', 'for this persona', 'concerns:', 'the following', 'alignment:', '*', 'point'))] points = [] for l in lines: l = l.lstrip('-•1234567890. ').strip() if l and len(points) < max_points: points.append(l) return points if points else [text.strip()] def ai_points(prompt, max_points=2, max_tokens=120): try: chat_completion = groq_client.chat.completions.create( model=GROQ_MODEL, messages=[ {"role": "system", "content": f"You are a market research strategist. Reply with ONLY exactly {max_points} very brief, but fully written bullet points—no intros, no repetition, no generic phrases. Each point should be a full, clear sentence. Never add 'Here are' or any extra intro. Dont mention any names."}, {"role": "user", "content": prompt} ], max_tokens=max_tokens, temperature=0.7, stop=None ) return clean_points(chat_completion.choices[0].message.content.strip(), max_points) except Exception as e: return [f"Error: {e}"] def ai_notification(prompt, max_tokens=44): try: chat_completion = groq_client.chat.completions.create( model=GROQ_MODEL, messages=[ {"role": "system", "content": "You are a copywriter. Write a single, short, energetic notification or email (max 30 words, no names, no symbols), ending with a call-to-action. Make it stand out and complete."}, {"role": "user", "content": prompt} ], max_tokens=max_tokens, temperature=0.72, stop=None ) return chat_completion.choices[0].message.content.strip().replace("**", "") except Exception as e: return f"Error: {e}" def ai_percent(prompt): try: chat_completion = groq_client.chat.completions.create( model=GROQ_MODEL, messages=[{"role": "system", "content": "You are a market research strategist."}, {"role": "user", "content": prompt}], max_tokens=8, temperature=0.25 ) s = chat_completion.choices[0].message.content.strip() percent = ''.join([c for c in s if c.isdigit()]) return percent + "%" if percent else s except Exception as e: return "?" def ai_graph_insights(prompt, max_tokens=160): try: chat_completion = groq_client.chat.completions.create( model=GROQ_MODEL, messages=[ {"role": "system", "content": "You are a market analyst. Give only 4 numbered, very concise but meaningful insights in separate sentences, no intro line or extra formatting, no 'Here are', no asterisks or stars, just the facts."}, {"role": "user", "content": prompt} ], max_tokens=max_tokens, temperature=0.7, stop=None ) # Always keep only 4, no prefix text lines = [l.lstrip('-•1234567890. ').strip().replace("**", "") for l in chat_completion.choices[0].message.content.strip().split('\n') if l.strip()] return lines[:4] except Exception as e: return [f"Error: {e}"] def ai_summary(prompt, max_tokens=90): try: chat_completion = groq_client.chat.completions.create( model=GROQ_MODEL, messages=[ {"role": "system", "content": "Write a concise, professional executive summary in 3 sentences. No intro lines, no 'Here is', no asterisks. Be direct and to the point."}, {"role": "user", "content": prompt} ], max_tokens=max_tokens, temperature=0.7, stop=None ) return chat_completion.choices[0].message.content.strip().replace("**", "") except Exception as e: return f"Error: {e}" st.markdown("
", unsafe_allow_html=True) # --- GENERATE BUTTON --- test_btn = st.button( "🚦 Run Persona–Product Fit Check", help="Instantly see AI-powered feedback from every persona's perspective!", use_container_width=True ) st.markdown("
", unsafe_allow_html=True) if test_btn and product_desc and personas: st.markdown(f"

2. Persona-by-Persona Results

", unsafe_allow_html=True) persona_colors = [neon_blue, neon_green, neon_pink, neon_orange, neon_cyan] persona_cycle = iter(persona_colors) section_icons = { "Probable Reaction": "💡", "Alignment with Persona": "✅", "Potential Mismatches or Concerns": "⚠️", "Marketing Strategy": "📢", "Personalized Notification": "🔔", } def persona_block(persona, color): return st.container() # Pair personas 2 per row for i in range(0, len(personas), 2): cols = st.columns(2, gap="large") for j, col in enumerate(cols): if i + j < len(personas): persona = personas[i + j] color = next(persona_cycle, neon_blue) with col: st.markdown(f"
{persona.get('icon','')} {persona['name']}
", unsafe_allow_html=True) st.markdown(f"
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown(f"
{section_icons['Probable Reaction']} Probable Reaction
", unsafe_allow_html=True) reactions = ai_points( f"Summarize two brief but complete points for this persona's likely reaction to the product: {product_desc}. Use clear, direct language.", max_points=2, max_tokens=90 ) st.markdown(f"", unsafe_allow_html=True) st.markdown(f"
{section_icons['Alignment with Persona']} Alignment with Persona
", unsafe_allow_html=True) aligns = ai_points( f"List two specific ways this persona's characteristics or needs will match with the features or benefits of the product: {product_desc}. " f"Be explicit: mention which part of the persona is satisfied by which product feature. Use clear, direct language.", max_points=2, max_tokens=100 ) st.markdown(f"", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown(f"
{section_icons['Potential Mismatches or Concerns']} Potential Mismatches or Concerns
", unsafe_allow_html=True) mismatches = ai_points( f"List two precise concerns or mismatches: Which features or aspects of the {product_desc} may NOT align with this persona's preferences or needs? " f"Be explicit: mention which product feature is likely to be a turn-off or ignored by this persona.", max_points=2, max_tokens=100 ) st.markdown(f"", unsafe_allow_html=True) st.markdown(f"
{section_icons['Marketing Strategy']} Marketing Strategy
", unsafe_allow_html=True) strategy = ai_points( f"Suggest two creative, product-specific marketing strategies targeted at this persona for this product: {product_desc}. " f"Each point must clearly connect a product feature with a unique marketing approach for this persona.", max_points=2, max_tokens=100 ) st.markdown(f"", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown( f"""
Interest Likelihood: {ai_percent('Estimate the likelihood (percent) that '+persona['name']+' would be interested in this product. Just the number and % sign, nothing else.')}
{section_icons['Personalized Notification']} Personalized Notification
{ai_notification( f"Write a concise, energetic notification or email about this product: {product_desc} aimed specifically at the persona {persona['name']}. " f"Address their top motivations and finish with a strong call-to-action. No names, no symbols." )}
""", unsafe_allow_html=True ) st.markdown("
", unsafe_allow_html=True) # --- CHARTS (Demo) --- st.markdown(f"

3. Projected Market Impact

", unsafe_allow_html=True) persona_names = [p['name'] for p in personas] np.random.seed(42) projected_market_share = np.random.dirichlet(np.ones(len(persona_names)), size=1)[0] projected_sentiment = projected_market_share * 0.6 + np.random.rand(len(persona_names)) * 0.4 # correlation c1, c2 = st.columns(2) with c1: st.markdown(f"
Projected Market Share by Persona
", unsafe_allow_html=True) fig1 = go.Figure(data=[go.Pie(labels=persona_names, values=projected_market_share, hole=0.45)]) fig1.update_traces(textinfo='percent+label') fig1.update_layout(margin=dict(l=14, r=14, b=14, t=14), showlegend=True) st.plotly_chart(fig1, use_container_width=True) with c2: st.markdown(f"
Projected Sentiment by Persona
", unsafe_allow_html=True) fig2 = go.Figure(data=[go.Bar(x=persona_names, y=projected_sentiment, marker=dict(color=[neon_green, neon_blue, neon_pink, neon_orange, neon_cyan][:len(persona_names)]))]) fig2.update_layout(xaxis_title="Persona", yaxis_title="Projected Sentiment", font=dict(size=15)) st.plotly_chart(fig2, use_container_width=True) # --- Combined Chart Insights --- combined_prompt = ( f"Given the projected market share {list(np.round(projected_market_share*100,1))} percent and projected sentiment {list(np.round(projected_sentiment*100,1))} for these personas: {', '.join(persona_names)}, " "summarize 4 concise points that correlate the two charts and reveal the most important market insights. Each point should be in a new line and fully written." ) insights = ai_graph_insights(combined_prompt, max_tokens=200) st.markdown( f"
Key Combined Insights
" f"
", unsafe_allow_html=True ) # --- OVERALL SUMMARY --- st.markdown(f"

4. Overall Summary

", unsafe_allow_html=True) overall_prompt = ( f"Given these personas: {', '.join([p['name'] for p in personas])}, and the new product: {product_desc}, " "write a concise executive summary (3 sentences, no intro, no asterisks), focusing on overall fit, the main challenge, and the best next move for launch." ) summary_text = ai_summary(overall_prompt, max_tokens=1000) st.markdown( f"
{summary_text}
", unsafe_allow_html=True ) st.markdown("---") elif test_btn: st.warning("Please enter your product description to see the results.")