Shresthh03 commited on
Commit
fc37241
·
verified ·
1 Parent(s): d788444

Update it.

Browse files
Files changed (1) hide show
  1. app.py +387 -132
app.py CHANGED
@@ -1,152 +1,407 @@
1
  import os
2
  import json
3
- from datetime import datetime, timedelta
 
 
4
  from flask import Flask, request, jsonify, send_from_directory
5
- from transformers import pipeline
6
- from openai import OpenAI
7
-
8
- app = Flask(__name__)
9
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
10
-
11
- # Load Hugging Face emotion model
12
- emotion_analyzer = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=None)
13
-
14
- USER_DATA_FILE = "user_data.json"
15
-
16
- # ---------------------- UTILITIES -------------------------
17
- def load_user_data():
18
- if os.path.exists(USER_DATA_FILE):
19
- with open(USER_DATA_FILE, "r") as f:
20
- return json.load(f)
21
- return {"name": None, "age": None, "mood": None, "last_interaction": None, "missed_days": 0}
22
-
23
- def save_user_data(data):
24
- with open(USER_DATA_FILE, "w") as f:
25
- json.dump(data, f, indent=4)
26
-
27
- def detect_emotion(text):
28
- result = emotion_analyzer(text)
29
- top_emotion = sorted(result[0], key=lambda x: x["score"], reverse=True)[0]["label"]
30
- return top_emotion.lower()
31
-
32
- def crisis_check(user_input, location="global"):
33
- crisis_keywords = ["kill myself", "end my life", "suicide", "die", "worthless"]
34
- if any(kw in user_input.lower() for kw in crisis_keywords):
35
- if location == "india":
36
- return ("I'm really sorry you’re feeling like this. You are not alone. "
37
- "You can reach out to AASRA Helpline at 91-9820466726 or Snehi at 91-9582208181.")
38
- elif location == "us":
39
- return ("It sounds like you’re going through a really difficult time. "
40
- "If you are in the U.S., please call or text 988 to connect with the Suicide and Crisis Lifeline.")
41
- else:
42
- return ("I’m so sorry you’re in pain right now. You are not alone. "
43
- "Please reach out to a local suicide helpline or emergency number right away.")
44
- return None
45
 
