HiteshiAglawe0505/safespace-ai
๐ฟ SafeSpace AI 2.0
AI-powered Medical & Mental Health Assistant Multi-agent ยท Multimodal ยท WhatsApp + Web ยท Production Deployed
     
What is SafeSpace AI?
SafeSpace is a production-deployed, multi-channel AI health assistant that provides:
- ๐ฉบ Medical guidance โ symptom analysis, image reading (blood reports, X-rays, ECG, skin conditions), home care advice, red-flag detection
- ๐ง Mental health support โ CBT-based therapy conversations, emotional support, crisis detection
- ๐จ Emergency escalation โ auto-triggers a real Twilio voice call when suicidal ideation or crisis is detected
- ๐ Therapist finder โ real clinic listings via Google Maps Places API through an MCP server
- ๐ผ๏ธ Medical image analysis โ upload a photo of a rash, wound, blood report, X-ray, or ECG
- ๐๏ธ Voice message support โ speak your symptoms on WhatsApp or web; Whisper transcribes them
Available on WhatsApp (via Twilio) and a Streamlit web app โ both powered by the same backend deployed on Hugging Face Spaces with Nginx routing everything through a single public port.
๐๏ธ Architecture
User (WhatsApp / Web Browser)
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ NGINX (port 7860 โ public) โ
โ โ
โ /whatsapp/* โ FastAPI :8000 โ
โ /mcp/* โ MCP Server:8001 โ NEW โ
โ /* โ Streamlit :8501 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโดโโโโโโโโโโโโโโโ
โ โ
โผ โผ
FastAPI Streamlit
(WhatsApp webhook) (Web UI)
โ โ
โโโโโโโโโโฌโโโโโโโโโโโ
โ same engine for both channels
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CORE ENGINE โ
โ โ
โ Multimodal Handler โ
โ (image โ Llama 4 Scout Vision) โ
โ (audio โ Groq Whisper large-v3) โ
โ โ โ
โ Intent Classifier โ
โ (Groq structured output + Pydantic) โ
โ โ MEDICAL / THERAPY / MIXED / UNKNOWN โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ CrewAI Crew (sequential) โ โ
โ โ โ โ
โ โ SafetyAgent โ always runs FIRST โ โ
โ โ โ โ โ
โ โ DoctorAgent or TherapistAgent โ โ
โ โ (context from SafetyAgent shared) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ SQLite Session Memory โโ LangSmith Tracing โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
ChatResponse โ WhatsApp (Twilio) / Streamlit UI
MCP Server (port 8001 โ internal)
TherapistAgent โ maps_tool.py โ POST /search
โ therapist_directory.py โ Google Maps๐ค Agentic Design โ The 3-Agent Crew
Every message passes through all agents in sequence, regardless of channel:
Why sequential? SafetyAgent runs first on every single message โ no medical or therapy response is ever sent before checking if it is a crisis. This is an architectural guarantee, not just a prompt instruction.
Why context sharing? Both DoctorAgent and TherapistAgent receive the SafetyAgent output via context=[safety_task]. If safety flagged a concern, the second agent's response accounts for it automatically.
๐ MCP Server โ Therapist Directory (Live on HF)
The therapist finder is exposed as an MCP (Model Context Protocol) server โ a standard by Anthropic for AI tool communication. The MCP server runs inside the same Docker container and is publicly reachable via Nginx routing.
TherapistAgent calls find_nearby_therapists tool
โ
maps_tool.py detects THERAPIST_MCP_URL is set
โ HTTP POST to http://127.0.0.1:8001/search
MCP Server (mcp_server/therapist_directory.py)
โ exposes 3 tools via /tools discovery endpoint
search_by_location() โ POST /search
get_therapist_details() โ POST /details
list_specialties() โ GET /specialties
โ
Google Maps Places API
โ
Real clinic listings with names, addresses, ratings, open/closed statusMCP endpoints (publicly accessible):
https://hiteshiaglawe0505-safespace-ai.hf.space/mcp/healthhttps://hiteshiaglawe0505-safespace-ai.hf.space/mcp/toolshttps://hiteshiaglawe0505-safespace-ai.hf.space/mcp/docs
Why MCP? The therapist search logic is completely decoupled from the agent. Any future agent, external app, or partner service can call the same MCP server. The data source (Google Maps today, Practo tomorrow) is swappable without touching a single line of agent code.
Fallback design: If the MCP server is temporarily unavailable (e.g., restarting), maps_tool.py automatically falls back to calling Google Maps directly. Users always receive results.
๐ LLMOps โ Observability with LangSmith
Every request is traced in LangSmith at smith.langchain.com under project safespace-ai.
What is logged per request (Traces tab):
Tool calls (Runs tab โ filter run_type=tool):
find_nearby_therapistsโ location, output clinic list, latencyemergency_callโ crisis reason, Twilio SID, latency
Dataset: Every request logs intent + confidence to the safespace_intent_classification dataset for future fine-tuning.
Note on LLM call tracing: CrewAI 1.14.4 uses LiteLLM directly (bypassing LangChain wrappers) and resets LiteLLM callbacks after each run โ blocking third-party callback nesting. Agent-level LLM calls are observable at the trace level via latency but are not individually nested under the parent run. This is a known CrewAI 1.14.4 + LangSmith version compatibility constraint.
๐ ๏ธ Tech Stack
๐ Quick Start (Local)
# 1. Clone
git clone https://github.com/HiteshiAglawe0505/safespace-ai.git
cd safespace-ai
# 2. Install
pip install -r requirements.txt
# 3. Configure
cp .env.example .env
# Required: GROQ_API_KEY, GOOGLE_MAPS_API_KEY
# Optional: LANGSMITH_API_KEY, TWILIO_* (for WhatsApp), THERAPIST_MCP_URL
# 4. Run Web UI only
streamlit run interfaces/streamlit_ui/app.py
# 5. Run WhatsApp backend (separate terminal)
python app.py
# Expose with: ngrok http 8000
# Set Twilio webhook: https://your-ngrok-url/whatsapp/webhook
# 6. Run MCP server (optional, separate terminal)
python mcp_server/therapist_directory.py
# Add to .env: THERAPIST_MCP_URL=http://localhost:8001๐ Deployment (Hugging Face Spaces)
pip install huggingface_hub
python fix_hf_upload.py --username HiteshiAglawe0505 --token hf_xxxxxAdd secrets in Space Settings โ Secrets:
THERAPIST_MCP_URL is not a secret โ it is hardcoded as http://127.0.0.1:8001 inside hf_app.py since the MCP server runs inside the same container.
Live: https://huggingface.co/spaces/HiteshiAglawe0505/safespace-ai
๐ Project Structure
safespace-ai/
โโโ app.py # Local dev: FastAPI entry point only
โโโ hf_app.py # HF Spaces: starts Nginx + FastAPI + MCP + Streamlit
โโโ Dockerfile
โโโ requirements.txt # crewai==1.14.4 pinned (cache_breakpoint compatibility)
โโโ .env.example
โ
โโโ core/
โ โโโ config.py # pydantic-settings: all env vars validated at startup
โ โโโ schemas.py # ChatRequest, ChatResponse, Intent, MessageType
โ โโโ engine.py # handle_request(): multimodal routing + LangSmith tracing
โ
โโโ agents/
โ โโโ crew.py # CrewAI crew: agents, tasks, location detection, kickoff
โ โโโ intent_classifier.py # Groq JSON mode + Pydantic โ Intent enum
โ โโโ safety.py # SafetyAgent: crisis detection + emergency call
โ โโโ doctor.py # DoctorAgent: medical guidance, 80/20 response structure
โ โโโ therapist.py # TherapistAgent: CBT support + therapist search
โ
โโโ tools/
โ โโโ maps_tool.py # FindTherapistsTool โ MCP server โ Google Maps
โ โโโ emergency_tool.py # EmergencyCallTool โ Twilio voice API
โ
โโโ mcp_server/
โ โโโ therapist_directory.py # MCP server on :8001 โ 3 tools over Google Maps Places API
โ
โโโ multimodal/
โ โโโ vision.py # describe_image() โ Groq Llama 4 Scout
โ โโโ speech.py # transcribe_audio() โ Groq Whisper large-v3
โ
โโโ memory/
โ โโโ store.py # SQLite: save/load session, format last 10 msgs for prompt
โ
โโโ interfaces/
โ โโโ whatsapp/
โ โ โโโ webhook.py # POST /whatsapp/webhook โ receives Twilio form data
โ โ โโโ sender.py # send_whatsapp_message() โ Twilio Messages API
โ โโโ streamlit_ui/
โ โโโ app.py # Chat UI: text + image upload + st.audio_input() mic
โ
โโโ observability/
โโโ tracer.py # LangSmith: parent runs, tool @traceable, intent dataset๐ Key Technical Decisions
1. Why CrewAI sequential over LangChain agents? CrewAI's Process.sequential guarantees SafetyAgent always runs first โ architecturally impossible to skip crisis detection. With a single LangChain agent, the router might send medical messages directly to DoctorAgent without a safety check.
2. Why Pydantic structured output for intent? Groq JSON mode + Pydantic IntentResult guarantees a valid Intent enum with confidence and reasoning. No string parsing, no hallucinated values, immediate ValidationError at the boundary rather than silent failures inside agents.
3. Why SQLite over Redis/Postgres? Session memory needs last 10 messages per user. SQLite is zero-infrastructure, async-capable via aiosqlite, and sufficient for prototype scale. The trade-off is ephemeral data on HF (wiped on container restart). Postgres is the production upgrade path with minimal code change.
4. Why Nginx on HF Spaces? HF Docker Spaces expose exactly one port (7860). Nginx routes /whatsapp/* to FastAPI, /mcp/* to the MCP server, and /* including WebSocket to Streamlit โ all in the same container. A Python httpx proxy blocks WebSocket upgrade headers (produces 403); Nginx handles them natively.
5. Why MCP for the therapist directory? Decouples data source from agent code. TherapistAgent calls the MCP server via HTTP โ it doesn't know or care whether data comes from Google Maps, Practo, or a hospital database. The backend is swappable without touching agent code.
6. Why is `crewai==1.14.4` pinned? CrewAI 1.14.5+ adds a cache_breakpoint property to system messages. Groq does not support this property and rejects every request with a 400 error. Version 1.14.4 is the last version confirmed working with Groq's API.
7. Why is `THERAPIST_MCP_URL` hardcoded as `127.0.0.1:8001`? The MCP server runs inside the same Docker container as the main app. Localhost communication is faster (no network hop), requires no HTTPS certificate, and needs no separate deployment. The public /mcp/* Nginx route exists for external callers; internal tool calls go direct.
๐ฎ Future Scope & Known Limitations
Current Limitations (Prototype vs Production)
Roadmap โ Production Upgrades
1. Persistent Database โ PostgreSQL / Supabase Replace SQLite with a hosted PostgreSQL database (Railway, Supabase, Neon). Session memory survives container restarts. Users retain full conversation history indefinitely. Code change is minimal โ aiosqlite and asyncpg have nearly identical interfaces.
2. User Authentication โ Login System Add a lightweight login so web users get a persistent, consistent user_id tied to their account rather than a new UUID per browser tab. Options: Google OAuth via streamlit-google-auth, or a simple email + magic link system. WhatsApp already has natural identity via phone number.
3. RAG over Medical Knowledge Base Add a vector database (Qdrant, Pinecone) loaded with verified medical sources โ DSM-5 criteria, drug interaction databases, symptom-to-condition mappings. Agents would retrieve relevant documents before responding, grounding answers in verified data rather than purely Llama's training data.
4. Doctor Search for MEDICAL Intent The MCP server already accepts a specialty parameter. Add a FindDoctorsTool in maps_tool.py and attach it to DoctorAgent. Patients asking about specific medical conditions could receive listings for relevant specialists (cardiologist, dermatologist, neurologist) without any MCP server changes.
5. Upgrade CrewAI โ Native LangSmith LLM Tracing Once Groq resolves the cache_breakpoint compatibility issue (or a newer CrewAI version fixes it), upgrading to CrewAI 1.15+ would restore full parent-child LLM call nesting in LangSmith. The LLM Calls and Cost & Tokens monitoring tabs would then populate automatically.
6. Fine-Tuning Pipeline LangSmith is already collecting every conversation as the safespace_intent_classification dataset. Once sufficient data accumulates, fine-tune a smaller model (Llama 3.1 8B) specifically on health-domain conversations. A fine-tuned 8B model could outperform the general 70B at a fraction of the inference cost.
7. Multi-Language Support Groq Whisper large-v3 supports 50+ languages including Hindi, Marathi, Bengali, Tamil, and Telugu. Add a language detection step in engine.py and inject a language instruction into agent task descriptions. The infrastructure already supports it โ just needs the detection + routing logic.
8. WhatsApp Business API โ Full Production Migrate from Twilio Sandbox to a WhatsApp Business Account. Removes the requirement for users to join a sandbox. Enables proactive messaging, message templates, and higher throughput limits.
9. Horizontal Scaling Replace SQLite with PostgreSQL (item 1), add a message queue (Redis + Celery or AWS SQS) so the webhook returns 200 OK immediately and processes LLM responses asynchronously, then deploy multiple FastAPI/Streamlit instances behind a load balancer. The adapter pattern means both channels use the same brain with no refactoring needed.
10. Twilio Webhook Signature Validation Add X-Twilio-Signature header verification in webhook.py using Twilio's HMAC-SHA1 algorithm. Prevents spoofed webhook requests from external actors. One-line change using twilio.request_validator.RequestValidator.
11. User Feedback Loop โ Model Improvement Add thumbs up / thumbs down buttons in the Streamlit UI that call langsmith_client.create_feedback(run_id, score). Over time, builds a labeled dataset of good and bad responses. Enables prompt optimization and identifies which intents or message types produce weak responses.
12. Voice Output โ Text-to-Speech Responses For WhatsApp voice users, convert SafeSpace responses to audio using a TTS API (ElevenLabs, Google TTS) and send back as voice notes via Twilio's media messaging. Creates a fully voice-native experience for accessibility.
๐ธ Demo โ What It Can Do
๐ฉโ๐ป Author
Hiteshi Aglawe Aspiring GenAI / ML Engineer GitHub ยท HuggingFace
โ ๏ธ Disclaimer
SafeSpace AI is a prototype research assistant โ not a substitute for professional medical or mental health care. Always consult a qualified doctor or licensed therapist for medical decisions. The emergency call feature is a demonstration of crisis escalation architecture and should not be relied upon as a sole safety mechanism.
