File size: 11,305 Bytes
fc94552
 
 
82211db
 
fc94552
 
 
 
 
5224787
 
fc94552
05dad7c
5224787
 
05dad7c
8e27b3a
fc94552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05dad7c
fc94552
9aa56c6
fc94552
 
9da624c
90267c3
1a945f1
 
fc94552
05dad7c
fc94552
 
9aa56c6
 
fc94552
 
 
05dad7c
6f0a5bf
 
 
 
 
 
 
 
82211db
6f0a5bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82211db
6f0a5bf
 
82211db
fc94552
9aa56c6
fc94552
05dad7c
fc94552
8e27b3a
 
4ce1c62
9aa56c6
fc94552
 
82b3c12
fc94552
4ce1c62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05dad7c
82b3c12
9aa56c6
fc94552
c88afb5
fc94552
 
4ce1c62
fc94552
 
 
 
82b3c12
fc94552
4ce1c62
fc94552
05dad7c
fc94552
 
05dad7c
fc94552
 
 
 
 
9aa56c6
fc94552
9aa56c6
fc94552
 
33c45f1
 
 
 
05dad7c
9aa56c6
05dad7c
 
5224787
 
 
 
 
 
63a3c84
05dad7c
 
5224787
 
9aa56c6
6f0a5bf
9aa56c6
6f0a5bf
 
 
 
 
 
 
 
 
 
 
 
fc94552
9aa56c6
68804d2
 
 
 
05dad7c
 
 
68804d2
05dad7c
c88afb5
05dad7c
 
c88afb5
05dad7c
c88afb5
 
 
 
 
 
f4ad55e
c88afb5
 
 
 
 
 
 
05dad7c
 
 
 
 
 
c88afb5
05dad7c
727b706
05dad7c
 
 
 
 
4ce1c62
05dad7c
 
 
 
 
 
 
 
0f55ff6
05dad7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d78888c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
import streamlit as st
import requests
import pandas as pd
import azure.cognitiveservices.speech as speechsdk
import tempfile
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("""
    <style>
    html, body, [class*="st-"] {
        background-color: #121212;
        color: #f5f5f5;
    }
    </style>
    """, 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"])
    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
# Setup usage tracking
if "tts_usage_count" not in st.session_state:
    st.session_state.tts_usage_count = 0
if "enable_audio" not in st.session_state:
    st.session_state.enable_audio = False

# Azure TTS function
def azure_speak(text, lang='en-US'):
    try:
        speech_config = speechsdk.SpeechConfig(
            subscription=st.secrets["AZURE_SPEECH_KEY"],
            region=st.secrets["AZURE_REGION"]
        )
        speech_config.speech_synthesis_language = lang
        with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmpfile:
            audio_config = speechsdk.audio.AudioOutputConfig(filename=tmpfile.name)
            synthesizer = speechsdk.SpeechSynthesizer(speech_config, audio_config)
            result = synthesizer.speak_text_async(text).get()
            if result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted:
                st.session_state.tts_usage_count += 1
                return tmpfile.name
            else:
                st.error("Speech synthesis failed.")
                return None
    except Exception as e:
        st.error(f"Azure TTS error: {e}")
        return None


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": 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:
                    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}")

        # Only show toggle and button for audio
        st.subheader("πŸ”Š Audio Summary")
        st.session_state.enable_audio = st.toggle("🎧 Generate Audio Summary")

        if st.session_state.enable_audio:
            if st.session_state.tts_usage_count > 20:
                st.warning("πŸ”‡ Azure TTS usage limit reached for this session.")
            else:
                if st.button("▢️ Generate & Play Audio"):
                    audio_path = azure_speak(data["summary"], lang=f"{voice_lang}-US")
                    if audio_path:
                        audio_bytes = open(audio_path, "rb").read()
                        st.audio(audio_bytes, format="audio/mp3")
                        st.download_button("⬇️ Download Audio", audio_bytes, "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:
            try:
                follow_payload = {
                    "text": st.session_state.review,
                    "question": q_input,
                    "verbosity": verbosity
                }
                headers = {"x-api-key": st.session_state.get("api_token", "my-secret-key")}  # βœ… FIX here
                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 get follow-up answer.")
            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": 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}")