Empathetic-AI-Chatbot / chatbot-py.py
suku9's picture
Upload 2 files
f5d7f17 verified
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()