Abdelrahmann121 commited on
Commit
f684524
·
verified ·
1 Parent(s): 88fb656

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +350 -118
app.py CHANGED
@@ -97,65 +97,152 @@ class AudioProcessor:
97
  # ============================
98
 
99
  def prepare_dataset():
100
- """Load and prepare the emotion dataset"""
101
- print("📊 Loading emotion dataset...")
102
-
103
- # Load the dataset
104
- ds = load_dataset("cardiffnlp/tweet_eval", "emotion")
105
-
106
- # Define emotion labels (matching the dataset)
107
- emotion_labels = ["anger", "joy", "optimism", "sadness"]
108
-
109
- def clean_text(text):
110
- """Clean and preprocess text"""
111
- text = text.lower()
112
- text = re.sub(r"http\S+", "", text) # remove URLs
113
- text = re.sub(r"[^\w\s]", "", text) # remove special characters
114
- text = re.sub(r"\d+", "", text) # remove numbers
115
- text = re.sub(r"\s+", " ", text) # normalize whitespace
116
- return text.strip()
117
-
118
- # Sample and prepare training data
119
- train_data = ds['train']
120
- train_sample = random.sample(list(train_data), min(1000, len(train_data)))
121
-
122
- # Convert to RAG format
123
- rag_json = []
124
- for row in train_sample:
125
- cleaned_text = clean_text(row['text'])
126
- if len(cleaned_text) > 10: # Filter out very short texts
127
- rag_json.append({
128
- "text": cleaned_text,
129
- "emotion": emotion_labels[row['label']],
130
- "original_text": row['text']
131
- })
132
-
133
- print(f"Dataset prepared with {len(rag_json)} samples")
134
- return rag_json
 
 
 
 
 
 
 
 
 
 
 
135
 
136
  # ============================
137
- # EMOTION DETECTION MODEL
138
  # ============================
139
 
140
  class EmotionDetector:
141
  def __init__(self):
142
- self.model_name = "j-hartmann/emotion-english-distilroberta-base"
143
-
144
- try:
145
- self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
146
- self.model = AutoModelForSequenceClassification.from_pretrained(self.model_name)
147
- self.classifier = pipeline(
148
- "text-classification",
149
- model=self.model,
150
- tokenizer=self.tokenizer,
151
- return_all_scores=False
152
- )
153
- except Exception as e:
154
- st.error(f"❌ Error loading emotion model: {e}")
155
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  def detect_emotion(self, text):
158
- """Detect emotion from text"""
 
 
 
159
  try:
160
  result = self.classifier(text)
161
  emotion = result[0]['label'].lower()
