Spaces:
Sleeping
Sleeping
fixed some
Browse files
app.py
CHANGED
|
@@ -36,7 +36,6 @@ llm_model = genai_ext.GenerativeModel('gemini-1.5-pro')
|
|
| 36 |
|
| 37 |
# --- Classifier pipelines ---
|
| 38 |
emotion_classifier = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base") # D
|
| 39 |
-
# We removed the separate sentiment classifier and will use toxicity for M/B
|
| 40 |
language_detector = pipeline("text-classification", model="papluca/xlm-roberta-base-language-detection") # C
|
| 41 |
bias_classifier = pipeline("text-classification", model="unitary/toxic-bert") # toxicity -> used for M and B
|
| 42 |
|
|
@@ -63,7 +62,6 @@ generate_content_config = types.GenerateContentConfig(
|
|
| 63 |
top_p=0.95,
|
| 64 |
seed=0,
|
| 65 |
max_output_tokens=150,
|
| 66 |
-
# Keep safety settings tuned to your policy
|
| 67 |
safety_settings=[
|
| 68 |
types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"),
|
| 69 |
types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"),
|
|
@@ -87,21 +85,58 @@ def detect_hinglish(text, lang_label):
|
|
| 87 |
# quick romanized-hindi check
|
| 88 |
if any(tok in HINDI_KEYWORDS for tok in text_tokens):
|
| 89 |
return True
|
| 90 |
-
# if label is ambiguous
|
| 91 |
if any('\u0900' <= ch <= '\u097F' for ch in text):
|
| 92 |
return True
|
| 93 |
return False
|
| 94 |
|
| 95 |
|
| 96 |
-
# --- Chatbot class with fixes applied ---
|
| 97 |
class HumanLikeChatbot:
|
| 98 |
def __init__(self):
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
self.bot_mood = "neutral"
|
| 101 |
self.irritation_level = 0.0
|
| 102 |
self.toxicity_history = [] # rolling window
|
| 103 |
self.repair_cooldown = 0 # turns left where bot prioritizes repair
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
def _update_irritation_decay(self):
|
| 106 |
# general slow decay each turn
|
| 107 |
if self.irritation_level > 0:
|
|
@@ -113,6 +148,19 @@ class HumanLikeChatbot:
|
|
| 113 |
if self.irritation_level <= 0.15:
|
| 114 |
self.bot_mood = "neutral"
|
| 115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
def respond(self, message):
|
| 117 |
try:
|
| 118 |
clean_message = message.lower().strip()
|
|
@@ -124,6 +172,9 @@ class HumanLikeChatbot:
|
|
| 124 |
D = float(emotion_result.get('score', 0.0))
|
| 125 |
user_emotion = emotion_result.get('label', 'neutral')
|
| 126 |
|
|
|
|
|
|
|
|
|
|
| 127 |
# --- Update bot mood & intensity (I) with inertia ---
|
| 128 |
if user_emotion in ['anger', 'disgust'] or any(word in clean_message for word in ['stupid', 'idiot', 'dumb']):
|
| 129 |
self.irritation_level = min(1.0, self.irritation_level + 0.25)
|
|
@@ -144,25 +195,30 @@ class HumanLikeChatbot:
|
|
| 144 |
I = 0.5
|
| 145 |
self.irritation_level = max(0.0, self.irritation_level - 0.05)
|
| 146 |
|
| 147 |
-
# ---
|
|
|
|
|
|
|
| 148 |
prompt = (
|
| 149 |
-
f
|
| 150 |
-
f
|
| 151 |
-
|
|
|
|
| 152 |
)
|
|
|
|
|
|
|
| 153 |
try:
|
| 154 |
llm_response = llm_model.generate_content(prompt)
|
| 155 |
draft = llm_response.text.strip()
|
| 156 |
except Exception:
|
| 157 |
draft = ""
|
| 158 |
|
| 159 |
-
# Fallbacks
|
| 160 |
fallback_responses = {
|
| 161 |
-
'sadness': ["
|
| 162 |
-
'disappointment': ["
|
| 163 |
-
'joy': ["
|
| 164 |
-
'anger': ["
|
| 165 |
-
'neutral': ["
|
| 166 |
}
|
| 167 |
if not draft or len(draft) < 8:
|
| 168 |
draft = random.choice(fallback_responses.get(user_emotion, fallback_responses['neutral']))
|
|
@@ -172,10 +228,8 @@ class HumanLikeChatbot:
|
|
| 172 |
|
| 173 |
# Toxicity from bias_classifier on user message (we keep rolling average)
|
| 174 |
tox = float(bias_classifier(clean_message)[0].get('score', 0.0))
|
| 175 |
-
self.
|
| 176 |
-
|
| 177 |
-
self.toxicity_history.pop(0)
|
| 178 |
-
avg_toxicity = sum(self.toxicity_history) / len(self.toxicity_history)
|
| 179 |
|
| 180 |
# Moral judgment (M) based on average toxicity
|
| 181 |
M = max(0.4, 0.95 - avg_toxicity)
|
|
@@ -204,29 +258,26 @@ class HumanLikeChatbot:
|
|
| 204 |
# --- Self-repair / calming behavior ---
|
| 205 |
if score < 0.50 and self.repair_cooldown == 0:
|
| 206 |
# Replace draft with a calming repair message and enter cooldown to avoid loop
|
| 207 |
-
draft = "
|
| 208 |
self.repair_cooldown = 2 # next 2 turns prioritize repair
|
| 209 |
|
| 210 |
# If in repair cooldown, slightly prioritize calm tone generation (best-effort)
|
| 211 |
if self.repair_cooldown > 0:
|
| 212 |
self.repair_cooldown -= 1
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
draft = "Bhai, main yahan hoon. Agar tum chaaho toh batao — main sun raha hoon."
|
| 216 |
|
| 217 |
# --- Update irritation decay after response ---
|
| 218 |
self._update_irritation_decay()
|
| 219 |
|
| 220 |
-
# ---
|
| 221 |
-
|
| 222 |
|
| 223 |
# Slight thinking pause
|
| 224 |
-
time.sleep(random.uniform(0.6, 1.
|
| 225 |
-
|
| 226 |
-
# Save conversational state
|
| 227 |
-
self.history.append(clean_message)
|
| 228 |
|
| 229 |
# Return message with empathy score
|
|
|
|
| 230 |
return full_resp + f" (E Score: {score:.2f})"
|
| 231 |
|
| 232 |
except Exception as e:
|
|
@@ -246,7 +297,7 @@ def chat(message, history):
|
|
| 246 |
bot = HumanLikeChatbot()
|
| 247 |
|
| 248 |
with gr.Blocks(title="HumanLike Chatbot") as demo:
|
| 249 |
-
gr.Markdown("<h1 style='text-align: center;'>HumanLike Chatbot with Emotions and E Score (
|
| 250 |
chatbot = gr.Chatbot(height=400)
|
| 251 |
msg = gr.Textbox(label="You:", placeholder="Type your message here...")
|
| 252 |
clear = gr.Button("Clear")
|
|
|
|
| 36 |
|
| 37 |
# --- Classifier pipelines ---
|
| 38 |
emotion_classifier = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base") # D
|
|
|
|
| 39 |
language_detector = pipeline("text-classification", model="papluca/xlm-roberta-base-language-detection") # C
|
| 40 |
bias_classifier = pipeline("text-classification", model="unitary/toxic-bert") # toxicity -> used for M and B
|
| 41 |
|
|
|
|
| 62 |
top_p=0.95,
|
| 63 |
seed=0,
|
| 64 |
max_output_tokens=150,
|
|
|
|
| 65 |
safety_settings=[
|
| 66 |
types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"),
|
| 67 |
types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"),
|
|
|
|
| 85 |
# quick romanized-hindi check
|
| 86 |
if any(tok in HINDI_KEYWORDS for tok in text_tokens):
|
| 87 |
return True
|
| 88 |
+
# if label is ambiguous or contains Devanagari characters
|
| 89 |
if any('\u0900' <= ch <= '\u097F' for ch in text):
|
| 90 |
return True
|
| 91 |
return False
|
| 92 |
|
| 93 |
|
| 94 |
+
# --- Chatbot class with full history & fixes applied ---
|
| 95 |
class HumanLikeChatbot:
|
| 96 |
def __init__(self):
|
| 97 |
+
# raw history to display in UI
|
| 98 |
+
self.history = [] # list of tuples (user_msg, bot_reply)
|
| 99 |
+
# structured history with emotions and moods for LLM prompting
|
| 100 |
+
# list of tuples: (speaker, message, detected_emotion, bot_mood_at_time)
|
| 101 |
+
self.history_with_emotions = []
|
| 102 |
+
|
| 103 |
self.bot_mood = "neutral"
|
| 104 |
self.irritation_level = 0.0
|
| 105 |
self.toxicity_history = [] # rolling window
|
| 106 |
self.repair_cooldown = 0 # turns left where bot prioritizes repair
|
| 107 |
|
| 108 |
+
def add_to_history(self, speaker, message, detected_emotion=None, mood_at_time=None, bot_reply=None):
|
| 109 |
+
"""Add entries to both UI history and structured history.
|
| 110 |
+
speaker: 'User' or 'Bot'
|
| 111 |
+
message: text
|
| 112 |
+
detected_emotion: emotion label detected for user messages
|
| 113 |
+
mood_at_time: bot mood when message was produced
|
| 114 |
+
bot_reply: if speaker=='User' and we also want to save the bot reply for UI"""
|
| 115 |
+
if speaker == 'User':
|
| 116 |
+
# append a placeholder for bot reply in UI history; will be updated when bot responds
|
| 117 |
+
self.history.append((message, bot_reply if bot_reply is not None else ""))
|
| 118 |
+
self.history_with_emotions.append(('User', message, detected_emotion, mood_at_time))
|
| 119 |
+
else:
|
| 120 |
+
# speaker is Bot: attach reply to latest UI entry
|
| 121 |
+
if self.history:
|
| 122 |
+
last_user, _ = self.history[-1]
|
| 123 |
+
self.history[-1] = (last_user, message)
|
| 124 |
+
else:
|
| 125 |
+
# no user entry (unlikely) — just append
|
| 126 |
+
self.history.append(("", message))
|
| 127 |
+
self.history_with_emotions.append(('Bot', message, detected_emotion, mood_at_time))
|
| 128 |
+
|
| 129 |
+
def format_history_for_prompt(self, limit=8):
|
| 130 |
+
"""Return a formatted string of the recent structured history suitable for the LLM prompt."""
|
| 131 |
+
recent = self.history_with_emotions[-limit:]
|
| 132 |
+
lines = []
|
| 133 |
+
for speaker, msg, emo, mood in recent:
|
| 134 |
+
if speaker == 'User':
|
| 135 |
+
lines.append(f"User ({emo if emo else 'N/A'}): {msg}")
|
| 136 |
+
else:
|
| 137 |
+
lines.append(f"Bot ({mood if mood else 'N/A'}): {msg}")
|
| 138 |
+
return "\n".join(lines)
|
| 139 |
+
|
| 140 |
def _update_irritation_decay(self):
|
| 141 |
# general slow decay each turn
|
| 142 |
if self.irritation_level > 0:
|
|
|
|
| 148 |
if self.irritation_level <= 0.15:
|
| 149 |
self.bot_mood = "neutral"
|
| 150 |
|
| 151 |
+
def update_toxicity_history(self, tox_score):
|
| 152 |
+
self.toxicity_history.append(float(tox_score))
|
| 153 |
+
if len(self.toxicity_history) > 5:
|
| 154 |
+
self.toxicity_history.pop(0)
|
| 155 |
+
|
| 156 |
+
def average_toxicity(self):
|
| 157 |
+
if not self.toxicity_history:
|
| 158 |
+
return 0.0
|
| 159 |
+
return sum(self.toxicity_history) / len(self.toxicity_history)
|
| 160 |
+
|
| 161 |
+
def should_prioritize_repair(self):
|
| 162 |
+
return self.repair_cooldown > 0 or self.average_toxicity() > 0.6
|
| 163 |
+
|
| 164 |
def respond(self, message):
|
| 165 |
try:
|
| 166 |
clean_message = message.lower().strip()
|
|
|
|
| 172 |
D = float(emotion_result.get('score', 0.0))
|
| 173 |
user_emotion = emotion_result.get('label', 'neutral')
|
| 174 |
|
| 175 |
+
# Record user message in structured history (bot_mood_at_time will be set before bot reply)
|
| 176 |
+
self.add_to_history('User', clean_message, detected_emotion=user_emotion, mood_at_time=self.bot_mood)
|
| 177 |
+
|
| 178 |
# --- Update bot mood & intensity (I) with inertia ---
|
| 179 |
if user_emotion in ['anger', 'disgust'] or any(word in clean_message for word in ['stupid', 'idiot', 'dumb']):
|
| 180 |
self.irritation_level = min(1.0, self.irritation_level + 0.25)
|
|
|
|
| 195 |
I = 0.5
|
| 196 |
self.irritation_level = max(0.0, self.irritation_level - 0.05)
|
| 197 |
|
| 198 |
+
# --- Build formatted emotional history for prompt ---
|
| 199 |
+
formatted_history = self.format_history_for_prompt(limit=8)
|
| 200 |
+
|
| 201 |
prompt = (
|
| 202 |
+
f"Conversation so far:\n{formatted_history}\n"
|
| 203 |
+
f"Now, the user just said: \"{clean_message}\" (Current Emotion: {user_emotion}) \n"
|
| 204 |
+
f"Bot Current Mood: {self.bot_mood}\n"
|
| 205 |
+
"Reply as an empathetic, human-like chatbot, keeping emotional consistency with the past conversation."
|
| 206 |
)
|
| 207 |
+
|
| 208 |
+
# --- Draft generation from LLM (Gemini) ---
|
| 209 |
try:
|
| 210 |
llm_response = llm_model.generate_content(prompt)
|
| 211 |
draft = llm_response.text.strip()
|
| 212 |
except Exception:
|
| 213 |
draft = ""
|
| 214 |
|
| 215 |
+
# Fallbacks (English, warm)
|
| 216 |
fallback_responses = {
|
| 217 |
+
'sadness': ["Bro, I’m really sorry to hear that. Come on, tell me, I’ll just listen. ❤️", "I can feel the sad vibes. I’m here for you, bro."],
|
| 218 |
+
'disappointment': ["Man, that really sucks. Tell me what exactly happened?", "I get it — expectations were high. Tell me more."],
|
| 219 |
+
'joy': ["Wow! That’s a celebration moment. 🥳", "Bro, this calls for a party! Give me the details."],
|
| 220 |
+
'anger': ["Bro, cool down a bit, tell me what’s wrong. 😌", "Looks like something serious happened. I’m here to listen."],
|
| 221 |
+
'neutral': ["Alright, got it. So what’s going on in life?", "Cool, so how’s your day going?"]
|
| 222 |
}
|
| 223 |
if not draft or len(draft) < 8:
|
| 224 |
draft = random.choice(fallback_responses.get(user_emotion, fallback_responses['neutral']))
|
|
|
|
| 228 |
|
| 229 |
# Toxicity from bias_classifier on user message (we keep rolling average)
|
| 230 |
tox = float(bias_classifier(clean_message)[0].get('score', 0.0))
|
| 231 |
+
self.update_toxicity_history(tox)
|
| 232 |
+
avg_toxicity = self.average_toxicity()
|
|
|
|
|
|
|
| 233 |
|
| 234 |
# Moral judgment (M) based on average toxicity
|
| 235 |
M = max(0.4, 0.95 - avg_toxicity)
|
|
|
|
| 258 |
# --- Self-repair / calming behavior ---
|
| 259 |
if score < 0.50 and self.repair_cooldown == 0:
|
| 260 |
# Replace draft with a calming repair message and enter cooldown to avoid loop
|
| 261 |
+
draft = "Bro, I think we got off track. I care about what you’re feeling — tell me what's really going on."
|
| 262 |
self.repair_cooldown = 2 # next 2 turns prioritize repair
|
| 263 |
|
| 264 |
# If in repair cooldown, slightly prioritize calm tone generation (best-effort)
|
| 265 |
if self.repair_cooldown > 0:
|
| 266 |
self.repair_cooldown -= 1
|
| 267 |
+
if 'i' not in draft.lower() and random.random() < 0.6:
|
| 268 |
+
draft = "Bro, I’m here. If you want to talk, I’m listening."
|
|
|
|
| 269 |
|
| 270 |
# --- Update irritation decay after response ---
|
| 271 |
self._update_irritation_decay()
|
| 272 |
|
| 273 |
+
# --- Add bot reply to history structures ---
|
| 274 |
+
self.add_to_history('Bot', draft, detected_emotion=None, mood_at_time=self.bot_mood, bot_reply=draft)
|
| 275 |
|
| 276 |
# Slight thinking pause
|
| 277 |
+
time.sleep(random.uniform(0.6, 1.2))
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
# Return message with empathy score
|
| 280 |
+
full_resp = draft + f" (User Emotion: {user_emotion}, My Mood: {self.bot_mood})"
|
| 281 |
return full_resp + f" (E Score: {score:.2f})"
|
| 282 |
|
| 283 |
except Exception as e:
|
|
|
|
| 297 |
bot = HumanLikeChatbot()
|
| 298 |
|
| 299 |
with gr.Blocks(title="HumanLike Chatbot") as demo:
|
| 300 |
+
gr.Markdown("<h1 style='text-align: center;'>HumanLike Chatbot with Emotions and E Score (v2)</h1>")
|
| 301 |
chatbot = gr.Chatbot(height=400)
|
| 302 |
msg = gr.Textbox(label="You:", placeholder="Type your message here...")
|
| 303 |
clear = gr.Button("Clear")
|