Shresthh03 commited on
Commit
536604a
·
verified ·
1 Parent(s): 38ae6b0

Update it.

Browse files
Files changed (1) hide show
  1. app.py +291 -93
app.py CHANGED
@@ -1,120 +1,318 @@
 
 
 
 
 
1
  from flask import Flask, request, jsonify, send_from_directory
2
- from transformers import pipeline
3
- import json, os, datetime
 
 
 
 
 
4
 
5
  app = Flask(__name__)
6
 
7
- # load emotion model
8
- emotion_analyzer = pipeline("text-classification",
9
- model="j-hartmann/emotion-english-distilroberta-base",
10
- top_k=None)
 
 
 
 
 
 
 
 
 
11
 
12
- DATA_FILE = "user_data.json"
13
 
14
- def load_user_data():
15
- if os.path.exists(DATA_FILE):
16
- with open(DATA_FILE, "r") as f:
17
- return json.load(f)
18
- return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- def save_user_data(data):
21
- with open(DATA_FILE, "w") as f:
22
- json.dump(data, f, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- def detect_emotion(text):
25
- result = emotion_analyzer(text)[0]
26
- scores = {r["label"]: r["score"] for r in result}
27
- return max(scores, key=scores.get)
 
 
 
 
28
 
29
- def crisis_check(text):
30
- crisis_keywords = ["end my life", "kill myself", "suicide", "hurt myself"]
31
- for word in crisis_keywords:
32
- if word in text.lower():
33
  return True
34
  return False
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  @app.route("/chat", methods=["POST"])
37
  def chat():
38
- data = request.get_json()
39
- user_message = data.get("message", "")
40
- user_info = load_user_data()
41
- user_name = user_info.get("name", "friend")
42
- user_age = user_info.get("age", None)
43
- emotion = detect_emotion(user_message)
44
-
45
- # save mood history
46
- now = datetime.datetime.now().strftime("%Y-%m-%d")
47
- user_info["last_mood"] = emotion
48
- user_info["last_seen"] = now
49
- save_user_data(user_info)
50
-
51
- # crisis check
52
- if crisis_check(user_message):
53
- return jsonify({
54
- "reply": f"{user_name}, you matter. You're not alone. If you’re in danger, please reach out to your local emergency helpline or someone you trust immediately ❤️",
55
- "emotion": "crisis"
56
- })
57
-
58
- empathy_responses = [
59
- "That sounds tough, but I’m here for you.",
60
- "I can sense how that feels. Tell me more, if you want.",
61
- "That must be hard, but you’re doing better than you think.",
62
- "I appreciate you sharing this with me.",
63
- "Sometimes words aren’t enough, but I’m listening fully.",
64
- "You’ve been strong for so long — it’s okay to let go here.",
65
- "I hear you, truly. You’re safe here."
66
- ]
67
 
68
- supportive_followups = [
69
- "Would you like to talk more about what’s been on your mind?",
70
- "Sometimes sharing even small details helps a little. Want to try?",
71
- "Would you like something calming, or maybe a bit of motivation?"
72
- ]
73
 
74
- motivational_quotes = [
75
- "Every sunrise brings a new chance to begin again 🌅",
76
- "Even the darkest nights end with the light of dawn.",
77
- "You’ve made it through 100% of your hardest days so far 💪",
78
- "Storms make trees take deeper roots.",
79
- "You are allowed to rest, not to quit."
80
- ]
 
 
 
 
 
 
 
 
 
81
 
82
- import random
83
- reply = ""
84
-
85
- if "name" not in user_info or "age" not in user_info:
86
- if "name" not in user_info:
87
- reply = "Hey there! What’s your name?"
88
- user_info["awaiting"] = "name"
89
- elif "age" not in user_info:
90
- reply = f"Nice to meet you, {user_name}! How old are you?"
91
- user_info["awaiting"] = "age"
92
- save_user_data(user_info)
93
- elif user_info.get("awaiting") == "name":
94
- user_info["name"] = user_message.strip().split()[0]
95
- reply = f"Lovely to meet you, {user_info['name']}! How old are you?"
96
- user_info["awaiting"] = "age"
97
- save_user_data(user_info)
98
- elif user_info.get("awaiting") == "age":
99
- user_info["age"] = user_message.strip().split()[0]
100
- reply = f"Got it! So {user_info['age']} it is. How are you feeling today?"
101
- user_info.pop("awaiting", None)
102
- save_user_data(user_info)
103
- elif "guidance" in user_message.lower() or "motivation" in user_message.lower():
104
- reply = random.choice(motivational_quotes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  else:
106
- if emotion in ["sadness", "fear", "anger"]:
107
- reply = random.choice(empathy_responses) + " " + random.choice(supportive_followups)
108
- elif emotion in ["joy", "love"]:
109
- reply = f"That’s wonderful, {user_name}! I’m so glad to hear it 🌟"
110
  else:
111
- reply = random.choice(motivational_quotes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  return jsonify({"reply": reply, "emotion": emotion})
114
 
 
115
  @app.route("/")
116
  def index():
117
  return send_from_directory(".", "index.html")
118
 
119
  if __name__ == "__main__":
120
- app.run(host="0.0.0.0", port=7860)
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import random
5
+ import datetime
6
  from flask import Flask, request, jsonify, send_from_directory
7
+
8
+ # Try to import huggingface pipeline; if unavailable we'll fallback to a keyword detector
9
+ try:
10
+ from transformers import pipeline
11
+ HF_AVAILABLE = True
12
+ except Exception:
13
+ HF_AVAILABLE = False
14
 
15
  app = Flask(__name__)
16
 
17
+ # ---------- Config ----------
18
+ MEMORY_FILE = "chat_memory.json"
19
+ MEMORY_KEEP_DAYS = 15
20
+
21
+ # Safe helplines map (country-code fallback handled via ipapi)
22
+ HELPLINES = {
23
+ "IN": "🇮🇳 India: AASRA Helpline 91-9820466726",
24
+ "US": "🇺🇸 USA: Call or text 988 (Suicide & Crisis Lifeline)",
25
+ "GB": "🇬🇧 UK: Samaritans at 116 123",
26
+ "CA": "🇨🇦 Canada: Talk Suicide Canada at 1-833-456-4566",
27
+ "AU": "🇦🇺 Australia: Lifeline 13 11 14",
28
+ "DEFAULT": "If you are in crisis, please contact your local emergency number or visit https://findahelpline.com"
29
+ }
30
 
31
+ CRISIS_KEYWORDS = ["suicide","kill myself","end my life","want to die","hurt myself","cant go on","can't go on","i want to die"]
32
 
33
+ # ---------- Try to load HF emotion model ----------
34
+ emotion_model = None
35
+ if HF_AVAILABLE:
36
+ try:
37
+ # this may be large; if it fails we'll catch
38
+ emotion_model = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=5)
39
+ except Exception:
40
+ emotion_model = None
41
+
42
+ # ---------- Memory helpers ----------
43
+ def read_memory():
44
+ if os.path.exists(MEMORY_FILE):
45
+ try:
46
+ with open(MEMORY_FILE, "r") as f:
47
+ data = json.load(f)
48
+ except Exception:
49
+ data = {}
50
+ else:
51
+ data = {}
52
+ # prune old entries
53
+ cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=MEMORY_KEEP_DAYS)
54
+ keep = {}
55
+ for k,v in data.items():
56
+ try:
57
+ t = datetime.datetime.fromisoformat(v.get("last_seen"))
58
+ if t >= cutoff:
59
+ keep[k] = v
60
+ except Exception:
61
+ # keep if malformed time
62
+ keep[k] = v
63
+ return keep
64
 
65
+ def write_memory(mem):
66
+ with open(MEMORY_FILE, "w") as f:
67
+ json.dump(mem, f, indent=2)
68
+
69
+ memory = read_memory()
70
+
71
+ # ---------- Utilities ----------
72
+ def extract_name(text:str):
73
+ # look for patterns: i'm X, i am X, my name is X
74
+ text = text.strip()
75
+ patterns = [
76
+ r"^(?:i am|i'm|im|i’m)\s+([A-Z][a-z]+(?:\s[A-Z][a-z]+)?)", # "I'm John", favor capitalization
77
+ r"my name is\s+([A-Z][a-z]+(?:\s[A-Z][a-z]+)?)",
78
+ r"^([A-Z][a-z]{1,20})$" # single capitalized word
79
+ ]
80
+ for p in patterns:
81
+ m = re.search(p, text, flags=re.IGNORECASE)
82
+ if m:
83
+ name = m.group(1).strip()
84
+ # normalize capitalization
85
+ return " ".join([w.capitalize() for w in name.split()])
86
+ return None
87
 
88
+ def extract_age(text:str):
89
+ # extract first integer that looks like an age between 8 and 120
90
+ nums = re.findall(r"\b([1-9][0-9]?)\b", text)
91
+ for n in nums:
92
+ val = int(n)
93
+ if 8 <= val <= 120:
94
+ return val
95
+ return None
96
 
97
+ def detect_crisis(text:str):
98
+ low = text.lower()
99
+ for kw in CRISIS_KEYWORDS:
100
+ if kw in low:
101
  return True
102
  return False
103
 
104
+ def get_country_from_ip(ip):
105
+ # Best-effort; if it fails return None
106
+ try:
107
+ import requests
108
+ r = requests.get(f"https://ipapi.co/{ip}/json/", timeout=3)
109
+ if r.status_code == 200:
110
+ data = r.json()
111
+ return data.get("country_code")
112
+ except Exception:
113
+ pass
114
+ return None
115
+
116
+ def emotion_from_text(text):
117
+ # Primary: use HF if available
118
+ if emotion_model:
119
+ try:
120
+ out = emotion_model(text)
121
+ # out is list of dicts; take top label
122
+ if isinstance(out, list) and len(out)>0:
123
+ # out may be list of lists: ensure flatten
124
+ if isinstance(out[0], list):
125
+ best = out[0][0]["label"]
126
+ else:
127
+ best = out[0]["label"]
128
+ return best.lower()
129
+ except Exception:
130
+ pass
131
+ # Fallback keyword mapping
132
+ low = text.lower()
133
+ if any(w in low for w in ["happy","glad","great","good","joy","joyful","thank"]):
134
+ return "joy"
135
+ if any(w in low for w in ["sad","down","depressed","unhappy","lonely","cry"]):
136
+ return "sadness"
137
+ if any(w in low for w in ["angry","mad","furious","annoyed","irritat"]):
138
+ return "anger"
139
+ if any(w in low for w in ["scared","afraid","anxious","panic","fear"]):
140
+ return "fear"
141
+ if any(w in low for w in ["love","loving","cherish","fond"]):
142
+ return "love"
143
+ return "neutral"
144
+
145
+ # Varied empathetic templates to avoid repetition
146
+ EMP_OPENERS = [
147
+ "That sounds really hard — thank you for telling me.",
148
+ "I can hear how much that means to you.",
149
+ "Wow — that must have taken a lot to share. I’m here.",
150
+ "You’ve taken a brave step by saying that. I’m listening.",
151
+ "I’m with you — that sounds heavy and important.",
152
+ "I appreciate your honesty; I’m right here to listen.",
153
+ "That must have felt deeply upsetting. Tell me more if you want."
154
+ ]
155
+ EMP_FOLLOWUPS = [
156
+ "Would you like to share more about what’s been going on?",
157
+ "How has that been affecting your day-to-day?",
158
+ "What helps you feel even a little bit better when that happens?",
159
+ "Would you prefer a calming exercise or some practical steps?",
160
+ "Do you want help breaking this down into smaller parts?"
161
+ ]
162
+ MOTIVATIONAL = [
163
+ "Even small steps are meaningful — you don’t have to do it all at once.",
164
+ "Rest is allowed. Healing isn’t a straight line.",
165
+ "You’ve come this far; that shows strength.",
166
+ "A single breath can sometimes soften what’s heavy."
167
+ ]
168
+
169
+ # ---------- Main chat route ----------
170
  @app.route("/chat", methods=["POST"])
171
  def chat():
172
+ global memory
173
+ payload = request.get_json() or {}
174
+ session = payload.get("session") or request.remote_addr or "default"
175
+ message = (payload.get("message") or "").strip()
176
+ # flag used by frontend to ask the server for initial greeting only once
177
+ init_flag = payload.get("init", False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ # ensure memory slot for session
180
+ slot = memory.get(session, {})
181
+ now_iso = datetime.datetime.utcnow().isoformat()
 
 
182
 
183
+ # On init: if no name known, return friendly opener (but only if init true)
184
+ if init_flag:
185
+ if not slot.get("name"):
186
+ return jsonify({"reply":"Hey there! What’s your name?", "emotion":"calm"})
187
+ else:
188
+ # If name present, maybe check recent mood
189
+ last_mood = slot.get("last_mood")
190
+ last_seen = slot.get("last_seen")
191
+ if last_mood in ("sadness","anger","fear") and last_seen:
192
+ try:
193
+ last_t = datetime.datetime.fromisoformat(last_seen)
194
+ if (datetime.datetime.utcnow() - last_t).days <= MEMORY_KEEP_DAYS:
195
+ return jsonify({"reply":f"Hey {slot.get('name')}, you seemed a bit down last time — how are you feeling now?", "emotion":"warm"})
196
+ except Exception:
197
+ pass
198
+ return jsonify({"reply":f"Hey {slot.get('name')} — I’m here. What’s on your mind?", "emotion":"calm"})
199
 
200
+ # If no text provided, ask user to say something
201
+ if not message:
202
+ return jsonify({"reply":"I’m listening — when you’re ready, share anything that’s on your mind.", "emotion":"neutral"})
203
+
204
+ # If session has awaiting flag (expecting name or age), handle it
205
+ awaiting = slot.get("awaiting") # could be "name" or "age" or None
206
+
207
+ # Try to extract name if message looks like a name
208
+ if not slot.get("name") and not awaiting:
209
+ # If message contains natural name phrases, extract
210
+ extracted = extract_name(message)
211
+ if extracted:
212
+ slot["name"] = extracted
213
+ slot["last_seen"] = now_iso
214
+ slot["last_mood"] = emotion_from_text(message)
215
+ memory[session] = slot
216
+ write_memory(memory)
217
+ # ask age next
218
+ slot["awaiting"]="age"
219
+ memory[session]=slot
220
+ write_memory(memory)
221
+ return jsonify({"reply":f"Lovely to meet you, {extracted}! How old are you?", "emotion":"curious"})
222
+ else:
223
+ # If not extractable, ask name explicitly and set awaiting flag
224
+ slot["awaiting"]="name"
225
+ slot["last_seen"] = now_iso
226
+ memory[session]=slot
227
+ write_memory(memory)
228
+ return jsonify({"reply":"Hey there! What’s your name?", "emotion":"calm"})
229
+
230
+ # If we are expecting name
231
+ if awaiting == "name":
232
+ # accept entire message as name (sanitize)
233
+ name_guess = extract_name(message) or message.split()[0].capitalize()
234
+ slot["name"] = name_guess
235
+ slot.pop("awaiting", None)
236
+ slot["last_seen"]=now_iso
237
+ memory[session]=slot
238
+ write_memory(memory)
239
+ # ask age
240
+ slot["awaiting"]="age"
241
+ memory[session]=slot
242
+ write_memory(memory)
243
+ return jsonify({"reply":f"Nice to meet you, {name_guess}! How old are you?", "emotion":"curious"})
244
+
245
+ # If we are expecting age
246
+ if awaiting == "age":
247
+ age_val = extract_age(message)
248
+ if age_val:
249
+ slot["age"]=age_val
250
+ slot.pop("awaiting", None)
251
+ slot["last_seen"]=now_iso
252
+ memory[session]=slot
253
+ write_memory(memory)
254
+ return jsonify({"reply":f"Thanks — got that. {slot.get('name')}, how are you feeling today?", "emotion":"curious"})
255
+ else:
256
+ # If message didn't include a clear age, ask again
257
+ return jsonify({"reply":"Could you tell me your age as a number (for example: 20)?", "emotion":"neutral"})
258
+
259
+ # At this point, name and age are present (or already set). Proceed to normal chat.
260
+
261
+ # If message indicates crisis, handle immediately
262
+ if detect_crisis(message):
263
+ # try to detect country from IP
264
+ ip = request.remote_addr or ""
265
+ country_code = get_country_from_ip(ip) or "DEFAULT"
266
+ helpline = HELPLINES.get(country_code, HELPLINES["DEFAULT"])
267
+ reply = (f"I’m really concerned about how you’re feeling. You are not alone. "
268
+ f"Please consider reaching out to emergency services or a helpline nearby: {helpline}")
269
+ # don't overwrite memory last_mood to crisis; set a flag
270
+ slot["last_seen"]=now_iso
271
+ slot["last_mood"]="crisis"
272
+ memory[session]=slot
273
+ write_memory(memory)
274
+ return jsonify({"reply":reply, "emotion":"crisis"})
275
+
276
+ # Normal chat: compute emotion
277
+ emotion = emotion_from_text(message)
278
+
279
+ # generate supportive response (use varied templates and avoid repetition)
280
+ base = ""
281
+ # Introspective responses for heavy emotions
282
+ if emotion in ("sadness","fear","anger"):
283
+ base = random.choice(EMP_OPENERS) + " " + random.choice(EMP_FOLLOWUPS)
284
+ elif emotion in ("joy","love"):
285
+ base = f"That’s lovely — I’m really glad to hear that, {slot.get('name','friend')}!"
286
  else:
287
+ # neutral / other
288
+ if any(word in message.lower() for word in ("help","guidance","motivate","motivation","inspire")):
289
+ base = random.choice(MOTIVATIONAL)
 
290
  else:
291
+ base = random.choice(EMP_OPENERS)
292
+
293
+ # occasionally add a short motivational bit when appropriate
294
+ if emotion in ("sadness","fear") and random.random() < 0.5:
295
+ base = base + " " + random.choice(MOTIVATIONAL)
296
+
297
+ # update memory
298
+ slot["last_mood"] = emotion
299
+ slot["last_seen"] = now_iso
300
+ if not slot.get("name"):
301
+ slot["name"] = "friend"
302
+ memory[session] = slot
303
+ write_memory(memory)
304
+
305
+ # Enrich to avoid very short repetitive replies
306
+ # If the base is very short or repeated wordy, diversify
307
+ reply = base
308
 
309
  return jsonify({"reply": reply, "emotion": emotion})
310
 
311
+ # Serve frontend
312
  @app.route("/")
313
  def index():
314
  return send_from_directory(".", "index.html")
315
 
316
  if __name__ == "__main__":
317
+ # On Spaces, they auto-run; but for standalone testing:
318
+ app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))