shivam-1706 commited on
Commit
24db65a
Β·
verified Β·
1 Parent(s): c890baf

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +78 -168
src/streamlit_app.py CHANGED
@@ -3,28 +3,25 @@ import torch
3
  from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
4
  from openai import OpenAI
5
  import re
6
- import os
7
 
8
- # Page configuration
9
  st.set_page_config(
10
  page_title="Sentiment Analyzer",
11
  page_icon="🎭",
12
  layout="wide"
13
  )
14
 
15
- # Title and description
16
  st.title("🎭 Sentiment Analysis with AI Responses")
17
  st.markdown("**5-Class Amazon Review Sentiment Analysis + AI-Generated Customer Support Responses**")
18
  st.markdown("*Powered by GPT4.0 mini*")
19
  st.markdown("---")
20
 
21
- # HARDCODE YOUR GITHUB API KEY HERE
22
- GITHUB_TOKEN = "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" # REPLACE WITH YOUR REAL KEY
23
 
24
- # Initialize LLM client with your key
25
  @st.cache_resource
26
  def load_llm_client():
27
- """Initialize GitHub Models client with hardcoded key"""
28
  try:
29
  return OpenAI(
30
  api_key=GITHUB_TOKEN,
@@ -34,24 +31,18 @@ def load_llm_client():
34
  st.error(f"Failed to initialize LLM client: {str(e)}")
35
  return None
36
 
37
- # Load model
38
  @st.cache_resource
39
  def load_sentiment_model():
40
- """Load the fine-tuned DistilBERT model with proper 5-class handling"""
41
  try:
42
- # EDIT THIS: Replace with your actual model path
43
- model_name = "shivam-1706/distilbert-amazon-sentiment" # UPDATE THIS
44
-
45
- # Load with explicit configuration
46
  tokenizer = AutoTokenizer.from_pretrained(model_name)
47
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
48
-
49
- # Verify model has 5 classes
50
  if model.config.num_labels == 5:
51
  st.success("βœ… 5-class sentiment model loaded successfully!")
52
  else:
53
  st.warning(f"⚠️ Model has {model.config.num_labels} classes, expected 5")
54
-
55
  return pipeline(
56
  "text-classification",
57
  model=model,
@@ -68,19 +59,16 @@ def load_sentiment_model():
68
  return_all_scores=True
69
  )
70
 
71
- # Load models
72
  with st.spinner("Loading DistilBERT model..."):
73
  sentiment_pipeline = load_sentiment_model()
74
 
75
- # Load LLM client automatically
76
  llm_client = load_llm_client()
77
  if llm_client:
78
  st.success("βœ… GPT4.0 mini connected automatically!")
79
 
 
80
  def fix_problematic_words(text):
81
- """Quick fix for specific words causing classification issues"""
82
  fixes = {
83
- # Extremely positive words that need emphasis
84
  'phenomenal': 'absolutely amazing excellent outstanding incredible wonderful',
85
  'fabulous': 'excellent amazing wonderful fantastic great',
86
  'superb': 'excellent outstanding amazing great wonderful',
@@ -96,8 +84,6 @@ def fix_problematic_words(text):
96
  'incredible': 'amazing excellent outstanding wonderful',
97
  'outstanding': 'excellent amazing great wonderful',
98
  'remarkable': 'excellent amazing outstanding wonderful',
99
-
100
- # Extremely negative words that need emphasis
101
  'horrendous': 'absolutely terrible awful horrible disgusting',
102
  'dreadful': 'terrible awful horrible bad disgusting',
103
  'atrocious': 'extremely terrible awful horrible disgusting',
@@ -109,154 +95,91 @@ def fix_problematic_words(text):
109
  'abominable': 'terrible awful horrible disgusting',
110
  'despicable': 'terrible awful horrible bad'
111
  }
112
-
113
  text_fixed = text
114
- for problematic, replacement in fixes.items():
115
- text_fixed = re.sub(rf'\b{problematic}\b', replacement, text_fixed, flags=re.IGNORECASE)
116
-
117
  return text_fixed
118
 
119
  def predict_sentiment_enhanced(text):
120
- """Enhanced sentiment prediction ensuring 5-class output"""
121
  if not text.strip():
122
  return "Average", 0.20, {
123
- 'Very Bad': 0.10,
124
- 'Bad': 0.15,
125
- 'Average': 0.50,
126
- 'Good': 0.15,
127
- 'Very Good': 0.10
128
  }
129
-
130
  try:
131
  results = sentiment_pipeline(fix_problematic_words(text))
132
  if isinstance(results[0], list):
133
  results = results[0]
134
-
135
- # Enhanced label mapping for 5-class model
136
  label_map = {
137
  'LABEL_0': 'Very Bad',
138
- 'LABEL_1': 'Bad',
139
  'LABEL_2': 'Average',
140
  'LABEL_3': 'Good',
141
  'LABEL_4': 'Very Good'
142
  }
143
-
144
- # Check if we have 5 classes (correct model)
145
  if len(results) == 5:
146
  best_result = max(results, key=lambda x: x['score'])
147
  sentiment = label_map.get(best_result['label'], best_result['label'])
148
  confidence = best_result['score']
149
-
150
- all_scores = {}
151
- for result in results:
152
- mapped_label = label_map.get(result['label'], result['label'])
153
- all_scores[mapped_label] = result['score']
154
-
155
  return sentiment, confidence, all_scores
156
-
157
- # Fallback for 3-class model
158
  else:
159
  fallback_map = {'NEGATIVE': 'Bad', 'NEUTRAL': 'Average', 'POSITIVE': 'Good'}
160
  best_result = max(results, key=lambda x: x['score'])
161
  sentiment = fallback_map.get(best_result['label'], 'Average')
162
  confidence = best_result['score']
163
-
164
- # Create approximated 5-class scores
165
  all_scores = {'Very Bad': 0.0, 'Bad': 0.0, 'Average': 0.0, 'Good': 0.0, 'Very Good': 0.0}
166
- for result in results:
167
- mapped = fallback_map.get(result['label'], 'Average')
168
- all_scores[mapped] = result['score']
169
-
170
  return sentiment, confidence, all_scores
171
-
172
  except Exception as e:
173
  st.error(f"Error in prediction: {str(e)}")
174
  return "Average", 0.5, {'Average': 0.5}
175
 
176
  def generate_llm_response(review_text, sentiment):
177
- """Generate AI-powered customer support response using your API key"""
178
  if not llm_client:
179
  return "❌ GitHub Models API not available. Please check your API key."
180
-
181
- # Same prompts as before...
182
- prompts = {
183
- 'Very Bad': f"""You are a professional customer service manager. A customer left this review: "{review_text}"
184
-
185
- Their sentiment is very negative. Provide a professional response that:
186
- 1. Shows genuine empathy and takes responsibility
187
- 2. Offers concrete solutions: refund, replacement, customer support contact
188
- 3. Provides next steps and assures resolution
189
- 4. Don't mention star ratings
190
-
191
- Keep it professional and solution-focused (2-3 sentences).
192
- Response:""",
193
-
194
- 'Bad': f"""You are a customer service representative. A customer wrote: "{review_text}"
195
-
196
- Their experience was negative. Provide a response that:
197
- 1. Acknowledges their concerns and validates feedback
198
- 2. Offers solutions: replacement, troubleshooting, partial refund
199
- 3. Asks for suggestions to improve
200
- 4. Shows commitment to making it right
201
-
202
- Keep it constructive (2-3 sentences). Don't mention star ratings.
203
- Response:""",
204
-
205
- 'Average': f"""You are responding to a customer who wrote: "{review_text}"
206
 
207
- They had a mixed experience. Provide a response that:
208
- 1. Thanks them for honest feedback
209
- 2. Acknowledges both positives and areas for improvement
210
- 3. Offers assistance and support
211
- 4. Invites suggestions for enhancement
212
-
213
- Keep it balanced and appreciative (2-3 sentences). Don't mention star ratings.
214
  Response:""",
215
 
216
- 'Good': f"""You are responding to a satisfied customer: "{review_text}"
 
217
 
218
- They had a positive experience. Provide a response that:
219
- 1. Thanks them genuinely for positive feedback
220
- 2. Acknowledges specific aspects they liked
221
- 3. Shows commitment to maintaining quality
222
- 4. Offers continued support
223
 
224
- Keep it appreciative (2-3 sentences). Don't mention star ratings.
225
- Response:""",
226
-
227
- 'Very Good': f"""You are responding to a delighted customer: "{review_text}"
228
 
229
- They had an excellent experience. Provide a response that:
230
- 1. Expresses genuine gratitude for amazing feedback
231
- 2. Celebrates their positive experience
232
- 3. Shows this motivates your team
233
- 4. Invites them to share their experience
234
-
235
- Keep it enthusiastic yet professional (2-3 sentences). Don't mention star ratings.
236
- Response:"""
237
  }
238
-
239
- prompt = prompts.get(sentiment, prompts['Average'])
240
-
241
  try:
242
  response = llm_client.chat.completions.create(
243
  model="gpt-4o-mini",
244
- messages=[{"role": "user", "content": prompt}],
245
  max_tokens=150,
246
  temperature=0.7
247
  )
248
  return response.choices[0].message.content.strip()
249
- except Exception as e:
250
- fallbacks = {
251
- 'Very Bad': "We sincerely apologize for this disappointing experience. Please contact our customer support immediately so we can arrange a full refund or replacement.",
252
- 'Bad': "Thank you for bringing these concerns to our attention. We'd like to work together to find a solution that meets your needs.",
253
- 'Average': "We appreciate your honest feedback and suggestions. Your input helps us continue improving our products and services.",
254
- 'Good': "Thank you for your positive feedback! We're delighted you had a good experience and appreciate your support.",
255
- 'Very Good': "Wow, thank you for this fantastic review! Your enthusiasm means the world to our team."
256
  }
257
- return fallbacks.get(sentiment, "Thank you for your valuable feedback!")
258
 
259
- # Main interface
260
  col1, col2 = st.columns([2, 1])
261
 
262
  with col1:
@@ -266,69 +189,56 @@ with col1:
266
  height=150,
267
  placeholder="Example: This product broke after just two days of use. Very disappointed with the quality and delivery was delayed too."
268
  )