46
- def days_since_last_interaction(user_data):
47
- if not user_data.get("last_interaction"):
48
- return None
49
- last = datetime.fromisoformat(user_data["last_interaction"])
50
- return (datetime.now() - last).days
51
-
52
- # ---------------------- PERSONALITY SYSTEM -------------------------
53
- PERSONALITIES = {
54
- "calm": {
55
- "tone": "gentle, understanding, patient",
56
- "style": "Uses short, soft phrases and empathy-driven responses."
57
- },
58
- "friendly": {
59
- "tone": "warm, chatty, and supportive",
60
- "style": "Uses casual language and light humor to uplift users."
61
- },
62
- "deep": {
63
- "tone": "reflective, philosophical, soulful",
64
- "style": "Encourages self-reflection and growth."
65
- },
66
- "spiritual": {
67
- "tone": "peaceful, grounding, and nurturing",
68
- "style": "Focuses on mindfulness, acceptance, and compassion."
69
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
- def generate_personality_prompt(personality):
73
- p = PERSONALITIES.get(personality, PERSONALITIES["calm"])
74
- return f"You are an emotional support AI with a {p['tone']} tone. {p['style']} Respond to users with empathy and variation."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- # ---------------------- RESPONSE LOGIC -------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  @app.route("/chat", methods=["POST"])
78
  def chat():
79
- user_input = request.json.get("message", "")
80
- personality = request.json.get("personality", "calm")
81
- location = request.json.get("location", "global")
 
 
 
 
 
 
 
82
 
83
- user_data = load_user_data()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  # Crisis detection
86
- crisis_response = crisis_check(user_input, location)
87
- if crisis_response:
88
- return jsonify({"response": crisis_response, "emotion": "worried"})
89
-
90
- # Daily check-in system
91
- days_passed = days_since_last_interaction(user_data)
92
- if days_passed is not None:
93
- if days_passed >= 3:
94
- reminder = "We missed you these past few days. How have you been feeling lately?"
95
- elif days_passed == 1 and user_data.get("mood") in ["sad", "angry", "worried"]:
96
- reminder = f"Hey {user_data.get('name','friend')}, you seemed a bit down yesterday. How are you feeling today?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  else:
98
- reminder = None
99
- else:
100
- reminder = None
101
-
102
- # Emotion detection
103
- emotion = detect_emotion(user_input)
104
- user_data["mood"] = emotion
105
- user_data["last_interaction"] = datetime.now().isoformat()
106
- save_user_data(user_data)
107
-
108
- # OpenAI response generation
109
- system_prompt = generate_personality_prompt(personality)
110
- prompt = f"""
111
- The user said: "{user_input}"
112
- Their name: {user_data.get('name')}
113
- Their age: {user_data.get('age')}
114
- Their recent mood: {user_data.get('mood')}
115
- Your goal: offer empathetic emotional support, avoid repetition, vary expressions naturally.
116
- """
117
-
118
- openai_reply = None
119
- try:
120
- response = client.chat.completions.create(
121
- model="gpt-4o-mini",
122
- messages=[
123
- {"role": "system", "content": system_prompt},
124
- {"role": "user", "content": prompt}
125
- ],
126
- temperature=0.9
127
- )
128
- openai_reply = response.choices[0].message.content.strip()
129
- except Exception as e:
130
- print("Error with OpenAI API:", e)
131
- openai_reply = "I'm here for you, even though I’m having a bit of trouble expressing myself right now."
132
-
133
- # Combine reminder if needed
134
- if reminder:
135
- final_reply = f"{reminder} {openai_reply}"
136
- else:
137
- final_reply = openai_reply
138
 
139
- return jsonify({"response": final_reply, "emotion": emotion})
 
 
 
 
 
140
 
141
- # ---------------------- FRONTEND -------------------------
 
 
 
 
 
 
 
 
 
 
 
 
142
  @app.route("/")
143
  def index():
144
  return send_from_directory(".", "index.html")
145
 
146
- @app.route("/<path:path>")
147
- def static_files(path):
148
- return send_from_directory(".", path)
149
-
150
- # ---------------------- RUN -------------------------
151
  if __name__ == "__main__":
152
- 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
+ # Optional libraries - loaded if available
9
+ try:
10
+ import openai
11
+ OPENAI_AVAILABLE = bool(os.environ.get("OPENAI_API_KEY"))
12
+ if OPENAI_AVAILABLE:
13
+ openai.api_key = os.environ.get("OPENAI_API_KEY")
14
+ except Exception:
15
+ OPENAI_AVAILABLE = False
16
+
17
+ # Try to load HF emotion model (optional, may be large)
18
+ try:
19
+ from transformers import pipeline
20
+ HF_AVAILABLE = True
21
+ except Exception:
22
+ HF_AVAILABLE = False
23
+
24
+ emotion_model = None
25
+ if HF_AVAILABLE:
26
+ try:
27
+ emotion_model = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=5)
28
+ except Exception:
29
+ emotion_model = None
30
+
31
+ app = Flask(__name__, static_folder=".", static_url_path="/")
32
+
33
+ # Memory file
34
+ MEMORY_FILE = "session_memory.json"
35
+ MEMORY_KEEP_DAYS = 15
36
+
37
+ # Crisis keywords and helplines
38
+ CRISIS_KEYWORDS = [
39
+ "suicide", "kill myself", "end my life", "want to die", "hurt myself", "i'm going to die",
40
+ "i want to die", "i cant go on", "can't go on"
41
+ ]
42
+ HELPLINES = {
43
+ "US": "USA: Call or text 988 (Suicide & Crisis Lifeline)",
44
+ "IN": "India: AASRA Helpline +91-9820466726",
45
+ "GB": "UK: Samaritans 116 123",
46
+ "CA": "Canada: Talk Suicide Canada 1-833-456-4566",
47
+ "AU": "Australia: Lifeline 13 11 14",
48
+ "DEFAULT": "If you're in immediate danger, contact local emergency services or search for a local helpline."
49
+ }
50
+
51
+ # Personalities -> small descriptor (informs OpenAI prompt if used)
52
+ PERSONA_TEXT = {
53
+ "calm": "Calm, grounding, gentle friend.",
54
+ "friendly": "Warm, slightly chatty and encouraging friend.",
55
+ "deep": "Reflective, thoughtful, soulful friend.",
56
+ "spiritual": "Peaceful, nurturing, mindful tone.",
57
+ "neutral": "Balanced, non-gendered, soothing voice."
58
  }
59
 
60
+ # Utility: load & save memory
61
+ def load_memory():
62
+ if os.path.exists(MEMORY_FILE):
63
+ try:
64
+ with open(MEMORY_FILE, "r") as f:
65
+ data = json.load(f)
66
+ except Exception:
67
+ data = {}
68
+ else:
69
+ data = {}
70
+ # prune entries older than MEMORY_KEEP_DAYS
71
+ cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=MEMORY_KEEP_DAYS)
72
+ good = {}
73
+ for k, v in data.items():
74
+ try:
75
+ t = datetime.datetime.fromisoformat(v.get("last_seen"))
76
+ if t >= cutoff:
77
+ good[k] = v
78
+ except Exception:
79
+ good[k] = v
80
+ return good
81
+
82
+ def save_memory(mem):
83
+ with open(MEMORY_FILE, "w") as f:
84
+ json.dump(mem, f, indent=2)
85
+
86
+ memory = load_memory()
87
+
88
+ # Small NLP helpers
89
+ def extract_name(text):
90
+ text = text.strip()
91
+ patterns = [
92
+ r"^(?:i am|i'm|im|i’m)\s+([A-Za-z][A-Za-z'\- ]{1,40})",
93
+ r"my name is\s+([A-Za-z][A-Za-z'\- ]{1,40})",
94
+ r"^([A-Z][a-z]{1,30})$"
95
+ ]
96
+ for p in patterns:
97
+ m = re.search(p, text, flags=re.IGNORECASE)
98
+ if m:
99
+ name = m.group(1).strip()
100
+ return " ".join([w.capitalize() for w in name.split()])
101
+ return None
102
+
103
+ def extract_age(text):
104
+ nums = re.findall(r"\b([1-9][0-9]?)\b", text)
105
+ for n in nums:
106
+ val = int(n)
107
+ if 8 <= val <= 120:
108
+ return val
109
+ return None
110
+
111
+ def detect_crisis(text):
112
+ t = text.lower()
113
+ return any(kw in t for kw in CRISIS_KEYWORDS)
114
+
115
+ def get_helpline_by_ip(ip):
116
+ # best-effort via ipapi.co (may fail); fallback to DEFAULT
117
+ try:
118
+ import requests
119
+ if ip and ":" not in ip:
120
+ url = f"https://ipapi.co/{ip}/json/"
121
+ else:
122
+ url = "https://ipapi.co/json/"
123
+ r = requests.get(url, timeout=2)
124
+ if r.status_code == 200:
125
+ data = r.json()
126
+ code = data.get("country_code", "").upper()
127
+ return HELPLINES.get(code, HELPLINES["DEFAULT"])
128
+ except Exception:
129
+ pass
130
+ return HELPLINES["DEFAULT"]
131
+
132
+ def classify_emotion(text):
133
+ # first attempt HF model if available
134
+ if emotion_model:
135
+ try:
136
+ out = emotion_model(text)
137
+ # pipeline returns list or list-of-lists. Get top label.
138
+ first = out[0]
139
+ if isinstance(first, list):
140
+ label = first[0]["label"]
141
+ else:
142
+ label = first["label"]
143
+ return label.lower()
144
+ except Exception:
145
+ pass
146
+ # fallback keywords
147
+ low = text.lower()
148
+ if any(w in low for w in ["happy","glad","joy","great","good","awesome","yay"]):
149
+ return "joy"
150
+ if any(w in low for w in ["sad","down","depress","lonely","cry","unhappy"]):
151
+ return "sadness"
152
+ if any(w in low for w in ["angry","mad","furious","annoy"]):
153
+ return "anger"
154
+ if any(w in low for w in ["scared","afraid","anxious","panic","worried"]):
155
+ return "fear"
156
+ if any(w in low for w in ["love","loving","fond","cherish"]):
157
+ return "love"
158
+ return "neutral"
159
+
160
+ # Intent detection simple rules
161
+ def detect_intent(text):
162
+ t = text.lower()
163
+ if detect_crisis(t):
164
+ return "CRISIS"
165
+ if any(q in t for q in ["how are you","how're you","how you doing","are you okay","are you mad","you okay","are you fine"]):
166
+ return "ASK_BOT"
167
+ if any(w in t for w in ["motivate","motivation","guidance","inspire","give me guidance","need motivation"]):
168
+ return "REQUEST_MOTIVATION"
169
+ if any(w in t for w in ["lol","haha","jk","dude","bro","have you gone","are you mad?","you mad"]):
170
+ return "CASUAL"
171
+ if any(w in t for w in ["sad","down","depressed","anxious","anxiety","lonely","suicidal","hurt myself"]):
172
+ return "SUPPORT"
173
+ if len(t.split()) <= 6:
174
+ return "CASUAL"
175
+ return "SUPPORT"
176
+
177
+ # Non repetitive picks
178
+ def pick_nonrep(slot, choices):
179
+ recent = slot.get("recent", [])
180
+ options = [c for c in choices if c not in recent]
181
+ if not options:
182
+ slot["recent"] = []
183
+ options = choices[:]
184
+ pick = random.choice(options)
185
+ slot.setdefault("recent", []).insert(0, pick)
186
+ slot["recent"] = slot["recent"][:8]
187
+ return pick
188
+
189
+ # Templates
190
+ CASUAL_TEMPLATES = [
191
+ "Haha — you got me! What's up?",
192
+ "I’m here for that laugh and also for the real talk. Tell me more.",
193
+ "LOL — I might be a little wired, but I’m listening. Go on.",
194
+ "Okay that’s funny — but seriously, how are you?"
195
+ ]
196
+ SUPPORT_OPENERS = [
197
+ "That sounds really heavy — thank you for sharing that with me.",
198
+ "I can feel how much that means. I'm here with you.",
199
+ "That must be hard — I'm listening and I care.",
200
+ "You did well telling me that. Would you like to tell me more?"
201
+ ]
202
+ SUPPORT_FOLLOWUPS = [
203
+ "Would you like a calming exercise or some small practical steps?",
204
+ "How has this been affecting your daily life recently?",
205
+ "What helps you even a little when you feel like that?"
206
+ ]
207
+ MOTIVATIONAL = [
208
+ "Even small steps matter — you don't have to fix everything at once.",
209
+ "You’ve made it this far; that already shows strength.",
210
+ "Rest is allowed. Healing isn't a straight line.",
211
+ "Breath by breath — keep going."
212
+ ]
213
+ BOT_SELF_REPLIES = [
214
+ "I’m doing well — glad to be here with you. How are you?",
215
+ "Feeling calm and ready to listen — how about you?",
216
+ "I’m okay! My favorite thing is talking with you. How are you doing?"
217
+ ]
218
 
