import streamlit as st import requests import pandas as pd from gtts import gTTS import base64 from io import BytesIO import os import plotly.express as px from datetime import datetime import uuid # Simulated in-memory storage for churn log if "churn_log" not in st.session_state: st.session_state.churn_log = [] st.set_page_config(page_title="ChurnSight AI", page_icon="๐Ÿง ", layout="wide") if os.path.exists("logo.png"): st.image("logo.png", width=180) # Session state setup defaults = { "review": "", "dark_mode": False, "intelligence_mode": True, "trigger_example_analysis": False, "last_response": None, "followup_answer": None } for k, v in defaults.items(): if k not in st.session_state: st.session_state[k] = v # Dark mode styling if st.session_state.dark_mode: st.markdown(""" """, unsafe_allow_html=True) # Sidebar config with st.sidebar: st.header("โš™๏ธ PM Config") st.session_state.dark_mode = st.toggle("๐ŸŒ™ Dark Mode", value=st.session_state.dark_mode) st.session_state.intelligence_mode = st.toggle("๐Ÿง  Intelligence Mode", value=st.session_state.intelligence_mode) if api_token.strip() == "my-secret-key": st.warning("๐Ÿงช Demo Mode โ€” Not all features are active. Add your API token to unlock full features.") backend_url = st.text_input("๐ŸŒ Backend URL", value="http://localhost:8000") sentiment_model = st.selectbox("๐Ÿ“Š Sentiment Model", ["Auto-detect", "distilbert-base-uncased-finetuned-sst-2-english"]) industry = st.selectbox("๐Ÿญ Industry", ["Auto-detect", "Generic", "E-commerce", "Healthcare", "Education"]) product_category = st.selectbox("๐Ÿงฉ Product Category", ["Auto-detect", "General", "Mobile Devices", "Laptops"]) use_aspects = st.checkbox("๐Ÿ” Detect Pain Points") use_explain_bulk = st.checkbox("๐Ÿง  Generate PM Insight (Bulk)") verbosity = st.radio("๐Ÿ—ฃ๏ธ Response Style", ["Brief", "Detailed"]) voice_lang = st.selectbox("๐Ÿ”ˆ Voice Language", ["en", "fr", "es", "de", "hi", "zh"]) # Text-to-Speech def speak(text, lang='en'): tts = gTTS(text, lang=lang) mp3 = BytesIO() tts.write_to_fp(mp3) b64 = base64.b64encode(mp3.getvalue()).decode() st.markdown(f'', unsafe_allow_html=True) mp3.seek(0) return mp3 tab1, tab2 = st.tabs(["๐Ÿง  Analyze Review", "๐Ÿ“š Bulk Reviews"]) # === SINGLE REVIEW ANALYSIS === with tab1: st.title("๐Ÿ“Š ChurnSight AI โ€” Product Feedback Assistant") st.markdown("Analyze feedback to detect churn risk, extract pain points, and support product decisions.") review = st.text_area("๐Ÿ“ Enter Customer Feedback", value=st.session_state.review, height=180) st.session_state.review = review col1, col2, col3 = st.columns(3) if col1.button("๐Ÿ” Analyze"): st.session_state.trigger_example_analysis = False if col2.button("๐ŸŽฒ Example"): st.session_state.review = ( "The app crashes every time I try to checkout. It's so slow and unresponsive. " "Customer support never replied. I'm switching to another brand." ) st.session_state.trigger_example_analysis = True st.rerun() if col3.button("๐Ÿงน Clear"): for key in ["review", "last_response", "followup_answer"]: st.session_state[key] = "" st.rerun() if st.session_state.review and (analyze or st.session_state.trigger_example_analysis): with st.spinner("Analyzing feedback..."): try: payload = { "text": st.session_state.review, "model": "distilbert-base-uncased-finetuned-sst-2-english" if sentiment_model == "Auto-detect" else sentiment_model, "industry": industry, "product_category": product_category, "verbosity": verbosity, "aspects": use_aspects, "intelligence": st.session_state.intelligence_mode } headers = {"x-api-key": api_token} res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers) if res.ok: st.session_state.last_response = res.json() else: st.error(f"Error: {res.status_code} - {res.json().get('detail')}") except Exception as e: st.error(f"๐Ÿšซ Exception: {e}") data = st.session_state.last_response if data: st.subheader("๐Ÿ“Œ PM Insight Summary") st.info(data["summary"]) st.markdown(f"**Industry:** `{data['industry']}` | **Category:** `{data['product_category']}` | **Device:** Web") st.metric("๐Ÿ“Š Sentiment", data["sentiment"]["label"], delta=f"{data['sentiment']['score']:.2%}") st.info(f"๐Ÿ’ข Emotion: {data['emotion']}") if "churn_risk" in data: risk = data["churn_risk"] color = "๐Ÿ”ด" if risk == "High Risk" else "๐ŸŸข" st.metric("๐Ÿšจ Churn Risk", f"{color} {risk}") if data.get("pain_points"): st.error("๐Ÿ” Pain Points: " + ", ".join(data["pain_points"])) # Add to churn log try: st.session_state.churn_log.append({ "timestamp": datetime.now(), "product": data.get("product_category", "General"), "churn_risk": data.get("churn_risk", "Unknown"), "session_id": str(uuid.uuid4()) }) if len(st.session_state.churn_log) > 1000: st.session_state.churn_log = st.session_state.churn_log[-1000:] except Exception as e: st.warning(f"๐Ÿงช Logging failed: {e}") st.subheader("๐Ÿ”Š Audio Summary") audio = speak(data["summary"], lang=voice_lang) st.download_button("โฌ‡๏ธ Download Audio", audio.read(), "summary.mp3") st.markdown("### ๐Ÿ” Ask a Follow-Up") sentiment = data["sentiment"]["label"].lower() churn = data.get("churn_risk", "") pain = data.get("pain_points", []) if sentiment == "positive" and churn == "Low Risk": suggestions = ["What features impressed the user?", "Would they recommend the product?"] elif churn == "High Risk": suggestions = ["What made the user upset?", "Is this user likely to churn?"] else: suggestions = ["What are the key takeaways?", "Is there any concern raised?"] selected_q = st.selectbox("๐Ÿ’ก Suggested Questions", ["Type your own..."] + suggestions) q_input = st.text_input("๐Ÿ” Your Question") if selected_q == "Type your own..." else selected_q if q_input: follow_payload = {"text": st.session_state.review, "question": q_input, "verbosity": verbosity} res = requests.post(f"{backend_url}/followup/", json=follow_payload, headers=headers) if res.ok: st.success(res.json().get("answer")) else: st.error("Failed to answer.") if st.checkbox("๐Ÿ“Š Show Churn Risk Trends"): try: df = pd.DataFrame(st.session_state.churn_log) df["date"] = pd.to_datetime(df["timestamp"]).dt.date trend = df.groupby(["date", "churn_risk"]).size().unstack(fill_value=0).reset_index() st.markdown("#### ๐Ÿ“… Daily Churn Trend") fig = px.bar(trend, x="date", y=["High Risk", "Low Risk"], barmode="group") st.plotly_chart(fig, use_container_width=True) st.download_button("โฌ‡๏ธ Export Trend CSV", trend.to_csv(index=False), "churn_trend.csv") except Exception as e: st.error(f"Trend error: {e}") # === BULK REVIEW ANALYSIS === with tab2: st.title("๐Ÿ“š Bulk Feedback Analysis") bulk_input = st.text_area("๐Ÿ“ฅ Paste multiple reviews (one per line)", height=250) if st.button("๐Ÿš€ Analyze Bulk"): lines = [l.strip() for l in bulk_input.strip().splitlines() if l.strip()] payload = { "reviews": lines, "model": "distilbert-base-uncased-finetuned-sst-2-english" if sentiment_model == "Auto-detect" else sentiment_model, "industry": None, "product_category": None, "device": None, "aspects": use_aspects, "intelligence": st.session_state.intelligence_mode } try: res = requests.post(f"{backend_url}/bulk/?token={api_token}", json=payload) if res.ok: results = res.json().get("results", []) df = pd.DataFrame(results) st.dataframe(df) st.download_button("โฌ‡๏ธ Export Results CSV", df.to_csv(index=False), "bulk_results.csv") else: st.error(f"API Error: {res.status_code}") except Exception as e: st.error(f"Bulk analysis failed: {e}")