269
-
270
  analyze_button = st.button("πŸ” Analyze & Generate Response", type="primary")
271
 
272
- with col2:
273
- st.subheader("πŸ“Š Analysis Results")
274
-
275
- if analyze_button and review_text.strip():
276
- with st.spinner("Analyzing sentiment..."):
277
- sentiment, confidence, all_scores = predict_sentiment_enhanced(review_text)
278
-
279
- # Display sentiment
280
- color_map = {
281
- 'Very Good': 'green',
282
- 'Good': 'blue',
283
- 'Average': 'orange',
284
- 'Bad': 'red',
285
- 'Very Bad': 'violet'
286
- }
287
-
288
- emoji_map = {
289
- 'Very Bad': '😑',
290
- 'Bad': '😞',
291
- 'Average': '😐',
292
- 'Good': '😊',
293
- 'Very Good': '🀩'
294
- }
295
-
296
- color = color_map.get(sentiment, 'blue')
297
- emoji = emoji_map.get(sentiment, '😐')
298
-
299
- st.markdown(f"**Sentiment:** :{color}[{sentiment}] {emoji}")
300
- st.progress(confidence)
301
- st.caption(f"Confidence: {confidence:.2%}")
302
-
303
- st.subheader("🎯 All 5 Class Predictions")
304
- class_order = ['Very Bad', 'Bad', 'Average', 'Good', 'Very Good']
305
- for class_name in class_order:
306
- score = all_scores.get(class_name, 0.0)
307
- emoji_display = emoji_map.get(class_name, '😐')
308
- st.write(f"{emoji_display} {class_name}: {score:.1%}")
309
 