219
+ # OpenAI reply wrapper (if available)
220
+ def openai_reply(user_text, persona_desc, session_slot):
221
+ if not OPENAI_AVAILABLE:
222
+ return None
223
+ try:
224
+ system = f"You are Serenity, {persona_desc} Respond empathetically, avoid repeating the same short phrase, be human and varied."
225
+ messages = [
226
+ {"role":"system","content":system},
227
+ {"role":"user","content":user_text}
228
+ ]
229
+ resp = openai.ChatCompletion.create(
230
+ model = os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
231
+ messages = messages,
232
+ temperature = 0.8,
233
+ max_tokens = 300,
234
+ n = 1
235
+ )
236
+ return resp.choices[0].message.content.strip()
237
+ except Exception:
238
+ return None
239
+
240
+ # Main /chat endpoint
241
  @app.route("/chat", methods=["POST"])
242
  def chat():
243
+ data = request.get_json() or {}
244
+ session = data.get("session") or request.remote_addr or "default"
245
+ message = (data.get("message") or "").strip()
246
+ personality = data.get("personality") or data.get("voice_profile") or "neutral"
247
+ init_flag = bool(data.get("init", False))
248
+
249
+ # ensure session slot
250
+ slot = memory.get(session, {})
251
+ if not slot:
252
+ slot = {"name": None, "age": None, "last_mood": None, "last_seen": datetime.datetime.utcnow().isoformat(), "recent": [], "history": []}
253
 
254
+ # init greeting logic (frontend uses init to fetch greeting)
255
+ if init_flag:
256
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
257
+ memory[session] = slot
258
+ save_memory(memory)
259
+ if not slot.get("name"):
260
+ return jsonify({"reply":"Hey — I'm Serenity. What’s your name?", "emotion":"calm", "intent":"INIT"})
261
+ else:
262
+ last_mood = slot.get("last_mood")
263
+ last_seen = slot.get("last_seen")
264
+ try:
265
+ t = datetime.datetime.fromisoformat(last_seen)
266
+ if last_mood in ("sadness","anger","fear") and (datetime.datetime.utcnow() - t).days <= MEMORY_KEEP_DAYS:
267
+ return jsonify({"reply":f"Hey {slot.get('name')}, I remember you were having a tough time last session. How are you today?", "emotion":"warm", "intent":"FOLLOWUP"})
268
+ except Exception:
269
+ pass
270
+ return jsonify({"reply":f"Welcome back {slot.get('name','friend')} — I'm here for you. What's on your mind?", "emotion":"calm", "intent":"INIT"})
271
+
272
+ if not message:
273
+ return jsonify({"reply":"I’m here whenever you’re ready to share — anything on your mind?", "emotion":"neutral", "intent":"NONE"})
274
+
275
+ # If awaiting name/age flows (simple)
276
+ awaiting = slot.get("awaiting")
277
+ if not slot.get("name") and not awaiting:
278
+ # try to extract name from message
279
+ nm = extract_name(message)
280
+ if nm:
281
+ slot["name"] = nm
282
+ slot["awaiting"] = "age"
283
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
284
+ memory[session] = slot
285
+ save_memory(memory)
286
+ return jsonify({"reply":f"Lovely to meet you, {nm}! How old are you?", "emotion":"curious", "intent":"ASK_AGE"})
287
+ else:
288
+ slot["awaiting"] = "name"
289
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
290
+ memory[session] = slot
291
+ save_memory(memory)
292
+ return jsonify({"reply":"Hey — what should I call you? What's your name?", "emotion":"calm", "intent":"ASK_NAME"})
293
+
294
+ if awaiting == "name":
295
+ guessed = extract_name(message) or message.split()[0].capitalize()
296
+ slot["name"] = guessed
297
+ slot.pop("awaiting", None)
298
+ slot["awaiting"] = "age"
299
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
300
+ memory[session] = slot
301
+ save_memory(memory)
302
+ return jsonify({"reply":f"Nice to meet you, {guessed}! How old are you?", "emotion":"curious", "intent":"ASK_AGE"})
303
+
304
+ if awaiting == "age":
305
+ age = extract_age(message)
306
+ if age:
307
+ slot["age"] = age
308
+ slot.pop("awaiting", None)
309
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
310
+ memory[session] = slot
311
+ save_memory(memory)
312
+ return jsonify({"reply":f"Thanks — got it. {slot.get('name')}, how have you been feeling lately?", "emotion":"curious", "intent":"ASK_MOOD"})
313
+ else:
314
+ return jsonify({"reply":"Could you tell me your age as a number (for example: 24)?", "emotion":"neutral", "intent":"ASK_AGE"})
315
 