@@ -165,14 +252,36 @@ class EmotionDetector:
165
  emotion_mapping = {
166
  'anger': 'anger',
167
  'disgust': 'sadness',
168
- 'neutral': 'neutral',
169
  'joy': 'joy',
170
  'love': 'joy',
171
  'happiness': 'joy',
172
  'sadness': 'sadness',
173
  'fear': 'sadness',
174
  'surprise': 'optimism',
175
- 'optimism': 'optimism'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
  mapped_emotion = emotion_mapping.get(emotion, 'optimism')
@@ -180,7 +289,75 @@ class EmotionDetector:
180
 
181
  except Exception as e:
182
  logger.error(f"Error in emotion detection: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  return 'optimism', 0.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
  # ============================
186
  # RAG SYSTEM WITH FAISS
@@ -190,80 +367,76 @@ class RAGSystem:
190
  """
191
  Retrieval-Augmented Generation (RAG) system for selecting text templates
192
  based on user input and detected emotion.
193
-
194
- Workflow:
195
-
196
- 1. Initialization (__init__):
197
- - Stores the input RAG data (list of entries with 'text' and 'emotion').
198
- - Extracts all texts into `self.texts`.
199
- - Loads a sentence embedding model (SentenceTransformer).
200
- - Computes embeddings for all texts.
201
- - Creates a FAISS index (L2 distance) for fast similarity search.
202
-
203
- 2. Template Retrieval (retrieve_templates):
204
- - Takes `user_input`, `detected_emotion`, and optional `top_k`.
205
- - Filters the RAG data to only include entries matching the detected emotion.
206
- If none match, all entries are considered.
207
- - Retrieves embeddings and texts for the filtered entries.
208
- - Creates a temporary FAISS index for the filtered subset.
209
- - Embeds the user input and searches the index for the most similar templates.
210
- - Returns the top `top_k` matching templates as a list.
211
-
212
- Key Points:
213
- - Uses semantic similarity via sentence embeddings to find relevant templates.
214
- - Prioritizes entries that match the detected emotion for more personalized responses.
215
- - FAISS ensures efficient similarity search even with large datasets.
216
  """
217
  def __init__(self, rag_data):
218
  self.rag_data = rag_data
219
  self.texts = [entry['text'] for entry in rag_data]
220
 
221
- # Initialize embedding model
222
- self.embed_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
 
 
 
 
223
 
224
- # Create embeddings
225
- self.embeddings = self.embed_model.encode(
226
- self.texts,
227
- convert_to_numpy=True,
228
- show_progress_bar=False
229
- )
 
 
 
 
230
 
231
- # Create FAISS index
232
- dimension = self.embeddings.shape[1]
233
- self.index = faiss.IndexFlatL2(dimension)
234
- self.index.add(self.embeddings)
 
 
 
 
 
235
 
236
  def retrieve_templates(self, user_input, detected_emotion, top_k=3):
237
  """Retrieve relevant templates based on emotion and similarity"""
 
 
238
 
239
- # Filter by emotion first
240
- emotion_filtered_indices = [
241
- i for i, entry in enumerate(self.rag_data)
242
- if entry['emotion'] == detected_emotion
243
- ]
 
244
 
245
- if not emotion_filtered_indices:
246
- emotion_filtered_indices = list(range(len(self.rag_data)))
247
 
248
- # Get filtered embeddings
249
- filtered_embeddings = self.embeddings[emotion_filtered_indices]
250
- filtered_texts = [self.texts[i] for i in emotion_filtered_indices]
251
 
252
- # Create temporary index for filtered data
253
- temp_index = faiss.IndexFlatL2(filtered_embeddings.shape[1])
254
- temp_index.add(filtered_embeddings)
255
 
256
- # Search for similar templates
257
- user_embedding = self.embed_model.encode([user_input], convert_to_numpy=True)
258
- distances, indices = temp_index.search(
259
- user_embedding,
260
- min(top_k, len(filtered_texts))
261
- )
262
 
263
- # Top templates
264
- top_templates = [filtered_texts[i] for i in indices[0]]
265
 
266
- return top_templates
 
 
 
267
 
268
  # ============================
269
  # RESPONSE GENERATOR
@@ -303,20 +476,20 @@ class ResponseGenerator:
303
  ]
304
  }
305
 
306
-
307
  def generate_response(self, user_input, top_k=3):
308
  """Generate empathetic response using RAG and few-shot prompting"""
309
-
310
  try:
311
  # Step 1: Detect emotion
312
  detected_emotion, confidence = self.emotion_detector.detect_emotion(user_input)
313
 
314
- # Step 2: Retrieve relevant templates
315
- templates = self.rag_system.retrieve_templates(
316
- user_input,
317
- detected_emotion,
318
- top_k=top_k
319
- )
 
 
320
 
321
  # Step 3: Create response using templates and emotion
