Spaces:
Running
Running
Update frontend.py
Browse files- frontend.py +30 -39
frontend.py
CHANGED
@@ -15,7 +15,7 @@ logo_path = "logo.png"
|
|
15 |
if os.path.exists(logo_path):
|
16 |
st.image(logo_path, width=160)
|
17 |
|
18 |
-
# Session State
|
19 |
if "review" not in st.session_state:
|
20 |
st.session_state.review = ""
|
21 |
if "dark_mode" not in st.session_state:
|
@@ -31,21 +31,14 @@ if st.session_state.dark_mode:
|
|
31 |
background-color: #1e1e1e;
|
32 |
color: #f5f5f5;
|
33 |
}
|
34 |
-
.stTextInput
|
35 |
-
background-color: #
|
36 |
-
color: white;
|
37 |
}
|
38 |
-
.
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
.stDownloadButton > button {
|
43 |
-
background-color: #444;
|
44 |
-
color: white;
|
45 |
-
}
|
46 |
-
.stSelectbox, .stCheckbox, .stRadio, .stFileUploader {
|
47 |
-
background-color: #2b2b2b;
|
48 |
-
color: white;
|
49 |
}
|
50 |
</style>
|
51 |
""", unsafe_allow_html=True)
|
@@ -65,26 +58,23 @@ with st.sidebar:
|
|
65 |
])
|
66 |
|
67 |
industry = st.selectbox("π Industry", [
|
68 |
-
"Auto-detect", "Generic", "E-commerce", "Healthcare", "Education", "Travel", "Banking", "Insurance",
|
|
|
69 |
])
|
70 |
|
71 |
product_category = st.selectbox("π§© Product Category", [
|
72 |
-
"Auto-detect", "General", "Mobile Devices", "Laptops", "Healthcare Devices", "Banking App", "Travel Service",
|
73 |
-
|
74 |
-
|
75 |
-
device_type = st.selectbox("π» Device", [
|
76 |
-
"Auto-detect", "Web", "Android", "iOS", "Desktop", "Smartwatch", "Kiosk"
|
77 |
])
|
78 |
|
79 |
use_aspects = st.checkbox("π¬ Enable Aspect Analysis")
|
80 |
use_smart_summary = st.checkbox("π§ Smart Summary (Single)")
|
81 |
use_smart_summary_bulk = st.checkbox("π§ Smart Summary for Bulk")
|
82 |
-
|
83 |
verbosity = st.radio("π£οΈ Response Style", ["Brief", "Detailed"])
|
84 |
follow_up = st.text_input("π Follow-up Question")
|
85 |
voice_lang = st.selectbox("π Voice Language", ["en", "fr", "es", "de", "hi", "zh"])
|
86 |
|
87 |
-
# Text-to-Speech
|
88 |
def speak(text, lang='en'):
|
89 |
tts = gTTS(text, lang=lang)
|
90 |
mp3 = BytesIO()
|
@@ -94,10 +84,9 @@ def speak(text, lang='en'):
|
|
94 |
mp3.seek(0)
|
95 |
return mp3
|
96 |
|
97 |
-
# Tabs
|
98 |
tab1, tab2 = st.tabs(["π§ Single Review", "π Bulk CSV"])
|
99 |
|
100 |
-
# --- SINGLE ---
|
101 |
with tab1:
|
102 |
st.title("π§ NeuroPulse AI β Multimodal Review Analyzer")
|
103 |
st.markdown("<div style='font-size:16px;color:#aaa;'>Minimum 50β100 words recommended.</div>", unsafe_allow_html=True)
|
@@ -122,20 +111,21 @@ with tab1:
|
|
122 |
else:
|
123 |
with st.spinner("Analyzing..."):
|
124 |
try:
|
|
|
|
|
|
|
125 |
payload = {
|
126 |
"text": review,
|
127 |
"model": sentiment_model,
|
128 |
-
"industry":
|
129 |
"aspects": use_aspects,
|
130 |
"follow_up": follow_up,
|
131 |
-
"product_category":
|
132 |
-
"device": device_type,
|
133 |
"verbosity": verbosity,
|
134 |
"intelligence": st.session_state.intelligence_mode
|
135 |
}
|
136 |
headers = {"x-api-key": api_token}
|
137 |
-
|
138 |
-
res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers, params=params)
|
139 |
|
140 |
if res.status_code == 200:
|
141 |
data = res.json()
|
@@ -143,7 +133,7 @@ with tab1:
|
|
143 |
st.subheader("π Summary")
|
144 |
st.info(data["summary"])
|
145 |
st.caption(f"π§ Summary Type: {'Smart' if use_smart_summary else 'Standard'}")
|
146 |
-
st.markdown(f"**Context:** {
|
147 |
st.subheader("π Audio")
|
148 |
audio = speak(data["summary"], lang=voice_lang)
|
149 |
st.download_button("β¬οΈ Download Summary Audio", audio.read(), "summary.mp3", mime="audio/mp3")
|
@@ -161,15 +151,16 @@ with tab1:
|
|
161 |
st.markdown(data["explanation"])
|
162 |
else:
|
163 |
st.error(f"β API Error {res.status_code}: {res.json().get('detail', 'Unknown error')}")
|
|
|
164 |
except Exception as e:
|
165 |
st.error(f"π« Exception occurred: {e}")
|
166 |
|
167 |
-
# --- BULK ---
|
168 |
with tab2:
|
169 |
st.title("π Bulk CSV Upload")
|
170 |
st.markdown("""
|
171 |
Upload a CSV with the following columns:<br>
|
172 |
-
<code>review</code> (required), <code>industry</code>, <code>product_category</code
|
173 |
""", unsafe_allow_html=True)
|
174 |
|
175 |
with st.expander("π Sample CSV"):
|
@@ -185,10 +176,12 @@ with tab2:
|
|
185 |
st.error("CSV must contain a `review` column.")
|
186 |
else:
|
187 |
st.success(f"β
Loaded {len(df)} reviews")
|
188 |
-
|
|
|
189 |
if col not in df.columns:
|
190 |
-
|
191 |
-
|
|
|
192 |
|
193 |
if st.button("π Analyze Bulk Reviews", use_container_width=True):
|
194 |
with st.spinner("Processing..."):
|
@@ -199,12 +192,10 @@ with tab2:
|
|
199 |
"aspects": use_aspects,
|
200 |
"industry": df["industry"].tolist(),
|
201 |
"product_category": df["product_category"].tolist(),
|
202 |
-
"device": df["device"].tolist(),
|
203 |
"intelligence": st.session_state.intelligence_mode
|
204 |
}
|
205 |
headers = {"x-api-key": api_token}
|
206 |
-
|
207 |
-
res = requests.post(f"{backend_url}/bulk/", json=payload, headers=headers, params=params)
|
208 |
|
209 |
if res.status_code == 200:
|
210 |
results = pd.DataFrame(res.json()["results"])
|
|
|
15 |
if os.path.exists(logo_path):
|
16 |
st.image(logo_path, width=160)
|
17 |
|
18 |
+
# Session State defaults
|
19 |
if "review" not in st.session_state:
|
20 |
st.session_state.review = ""
|
21 |
if "dark_mode" not in st.session_state:
|
|
|
31 |
background-color: #1e1e1e;
|
32 |
color: #f5f5f5;
|
33 |
}
|
34 |
+
.stTextInput input, .stTextArea textarea, .stSelectbox div, .stButton button, .stRadio div {
|
35 |
+
background-color: #2c2c2c !important;
|
36 |
+
color: white !important;
|
37 |
}
|
38 |
+
.stToggle {
|
39 |
+
border: 1px solid #555;
|
40 |
+
border-radius: 10px;
|
41 |
+
padding: 5px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
}
|
43 |
</style>
|
44 |
""", unsafe_allow_html=True)
|
|
|
58 |
])
|
59 |
|
60 |
industry = st.selectbox("π Industry", [
|
61 |
+
"Auto-detect", "Generic", "E-commerce", "Healthcare", "Education", "Travel", "Banking", "Insurance",
|
62 |
+
"Gaming", "Food Delivery", "Real Estate", "Fitness", "Entertainment"
|
63 |
])
|
64 |
|
65 |
product_category = st.selectbox("π§© Product Category", [
|
66 |
+
"Auto-detect", "General", "Mobile Devices", "Laptops", "Healthcare Devices", "Banking App", "Travel Service",
|
67 |
+
"Educational Tool", "Insurance Portal", "Streaming App", "Wearables", "Home Appliances", "Food Apps"
|
|
|
|
|
|
|
68 |
])
|
69 |
|
70 |
use_aspects = st.checkbox("π¬ Enable Aspect Analysis")
|
71 |
use_smart_summary = st.checkbox("π§ Smart Summary (Single)")
|
72 |
use_smart_summary_bulk = st.checkbox("π§ Smart Summary for Bulk")
|
|
|
73 |
verbosity = st.radio("π£οΈ Response Style", ["Brief", "Detailed"])
|
74 |
follow_up = st.text_input("π Follow-up Question")
|
75 |
voice_lang = st.selectbox("π Voice Language", ["en", "fr", "es", "de", "hi", "zh"])
|
76 |
|
77 |
+
# Text-to-Speech
|
78 |
def speak(text, lang='en'):
|
79 |
tts = gTTS(text, lang=lang)
|
80 |
mp3 = BytesIO()
|
|
|
84 |
mp3.seek(0)
|
85 |
return mp3
|
86 |
|
|
|
87 |
tab1, tab2 = st.tabs(["π§ Single Review", "π Bulk CSV"])
|
88 |
|
89 |
+
# --- SINGLE REVIEW ---
|
90 |
with tab1:
|
91 |
st.title("π§ NeuroPulse AI β Multimodal Review Analyzer")
|
92 |
st.markdown("<div style='font-size:16px;color:#aaa;'>Minimum 50β100 words recommended.</div>", unsafe_allow_html=True)
|
|
|
111 |
else:
|
112 |
with st.spinner("Analyzing..."):
|
113 |
try:
|
114 |
+
clean_industry = "Generic" if industry == "Auto-detect" else industry
|
115 |
+
clean_category = "General" if product_category == "Auto-detect" else product_category
|
116 |
+
|
117 |
payload = {
|
118 |
"text": review,
|
119 |
"model": sentiment_model,
|
120 |
+
"industry": clean_industry,
|
121 |
"aspects": use_aspects,
|
122 |
"follow_up": follow_up,
|
123 |
+
"product_category": clean_category,
|
|
|
124 |
"verbosity": verbosity,
|
125 |
"intelligence": st.session_state.intelligence_mode
|
126 |
}
|
127 |
headers = {"x-api-key": api_token}
|
128 |
+
res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers)
|
|
|
129 |
|
130 |
if res.status_code == 200:
|
131 |
data = res.json()
|
|
|
133 |
st.subheader("π Summary")
|
134 |
st.info(data["summary"])
|
135 |
st.caption(f"π§ Summary Type: {'Smart' if use_smart_summary else 'Standard'}")
|
136 |
+
st.markdown(f"**Context:** {clean_industry} | {clean_category}")
|
137 |
st.subheader("π Audio")
|
138 |
audio = speak(data["summary"], lang=voice_lang)
|
139 |
st.download_button("β¬οΈ Download Summary Audio", audio.read(), "summary.mp3", mime="audio/mp3")
|
|
|
151 |
st.markdown(data["explanation"])
|
152 |
else:
|
153 |
st.error(f"β API Error {res.status_code}: {res.json().get('detail', 'Unknown error')}")
|
154 |
+
|
155 |
except Exception as e:
|
156 |
st.error(f"π« Exception occurred: {e}")
|
157 |
|
158 |
+
# --- BULK CSV UPLOAD ---
|
159 |
with tab2:
|
160 |
st.title("π Bulk CSV Upload")
|
161 |
st.markdown("""
|
162 |
Upload a CSV with the following columns:<br>
|
163 |
+
<code>review</code> (required), <code>industry</code>, <code>product_category</code> (optional)
|
164 |
""", unsafe_allow_html=True)
|
165 |
|
166 |
with st.expander("π Sample CSV"):
|
|
|
176 |
st.error("CSV must contain a `review` column.")
|
177 |
else:
|
178 |
st.success(f"β
Loaded {len(df)} reviews")
|
179 |
+
|
180 |
+
for col in ["industry", "product_category"]:
|
181 |
if col not in df.columns:
|
182 |
+
default = "Generic" if col == "industry" else "General"
|
183 |
+
df[col] = [default] * len(df)
|
184 |
+
df[col] = df[col].fillna("").astype(str).replace("Auto-detect", "Generic" if col == "industry" else "General")
|
185 |
|
186 |
if st.button("π Analyze Bulk Reviews", use_container_width=True):
|
187 |
with st.spinner("Processing..."):
|
|
|
192 |
"aspects": use_aspects,
|
193 |
"industry": df["industry"].tolist(),
|
194 |
"product_category": df["product_category"].tolist(),
|
|
|
195 |
"intelligence": st.session_state.intelligence_mode
|
196 |
}
|
197 |
headers = {"x-api-key": api_token}
|
198 |
+
res = requests.post(f"{backend_url}/bulk/", json=payload, headers=headers)
|
|
|
199 |
|
200 |
if res.status_code == 200:
|
201 |
results = pd.DataFrame(res.json()["results"])
|