Skip to content

Repository files navigation

💊 MedAI — Medical Q&A Assistant

A production FastAPI backend + static JS frontend that answers medical questions with Gemini 2.5 Flash, plus a drug interaction checker and a PDF/image medical-report analyzer. Backend runs on Render, frontend on Vercel.

For educational purposes only. Not a substitute for professional medical advice.


Architecture

Browser (Vercel: medical-ai-drab.vercel.app)
    │  fetch() calls
    ▼
FastAPI Backend (Render: medical-ai-backend-00qv.onrender.com)
    ├── /auth      → JWT register / login
    ├── /chat
    │     ├── POST /chat/ask         → non-streaming answer (fallback)
    │     ├── POST /chat/feedback    → thumbs up/down logging
    │     └── GET/DELETE /chat/history, /chat/sessions
    ├── /stream
    │     ├── POST /stream/ask         → SSE streaming answer (used by the UI)
    │     └── POST /stream/suggestions → follow-up question suggestions
    ├── /drugs
    │     ├── POST /drugs/check        → multi-drug interaction check (RxNorm + Gemini)
    │     ├── GET  /drugs/search       → drug name autocomplete (RxNorm)
    │     └── GET  /drugs/info/{name}  → single drug label info (OpenFDA)
    ├── /report
    │     ├── POST /report/analyze     → upload a PDF/image lab report, get AI analysis
    │     └── GET  /report/history     → past uploaded reports
    └── /admin
          ├── GET  /admin/metrics, /admin/diseases, /admin/users
          ├── GET  /admin/api-keys/status
          └── POST /admin/users/{id}/action
    │
    ▼
SQLite / Postgres  (users · chat_history · feedback · report_uploads)

Every chat/stream/drug/report request goes through an intent classifier (TF-IDF + Logistic Regression, trained via ml/train_classifiers.py) that tags a question as medical, off_topic, or emergency before it's routed to Gemini. Emergency and off-topic questions get canned safe responses instead of a model call.

Note on retrieval: the indexes/ FAISS files, category_router.py, and PubMedBERT embeddings from the original RAG prototype are still in the repo but are not wired into the live /chat/ask or /stream/ask paths — both call Gemini directly with conversation history, no retrieval step. Re-enabling retrieval would mean adding a call to category_router / FAISS search back into stream.py before the Gemini prompt is built.

Gemini calls rotate across multiple API keys (GEMINI_API_KEY_1/2/3) via services/api_key_manager.py to survive per-key rate limits (TPM/TPD), automatically marking a key as exhausted and falling back to the next.


Tech Stack

Layer Technology
Frontend Vanilla JS + HTML (frontend/index.html, frontend/app.js), Vite dev server
Backend API FastAPI, Uvicorn
Auth JWT (python-jose), bcrypt (passlib)
LLM Google Gemini 2.5 Flash, multi-key rotation
Drug data RxNorm (interactions/autocomplete), OpenFDA (drug labels)
Report analysis PyMuPDF (PDF text extraction) + Gemini
Database SQLite (local) / Postgres (Render), SQLAlchemy ORM
Tests Pytest, FastAPI TestClient
Hosting Render (backend), Vercel (frontend)
CI GitHub Actions

Project Structure

