| import os |
| import requests |
| import streamlit as st |
| import torch |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification |
|
|
| |
| MURIL_REPO = "yashsha7/mindlens-muril" |
| OPENROUTER_KEY = os.environ.get("OPENAI_API_KEY", "") |
|
|
| st.set_page_config(page_title="MindLens", page_icon="π§ ", layout="wide") |
|
|
| NEG = {"empty","hopeless","worthless","exhausted","lonely","burden","cry", |
| "dark","helpless","numb","tired","alone","lost","overwhelmed","depressed"} |
| POS = {"happy","grateful","excited","love","great","good","joy","proud", |
| "calm","peaceful","hopeful","motivated","energized","positive"} |
|
|
|
|
| |
| @st.cache_resource(show_spinner="Loading MuRIL model (first load only)...") |
| def load_muril(): |
| tok = AutoTokenizer.from_pretrained(MURIL_REPO) |
| model = AutoModelForSequenceClassification.from_pretrained(MURIL_REPO) |
| model.eval() |
| return tok, model |
|
|
|
|
| @st.cache_resource(show_spinner="Loading spaCy NER...") |
| def load_spacy(): |
| import spacy |
| try: |
| return spacy.load("en_core_web_sm") |
| except OSError: |
| from spacy.cli import download |
| download("en_core_web_sm") |
| return spacy.load("en_core_web_sm") |
|
|
|
|
| @st.cache_resource(show_spinner="Building FAISS knowledge base (first load only)...") |
| def load_vectorstore(): |
| from langchain_community.vectorstores import FAISS |
| from langchain_community.embeddings import HuggingFaceEmbeddings |
| from langchain.text_splitter import RecursiveCharacterTextSplitter |
| from langchain.schema import Document |
| from datasets import load_dataset |
|
|
| raw = load_dataset("hugginglearners/reddit-depression-cleaned", split="train") |
| df = raw.to_pandas()[["clean_text"]].dropna().head(500) |
| posts = df["clean_text"].astype(str).tolist() |
|
|
| split_docs = RecursiveCharacterTextSplitter( |
| chunk_size=300, chunk_overlap=30 |
| ).split_documents([Document(page_content=p) for p in posts]) |
|
|
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") |
| return FAISS.from_documents(split_docs, embeddings) |
|
|
|
|
| tokenizer_bert, muril_model = load_muril() |
|
|
|
|
| |
| def predict(text): |
| enc = tokenizer_bert(str(text), return_tensors="pt", truncation=True, |
| max_length=128, padding=True) |
| with torch.no_grad(): |
| probs = torch.softmax(muril_model(**enc).logits, dim=1)[0] |
| pred = torch.argmax(probs).item() |
| label = "Depression" if pred == 1 else "Normal" |
| conf = round(probs[pred].item() * 100, 1) |
| dep_prob = round(probs[1].item() * 100, 1) |
| return label, conf, dep_prob |
|
|
|
|
| def sentiment(text): |
| t = set(text.lower().split()) |
| ns, ps = len(t & NEG), len(t & POS) |
| if ns > ps: |
| return f"Negative({ns})" |
| if ps > ns: |
| return f"Positive({ps})" |
| return "Neutral" |
|
|
|
|
| def detect_lang(text): |
| return "Hindi" if any('\u0900' <= c <= '\u097F' for c in text) else "English" |
|
|
|
|
| def gpt_chat(messages, max_tokens=400): |
| if not OPENROUTER_KEY: |
| return "(No API key set β add OPENAI_API_KEY in Space secrets.)" |
| r = requests.post( |
| "https://openrouter.ai/api/v1/chat/completions", |
| headers={ |
| "Authorization": f"Bearer {OPENROUTER_KEY}", |
| "Content-Type": "application/json", |
| "HTTP-Referer": "https://huggingface.co", |
| "X-Title": "MindLens", |
| }, |
| json={"model": "openai/gpt-4o-mini", "messages": messages, |
| "temperature": 0.7, "max_tokens": max_tokens}, |
| ) |
| try: |
| return r.json()["choices"][0]["message"]["content"] |
| except Exception: |
| return f"(Error calling model: {r.text[:200]})" |
|
|
|
|
| |
| st.sidebar.title("π§ MindLens") |
| st.sidebar.caption("Mental Health NLP Β· v1.0") |
| page = st.sidebar.radio("Navigate", [ |
| "Depression Detector", "Bulk Analyze", "AI Wellness Chatbot", |
| "Agentic AI Pipeline", "RAG Knowledge Chat" |
| ]) |
| st.sidebar.markdown("---") |
| st.sidebar.caption("Models: MuRIL Β· FLAN-T5 Β· BiLSTM") |
|
|
| |
| if page == "Depression Detector": |
| st.title("Depression Detector") |
| st.caption("Paste a social media post in English or Hindi β analyzed instantly.") |
| text = st.text_area("Post text", height=100) |
| if st.button("Analyze", type="primary") and text.strip(): |
| label, conf, dep_prob = predict(text) |
| lang = detect_lang(text) |
| sent = sentiment(text) |
| col1, col2 = st.columns(2) |
| col1.metric("Result", label) |
| col2.metric("Language", lang) |
| st.progress(conf / 100, text=f"Confidence Score: {conf}%") |
| st.write(f"**Sentiment:** {sent}") |
| if label == "Depression": |
| st.warning( |
| "I'm really sorry you're feeling this way. It might help to talk to " |
| "someone you trust, or try journaling your feelings β it can be a " |
| "powerful way to gain some clarity." |
| ) |
|
|
| |
| elif page == "Bulk Analyze": |
| st.title("Bulk Analyze") |
| st.caption("Paste multiple posts (one per line) β analyze all at once. English & Hindi supported.") |
| bulk_text = st.text_area("Posts (one per line)", height=200) |
| if st.button("Analyze All", type="primary") and bulk_text.strip(): |
| lines = [l for l in bulk_text.split("\n") if l.strip()] |
| rows = [] |
| for line in lines: |
| label, conf, _ = predict(line) |
| rows.append({ |
| "Post": line[:60] + ("..." if len(line) > 60 else ""), |
| "Label": label, |
| "Confidence": f"{conf}%", |
| "Sentiment": sentiment(line), |
| "Language": detect_lang(line), |
| }) |
| import pandas as pd |
| res_df = pd.DataFrame(rows) |
| st.dataframe(res_df, use_container_width=True) |
|
|
| dep_count = sum(1 for r in rows if r["Label"] == "Depression") |
| norm_count = len(rows) - dep_count |
| avg_conf = sum(float(r["Confidence"].strip("%")) for r in rows) / len(rows) |
|
|
| c1, c2, c3, c4 = st.columns(4) |
| c1.metric("Posts Analyzed", len(rows)) |
| c2.metric("Depression", dep_count) |
| c3.metric("Normal", norm_count) |
| c4.metric("Avg Confidence", f"{avg_conf:.1f}%") |
|
|
| st.bar_chart(res_df["Label"].value_counts()) |
| st.download_button("Download CSV", res_df.to_csv(index=False), "results.csv") |
|
|
| |
| elif page == "AI Wellness Chatbot": |
| st.title("Wellness Chatbot") |
| st.caption("Bilingual (EN + HI) Β· Depression detection on every message Β· GPT-4o-mini responses") |
|
|
| if "history" not in st.session_state: |
| st.session_state.history = [] |
|
|
| for msg in st.session_state.history: |
| with st.chat_message(msg["role"]): |
| st.write(msg["content"]) |
|
|
| user_msg = st.chat_input("How are you feeling today?") |
| if user_msg: |
| label, conf, _ = predict(user_msg) |
| sent = sentiment(user_msg) |
| lang = detect_lang(user_msg) |
| det = f"[{label} | {conf}% | {sent} | {lang}]" |
|
|
| st.session_state.history.append({"role": "user", "content": user_msg}) |
| with st.chat_message("user"): |
| st.write(user_msg) |
|
|
| sys_prompt = ( |
| "You are a compassionate mental health support assistant. " |
| "If depression detected: validate feelings, suggest one coping strategy. " |
| "If normal: respond warmly. Keep it short and human. Respond in user's language." |
| ) |
| msgs = [{"role": "system", "content": sys_prompt}, |
| {"role": "system", "content": det}] + st.session_state.history |
| reply = gpt_chat(msgs) |
|
|
| st.session_state.history.append({"role": "assistant", "content": reply}) |
| with st.chat_message("assistant"): |
| st.caption(det) |
| st.write(reply) |
|
|
| if st.button("Clear conversation"): |
| st.session_state.history = [] |
| st.rerun() |
|
|
| |
| elif page == "Agentic AI Pipeline": |
| st.title("Agentic AI Pipeline") |
| st.caption("5-step autonomous analysis: Detect β NER β Sentiment β Similar Posts β Clinical Report") |
|
|
| post = st.text_area("Social media post to analyze", height=100) |
| if st.button("Run Pipeline", type="primary") and post.strip(): |
| nlp_spacy = load_spacy() |
| vectorstore = load_vectorstore() |
|
|
| with st.status("Running pipeline...", expanded=True) as status: |
| label, conf, dep_prob = predict(post) |
| st.write(f"**1. Detection:** {label} Β· Confidence: {conf}% Β· Dep prob: {dep_prob}%") |
|
|
| doc = nlp_spacy(post[:400]) |
| ents = [(e.text, e.label_) for e in doc.ents] |
| st.write(f"**2. NER:** {ents if ents else 'No named entities found.'}") |
|
|
| sent = sentiment(post) |
| st.write(f"**3. Sentiment:** {sent}") |
|
|
| similar = vectorstore.similarity_search(post, k=3) |
| st.write("**4. Similar posts retrieved:**") |
| for s in similar: |
| st.caption("- " + s.page_content[:100] + "...") |
|
|
| report = gpt_chat([{ |
| "role": "user", |
| "content": ( |
| f"Clinical NLP report for:\nPost: {post[:200]}\n" |
| f"Condition: {label} ({conf}%)\nSentiment: {sent}\nEntities: {ents}\n" |
| "Write: 1.Summary 2.Risk indicators 3.Linguistic patterns 4.Recommendations" |
| ) |
| }]) |
| st.write("**5. Clinical Report:**") |
| st.write(report) |
| status.update(label="Pipeline complete", state="complete") |
|
|
| c1, c2, c3 = st.columns(3) |
| c1.metric("Condition", label[:4]) |
| c2.metric("Confidence", f"{conf}%") |
| c3.metric("Entities", len(ents)) |
|
|
| |
| elif page == "RAG Knowledge Chat": |
| st.title("RAG Knowledge Chat") |
| st.caption("Ask questions about mental health patterns in the Reddit dataset. FAISS + GPT-4o-mini.") |
|
|
| query = st.text_input("Ask about the dataset...") |
| if st.button("Ask") and query.strip(): |
| vectorstore = load_vectorstore() |
| docs = vectorstore.similarity_search(query, k=4) |
| context = "\n\n".join(d.page_content for d in docs) |
|
|
| answer = gpt_chat([{ |
| "role": "user", |
| "content": f"Context from Reddit posts:\n{context}\n\nQuestion: {query}\nAnswer based on the context above:" |
| }]) |
| st.write(answer) |
| with st.expander(f"Retrieved {len(docs)} chunks Β· FAISS Β· all-MiniLM-L6-v2 embeddings"): |
| for i, d in enumerate(docs, 1): |
| st.caption(f"Source {i}: {d.page_content[:150]}...") |