310
- # NEW: put response in col1 below the button
311
  if analyze_button and review_text.strip():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  with col1:
313
  st.markdown("---")
314
  with st.spinner("Generating AI response..."):
315
  ai_response = generate_llm_response(review_text, sentiment)
316
-
317
  st.subheader("πŸ€– AI Customer Support Response")
318
  st.info(ai_response)
319
-
320
- # Show strategy
321
  strategies = {
322
  'Very Bad': "πŸ†˜ Crisis Management: Immediate resolution",
323
- 'Bad': "πŸ”§ Problem Resolution: Solutions & improvements",
324
  'Average': "βš–οΈ Balanced: Acknowledge & enhance",
325
  'Good': "πŸ‘ Appreciation: Maintain quality",
326
  'Very Good': "πŸŽ‰ Celebration: Encourage sharing"
327
  }
328
- st.caption(f"**Strategy:** {strategies.get(sentiment, 'βš–οΈ Balanced')}")
329
-
330
- elif analyze_button and not review_text.strip():
331
- st.warning("⚠️ Please enter a review to analyze!")
332
-
333
- # Rest of your app (examples section, etc.) remains the same...
334
- # [Include the examples section, instructions, and footer from the previous code]
 
3
  from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
4
  from openai import OpenAI
5
  import re
 
6
 
7
+ # --- Page configuration ---
8
  st.set_page_config(
9
  page_title="Sentiment Analyzer",
10
  page_icon="🎭",
11
  layout="wide"
12
  )
13
 
14
+ # --- App Header ---
15
  st.title("🎭 Sentiment Analysis with AI Responses")
16
  st.markdown("**5-Class Amazon Review Sentiment Analysis + AI-Generated Customer Support Responses**")
17
  st.markdown("*Powered by GPT4.0 mini*")
