import streamlit as st import requests import pandas as pd import tempfile 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, "use_aspects": False, "use_explain_bulk": False } 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) api_token = st.text_input("๐Ÿ” API Token", value="my-secret-key", type="password") 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"]) st.session_state.use_aspects = st.checkbox("๐Ÿ” Detect Pain Points", value=st.session_state.get("use_aspects", False)) st.session_state.use_explain_bulk = st.checkbox("๐Ÿง  Generate PM Insight (Bulk)", value=st.session_state.get("use_explain_bulk", False)) verbosity = st.radio("๐Ÿ—ฃ๏ธ Response Style", ["Brief", "Detailed"]) 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 analyze = False col1, col2, col3 = st.columns(3) with col1: analyze = st.button("๐Ÿ” Analyze") with col2: if st.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() with col3: if st.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.get("trigger_example_analysis")): with st.spinner("Analyzing feedback..."): try: model_used = None if sentiment_model == "Auto-detect" else sentiment_model payload = { "text": st.session_state.review, "model": model_used or "distilbert-base-uncased-finetuned-sst-2-english", "industry": industry, "product_category": product_category, "verbosity": verbosity, "aspects": st.session_state.use_aspects, "intelligence": st.session_state.get("intelligence_mode", False) } headers = {"x-api-key": st.session_state.get("api_token", "my-secret-key")} res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers) if res.ok: st.session_state.last_response = res.json() else: try: err_detail = res.json().get("detail", "No detail provided.") except Exception: err_detail = res.text st.error(f"โŒ Backend Error ({res.status_code}): {err_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.progress(data["sentiment"]["score"]) 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 st.session_state.use_aspects: if data.get("pain_points"): st.error("๐Ÿ” Pain Points: " + ", ".join(data["pain_points"])) else: st.info("โœ… No specific pain points were detected.") 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.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: try: follow_payload = { "text": st.session_state.review, "question": q_input, "verbosity": verbosity } headers = {"x-api-key": api_token} res = requests.post(f"{backend_url}/followup/", json=follow_payload, headers=headers) if res.ok: st.success(res.json().get("answer")) else: try: err_detail = res.json().get("detail", "No detail provided.") except Exception: err_detail = res.text st.error(f"โŒ Follow-up API Error ({res.status_code}): {err_detail}") except Exception as e: st.error(f"โš ๏ธ Follow-up error: {e}") 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() y_columns = [col for col in trend.columns if col != "date"] st.markdown("#### ๐Ÿ“… Daily Churn Trend") fig = px.bar(trend, x="date", y=y_columns, 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": st.session_state.use_aspects, "intelligence": st.session_state.intelligence_mode "explain_bulk": st.session_state.use_explain_bulk } 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: try: err_detail = res.json().get("detail", "No detail provided.") except Exception: err_detail = res.text st.error(f"โŒ Bulk API Error ({res.status_code}): {err_detail}") except Exception as e: st.error(f"Bulk analysis failed: {e}")