Spaces:
Sleeping
Sleeping
File size: 8,789 Bytes
f5d7f17 | 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 | import os
import json
import random
import datetime
import re
import sys
# Define emotion label mapping
EMOTION_LABELS = [
"admiration", "amusement", "anger", "annoyance", "approval", "caring", "confusion",
"curiosity", "desire", "disappointment", "disapproval", "disgust", "embarrassment",
"excitement", "fear", "gratitude", "grief", "joy", "love", "nervousness", "optimism",
"pride", "realization", "relief", "remorse", "sadness", "surprise", "neutral"
]
# Map similar emotions to our response categories
EMOTION_MAPPING = {
"admiration": "joy",
"amusement": "joy",
"anger": "anger",
"annoyance": "anger",
"approval": "joy",
"caring": "joy",
"confusion": "neutral",
"curiosity": "neutral",
"desire": "neutral",
"disappointment": "sadness",
"disapproval": "anger",
"disgust": "disgust",
"embarrassment": "sadness",
"excitement": "joy",
"fear": "fear",
"gratitude": "joy",
"grief": "sadness",
"joy": "joy",
"love": "joy",
"nervousness": "fear",
"optimism": "joy",
"pride": "joy",
"realization": "neutral",
"relief": "joy",
"remorse": "sadness",
"sadness": "sadness",
"surprise": "surprise",
"neutral": "neutral"
}
class ChatbotContext:
"""Class to maintain conversation context and history"""
def __init__(self):
self.conversation_history = []
self.detected_emotions = []
self.user_feedback = []
self.current_session_id = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# Track emotional progression for therapeutic conversation flow
self.conversation_stage = "initial" # initial, middle, advanced
self.emotion_trajectory = [] # track emotion changes over time
self.consecutive_positive_count = 0
self.consecutive_negative_count = 0
# Add user name tracking
self.user_name = None
self.bot_name = "Mira" # Friendly, easy to remember name
self.introduced = False
def add_message(self, role, text, emotions=None):
"""Add a message to the conversation history"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message = {
"role": role,
"text": text,
"timestamp": timestamp
}
if emotions and role == "user":
message["emotions"] = emotions
self.detected_emotions.append(emotions)
self._update_emotional_trajectory(emotions)
self.conversation_history.append(message)
return message
def _update_emotional_trajectory(self, emotions):
"""Update the emotional trajectory based on newly detected emotions"""
# Get the primary emotion
primary_emotion = emotions[0]["emotion"] if emotions else "neutral"
# Add to trajectory
self.emotion_trajectory.append(primary_emotion)
# Classify as positive, negative, or neutral
positive_emotions = ["joy", "admiration", "amusement", "excitement",
"optimism", "gratitude", "pride", "love", "relief"]
negative_emotions = ["sadness", "anger", "fear", "disgust", "disappointment",
"annoyance", "disapproval", "embarrassment", "grief",
"remorse", "nervousness"]
if primary_emotion in positive_emotions:
self.consecutive_positive_count += 1
self.consecutive_negative_count = 0
elif primary_emotion in negative_emotions:
self.consecutive_negative_count += 1
self.consecutive_positive_count = 0
else: # neutral or other
# Don't reset counters for neutral emotions to maintain progress
pass
# Update conversation stage based on trajectory and message count
# This now uses conversation history length rather than just emotion trajectory
msg_count = len(self.conversation_history) // 2 # Count actual exchanges (user/bot pairs)
if msg_count <= 1: # First real exchange
self.conversation_stage = "initial"
elif msg_count <= 3: # First few exchanges
self.conversation_stage = "middle"
else: # More established conversation
self.conversation_stage = "advanced"
def get_emotional_state(self):
"""Get the current emotional state of the conversation"""
if len(self.emotion_trajectory) < 2:
return "unknown"
# Get the last few emotions (with 'neutral' having less weight)
recent_emotions = self.emotion_trajectory[-3:]
positive_emotions = ["joy", "admiration", "amusement", "excitement",
"optimism", "gratitude", "pride", "love", "relief"]
negative_emotions = ["sadness", "anger", "fear", "disgust", "disappointment",
"annoyance", "disapproval", "embarrassment", "grief",
"remorse", "nervousness"]
# Count positive and negative emotions
pos_count = sum(1 for e in recent_emotions if e in positive_emotions)
neg_count = sum(1 for e in recent_emotions if e in negative_emotions)
if self.consecutive_positive_count >= 2:
return "positive"
elif self.consecutive_negative_count >= 2:
return "negative"
elif pos_count > neg_count:
return "improving"
elif neg_count > pos_count:
return "declining"
else:
return "neutral"
def add_feedback(self, rating, comments=None):
"""Add user feedback about the chatbot's response"""
feedback = {
"rating": rating,
"comments": comments,
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.user_feedback.append(feedback)
return feedback
def get_recent_messages(self, count=5):
"""Get the most recent messages from the conversation history"""
return self.conversation_history[-count:] if len(self.conversation_history) >= count else self.conversation_history
def save_conversation(self, filepath=None):
"""Save the conversation history to a JSON file"""
if not filepath:
os.makedirs("./conversations", exist_ok=True)
filepath = f"./conversations/conversation_{self.current_session_id}.json"
data = {
"conversation_history": self.conversation_history,
"user_feedback": self.user_feedback,
"emotion_trajectory": self.emotion_trajectory,
"session_id": self.current_session_id,
"start_time": self.conversation_history[0]["timestamp"] if self.conversation_history else None,
"end_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
with open(filepath, 'w') as f:
json.dump(data, f, indent=2)
print(f"Conversation saved to {filepath}")
return filepath
def clean_response_text(response, user_name):
"""Clean up the response text to make it more natural"""
# Remove repeated name mentions
if user_name:
# Replace patterns like "Hey user_name," or "Hi user_name,"
response = re.sub(r'^(Hey|Hi|Hello)\s+' + re.escape(user_name) + r',?\s+', '', response, flags=re.IGNORECASE)
# Replace duplicate name mentions
pattern = re.escape(user_name) + r',?\s+.*' + re.escape(user_name)
if re.search(pattern, response, re.IGNORECASE):
response = re.sub(r',?\s+' + re.escape(user_name) + r'([,.!?])', r'\1', response, flags=re.IGNORECASE)
# Remove name at the end of sentences if it appears earlier
if response.count(user_name) > 1:
response = re.sub(r',\s+' + re.escape(user_name) + r'([.!?])(\s|$)', r'\1\2', response, flags=re.IGNORECASE)
# Remove phrases that feel repetitive or formulaic
phrases_to_remove = [
r"let me know what you'd prefer,?\s+",
r"i'm here to listen,?\s+",
r"let me know if there's anything else,?\s+",
r"i'm all ears,?\s+",
r"i'm here for you,?\s+"
]
for phrase in phrases_to_remove:
response = re.sub(phrase, "", response, flags=re.IGNORECASE)
# Fix multiple punctuation
response = re.sub(r'([.!?])\s+\1', r'\1', response)
# Fix missing space after punctuation
response = re.sub(r'([.!?])([A-Za-z])', r'\1 \2', response)
# Make sure first letter is capitalized
if response and len(response) > 0:
response = response[0].upper() + response[1:]
return response.strip()
|