from gtts import gTTS import tempfile import json import datetime import gradio as gr # ---------------------------- # User data & existing lists # ---------------------------- user_info = {"name": "", "age": "", "gender": "", "language": "english" , "Guardian_info": ""} chat_history = [] journal_entries = [] calm_tips = [ "Take 3 deep breaths.", "Listen to nature.", "Stretch your body.", "Drink water.", "Think of one good thing today.", "Close your eyes for 1 minute.", "Write your feelings.", "Smile at yourself.", "Imagine a peaceful place.", "Say a positive affirmation." ] study_tips = [ "Use Pomodoro: 25min study, 5min break", "Make a daily to-do list", "Avoid multitasking", "Use color-coded notes", "Take 10-min exercise breaks", "Sleep 7โ9 hrs daily", "Drink water during study", "Use active recall", "Study hardest topics first", "Test yourself often" ] tip_index = {"calm": 0, "study": 0} lang_codes = { "english": "en", "hindi": "hi", "marathi": "mr", "bengali": "bn", "tamil": "ta", "telugu": "te", "malayalam": "ml", "spanish": "es", "french": "fr", "german": "de" } # (Keep your harmful_keywords and emotions as-is โ shortened here for clarity) harmful_keywords = [ # ... (keep the full list from your original file) ... "suicide", "kill myself", "end my life", "harm myself", "cut myself", "want to die", "die", "jump off", "self-harm", "self-hate", "suicidal", # etc. ] harmful_response = ( "โ ๏ธ It sounds like you're going through a really tough time.\n\n" "Please reach out to a professional:\n\n" "๐ฎ๐ณ **India Helplines**\n" "๐ง *Dr. Rachna Khanna Singh* โ +91 99103 90559\n" "๐ *iCall Helpline* โ +91 9152987821\n" "๐ *Vandrevala Foundation* โ 1860 266 2345 or 1800 233 3330\n\n" "๐ **International Helplines**\n" "๐ *Lifeline (USA)* โ 988\n" "๐ *Samaritans (UK)* โ 116 123\n" "๐ *Lifeline (Australia)* โ 13 11 14\n\n" "You are not alone. There are people who care and want to help ๐" ) # Simplified emotions dictionary (keep your full mapping; trimmed in this view) emotions = { "sad": "I hear you ๐. Iโm really sorry youโre feeling sad right now. Do you want to tell me whatโs making you feel this way, or would you rather I just try to gently cheer you up?", "anxiety": "Itโs okay โ anxiety can feel overwhelming, like your mind is running a race it didnโt sign up for. Try slowing down your breathing: inhale for 4 seconds, hold for 4, exhale for 6.", "angry": "Itโs normal to feel angry โ itโs your mindโs way of telling you something feels unfair or hurtful.", # ... keep the rest of your emotion replies ... } # ---------------------------- # Conversation state for Hybrid (C) memory model # ---------------------------- conversation_state = { "emotion": None, # e.g., "sad" "context": None, # e.g., "school", "parents", "friend", "exam" "followup_stage": 0, # how many follow-ups we've done for this emotion "turns_since_set": 0 # used to expire after a small idle period if needed } # Keywords to detect context quickly context_markers = { "school": ["school", "exam", "test", "teacher", "class"], "parents": ["parent", "mom", "dad", "parents", "home"], "friend": ["friend", "friends", "bully", "shouted", "argument", "fight"], "health": ["sick", "unwell", "hospital", "doctor"], "love": ["crush", "boyfriend", "girlfriend", "heartbroken", "love"], # add more markers as needed } # Follow-up templates (3 steps each, then action suggestions) followups = { "sad": [ "Iโm really sorry youโre feeling sad. Can you tell me if this happened recently or is it been building up?", "Thank you for sharing. How did that make you feel โ more hurt, embarrassed, or something else?", "That sounds heavy. Would you like a calming exercise, to write about it in the journal, or to talk more?" ], "anxiety": [ "Anxiety can be overwhelming. Is it about something specific like exams or relationships?", "When your anxiety spikes, do you notice physical signs like a racing heart or trouble breathing?", "Would you like to try a short breathing exercise now, or do you want to talk about whatโs triggering it?" ], "angry": [ "Anger is a valid response. Do you want to share what caused this anger?", "When you feel angry, what usually helps โ space, talking, or doing something active?", "Would you like ideas to safely express the anger, or would you prefer calming techniques first?" ], # fallback for other emotions: "default": [ "Tell me more about that โ when did you start feeling this way?", "How strong does this feeling feel on a scale of 1 to 10?", "Would you like a calming activity, a journal prompt, or to continue talking?" ] } # Reset phrases (user indicates they are fine or want to change topic) reset_phrases = [ "i'm fine", "i am fine", "i'm okay", "i am okay", "i feel better", "thank you", "thanks", "that's all", "let's talk about something else", "stop", "no", "I'm good", "im good" ] # ---------------------------- # Helper functions # ---------------------------- def _contains_any(text, keywords): text = text.lower() for kw in keywords: if kw in text: return True return False def detect_harmful(text): t = text.lower() for kw in harmful_keywords: if kw in t: return True return False def detect_emotion_in_text(text): t = text.lower() # Prefer longer keys first (to avoid 'sad' matching inside other words) โ optional improvement sorted_keys = sorted(emotions.keys(), key=lambda x: -len(x)) for key in sorted_keys: if key in t: return key return None def detect_context_in_text(text): t = text.lower() for ctx, markers in context_markers.items(): for m in markers: if m in t: return ctx return None def is_reset_phrase(text): t = text.lower() for phrase in reset_phrases: if phrase in t: return True return False def build_empathy_reply(emotion): # Base empathy from emotions dict, plus a gentle follow-up question from followups base = emotions.get(emotion, "") # Pick a gentle opener if no followups defined return base # ---------------------------- # Upgraded reply generator with memory, context, multi-turn flow # ---------------------------- def generate_reply(input_text): text = input_text.strip() if not text: return "Please say or type something when you are ready." lower = text.lower() # 1) Immediate harmful check (escalate without delay) if detect_harmful(lower): # Reset conversation state because we escalate conversation_state["emotion"] = None conversation_state["context"] = None conversation_state["followup_stage"] = 0 conversation_state["turns_since_set"] = 0 return harmful_response # 2) User reset intent โ end current flow politely if is_reset_phrase(lower): conversation_state["emotion"] = None conversation_state["context"] = None conversation_state["followup_stage"] = 0 conversation_state["turns_since_set"] = 0 return "That's ok โ we can pause here. If you'd like to talk again later, I'm right here." # 3) Detect if user explicitly states a new emotion -> override current state new_emotion = detect_emotion_in_text(lower) if new_emotion and new_emotion != conversation_state["emotion"]: # start new flow conversation_state["emotion"] = new_emotion conversation_state["context"] = detect_context_in_text(lower) conversation_state["followup_stage"] = 0 conversation_state["turns_since_set"] = 0 # Compose reply: empathy + first follow-up empathy = build_empathy_reply(new_emotion) # get followup prompt prompts = followups.get(new_emotion, followups["default"]) follow = prompts[0] if prompts else "" # Combine neatly combined = f"{empathy}\n\n{follow}" return combined # 4) If an emotion is already active, continue that flow if conversation_state["emotion"]: emotion = conversation_state["emotion"] # Update context if we find context in this message ctx = detect_context_in_text(lower) if ctx: conversation_state["context"] = ctx # If the user provided details (short heuristic: message length > 20 or contains certain markers), # treat this as progress and advance the follow-up stage provided_detail = (len(lower) > 20) or any(w in lower for w in ["because", "so", "then", "after", "today", "yesterday", "last", "friend", "parents", "teacher", "exam", "test"]) stage = conversation_state["followup_stage"] prompts = followups.get(emotion, followups["default"]) # If user asked a question (contains '?'), attempt to respond empathetically referencing emotion/context if "?" in lower: # Respond referencing emotion and context ctx_text = f" about {conversation_state['context']}" if conversation_state["context"] else "" response = f"I hear your question{ctx_text}. Let's look at that together. {emotions.get(emotion,'')}" # do not advance stage on simple question conversation_state["turns_since_set"] += 1 return response if provided_detail: # Provide a reply that references the emotion and context and then advance ctx_text = "" if conversation_state["context"]: ctx_text = f" because of {conversation_state['context']}" # Construct reflective reply if stage < len(prompts): response = f"I understand โ it makes sense you'd feel {emotion}{ctx_text}. {prompts[stage]}" conversation_state["followup_stage"] += 1 conversation_state["turns_since_set"] = 0 return response else: # Follow-ups exhausted; offer actionable choices conversation_state["turns_since_set"] += 1 return ("You have shared a lot โ would you like to: \n" "1) Try a calming exercise now\n" "2) Write this in your journal\n" "3) Talk more about it\n\n" "Type '1', '2', or '3' to choose, or tell me if you want a helpline.") else: # User gave a short reply โ ask a clarifying / gentle follow-up without advancing too far if stage < len(prompts): response = prompts[stage] # only advance if we already showed that prompt previously conversation_state["turns_since_set"] += 1 # If we had asked the same prompt previously and user still gives short replies, advance stage if conversation_state["turns_since_set"] > 1: conversation_state["followup_stage"] = min(len(prompts), stage + 1) conversation_state["turns_since_set"] = 0 return response else: # if stages done but no detail, ask for choice return ("Iโm listening. If it's hard to describe, choose: 1) calming exercise 2) journal 3) talk more. " "Or say 'I'm fine' to stop.") # 5) No active emotion and no new emotion detected -> try to detect context or ask to elaborate ctx = detect_context_in_text(lower) if ctx: # Gentle probing return f"You mentioned {ctx}. Is this making you feel upset or worried? How does it make you feel?" # Default fallback return "Tell me more about your day โ what happened, or how are you feeling right now?" # ---------------------------- # Chat function (uses TTS + conversation_state) # ---------------------------- def chat_function(audio_input, text_input): # We ignore audio_input here for text-only flow; you can add speech-to-text to use it. user_text = (text_input or "").strip() if not user_text: return "Please type something.", None reply = generate_reply(user_text) # Save conversation to history chat_history.append({"user": user_text, "bot": reply, "emotion": conversation_state.get("emotion"), "context": conversation_state.get("context")}) # Text-to-speech (language selection) lang_code = lang_codes.get(user_info["language"], "en") try: tts = gTTS(reply, lang=lang_code) audio_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") tts.save(audio_file.name) voice_path = audio_file.name except Exception as e: # If TTS fails, still return the reply and no voice voice_path = None return reply, voice_path # ---------------------------- # Remaining utility functions (unchanged) # ---------------------------- def set_personal_info(name, age, gender, language, Guardian_info): user_info.update({"name": name, "age": age, "gender": gender, "language": language, "Guardian_info": Guardian_info,}) return gr.update(visible=True), f"โ Welcome {name}! Preferences saved." def show_personal_data(): today = datetime.date.today().strftime("%Y-%m-%d (%A)") return f"๐ {today}\n๐ค Name: {user_info['name']}\n๐ Age: {user_info['age']}\nโ Gender: {user_info['gender']}\n๐ Language: {user_info['language']}\n Guardian_info: {user_info['Guardian_info']}" def get_chat_history(): if not chat_history: return "No conversations yet." return "\n\n".join([f"You: {c['user']}\nBot: {c['bot']}" for c in chat_history]) def save_journal(entry): journal_entries.append(entry) with open("journal.json", "w") as f: json.dump(journal_entries, f) return "โ Journal saved!" def show_journal_history(): return "\n---\n".join(journal_entries) if journal_entries else "No journal entries yet." def next_calm_tip(): tip = calm_tips[tip_index["calm"] % len(calm_tips)] tip_index["calm"] += 1 return tip def next_study_tip(): tip = study_tips[tip_index["study"] % len(study_tips)] tip_index["study"] += 1 return tip # ---------------------------- # Gradio UI (kept same as your original file) # ---------------------------- with gr.Blocks() as app: welcome_screen = gr.Column(visible=True) full_app = gr.Tabs(visible=False) with welcome_screen: gr.HTML(""" """) gr.Markdown("