Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- app.py +182 -0
- requirements.txt +5 -0
app.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from transformers import pipeline
|
| 3 |
+
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
|
| 4 |
+
import random
|
| 5 |
+
import re
|
| 6 |
+
|
| 7 |
+
# ------------------------- SAFETY DISCLAIMER -------------------------
|
| 8 |
+
DISCLAIMER = """
|
| 9 |
+
⚠️ **Important**: I am NOT a licensed therapist or doctor.
|
| 10 |
+
I can offer emotional support, listening, and simple coping tips, but **I am not a substitute for professional help**.
|
| 11 |
+
|
| 12 |
+
If you are in crisis or feel unsafe:
|
| 13 |
+
- **India**: AASRA Helpline → 9820466726 (24×7)
|
| 14 |
+
- **US**: 988 Suicide & Crisis Lifeline (call or text)
|
| 15 |
+
- Emergency: Call 112 / 108 / local emergency services immediately.
|
| 16 |
+
|
| 17 |
+
You are never alone. Reach out — help is available.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
# ------------------------- LOAD MODELS (cached) -------------------------
|
| 21 |
+
@st.cache_resource
|
| 22 |
+
def load_models():
|
| 23 |
+
st.write("Loading AI models... (only once)") # visible only on first run
|
| 24 |
+
sentiment_model = pipeline("sentiment-analysis", model="distilbert/distilbert-base-uncased-finetuned-sst-2-english")
|
| 25 |
+
vader = SentimentIntensityAnalyzer()
|
| 26 |
+
return sentiment_model, vader
|
| 27 |
+
|
| 28 |
+
sentiment_model, vader = load_models()
|
| 29 |
+
|
| 30 |
+
# ------------------------- IMPROVED CRISIS DETECTION -------------------------
|
| 31 |
+
CRISIS_PATTERNS = [
|
| 32 |
+
r"\b(suicide|suicidal|kill myself|end my life|die|self harm|cutting|overdose)\b",
|
| 33 |
+
r"no (point|reason) (to live|living|exist)",
|
| 34 |
+
r"want to disappear|not wake up|better off dead",
|
| 35 |
+
r"\b(jump|hang|pill|cut).*myself",
|
| 36 |
+
r"i can't do this anymore.*(anymore|life)",
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
def detect_crisis(text: str) -> bool:
|
| 40 |
+
if not text:
|
| 41 |
+
return False
|
| 42 |
+
text_lower = text.lower()
|
| 43 |
+
return any(re.search(pattern, text_lower) for pattern in CRISIS_PATTERNS)
|
| 44 |
+
|
| 45 |
+
# ------------------------- MOOD DETECTION (Hybrid VADER + Transformers) -------------------------
|
| 46 |
+
def detect_mood(text: str) -> str:
|
| 47 |
+
if detect_crisis(text):
|
| 48 |
+
return "critical"
|
| 49 |
+
|
| 50 |
+
# VADER score (great for short emotional text)
|
| 51 |
+
vader_score = vader.polarity_scores(text)['compound']
|
| 52 |
+
|
| 53 |
+
# Transformers model (more contextual)
|
| 54 |
+
hf_result = sentiment_model(text)[0]
|
| 55 |
+
hf_label = hf_result['label'] # POSITIVE / NEGATIVE
|
| 56 |
+
hf_score = hf_result['score']
|
| 57 |
+
|
| 58 |
+
# Strong positive
|
| 59 |
+
if vader_score >= 0.45 or (hf_label == "POSITIVE" and hf_score > 0.85):
|
| 60 |
+
return "positive"
|
| 61 |
+
|
| 62 |
+
# Strong negative
|
| 63 |
+
if vader_score <= -0.45 or (hf_label == "NEGATIVE" and hf_score > 0.88):
|
| 64 |
+
lowered = text.lower()
|
| 65 |
+
if any(word in lowered for word in ["anxious", "panic", "worry", "nervous", "heart racing", "overwhelmed"]):
|
| 66 |
+
return "anxious"
|
| 67 |
+
if any(word in lowered for word in ["stress", "exam", "deadline", "placement", "study", "assignment", "overloaded"]):
|
| 68 |
+
return "stressed"
|
| 69 |
+
return "sad"
|
| 70 |
+
|
| 71 |
+
# Mild negative or neutral
|
| 72 |
+
if vader_score <= -0.15:
|
| 73 |
+
return "sad" if "sad" in text.lower() or "low" in text.lower() else "neutral"
|
| 74 |
+
|
| 75 |
+
return "neutral"
|
| 76 |
+
|
| 77 |
+
# ------------------------- RESPONSE POOLS (Varied & Natural) -------------------------
|
| 78 |
+
RESPONSES = {
|
| 79 |
+
"critical": [
|
| 80 |
+
"I'm really sorry you're feeling this much pain right now. You don't have to go through this alone. Please reach out to AASRA (9820466726) or 988 immediately. I'm here to listen until you can talk to them.",
|
| 81 |
+
"This sounds incredibly heavy. Your life matters, and help is available 24/7. Call AASRA at 9820466726 right now — they are trained for exactly this. I'm staying with you."
|
| 82 |
+
],
|
| 83 |
+
"sad": [
|
| 84 |
+
"I hear how heavy this feels right now. It's okay to not be okay. Would you like to tell me what's been weighing on you?",
|
| 85 |
+
"That sounds really painful. You've survived every difficult day so far — I'm proud of you for that. I'm here with you.",
|
| 86 |
+
"Feeling low is exhausting. Even sharing it here is a small brave step. What's one thing that's been hardest lately?",
|
| 87 |
+
"I'm really sorry you're carrying this. You are not alone in this moment."
|
| 88 |
+
],
|
| 89 |
+
"anxious": [
|
| 90 |
+
"Anxiety can feel like everything is too loud inside. Let's try a quick calming breath together: Inhale 4 seconds… hold 7… exhale 8. Repeat 3 times whenever you're ready.",
|
| 91 |
+
"That racing feeling is really uncomfortable. You're safe here. Would you like a 60-second grounding exercise?",
|
| 92 |
+
"I can hear the worry in your words. Your nervous system is just trying to protect you. Let's slow it down together."
|
| 93 |
+
],
|
| 94 |
+
"stressed": [
|
| 95 |
+
"Exams, placements, assignments — it's a lot. You've handled tough phases before. Let's break today into tiny manageable pieces.",
|
| 96 |
+
"I hear the stress loud and clear. The 25-minute Pomodoro + 5-minute break technique works wonders for most students. Want me to guide you through one?",
|
| 97 |
+
"You're not lazy or failing — you're just carrying a heavy load. One task at a time. You've got this."
|
| 98 |
+
],
|
| 99 |
+
"positive": [
|
| 100 |
+
"That's wonderful to hear! 😊 Keep doing whatever is lighting you up right now. Want a quick gratitude boost or productivity tip?",
|
| 101 |
+
"Love seeing this energy! Celebrate the small wins today. Is there anything you'd like to build on?",
|
| 102 |
+
"This positivity looks good on you! What's one thing that made you smile today?"
|
| 103 |
+
],
|
| 104 |
+
"neutral": [
|
| 105 |
+
"Thanks for checking in. How are you really feeling today? I'm here whenever you want to talk.",
|
| 106 |
+
"I'm listening. What's on your mind today?"
|
| 107 |
+
]
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
# ------------------------- SMART RESPONSE GENERATOR -------------------------
|
| 111 |
+
def generate_response(mood: str, user_text: str) -> str:
|
| 112 |
+
# Check conversation history for escalation
|
| 113 |
+
if "mood_history" in st.session_state and len(st.session_state.mood_history) >= 3:
|
| 114 |
+
recent_moods = st.session_state.mood_history[-3:]
|
| 115 |
+
if recent_moods.count("sad") >= 2 or recent_moods.count("anxious") >= 2:
|
| 116 |
+
return ("It feels like this heaviness has been around for a few days now. "
|
| 117 |
+
"I'm genuinely concerned about you. Would you feel comfortable talking to someone on campus "
|
| 118 |
+
"or calling AASRA (9820466726)? I'm here either way.")
|
| 119 |
+
|
| 120 |
+
# Normal varied response
|
| 121 |
+
if mood in RESPONSES:
|
| 122 |
+
response = random.choice(RESPONSES[mood])
|
| 123 |
+
else:
|
| 124 |
+
response = "Thanks for sharing. I'm here to listen — no judgment."
|
| 125 |
+
|
| 126 |
+
# Add gentle follow-up for most moods
|
| 127 |
+
if mood in ["sad", "anxious", "stressed", "neutral"]:
|
| 128 |
+
response += "\n\nI'm here whenever you want to continue."
|
| 129 |
+
|
| 130 |
+
return response
|
| 131 |
+
|
| 132 |
+
# ------------------------- STREAMLIT UI -------------------------
|
| 133 |
+
st.set_page_config(page_title="MindMate • Student Mental Health Companion", page_icon="🧠", layout="centered")
|
| 134 |
+
|
| 135 |
+
st.title("🧠 MindMate")
|
| 136 |
+
st.caption("A safe space for students • Built with care")
|
| 137 |
+
|
| 138 |
+
st.markdown(DISCLAIMER)
|
| 139 |
+
|
| 140 |
+
# Initialize session state
|
| 141 |
+
if "chat_history" not in st.session_state:
|
| 142 |
+
st.session_state.chat_history = []
|
| 143 |
+
if "mood_history" not in st.session_state:
|
| 144 |
+
st.session_state.mood_history = []
|
| 145 |
+
|
| 146 |
+
# Sidebar
|
| 147 |
+
with st.sidebar:
|
| 148 |
+
st.header("About MindMate")
|
| 149 |
+
st.write("Detects mood using AI and responds with empathy + practical tips.")
|
| 150 |
+
st.write("Made for students, by students who care.")
|
| 151 |
+
|
| 152 |
+
if st.button("🗑️ Clear Conversation"):
|
| 153 |
+
st.session_state.chat_history = []
|
| 154 |
+
st.session_state.mood_history = []
|
| 155 |
+
st.rerun()
|
| 156 |
+
|
| 157 |
+
# Display chat history with nice bubbles
|
| 158 |
+
for sender, message in st.session_state.chat_history:
|
| 159 |
+
if sender == "You":
|
| 160 |
+
st.chat_message("user").write(message)
|
| 161 |
+
else:
|
| 162 |
+
st.chat_message("assistant").write(message)
|
| 163 |
+
|
| 164 |
+
# User input
|
| 165 |
+
user_input = st.chat_input("How are you feeling today? (It's okay to say anything)")
|
| 166 |
+
|
| 167 |
+
if user_input:
|
| 168 |
+
# Save user message
|
| 169 |
+
st.session_state.chat_history.append(("You", user_input))
|
| 170 |
+
|
| 171 |
+
# Detect mood
|
| 172 |
+
mood = detect_mood(user_input)
|
| 173 |
+
st.session_state.mood_history.append(mood)
|
| 174 |
+
|
| 175 |
+
# Generate response
|
| 176 |
+
response = generate_response(mood, user_input)
|
| 177 |
+
|
| 178 |
+
# Save bot message
|
| 179 |
+
st.session_state.chat_history.append(("Companion", response))
|
| 180 |
+
|
| 181 |
+
# Rerun to show new messages
|
| 182 |
+
st.rerun()
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
transformers
|
| 3 |
+
torch
|
| 4 |
+
nltk
|
| 5 |
+
vaderSentiment
|