Mindlens / src /streamlit_app.py
yashsha7's picture
Update src/streamlit_app.py
2296930 verified
Raw
History Blame Contribute Delete
11.9 kB
import os
import requests
import streamlit as st
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# ───────────────────────── CONFIG ─────────────────────────
MURIL_REPO = "yashsha7/mindlens-muril" # your pushed model
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"}
# ───────────────────────── CACHED LOADERS ─────────────────────────
@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()
# ───────────────────────── CORE FUNCTIONS ─────────────────────────
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]})"
# ───────────────────────── SIDEBAR NAV ─────────────────────────
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")
# ───────────────────────── PAGE: DETECTOR ─────────────────────────
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."
)
# ───────────────────────── PAGE: BULK ANALYZE ─────────────────────────
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")
# ───────────────────────── PAGE: CHATBOT ─────────────────────────
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()
# ───────────────────────── PAGE: AGENTIC PIPELINE ─────────────────────────
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))
# ───────────────────────── PAGE: RAG CHAT ─────────────────────────
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]}...")