18
  st.markdown("---")
19
 
20
+ # --- API Key and Model Setup ---
21
+ GITHUB_TOKEN = "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"
22
 
 
23
  @st.cache_resource
24
  def load_llm_client():
 
25
  try:
26
  return OpenAI(
27
  api_key=GITHUB_TOKEN,
 
31
  st.error(f"Failed to initialize LLM client: {str(e)}")
32
  return None
33
 
 
34
  @st.cache_resource
35
  def load_sentiment_model():
 
36
  try:
37
+ model_name = "shivam-1706/distilbert-amazon-sentiment"
 
 
 
38
  tokenizer = AutoTokenizer.from_pretrained(model_name)
39
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
40
+
 
41
  if model.config.num_labels == 5:
42
  st.success("βœ… 5-class sentiment model loaded successfully!")
43
  else:
44
  st.warning(f"⚠️ Model has {model.config.num_labels} classes, expected 5")
45
+
46
  return pipeline(
47
  "text-classification",
48
  model=model,
 
59
  return_all_scores=True
60
  )
61
 
 
62
  with st.spinner("Loading DistilBERT model..."):
63
  sentiment_pipeline = load_sentiment_model()
64
 
 
65
  llm_client = load_llm_client()
66
  if llm_client:
67
  st.success("βœ… GPT4.0 mini connected automatically!")
68
 
69
+ # --- Helper functions ---
70
  def fix_problematic_words(text):
 
71
  fixes = {
 
72
  'phenomenal': 'absolutely amazing excellent outstanding incredible wonderful',
73
  'fabulous': 'excellent amazing wonderful fantastic great',
74
  'superb': 'excellent outstanding amazing great wonderful',
 
84
  'incredible': 'amazing excellent outstanding wonderful',
85
  'outstanding': 'excellent amazing great wonderful',
86
  'remarkable': 'excellent amazing outstanding wonderful',
 
 
87
  'horrendous': 'absolutely terrible awful horrible disgusting',
88
  'dreadful': 'terrible awful horrible bad disgusting',
89
  'atrocious': 'extremely terrible awful horrible disgusting',
 
95
  'abominable': 'terrible awful horrible disgusting',
96
  'despicable': 'terrible awful horrible bad'
97
  }
 
98
  text_fixed = text
99
+ for word, replacement in fixes.items():
100
+ text_fixed = re.sub(rf'\b{word}\b', replacement, text_fixed, flags=re.IGNORECASE)
 
101
  return text_fixed
102
 
103
  def predict_sentiment_enhanced(text):
 
104
  if not text.strip():
105
  return "Average", 0.20, {
106
+ 'Very Bad': 0.10, 'Bad': 0.15, 'Average': 0.50, 'Good': 0.15, 'Very Good': 0.10
 
 
 
 
107
  }
 
108
  try:
109
  results = sentiment_pipeline(fix_problematic_words(text))
110
  if isinstance(results[0], list):
111
  results = results[0]
112
+
 
113
  label_map = {
114
  'LABEL_0': 'Very Bad',
115
+ 'LABEL_1': 'Bad',
116
  'LABEL_2': 'Average',
117
  'LABEL_3': 'Good',
118
  'LABEL_4': 'Very Good'
119
  }
120
+
 
121
  if len(results) == 5:
122
  best_result = max(results, key=lambda x: x['score'])
123
  sentiment = label_map.get(best_result['label'], best_result['label'])
124
  confidence = best_result['score']
125
+ all_scores = {label_map.get(r['label'], r['label']): r['score'] for r in results}
 
 
 
 
 
126
  return sentiment, confidence, all_scores
 
 
127
  else:
128
  fallback_map = {'NEGATIVE': 'Bad', 'NEUTRAL': 'Average', 'POSITIVE': 'Good'}
129
  best_result = max(results, key=lambda x: x['score'])
130
  sentiment = fallback_map.get(best_result['label'], 'Average')
131
  confidence = best_result['score']
 
 
132
  all_scores = {'Very Bad': 0.0, 'Bad': 0.0, 'Average': 0.0, 'Good': 0.0, 'Very Good': 0.0}
133
+ all_scores[sentiment] = confidence
 
 
 
134
  return sentiment, confidence, all_scores
 
135
  except Exception as e:
136
  st.error(f"Error in prediction: {str(e)}")
137
  return "Average", 0.5, {'Average': 0.5}
138
 
139
  def generate_llm_response(review_text, sentiment):
 
140
  if not llm_client:
141
  return "❌ GitHub Models API not available. Please check your API key."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ prompts = {
144
+ 'Very Bad': f"""You are a professional customer service manager. A customer left this review: "{review_text}"...
145
+ Their sentiment is very negative. Provide a response that:
146
+ 1. Shows empathy
147
+ 2. Offers resolution (refund/replacement)
148
+ 3. Provides next steps
 
149
  Response:""",
150
 
151
+ 'Bad': f"""You are a customer service rep. Customer said: "{review_text}"...
152
+ Their experience was bad. Acknowledge & offer help. Response:""",
153
 
154
+ 'Average': f"""Respond to a mixed review: "{review_text}"...
155
+ Thank, acknowledge, offer help, ask for suggestions. Response:""",
 
 
 
156
 
157
+ 'Good': f"""Customer left a positive review: "{review_text}"...
158
+ Thank them and reinforce quality. Response:""",
 
 
159
 
160
+ 'Very Good': f"""Delighted customer wrote: "{review_text}"...
161
+ Celebrate, thank, and invite sharing. Response:"""
 
 
 
 
 
 
162
  }
163
+
 
 
164
  try:
165
  response = llm_client.chat.completions.create(
166
  model="gpt-4o-mini",
167
+ messages=[{"role": "user", "content": prompts.get(sentiment)}],
168
  max_tokens=150,
169
  temperature=0.7
170
  )
171
  return response.choices[0].message.content.strip()
172
+ except Exception:
173
+ fallback = {
174
+ 'Very Bad': "We’re truly sorry for your experience. Please reach out so we can make this right.",
175
+ 'Bad': "Thanks for your feedback. We’d like to help resolve your concern.",
176
+ 'Average': "Thanks for your honest review. We're listening and happy to improve.",
177
+ 'Good': "Thank you for your kind words! We appreciate your support.",
178
+ 'Very Good': "We’re thrilled you loved it! Thanks for your amazing review."
179
  }
180
+ return fallback.get(sentiment, "Thank you for your feedback!")
181
 
182
+ # --- App Interface ---
183
  col1, col2 = st.columns([2, 1])
184
 
185
  with col1:
 
189
  height=150,
190
  placeholder="Example: This product broke after just two days of use. Very disappointed with the quality and delivery was delayed too."
191
  )
 
