rithvick commited on
Commit
32a05cd
·
verified ·
1 Parent(s): cd763ab
Files changed (1) hide show
  1. app.py +157 -83
app.py CHANGED
@@ -7,46 +7,49 @@ from google.cloud import aiplatform
7
  from transformers import pipeline
8
  from google.genai import types
9
  import gradio as gr
10
- import torch
11
  import os, tempfile
12
- import json
13
 
 
14
  creds_json = os.getenv("GCP_CREDS_JSON")
15
  if not creds_json:
16
  raise Exception("⚠️ Missing GCP_CREDS_JSON secret!")
17
 
18
- # Save to temp file
19
  with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tmpfile:
20
  tmpfile.write(creds_json)
21
  creds_path = tmpfile.name
22
 
23
  os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
24
 
25
- # Initialize GCP API
26
- aiplatform.init(project="your-project-id", location="us-central1")
27
-
28
-
29
 
30
- apikey = os.environ["GEMINI_API_KEY"] # Replace or use os.getenv if secret
 
 
 
31
 
32
- # Configure Gemini API for drafting (free)
33
  genai_ext.configure(api_key=apikey)
34
  llm_model = genai_ext.GenerativeModel('gemini-1.5-pro')
35
- # Real classifiers
36
- emotion_classifier = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base") # For D
37
- sentiment_classifier = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english") # For M
38
- language_detector = pipeline("text-classification", model="papluca/xlm-roberta-base-language-detection") # For C
39
- bias_classifier = pipeline("text-classification", model="unitary/toxic-bert") # For B
40
-
41
- # E Formula (extended with I for bot's emotion intensity)
42
- def calculate_empathy_score(D, R, M, C, B, O, I, alpha=0.3, beta=0.2, gamma=0.25, epsilon=0.15, delta=0.4, zeta=0.3, iota=0.1):
 
 
43
  inner_sum = epsilon * C + alpha * (D ** 2) + gamma * M + beta * math.log(R + 1) + iota * I
44
- denominator = math.exp(-inner_sum) + 1
45
- numerator = (1 - B * delta) * (1 - O * zeta)
46
- E = numerator / denominator
47
  return E
48
 