322
  base_responses = self.response_templates.get(
@@ -346,6 +519,65 @@ class ResponseGenerator:
346
  return error_msg + disclaimer, 'neutral', 0.0
347
 
348
  # ============================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  # STREAMLIT APP
350
  # ============================
351
 
 
97
  # ============================
98
 
99
  def prepare_dataset():
100
+ """Load and prepare the emotion dataset with error handling"""
101
+ try:
102
+ print("📊 Loading emotion dataset...")
103
+
104
+ # Load the dataset
105
+ ds = load_dataset("cardiffnlp/tweet_eval", "emotion")
106
+
107
+ # Define emotion labels (matching the dataset)
108
+ emotion_labels = ["anger", "joy", "optimism", "sadness"]
109
+
110
+ def clean_text(text):
111
+ """Clean and preprocess text"""
112
+ text = text.lower()
113
+ text = re.sub(r"http\S+", "", text) # remove URLs
114
+ text = re.sub(r"[^\w\s]", "", text) # remove special characters
115
+ text = re.sub(r"\d+", "", text) # remove numbers
116
+ text = re.sub(r"\s+", " ", text) # normalize whitespace
117
+ return text.strip()
118
+
119
+ # Sample and prepare training data
120
+ train_data = ds['train']
121
+ train_sample = random.sample(list(train_data), min(1000, len(train_data)))
122
+
123
+ # Convert to RAG format
124
+ rag_json = []
125
+ for row in train_sample:
126
+ cleaned_text = clean_text(row['text'])
127
+ if len(cleaned_text) > 10: # Filter out very short texts
128
+ rag_json.append({
129
+ "text": cleaned_text,
130
+ "emotion": emotion_labels[row['label']],
131
+ "original_text": row['text']
132
+ })
133
+
134
+ print(f"Dataset prepared with {len(rag_json)} samples")
135
+ return rag_json
136
+
137
+ except Exception as e:
138
+ print(f"Warning: Could not load dataset: {e}")
139
+ # Return minimal fallback dataset
140
+ return [
141
+ {"text": "feeling happy and excited", "emotion": "joy"},
142
+ {"text": "really angry and frustrated", "emotion": "anger"},
143
+ {"text": "sad and lonely today", "emotion": "sadness"},
144
+ {"text": "optimistic about the future", "emotion": "optimism"}
145
+ ]
146
 
147
  # ============================
148
+ # FIXED EMOTION DETECTION MODEL
149
  # ============================
150
 
151
  class EmotionDetector:
152
  def __init__(self):
153
+ # Try multiple emotion models in order of preference
154
+ self.model_options = [
155
+ "j-hartmann/emotion-english-distilroberta-base",
156
+ "cardiffnlp/twitter-roberta-base-emotion-latest",
157
+ "nateraw/bert-base-uncased-emotion",
158
+ "michellejieli/emotion_text_classifier"
159
+ ]
160
+
161
+ self.model = None
162
+ self.tokenizer = None
163
+ self.classifier = None
164
+
165
+ # Try loading models in order
166
+ for model_name in self.model_options:
167
+ try:
168
+ st.info(f"🔄 Trying to load {model_name}...")
169
+
170
+ # Force download and load with specific parameters
171
+ self.tokenizer = AutoTokenizer.from_pretrained(
172
+ model_name,
173
+ force_download=False,
174
+ resume_download=True
175
+ )
176
+
177
+ # Load model with specific device mapping to avoid meta tensor issues
178
+ self.model = AutoModelForSequenceClassification.from_pretrained(
179
+ model_name,
180
+ force_download=False,
181
+ resume_download=True,
182
+ device_map=None, # Don't use device_map
183
+ torch_dtype=torch.float32, # Specify dtype explicitly
184
+ low_cpu_mem_usage=False # Disable low_cpu_mem_usage
185
+ )
186
+
187
+ # Move to CPU explicitly if needed
188
+ if torch.cuda.is_available():
189
+ self.model = self.model.to('cpu')
190
+
191
+ self.classifier = pipeline(
192
+ "text-classification",
193
+ model=self.model,
194
+ tokenizer=self.tokenizer,
195
+ return_all_scores=False,
196
+ device=-1 # Force CPU usage
197
+ )
198
+
199
+ st.success(f"✅ Successfully loaded {model_name}")
200
+ break
201
+
202
+ except Exception as e:
203
+ st.warning(f"⚠️ Failed to load {model_name}: {str(e)}")
204
+ continue
205
+
206
+ # Fallback to simple rule-based detection if all models fail
207
+ if self.classifier is None:
208
+ st.warning("⚠️ All emotion models failed. Using rule-based fallback.")
209
+ self.use_fallback = True
210
+ else:
211
+ self.use_fallback = False
212
+
213
+ def detect_emotion_fallback(self, text):
214
+ """Simple rule-based emotion detection as fallback"""
215
+ text_lower = text.lower()
216
+
217
+ # Define keyword patterns for emotions
218
+ emotion_keywords = {
219
+ 'joy': ['happy', 'joy', 'excited', 'thrilled', 'wonderful', 'amazing', 'great', 'fantastic', 'love', 'awesome'],
220
+ 'anger': ['angry', 'mad', 'furious', 'annoyed', 'frustrated', 'irritated', 'hate', 'terrible', 'awful'],
221
+ 'sadness': ['sad', 'depressed', 'upset', 'down', 'lonely', 'miserable', 'disappointed', 'heartbroken'],
222
+ 'optimism': ['hope', 'optimistic', 'positive', 'confident', 'believe', 'future', 'better', 'improve']
223
+ }
224
+
225
+ # Count keyword matches
226
+ emotion_scores = {}
227
+ for emotion, keywords in emotion_keywords.items():
228
+ score = sum(1 for keyword in keywords if keyword in text_lower)
229
+ emotion_scores[emotion] = score
230
+
231
+ # Get emotion with highest score
232
+ if max(emotion_scores.values()) > 0:
233
+ detected_emotion = max(emotion_scores, key=emotion_scores.get)
234
+ confidence = min(emotion_scores[detected_emotion] * 0.3 + 0.4, 0.9) # Scale confidence
235
+ else:
236
+ detected_emotion = 'optimism' # Default
237
+ confidence = 0.5
238
+
239
+ return detected_emotion, confidence
240
 
241
  def detect_emotion(self, text):
242
+ """Detect emotion from text with fallback"""
243
+ if self.use_fallback or not text.strip():
244
+ return self.detect_emotion_fallback(text)
245
+
246
  try:
247
  result = self.classifier(text)
248
  emotion = result[0]['label'].lower()
 
252
  emotion_mapping = {
253
  'anger': 'anger',
254
  'disgust': 'sadness',
255
+ 'neutral': 'optimism',
256
  'joy': 'joy',
257
  'love': 'joy',
258
  'happiness': 'joy',
259
  'sadness': 'sadness',
260
  'fear': 'sadness',
261
  'surprise': 'optimism',
262
+ 'optimism': 'optimism',
263
+ # Additional mappings for different model outputs
264
+ 'positive': 'joy',
265
+ 'negative': 'sadness',
266
+ 'admiration': 'joy',
267
+ 'amusement': 'joy',
268
+ 'annoyance': 'anger',
269
+ 'approval': 'optimism',
270
+ 'caring': 'joy',
271
+ 'confusion': 'sadness',
272
+ 'curiosity': 'optimism',
273
+ 'desire': 'optimism',
274
+ 'disappointment': 'sadness',
275
+ 'disapproval': 'anger',
276
+ 'embarrassment': 'sadness',
277
+ 'excitement': 'joy',
278
+ 'gratitude': 'joy',
279
+ 'grief': 'sadness',
280
+ 'nervousness': 'sadness',
281
+ 'pride': 'joy',
282
+ 'realization': 'optimism',
283
+ 'relief': 'joy',
284
+ 'remorse': 'sadness'
285
  }
286
 
287
  mapped_emotion = emotion_mapping.get(emotion, 'optimism')
 
289
 
290
  except Exception as e:
291
  logger.error(f"Error in emotion detection: {e}")
292
+ # Fall back to rule-based detection
293
+ return self.detect_emotion_fallback(text)
294
+
295
+ # ============================
296
+ # LIGHTWEIGHT EMOTION DETECTOR (ALTERNATIVE)
297
+ # ============================
298
+
299
+ class LightweightEmotionDetector:
300
+ """A simple, reliable emotion detector that doesn't rely on heavy models"""
301
+
302
+ def __init__(self):
303
+ # Enhanced keyword-based emotion detection
304
+ self.emotion_patterns = {
305
+ 'joy': {
306
+ 'keywords': ['happy', 'joy', 'joyful', 'excited', 'thrilled', 'wonderful', 'amazing', 'great', 'fantastic',
307
+ 'love', 'awesome', 'brilliant', 'perfect', 'delighted', 'cheerful', 'elated', 'glad', 'pleased'],
308
+ 'phrases': ['feel good', 'so happy', 'really excited', 'love it', 'makes me happy', 'feeling great']
309
+ },
310
+ 'anger': {
311
+ 'keywords': ['angry', 'mad', 'furious', 'annoyed', 'frustrated', 'irritated', 'hate', 'terrible', 'awful',
312
+ 'disgusting', 'outraged', 'livid', 'enraged', 'pissed', 'infuriated', 'resentful'],
313
+ 'phrases': ['so angry', 'really mad', 'hate it', 'makes me angry', 'fed up', 'sick of']
314
+ },
315
+ 'sadness': {
316
+ 'keywords': ['sad', 'depressed', 'upset', 'down', 'lonely', 'miserable', 'disappointed', 'heartbroken',
317
+ 'devastated', 'hopeless', 'melancholy', 'sorrowful', 'dejected', 'despondent', 'gloomy'],
318
+ 'phrases': ['feel sad', 'so down', 'really upset', 'makes me sad', 'feeling low', 'broken hearted']
319
+ },
320
+ 'optimism': {
321
+ 'keywords': ['hope', 'hopeful', 'optimistic', 'positive', 'confident', 'believe', 'future', 'better',
322
+ 'improve', 'progress', 'opportunity', 'potential', 'bright', 'promising', 'encouraging'],
323
+ 'phrases': ['looking forward', 'things will get better', 'positive about', 'have hope', 'bright future']
324
+ }
325
+ }
326
+
327
+ def detect_emotion(self, text):
328
+ """Detect emotion using enhanced pattern matching"""
329
+ if not text.strip():
330
  return 'optimism', 0.5
331
+
332
+ text_lower = text.lower()
333
+ emotion_scores = {emotion: 0 for emotion in self.emotion_patterns.keys()}
334
+
335
+ # Score based on keywords and phrases
336
+ for emotion, patterns in self.emotion_patterns.items():
337
+ # Keyword matching
338
+ for keyword in patterns['keywords']:
339
+ if keyword in text_lower:
340
+ emotion_scores[emotion] += 1
341
+
342
+ # Phrase matching (higher weight)
343
+ for phrase in patterns['phrases']:
344
+ if phrase in text_lower:
345
+ emotion_scores[emotion] += 2
346
+
347
+ # Intensity modifiers
348
+ intensifiers = ['very', 'really', 'extremely', 'so', 'absolutely', 'totally', 'completely']
349
+ intensity_boost = sum(1 for word in intensifiers if word in text_lower) * 0.5
350
+
351
+ # Get the emotion with highest score
352
+ if max(emotion_scores.values()) > 0:
353
+ detected_emotion = max(emotion_scores, key=emotion_scores.get)
354
+ base_confidence = min(emotion_scores[detected_emotion] * 0.2 + 0.5, 0.95)
355
+ confidence = min(base_confidence + intensity_boost * 0.1, 0.98)
356
+ else:
357
+ detected_emotion = 'optimism' # Default to optimism
358
+ confidence = 0.6
359
+
360
+ return detected_emotion, confidence
361
 
362
  # ============================
363
  # RAG SYSTEM WITH FAISS
 
367
  """
368
  Retrieval-Augmented Generation (RAG) system for selecting text templates
369
  based on user input and detected emotion.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  """
371
  def __init__(self, rag_data):
372
  self.rag_data = rag_data
373
  self.texts = [entry['text'] for entry in rag_data]
374
 
375
+ if len(self.texts) == 0:
376
+ st.warning("⚠️ No RAG data available. Using simple responses.")
377
+ self.embed_model = None
378
+ self.embeddings = None
379
+ self.index = None
380
+ return
381
 
382
+ try:
383
+ # Initialize embedding model
384
+ self.embed_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
385
+
386
+ # Create embeddings
387
+ self.embeddings = self.embed_model.encode(
388
+ self.texts,
389
+ convert_to_numpy=True,
390
+ show_progress_bar=False
391
+ )
392
 
393
+ # Create FAISS index
394
+ dimension = self.embeddings.shape[1]
395
+ self.index = faiss.IndexFlatL2(dimension)
396
+ self.index.add(self.embeddings)
397
+ except Exception as e:
398
+ st.warning(f"⚠️ Could not initialize RAG system: {e}")
399
+ self.embed_model = None
400
+ self.embeddings = None
401
+ self.index = None
402
 
403
  def retrieve_templates(self, user_input, detected_emotion, top_k=3):
404
  """Retrieve relevant templates based on emotion and similarity"""
405
+ if not self.embed_model or not self.index:
406
+ return []
407
 
408
+ try:
409
+ # Filter by emotion first
410
+ emotion_filtered_indices = [
411
+ i for i, entry in enumerate(self.rag_data)
412
+ if entry['emotion'] == detected_emotion
413
+ ]
414
 
415
+ if not emotion_filtered_indices:
416
+ emotion_filtered_indices = list(range(len(self.rag_data)))
417
 
418
+ # Get filtered embeddings
419
+ filtered_embeddings = self.embeddings[emotion_filtered_indices]
420
+ filtered_texts = [self.texts[i] for i in emotion_filtered_indices]
421
 
422
+ # Create temporary index for filtered data
423
+ temp_index = faiss.IndexFlatL2(filtered_embeddings.shape[1])
424
+ temp_index.add(filtered_embeddings)
425
 
426
+ # Search for similar templates
427
+ user_embedding = self.embed_model.encode([user_input], convert_to_numpy=True)
428
+ distances, indices = temp_index.search(
429
+ user_embedding,
430
+ min(top_k, len(filtered_texts))
431
+ )
432
 
433
+ # Top templates
434
+ top_templates = [filtered_texts[i] for i in indices[0]]
435
 
436
+ return top_templates
437
+ except Exception as e:
438
+ logger.error(f"Error in template retrieval: {e}")
439
+ return []
440
 
441
  # ============================
442
  # RESPONSE GENERATOR
 
476
  ]