192
  analyze_button = st.button("πŸ” Analyze & Generate Response", type="primary")
193
 
194
+ if analyze_button and not review_text.strip():
195
+ st.warning("⚠️ Please enter a review to analyze!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
 
197
  if analyze_button and review_text.strip():
198
+ sentiment, confidence, all_scores = predict_sentiment_enhanced(review_text)
199
+
200
+ with col2:
201
+ st.subheader("πŸ“Š Analysis Results")
202
+
203
+ color_map = {
204
+ 'Very Good': 'green',
205
+ 'Good': 'blue',
206
+ 'Average': 'orange',
207
+ 'Bad': 'red',
208
+ 'Very Bad': 'violet'
209
+ }
210
+ emoji_map = {
211
+ 'Very Bad': '😑',
212
+ 'Bad': '😞',
213
+ 'Average': '😐',
214
+ 'Good': '😊',
215
+ 'Very Good': '🀩'
216
+ }
217
+ color = color_map.get(sentiment, 'blue')
218
+ emoji = emoji_map.get(sentiment, '😐')
219
+
220
+ st.markdown(f"**Sentiment:** :{color}[{sentiment}] {emoji}")
221
+ st.progress(confidence)
222
+ st.caption(f"Confidence: {confidence:.2%}")
223
+
224
+ st.subheader("🎯 All 5 Class Predictions")
225
+ class_order = ['Very Bad', 'Bad', 'Average', 'Good', 'Very Good']
226
+ for class_name in class_order:
227
+ score = all_scores.get(class_name, 0.0)
228
+ st.write(f"{emoji_map.get(class_name)} {class_name}: {score:.1%}")
229
+
230
  with col1:
231
  st.markdown("---")
232
  with st.spinner("Generating AI response..."):
233
  ai_response = generate_llm_response(review_text, sentiment)
 
234
  st.subheader("πŸ€– AI Customer Support Response")
235
  st.info(ai_response)
236
+
 
237
  strategies = {
238
  'Very Bad': "πŸ†˜ Crisis Management: Immediate resolution",
239
+ 'Bad': "πŸ”§ Problem Resolution: Solutions & improvements",
240
  'Average': "βš–οΈ Balanced: Acknowledge & enhance",
241
  'Good': "πŸ‘ Appreciation: Maintain quality",
242
  'Very Good': "πŸŽ‰ Celebration: Encourage sharing"
243
  }
244
+ st.caption(f"**Strategy:** {strategies.get(sentiment)}")