# [STREAMLIT FRONTEND - Product Feedback AI Assistant] 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 st.set_page_config(page_title="PM Feedback Assistant", 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 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 not api_token or api_token.strip() == "my-secret-key": st.warning("๐Ÿงช Demo Mode โ€” Not all features active.") 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", "nlptown/bert-base-multilingual-uncased-sentiment" ]) 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"]) # TTS 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 === with tab1: st.title("๐Ÿ“Š Product Feedback AI Assistant") st.markdown("Get insights from real user feedback to reduce churn and improve product strategy.") 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) 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 (analyze or st.session_state.trigger_example_analysis) and st.session_state.review: st.session_state.trigger_example_analysis = False st.session_state.followup_answer = None with st.spinner("Analyzing feedback..."): try: model = None if sentiment_model == "Auto-detect" else sentiment_model payload = { "text": st.session_state.review, "model": model or "distilbert-base-uncased-finetuned-sst-2-english", "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.status_code == 200: st.session_state.last_response = res.json() else: st.error(f"API 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.caption("๐Ÿ”Ž Summary Model: facebook/bart-large-cnn | " + verbosity + " response") 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: st.warning(f"โš ๏ธ Estimated Churn Risk: {data['churn_risk']}") if "pain_points" in data and data["pain_points"]: st.error("๐Ÿ” Pain Points: " + ", ".join(data["pain_points"])) 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") sample_questions = ["What made the user upset?", "Any feature complaints?", "How urgent is this?"] selected_q = st.selectbox("๐Ÿ’ก Suggested Questions", ["Type your own..."] + sample_questions) custom_q = selected_q if selected_q != "Type your own..." else st.text_input("๐Ÿ” Follow-up Question") if custom_q: with st.spinner("Thinking..."): try: follow_payload = { "text": st.session_state.review, "question": custom_q, "verbosity": verbosity } headers = {"x-api-key": api_token} res = requests.post(f"{backend_url}/followup/", json=follow_payload, headers=headers) if res.status_code == 200: st.session_state.followup_answer = res.json().get("answer") else: st.error(f"โŒ Follow-up failed: {res.json().get('detail')}") except Exception as e: st.error(f"โš ๏ธ Follow-up error: {e}") if st.session_state.followup_answer: st.subheader("โœ… Answer") st.success(st.session_state.followup_answer)