316
  # Crisis detection
317
+ if detect_crisis(message):
318
+ helpline = get_helpline_by_ip(request.remote_addr)
319
+ slot["last_mood"] = "crisis"
320
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
321
+ memory[session] = slot
322
+ save_memory(memory)
323
+ reply = (f"I’m really concerned about how you're feeling. You are not alone. "
324
+ f"Please consider contacting emergency services or this helpline: {helpline}")
325
+ return jsonify({"reply":reply, "emotion":"crisis", "intent":"CRISIS"})
326
+
327
+ # Normal flow: detect intent & emotion
328
+ intent = detect_intent(message)
329
+ emotion = classify_emotion(message)
330
+
331
+ # Behavior by intent
332
+ # 1) Asking about bot -> casual short human reply then pivot to user
333
+ if intent == "ASK_BOT":
334
+ bot_line = random.choice(BOT_SELF_REPLIES := BOT_SELF_REPLIES if 'BOT_SELF_REPLIES' in globals() else [
335
+ "I’m doing well — glad to be here with you.",
336
+ "Feeling calm and ready to listen — how about you?",
337
+ "I’m okay! Just happy we’re chatting. How are you?"
338
+ ])
339
+ pivot = random.choice(["How are you doing right now?", "And how about you?"])
340
+ reply = f"{bot_line} {pivot}"
341
+ slot["last_mood"] = emotion
342
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
343
+ memory[session] = slot
344
+ save_memory(memory)
345
+ return jsonify({"reply": reply, "emotion": emotion, "intent": intent})
346
+
347
+ # 2) Casual -> friendly replies
348
+ if intent == "CASUAL":
349
+ reply = openai_reply(message, PERSONA_TEXT.get(personality, "friendly"), slot) if OPENAI_AVAILABLE else None
350
+ if not reply or reply.strip().lower() in ("i understand","i see","okay"):
351
+ reply = pick_nonrep(slot, CASUAL_TEMPLATES)
352
+ slot["last_mood"] = emotion
353
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
354
+ slot.setdefault("history", []).append({"in": message, "out": reply, "time": slot["last_seen"], "intent": intent})
355
+ memory[session] = slot
356
+ save_memory(memory)
357
+ return jsonify({"reply": reply, "emotion": emotion, "intent": intent})
358
+
359
+ # 3) Request motivation
360
+ if intent == "REQUEST_MOTIVATION":
361
+ reply = pick_nonrep(slot, MOTIVATIONAL)
362
+ slot["last_mood"] = emotion
363
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
364
+ memory[session] = slot
365
+ save_memory(memory)
366
+ return jsonify({"reply": reply, "emotion": emotion, "intent": intent})
367
+
368
+ # 4) Support mode (default)
369
+ # Attempt OpenAI for the best reply
370
+ reply = None
371
+ if OPENAI_AVAILABLE:
372
+ reply = openai_reply(message, PERSONA_TEXT.get(personality, ""), slot)
373
+ if not reply:
374
+ opener = pick_nonrep(slot, SUPPORT_OPENERS)
375
+ follow = pick_nonrep(slot, SUPPORT_FOLLOWUPS)
376
+ if random.random() < 0.35:
377
+ reply = f"{opener} {random.choice(MOTIVATIONAL)} {follow}"
378
  else:
379
+ reply = f"{opener} {follow}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
+ # store and return
382
+ slot["last_mood"] = emotion
383
+ slot["last_seen"] = datetime.datetime.utcnow().isoformat()
384
+ slot.setdefault("history", []).append({"in": message, "out": reply, "time": slot["last_seen"], "intent": intent})
385
+ memory[session] = slot
386
+ save_memory(memory)
387
 
388
+ return jsonify({"reply": reply, "emotion": emotion, "intent": intent})
389
+
390
+ # Reset session endpoint
391
+ @app.route("/reset_session", methods=["POST"])
392
+ def reset_session():
393
+ data = request.get_json() or {}
394
+ session = data.get("session")
395
+ if session and session in memory:
396
+ memory.pop(session, None)
397
+ save_memory(memory)
398
+ return jsonify({"ok": True})
399
+
400
+ # Serve frontend (index.html)
401
  @app.route("/")
402
  def index():
403
  return send_from_directory(".", "index.html")
404
 
 
 
 
 
 
405
  if __name__ == "__main__":
406
+ port = int(os.environ.get("PORT", 7860))
407
+ app.run(host="0.0.0.0", port=port)