Spaces:
Sleeping
Sleeping
| 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) |