medical-AI/
├── backend/                     ← Render root directory — run from here
│   ├── main.py                  ← FastAPI app entry point, seeds admin user on boot
│   ├── dependencies.py          ← JWT auth dependencies (get_current_user, get_admin_user)
│   ├── requirements.txt
│   ├── routers/
│   │   ├── auth.py              ← POST /auth/register, /auth/login
│   │   ├── chat.py              ← non-streaming chat + history + feedback
│   │   ├── stream.py            ← SSE streaming chat (used by the frontend)
│   │   ├── drugs.py             ← drug interaction checker
│   │   ├── report.py            ← PDF/image lab report analyzer
│   │   └── admin.py             ← analytics dashboard + user/key management
│   ├── services/
│   │   ├── gemini_client.py     ← non-streaming Gemini calls, emergency/off-topic responses
│   │   ├── gemini_streamer.py   ← streaming Gemini calls
│   │   ├── intent_classifier.py ← medical / off_topic / emergency classification
│   │   ├── api_key_manager.py   ← multi-key rotation, rate-limit tracking
│   │   ├── drug_checker.py      ← RxNorm + OpenFDA + Gemini drug interaction logic
│   │   ├── report_analyzer.py   ← PDF/image report analysis
│   │   ├── category_router.py   ← (legacy) disease-category routing for FAISS retrieval
│   │   └── rag_pipeline.py      ← (unused placeholder from the original RAG prototype)
│   ├── models/
│   │   └── schemas.py           ← Pydantic request/response models
│   └── db/
│       ├── database.py          ← SQLAlchemy engine + session (SQLite/Postgres)
│       ├── db_models.py         ← User, ChatMessage, Feedback, ReportUpload tables
│       └── crud.py              ← DB read/write operations
│
├── frontend/
│   ├── index.html               ← single-page app shell, sets the `API` base URL
│   └── app.js                   ← all UI logic + fetch calls to the backend
│
├── ml/                           ← legacy RAG-prototype tooling (index building, classifier training)
│   ├── create_indexes.py
│   ├── train_classifiers.py
│   └── evaluate.py
│
├── tests/
│   ├── conftest.py
│   ├── test_api.py
│   ├── test_intent_classifier.py
│   ├── test_rag_pipeline.py
│   └── test_retrieval.py
│
├── scripts/
│   ├── seed_db.py               ← creates tables + admin user
│   └── download_assets.py
│
├── indexes/                     ← FAISS index files (legacy, git-ignored, unused at runtime)
├── models_saved/                ← trained classifier .pkl files (git-ignored)
├── data/                        ← MedQuAD CSVs (git-ignored)
├── render.yaml                  ← Render Blueprint (rootDir: backend)
├── vite.config.js               ← proxies /auth,/chat,/stream,/drugs,/report,/admin to :8000 in dev
├── Dockerfile.backend / Dockerfile.frontend / docker-compose.yml   ← legacy, not used by the current Render+Vercel deploy
├── .github/workflows/ci.yml
├── requirements.frontend.txt
└── .env.example

Setup & Run (Local)

1. Clone and configure

git clone https://github.com/kashish334/medical-AI.git
cd medical-AI

cp .env.example .env
# Edit .env: add GEMINI_API_KEY_1 (and optionally _2, _3), set SECRET_KEY

2. Install backend dependencies

cd backend
pip install -r requirements.txt
cd ..

3. Seed the database

python scripts/seed_db.py

4. Start the backend

cd backend
uvicorn main:app --reload --port 8000

API docs available at: http://localhost:8000/docs

5. Start the frontend (new terminal, from repo root)

npm install
npm run dev

App available at: http://localhost:5173 — Vite proxies API calls to localhost:8000 (see vite.config.js).

The deployed frontend (frontend/index.html) instead hits the live backend directly via a hardcoded API constant — update that URL if you redeploy the backend elsewhere.


Deployment

  • Backend → Render. render.yaml sets rootDir: backend, so every import in backend/ (from db..., from routers..., from services...) resolves relative to that directory, not the repo root.
  • Frontend → Vercel, serving frontend/index.html + app.js as a static site.
  • CORS is restricted to the origins listed in backend/main.py's ALLOWED_ORIGINS (plus an optional ALLOWED_ORIGIN env var) — add any new frontend domain there.

Run Tests

cd backend
pip install -r requirements.txt pytest pytest-cov
cd ..
pytest tests/ -v --cov=backend --cov-report=term-missing

ci.yml currently installs from requirements.backend.txt at the repo root, which doesn't exist (the real file is backend/requirements.txt) — CI needs that path fixed before it will pass.


Environment Variables

GEMINI_API_KEY_1=...        # primary Gemini key
GEMINI_API_KEY_2=...        # optional — used for rotation on rate limits
GEMINI_API_KEY_3=...        # optional
SECRET_KEY=...              # JWT signing secret
DATABASE_URL=sqlite:///./medical_chatbot.db   # or a Postgres URL on Render
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
ALLOWED_ORIGIN=https://your-frontend-domain.com   # extra CORS origin, beyond the defaults

Log in with the admin credentials to access the Analytics Dashboard (/admin/*).


Dataset

The ml/ and indexes/ folders build on MedQuAD (Medical Question Answering Dataset, sourced from NIH, National Cancer Institute, CDC, NHLBI, and more) from an earlier retrieval-based version of this project. They're kept for reference but aren't part of the current live answer pipeline.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages