CalmMe / app.py
Siddu2004-2006's picture
Update app.py
559188c verified
from gtts import gTTS
from transformers import pipeline
from langchain_groq import ChatGroq
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os
import random
import chromadb
import gradio as gr
import tempfile
from collections import deque
import re
# Initialize conversation memory (last 40 exchanges)
conversation_memory = deque(maxlen=40)
stt_pipe = pipeline("automatic-speech-recognition", model="openai/whisper-small")
def initialize_llm():
llm = ChatGroq(
temperature=0.8,
groq_api_key=os.environ.get("GROQ_API_KEY"),
model_name="llama-3.3-70b-versatile"
)
return llm
def create_vector_db():
loader = DirectoryLoader(r"Training_doc", glob='*.pdf', loader_cls=PyPDFLoader)
documents = loader.load()
print(f"Loaded {len(documents)} documents.")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
texts = text_splitter.split_documents(documents)
print(f"Generated {len(texts)} text chunks.")
embeddings = HuggingFaceBgeEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
vector_db = Chroma.from_documents(texts, embeddings, persist_directory='./chroma_db')
vector_db.persist()
return vector_db
def get_conversation_context():
if not conversation_memory:
return "This is the beginning of our conversation."
context_parts = []
for i, (user_msg, bot_response) in enumerate(list(conversation_memory)[-10:]):
context_parts.append(f"You: {user_msg}")
context_parts.append(f"Me: {bot_response[:80]}...")
return "\n".join(context_parts)
def add_to_memory(user_input, bot_response):
conversation_memory.append((user_input, bot_response))
def audio_to_text(audio_path):
if audio_path is None:
return None
text = stt_pipe(audio_path)["text"]
return text
def text_to_audio(text):
if not text:
return None
clean_text = re.sub(r'[^\w\s.,!?-]', '', text)
tts = gTTS(text=clean_text, lang='en', slow=False)
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3")
tts.save(temp_file.name)
return temp_file.name
def is_greeting(text):
greetings = ['hi', 'hello', 'hey', 'good morning', 'good afternoon', 'good evening', 'how are you']
return any(greeting in text.lower() for greeting in greetings)
def should_include_tips(user_input, response):
tip_triggers = [
'anxious', 'stressed', 'worry', 'panic', 'overwhelmed', 'tired', 'sad', 'depressed',
'can\'t sleep', 'insomnia', 'relationship', 'work stress', 'burnout', 'lonely',
'motivation', 'confidence', 'self-esteem', 'help', 'advice', 'tips', 'what should i do'
]
return any(trigger in user_input.lower() for trigger in tip_triggers) and not is_greeting(user_input)
def generate_tips(user_input, main_response):
tips_map = {
'anxiety': [
"Try the 4-7-8 breathing technique: inhale for 4 seconds, hold for 7 seconds, exhale for 8 seconds",
"Practice grounding with the 5-4-3-2-1 method: notice 5 things you see, 4 you can touch, 3 you hear, 2 you smell, 1 you taste",
"Remember that anxiety is temporary - this feeling will pass like weather changing"
],
'stress': [
"Take micro-breaks throughout your day - even 60 seconds to stretch and breathe can help",
"Try journaling your worries to get them out of your head and onto paper",
"Prioritize tasks using the Eisenhower Matrix: urgent vs important"
],
'sleep': [
"Create a consistent bedtime routine - try reading or gentle stretching before bed",
"Keep your bedroom cool (around 18-20°C) and completely dark for optimal sleep",
"Avoid screens 1 hour before bed - the blue light disrupts melatonin production"
],
'relationship': [
"Practice active listening - focus on understanding rather than responding",
"Use 'I feel' statements to express your emotions without blaming others",
"Schedule regular quality time without distractions like phones or TV"
],
'confidence': [
"Start each day by acknowledging one thing you appreciate about yourself",
"Keep a 'win jar' where you note small accomplishments each day",
"Practice power poses before challenging situations to boost confidence"
],
'work': [
"Use the Pomodoro technique: 25 minutes focused work, 5 minute break",
"Set clear boundaries for work hours and communicate them to colleagues",
"End each workday by planning your top 3 priorities for tomorrow"
]
}
user_lower = user_input.lower()
relevant_tips = []
for category, tips in tips_map.items():
if category in user_lower or any(word in user_lower for word in ['anxious', 'stressed', 'worry'] if category == 'anxiety'):
relevant_tips = tips
break
if not relevant_tips and any(word in user_lower for word in ['help', 'advice', 'what should i do', 'tips']):
relevant_tips = [
"Practice self-compassion - treat yourself as you would a dear friend",
"Connect with nature - even a short walk outside can shift perspective",
"Focus on what you can control rather than what you can't"
]
return relevant_tips[:3]
greeting_responses = [
"Hi there! How are you feeling today?",
"Hello! I'm here to listen. What would you like to share?",
"Hey! It's good to connect. What's been on your mind lately?",
"Hi! I'm glad you're here. How can I support you today?",
"Hello! What would you like to talk about today?"
]
def process_user_input(user_input, chat_history, is_voice=False):
if not user_input.strip():
return chat_history, None
if is_greeting(user_input) and len(user_input.split()) <= 4:
response = random.choice(greeting_responses)
add_to_memory(user_input, response)
audio_path = text_to_audio(response)
display_input = f"🎤 {user_input}" if is_voice else user_input
chat_history.append((display_input, response))
return chat_history, audio_path
context = get_conversation_context()
enhanced_query = f"""
Previous conversation context:
{context}
Current message: {user_input}
Please respond as a caring friend and wise companion, considering our conversation history.
"""
response = qa_chain({"query": enhanced_query})
answer = response["result"]
# Use the model's response directly without artificial starters
final_answer = answer
if should_include_tips(user_input, final_answer):
tips = generate_tips(user_input, final_answer)
if tips:
final_answer += "\n\n**Here are some practical suggestions that might help:**\n"
for tip in tips:
final_answer += f"• {tip}\n"
crisis_keywords = ['suicide', 'self-harm', 'kill myself', 'cutting', 'hurt myself', 'end my life',
'no reason to live', 'want to die', 'jump off', 'overdose', 'slit my wrists',
'hang myself', 'drown myself', 'hopeless', 'worthless', 'useless', 'no one cares',
'empty', 'tired of life', 'can`t go on', 'nothing matters', 'lost all hope',
'abused', 'molested', 'assaulted', 'raped', 'harassed', 'beaten', 'domestic violence',
'forced', 'threatened', 'bullied', 'panic attack', 'can`t breathe', 'chest pain',
'heart racing', 'shaking', 'dizzy', 'faint', 'feel like dying']
if any(keyword in user_input.lower() for keyword in crisis_keywords):
crisis_response = """
💙 **I'm deeply concerned about what you're sharing, and I want you to know your life has profound value.**
**Please reach out for immediate professional help:**
• **India**: Vandrevala Foundation - 1860 266 2345 | Snehi - 91-9582208181
• **USA**: 988 Suicide & Crisis Lifeline | Text HOME to 741741
• **UK**: Samaritans - 116 123
• **Emergency**: 911/112
Sharing these feelings takes tremendous courage, and it shows part of you wants to find a way through this pain. What you're experiencing right now is temporary, even though it feels overwhelming. Professional support can help you navigate these intense emotions.
You deserve care and support. Please connect with one of these resources - they have trained professionals ready to help. You're not alone in this. 💙
"""
final_answer += crisis_response
add_to_memory(user_input, final_answer)
audio_path = text_to_audio(final_answer)
display_input = f"🎤 {user_input}" if is_voice else user_input
chat_history.append((display_input, final_answer))
return chat_history, audio_path
def gradio_chat(user_input, chat_history):
if not user_input.strip():
return "", chat_history, None
chat_history, audio_path = process_user_input(user_input, chat_history, is_voice=False)
return "", chat_history, audio_path
def voice_chat(audio_path, chat_history):
if audio_path is None:
return chat_history, None
user_input = audio_to_text(audio_path)
if not user_input.strip():
chat_history.append(("", "I couldn't quite catch that. Could you try again?"))
return chat_history, None
chat_history, audio_path = process_user_input(user_input, chat_history, is_voice=True)
return chat_history, audio_path
def get_memory_summary():
if not conversation_memory:
return "No conversation history yet."
return f"Remembering {len(conversation_memory)} conversation exchanges"
def clear_memory():
global conversation_memory
conversation_memory.clear()
return "Memory cleared! Starting fresh."
# Initialize LLM and vector database
llm = initialize_llm()
if not os.path.exists("./chroma_db"):
vector_db = create_vector_db()
else:
vector_db = Chroma(
persist_directory='./chroma_db',
embedding_function=HuggingFaceBgeEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
)
def setup_qa_chain(vector_db, llm):
retriever = vector_db.as_retriever()
prompt_template = """
You are a wise, caring friend and mental health companion. You're not a clinical therapist, but rather a supportive friend who happens to have good insights about mental health and life.
Your personality:
- Warm, genuine, and authentically caring
- Wise but not preachy
- Conversational and natural (like talking to a close friend)
- Sometimes use gentle humor when appropriate
- Share wisdom through stories, metaphors, or personal insights
- Remember previous conversations and reference them naturally
Your conversation style:
- Talk like a real person, not a chatbot or therapist
- Use contractions (I'll, you're, can't, etc.)
- Vary your sentence structure and length
- Sometimes ask questions, sometimes just listen and respond
- Use empathy and emotional intelligence
- Be encouraging but realistic
Guidelines:
✅ Provide mental health support and emotional wellbeing guidance
✅ Offer life advice and personal growth insights
✅ Give relationship guidance and social connection suggestions
✅ Share practical coping strategies and self-care techniques
✅ Teach mindfulness and stress management approaches
✅ Reference our conversation history naturally
❌ Don't sound clinical or robotic
❌ Don't give medical diagnoses or prescriptions
❌ Don't be overly formal or therapeutic-sounding
❌ Don't lecture or give long bullet-point lists
Response approach:
- Start responses naturally without artificial phrases
- Keep responses conversational (2-4 sentences typically)
- Show genuine empathy for the user's situation
- Provide actionable advice when appropriate
- Use metaphors or analogies to explain complex feelings
- Validate emotions before offering solutions
Context from knowledge base: {context}
Current message: {question}
Respond as a caring, wise friend who genuinely wants to help:
"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
return RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True
)
qa_chain = setup_qa_chain(vector_db, llm)
custom_css = """
/* Main container styling */
.gradio-container {
background: linear-gradient(135deg, #4B0082 0%, #9370DB 100%);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
position: relative;
}
.gradio-container::before {
content: "😊 ❤️";
position: absolute;
top: 20%;
left: 10%;
font-size: 100px;
opacity: 0.1;
z-index: -1;
}
.gradio-container::after {
content: "☀️ 🌈";
position: absolute;
bottom: 20%;
right: 10%;
font-size: 100px;
opacity: 0.1;
z-index: -1;
}
/* Header styling */
.main-header {
text-align: center;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
backdrop-filter: blur(10px);
margin-bottom: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* Chat interface styling */
.chat-container {
background: rgba(72, 61, 139, 0.8);
border: none;
border-radius: 20px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
}
/* Button styling */
.custom-button {
background: linear-gradient(45deg, #6a11cb 0%, #2575fc 100%);
border: none;
border-radius: 25px;
padding: 12px 24px;
color: white;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.custom-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
/* Input field styling */
.custom-input {
background: #2F4F4F;
color: white;
border: 2px solid #9370DB;
border-radius: 15px;
padding: 12px;
font-size: 16px;
transition: all 0.3s ease;
}
.custom-input::placeholder {
color: #A9A9A9;
}
.custom-input:focus {
border-color: #4a69bd;
box-shadow: 0 0 10px rgba(74, 105, 189, 0.3);
}
/* Memory indicator styling */
.memory-indicator {
background: #AFEEEE;
color: white;
padding: 10px 15px;
border-radius: 20px;
font-size: 14px;
text-align: center;
margin: 10px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
/* Chat bubble styling */
.user-message {
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%) !important;
color: white !important;
border-radius: 18px 18px 4px 18px !important;
padding: 12px 16px !important;
}
.bot-message {
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%) !important;
color: #2d3748 !important;
border-radius: 18px 18px 18px 4px !important;
padding: 12px 16px !important;
border: 1px solid #e2e8f0 !important;
}
"""
with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
with gr.Row():
gr.HTML("""
<div class="main-header">
<h1 style="color: white; font-size: 3em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
CalmMe
</h1>
<p style="color: rgba(255,255,255,0.9); font-size: 1.2em; margin: 10px 0;">
Your Compassionate Mental Health Companion
</p>
<p style="color: rgba(255,255,255,0.8); font-size: 1em;">
I'm here to listen, support, and remember our journey together
</p>
</div>
""")
memory_status = gr.HTML("""
<div class="memory-indicator">
Conversation Memory: Ready to remember our journey together
</div>
""")
with gr.Row():
with gr.Column(scale=4):
chatbot = gr.Chatbot(
height=500,
show_label=False,
container=True,
bubble_full_width=False,
avatar_images=(None, None),
elem_classes=["user-message", "bot-message"]
)
audio_output = gr.Audio(
label="Voice Response",
autoplay=True,
visible=False,
show_download_button=False
)
with gr.Row():
with gr.Column(scale=3):
msg = gr.Textbox(
label="Share your thoughts...",
placeholder="How are you feeling today? I'm here to listen...",
container=True,
lines=2,
elem_classes="custom-input"
)
with gr.Column(scale=1):
voice_input = gr.Audio(
sources=["microphone"],
type="filepath",
label="Voice Message",
show_download_button=False
)
with gr.Row():
text_submit = gr.Button("Send Message", elem_classes="custom-button", variant="primary")
voice_submit = gr.Button("Send Voice", elem_classes="custom-button", variant="secondary")
clear = gr.Button("New Conversation", elem_classes="custom-button", variant="stop")
memory_clear = gr.Button("Clear Memory", elem_classes="custom-button")
with gr.Row():
gr.HTML("""
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin-top: 20px;">
<h3 style="color: white; text-align: center;">Features</h3>
<div style="display: flex; justify-content: space-around; flex-wrap: wrap;">
<div style="color: rgba(255,255,255,0.9); text-align: center; margin: 10px;">
<div style="font-size: 1.5em; font-weight: bold;">Memory</div>
<div>Remembers 40 conversations</div>
</div>
<div style="color: rgba(255,255,255,0.9); text-align: center; margin: 10px;">
<div style="font-size: 1.5em; font-weight: bold;">Voice & Text</div>
<div>Multiple input methods</div>
</div>
<div style="color: rgba(255,255,255,0.9); text-align: center; margin: 10px;">
<div style="font-size: 1.5em; font-weight: bold;">AI Companion</div>
<div>Empathetic support</div>
</div>
<div style="color: rgba(255,255,255,0.9); text-align: center; margin: 10px;">
<div style="font-size: 1.5em; font-weight: bold;">Crisis Support</div>
<div>Emergency resources</div>
</div>
</div>
</div>
""")
def update_memory_status():
return get_memory_summary()
text_submit.click(
gradio_chat,
[msg, chatbot],
[msg, chatbot, audio_output]
).then(
lambda: update_memory_status(),
outputs=[memory_status]
)
voice_submit.click(
voice_chat,
[voice_input, chatbot],
[chatbot, audio_output]
).then(
lambda: update_memory_status(),
outputs=[memory_status]
)
msg.submit(
gradio_chat,
[msg, chatbot],
[msg, chatbot, audio_output]
).then(
lambda: update_memory_status(),
outputs=[memory_status]
)
clear.click(
lambda: (None, None),
outputs=[chatbot, voice_input],
queue=False
)
memory_clear.click(
clear_memory,
outputs=[memory_status],
queue=False
)
if __name__ == "__main__":
demo.launch(debug=True, share=True)