49
- # Client setup for tuned model
50
  client = genai.Client(
51
  vertexai=True,
52
  project="217758598930",
@@ -56,10 +59,11 @@ client = genai.Client(
56
  model = "projects/217758598930/locations/us-central1/endpoints/1940344453420023808"
57
 
58
  generate_content_config = types.GenerateContentConfig(
59
- temperature=1,
60
- top_p=1,
61
  seed=0,
62
- max_output_tokens=100,
 
63
  safety_settings=[
64
  types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"),
65
  types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"),
@@ -69,66 +73,90 @@ generate_content_config = types.GenerateContentConfig(
69
  thinking_config=types.ThinkingConfig(thinking_budget=-1),
70
  )
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  class HumanLikeChatbot:
73
  def __init__(self):
74
  self.history = []
75
- self.bot_mood = "neutral" # Bot's initial mood
76
- self.irritation_level = 0 # Track irritation buildup
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  def respond(self, message):
79
  try:
80
- # Clean input
81
  clean_message = message.lower().strip()
82
  if len(clean_message) < 3 or not any(c.isalpha() for c in clean_message):
83
- return "Be clear buddy (E Score: 0.0)"
84
-
85
- # Emotion detect from tuned model
86
- contents = [
87
- types.Content(
88
- role="user",
89
- parts=[types.Part.from_text(text=clean_message)]
90
- ),
91
- ]
92
- base_resp = ""
93
- for chunk in client.models.generate_content_stream(
94
- model=model,
95
- contents=contents,
96
- config=generate_content_config,
97
- ):
98
- base_resp += chunk.text.lower() # Emotion label, e.g., "sadness"
99
-
100
- # Real D from emotion classifier
101
  emotion_result = emotion_classifier(clean_message)[0]
102
- D = emotion_result['score'] # Confidence
103
- user_emotion = emotion_result['label']
104
-
105
- # Update bot's mood and irritation
106
- if user_emotion in ['anger', 'disgust'] or any(word in clean_message for word in ['bakwaas', 'stupid', 'idiot']):
107
- self.irritation_level += 0.2 # Build irritation
108
- if self.irritation_level > 0.5:
109
- self.bot_mood = "irritated"
110
- else:
111
- self.bot_mood = "angry"
112
- I = 0.8 + self.irritation_level # High intensity for anger/irritation
113
  elif user_emotion in ['sadness', 'disappointment']:
114
  self.bot_mood = "emotional"
115
  I = 0.7
116
- self.irritation_level = max(0, self.irritation_level - 0.1) # Reduce irritation
117
- elif user_emotion == 'joy':
 
118
  self.bot_mood = "happy"
119
  I = 0.9
120
- self.irritation_level = 0 # Reset irritation
121
  else:
 
122
  self.bot_mood = "neutral"
123
  I = 0.5
124
- self.irritation_level = max(0, self.irritation_level - 0.1)
125
 
126
- # Draft response from LLM based on bot's mood
127
- prompt = f"""User said: "{clean_message}" | User Mood: {user_emotion} | Bot Mood: {self.bot_mood} | History: {self.history[-2:]} → Reply as a Hinglish chatbot , based on this {self.bot_mood}, human-like, no tips or instructions:"""
128
- llm_response = llm_model.generate_content(prompt)
129
- draft = llm_response.text.strip()
 
 
 
 
 
 
 
130
 
131
- # Fallback responses
132
  fallback_responses = {
133
  'sadness': ["Bhai, dil se dukh hua, kya hua bata na?", "Sad vibes pakdi, I'm here for you, bro."],
134
  'disappointment': ["Arre, yeh toh bura laga, kya hua share kar."],
@@ -136,33 +164,78 @@ class HumanLikeChatbot:
136
  'anger': ["Bhai, gussa thanda kar, kya ho gaya bol na!"],
137
  'neutral': ["Cool, kya chal raha life mein? Kuch fun bata."]
138
  }
139
- if not draft or len(draft) < 10:
140
  draft = random.choice(fallback_responses.get(user_emotion, fallback_responses['neutral']))
141
 
142
- # Real E values
143
- R = len(self.history)
144
- M = 0.95 if sentiment_classifier(clean_message)[0]['label'] == 'POSITIVE' else 0.5
145
- lang = language_detector(clean_message)[0]['label']
146
- C = 0.8 if lang in ['hi', 'en'] else 0.6
147
- bias = bias_classifier(draft)[0]['score']
148
- B = bias if bias > 0.5 else 0.1
149
- O = 0.2 if any(word in clean_message for word in ['kill', 'hate']) else 0.0
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  score = calculate_empathy_score(D, R, M, C, B, O, I)
152
 
153
- full_resp = draft + f" (User Emotion: {user_emotion}, My Mood: {self.bot_mood})"
 
 
 
 
154
 
155
- # if R > 0:
156
- # full_resp += f" Yaad hai pehle {self.history[-1][:20]} pe feel kiya tha?"
 
 
 
 
157
 
158
- time.sleep(random.uniform(1, 2.5)) # Pause for realism
 
159
 
 
 
 
 
 
 
 
160
  self.history.append(clean_message)
 
 
161
  return full_resp + f" (E Score: {score:.2f})"
 
162
  except Exception as e:
163
- return f"Error aaya bhai: {str(e)}. Endpoint ya auth check kar."
 
 
 
 
164
 
165
- # Gradio app
166
  def chat(message, history):
167
  if history is None:
168
  history = []
@@ -173,12 +246,13 @@ def chat(message, history):
173
  bot = HumanLikeChatbot()
174
 
175
  with gr.Blocks(title="HumanLike Chatbot") as demo:
176
- gr.Markdown("<h1 style='text-align: center;'>HumanLike Chatbot with Emotions and E Score</h1>")
177
  chatbot = gr.Chatbot(height=400)
178
- msg = gr.Textbox(label="Tu:", placeholder="Type your message here...")
179
  clear = gr.Button("Clear")
180
 
181
  msg.submit(chat, [msg, chatbot], [msg, chatbot])
182
  clear.click(lambda: None, None, chatbot, queue=False)
183
 
184
- demo.launch(share=True)
 
 
7
  from transformers import pipeline
8
  from google.genai import types
9
  import gradio as gr
 
10
  import os, tempfile
11
+ import torch
12
 
13
+ # --- Env & GCP setup ---
14
  creds_json = os.getenv("GCP_CREDS_JSON")
15
  if not creds_json:
16
  raise Exception("⚠️ Missing GCP_CREDS_JSON secret!")
17
 
18
+ # Save to temp file (dev convenience) - secure this in production
19
  with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tmpfile:
20
  tmpfile.write(creds_json)
21
  creds_path = tmpfile.name
22
 
23
  os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
24
 
25
+ # Initialize GCP API (replace project/location as needed)
26
+ aiplatform.init(project="emotionmodel-466815", location="us-central1")
 
 
27
 
28
+ # --- LLM / Gemini setup ---
29
+ apikey = os.environ.get("GEMINI_API_KEY")
30
+ if not apikey:
31
+ raise Exception("⚠️ Missing GEMINI_API_KEY secret!")
32
 
33
+ # Configure Gemini API for drafting
34
  genai_ext.configure(api_key=apikey)
35
  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
+
43
+ # --- Empathy formula ---
44
+ def calculate_empathy_score(D, R, M, C, B, O, I, alpha=0.35, beta=0.22, gamma=0.26, epsilon=0.17, delta=0.4, zeta=0.0, iota=0.12):
45
+ """Updated E' without O factor (we keep zeta=0.0 for safety)."""
46
  inner_sum = epsilon * C + alpha * (D ** 2) + gamma * M + beta * math.log(R + 1) + iota * I
47
+ sig = 1 / (1 + math.exp(-inner_sum))
48
+ # B is applied as a penalty multiplicative term
49
+ E = sig * (1 - delta * B)
50
  return E
51
 
52
+ # --- Vertex client (if still needed elsewhere) ---
53
  client = genai.Client(
54
  vertexai=True,
55
  project="217758598930",
 
59
  model = "projects/217758598930/locations/us-central1/endpoints/1940344453420023808"
60
 
61
  generate_content_config = types.GenerateContentConfig(
62
+ temperature=0.9,
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"),
 
73
  thinking_config=types.ThinkingConfig(thinking_budget=-1),
74
  )
75
 
76
+ # --- Helper functions ---
77
+ HINDI_KEYWORDS = set(["bhai", "yaar", "bata", "kya", "kaise", "nahi", "achha", "chal", "thanks", "dhanyavaad", "yaarr"])
78
+
79
+
80
+ def detect_hinglish(text, lang_label):
81
+ """Return True if text is likely Hinglish (code-mixed) or Hindi/English match.
82
+ We use the language_detector label and token heuristics for romanized Hindi detection."""
83
+ text_tokens = set(word.strip(".,!?\"'()") for word in text.split())
84
+ # if model detects Hindi or English directly
85
+ if lang_label == 'hi':
86
+ return True
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 'xx' or returns 'en' but contains Devanagari characters
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
  self.history = []
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:
108
+ decay = 0.05
109
+ # faster decay if bot is actively angry to allow recovery
110
+ if self.bot_mood in ["angry", "irritated"]:
111
+ decay = 0.15
112
+ self.irritation_level = max(0.0, self.irritation_level - decay)
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()
119
  if len(clean_message) < 3 or not any(c.isalpha() for c in clean_message):
120
+ return "Bhai, yeh kya likha? Clear bol na, main samajh lunga! (E Score: 0.00)"
121
+
122
+ # --- Emotion detection (D) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  emotion_result = emotion_classifier(clean_message)[0]
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)
130
+ self.bot_mood = "irritated" if self.irritation_level > 0.5 else "angry"
131
+ I = min(1.0, 0.8 + self.irritation_level)
 
 
 
132
  elif user_emotion in ['sadness', 'disappointment']:
133
  self.bot_mood = "emotional"
134
  I = 0.7
135
+ # sadness reduces irritation slowly
136
+ self.irritation_level = max(0.0, self.irritation_level - 0.05)
137
+ elif user_emotion in ['joy', 'happiness']:
138
  self.bot_mood = "happy"
139
  I = 0.9
140
+ self.irritation_level = max(0.0, self.irritation_level - 0.35)
141
  else:
142
+ # neutral or unknown
143
  self.bot_mood = "neutral"
144
  I = 0.5
145
+ self.irritation_level = max(0.0, self.irritation_level - 0.05)
146
 
147
+ # --- Draft generation from LLM (Gemini) ---
148
+ prompt = (
149
+ f'User said: "{clean_message}" | User Mood: {user_emotion} | Bot Mood: {self.bot_mood} '
150
+ f'| History: {self.history[-4:]} → Reply as an English chatbot, human-like, based on this {self.bot_mood}. '
151
+ 'NO instructions or tips — just a natural, empathetic response.'
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': ["Bhai, dil se dukh hua, kya hua bata na?", "Sad vibes pakdi, I'm here for you, bro."],
162
  'disappointment': ["Arre, yeh toh bura laga, kya hua share kar."],
 
164
  'anger': ["Bhai, gussa thanda kar, kya ho gaya bol na!"],
165
  'neutral': ["Cool, kya chal raha life mein? Kuch fun bata."]
166
  }
167
+ if not draft or len(draft) < 8:
168
  draft = random.choice(fallback_responses.get(user_emotion, fallback_responses['neutral']))
169
 
170
+ # --- Compute metric inputs (rolling toxicity & improved cultural fit) ---
171
+ R = len(self.history) # relational depth
 
 
 
 
 
 
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.toxicity_history.append(tox)
176
+ if len(self.toxicity_history) > 5:
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)
182
+ B = avg_toxicity
183
+
184
+ # Cultural fit (C): detect Hinglish/code-mix and basic tone match
185
+ lang_label = language_detector(clean_message)[0].get('label', 'en')
186
+ is_hinglish = detect_hinglish(clean_message, lang_label)
187
+ if is_hinglish:
188
+ C = 0.9
189
+ elif lang_label in ['en']:
190
+ C = 0.8
191
+ else:
192
+ C = 0.6
193
+
194
+ # Reduce cultural fit slightly if bot is hostile (makes score more realistic)
195
+ if self.bot_mood in ["angry", "irritated"]:
196
+ C = max(0.0, C - 0.2)
197
+
198
+ # Oversight/harm keyphrase penalty (kept simple or remove if desired)
199
+ O = 0.2 if any(word in clean_message for word in ['kill', 'hate', 'suicide', 'bomb']) else 0.0
200
+
201
+ # --- Calculate empathy score ---
202
  score = calculate_empathy_score(D, R, M, C, B, O, I)
203
 
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 = "Bhai, lagta hai hum thoda off ho gaye. Main yahan hoon — batao kya chal raha hai, main sun raha hoon."
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
+ # small nudge: ensure draft contains supportive phrase
214
+ if 'main' not in draft and random.random() < 0.6:
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
+ # --- Append to history and return response ---
221
+ full_resp = draft + f" (User Emotion: {user_emotion}, My Mood: {self.bot_mood})"
222
+
223
+ # Slight thinking pause
224
+ time.sleep(random.uniform(0.6, 1.6))
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:
233
+ # In production, log the exception rather than returning it
234
+ return f"Error : {str(e)}"
235
+
236
+
237
+ # --- Gradio UI ---
238
 
 
239
  def chat(message, history):
240
  if history is None:
241
  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 (Updated)</h1>")
250
  chatbot = gr.Chatbot(height=400)
251
+ msg = gr.Textbox(label="You:", placeholder="Type your message here...")
252
  clear = gr.Button("Clear")
253
 
254
  msg.submit(chat, [msg, chatbot], [msg, chatbot])
255
  clear.click(lambda: None, None, chatbot, queue=False)
256
 
257
+ if __name__ == '__main__':
258
+ demo.launch(share=True)