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()