477
  }
478
 
 
479
  def generate_response(self, user_input, top_k=3):
480
  """Generate empathetic response using RAG and few-shot prompting"""
 
481
  try:
482
  # Step 1: Detect emotion
483
  detected_emotion, confidence = self.emotion_detector.detect_emotion(user_input)
484
 
485
+ # Step 2: Retrieve relevant templates (if RAG is available)
486
+ templates = []
487
+ if self.rag_system and self.rag_system.embed_model:
488
+ templates = self.rag_system.retrieve_templates(
489
+ user_input,
490
+ detected_emotion,
491
+ top_k=top_k
492
+ )
493
 
494
  # Step 3: Create response using templates and emotion
495
  base_responses = self.response_templates.get(
 
519
  return error_msg + disclaimer, 'neutral', 0.0
520
 
521
  # ============================
522
+ # SIMPLE RESPONSE GENERATOR (FALLBACK)
523
+ # ============================
524
+
525
+ class SimpleResponseGenerator:
526
+ """Simplified response generator that works without RAG"""
527
+
528
+ def __init__(self, emotion_detector):
529
+ self.emotion_detector = emotion_detector
530
+
531
+ # Enhanced response templates
532
+ self.response_templates = {
533
+ 'anger': [
534
+ "I can understand why you're feeling frustrated. It's completely valid to feel this way. Sometimes situations can be really challenging, and it's important to acknowledge these feelings.",
535
+ "Your anger is understandable. When things don't go as expected, it's natural to feel upset. Would you like to talk about what's causing these feelings?",
536
+ "I hear that you're upset, and that's okay. These feelings are important and deserve attention. Take a moment to breathe if you need it."
537
+ ],
538
+ 'sadness': [
539
+ "I'm sorry you're going through a difficult time. Your feelings are valid, and it's okay to feel sad sometimes. Remember that this feeling will pass.",
540
+ "It sounds like you're dealing with something really tough right now. I want you to know that it's perfectly normal to feel this way, and you're not alone.",
541
+ "I can sense your sadness, and I want you to know that it's okay to feel this way. Sometimes life presents us with challenges that naturally make us feel down."
542
+ ],
543
+ 'joy': [
544
+ "I'm so happy to hear about your positive experience! That's wonderful, and your joy is really uplifting. It's great when life gives us these beautiful moments.",
545
+ "Your joy is contagious! It's amazing to hear such positive news. These happy moments are precious and worth celebrating.",
546
+ "I love hearing about things that make you happy. That sounds absolutely amazing! Your enthusiasm is really inspiring."
547
+ ],
548
+ 'optimism': [
549
+ "Your positive outlook is truly inspiring. That's such a great way to look at things, and your hopefulness is really encouraging.",
550
+ "I appreciate your hopeful perspective. That kind of optimism can make such a difference, not just for you but for others around you too.",
551
+ "It's wonderful to hear your optimistic thoughts. Keep that positive energy flowing - it's a powerful force for good!"
552
+ ]
553
+ }
554
+
555
+ def generate_response(self, user_input, top_k=3):
556
+ """Generate response without RAG system"""
557
+ try:
558
+ # Detect emotion
559
+ detected_emotion, confidence = self.emotion_detector.detect_emotion(user_input)
560
+
561
+ # Get appropriate response template
562
+ templates = self.response_templates.get(detected_emotion, self.response_templates['optimism'])
563
+ selected_response = random.choice(templates)
564
+
565
+ # Add personalized touch based on input length and content
566
+ if len(user_input) > 100:
567
+ selected_response += " I can see you've shared quite a bit with me, and I appreciate your openness."
568
+ elif any(word in user_input.lower() for word in ['help', 'advice', 'what should']):
569
+ selected_response += " If you'd like to talk more about this, I'm here to listen."
570
+
571
+ # Add disclaimer
572
+ disclaimer = "\n\n⚠️ This is an automated response. For serious emotional concerns, please consult a mental health professional."
573
+
574
+ return selected_response + disclaimer, detected_emotion, confidence
575
+
576
+ except Exception as e:
577
+ error_msg = f"I apologize, but I encountered an error: {str(e)}"
578
+ disclaimer = "\n\n⚠️ This is an automated response. Please consult a professional if needed."
579
+ return error_msg + disclaimer, 'optimism', 0.0
580
+ # ============================
581
  # STREAMLIT APP
582
  # ============================
583