Spaces:
Running
Running
File size: 9,274 Bytes
7d0cedc b0ee326 7d0cedc e20732f 7d0cedc ca376b7 7d0cedc b0ee326 7d0cedc 04f7846 b0ee326 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 04f7846 71a58b0 b0ee326 71a58b0 04f7846 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 04f7846 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 04f7846 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 04f7846 71a58b0 b0ee326 71a58b0 b0ee326 71a58b0 b0ee326 04f7846 71a58b0 b0ee326 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 71a58b0 04f7846 1649dde | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | ---
title: ClinIQ — Clinical Document Assistant
emoji: 🩺
colorFrom: blue
colorTo: green
sdk: gradio
sdk_version: "6.11.0"
app_file: app.py
python_version: "3.11"
pinned: false
license: apache-2.0
tags:
- medical
- rag
- qwen
- privacy
- langgraph
- llama-cpp
- hackathon
- build-small-2026
- tiny-titan
---
# ClinIQ — Privacy-First Clinical Document Assistant
ClinIQ is an AI-powered clinical document assistant built for small medical clinics. Upload patient records — discharge summaries, intake notes, consult reports, scanned documents — and instantly get answers, structured extractions, and proactive drug safety alerts.
**Model:** Qwen2.5-3B-Instruct (Q4_K_M) · **Inference:** llama.cpp on Modal A10G GPU · **Framework:** Gradio gr.Server + LangGraph
---
## The Problem
Small clinics handle dozens of patients daily. Nurses upload a discharge summary from the hospital, an intake note from last week, and a specialist consult from yesterday — all as separate files. Nobody has time to cross-reference all three. A medication prescribed in one document may conflict with an allergy documented in another. These conflicts get missed.
ClinIQ solves this by reading all documents together and flagging dangerous combinations automatically — before any question is asked.
---
## What ClinIQ Does
### 1. Proactive Drug Safety Check
The moment documents are uploaded, ClinIQ automatically scans every medication and allergy across all files and flags dangerous combinations. No question needed.
Example: a discharge summary prescribes Aspirin 81mg. An intake note documents an Aspirin allergy causing bronchospasm. These are two separate files from two different visits. ClinIQ catches the conflict instantly:
```
DANGER Aspirin allergy contraindication
Patient prescribed Aspirin 81mg but has documented bronchospasm reaction
Recommendation: Switch to Clopidogrel (noted in cardiology consult)
```
### 2. Clinical Q&A
Ask any question in plain English. ClinIQ retrieves relevant chunks from all uploaded documents and generates a precise, grounded answer in ~2 seconds.
- *"Who is the attending physician?"* → direct fact lookup
- *"What are the follow-up instructions?"* → extracts from discharge summary
- *"Is it safe to give this patient aspirin?"* → reasons across multiple documents
- *"What alternative was recommended instead of aspirin?"* → retrieves from consult note
### 3. Structured Extraction
Ask for a list and get a formatted table — not a paragraph of text.
- *"List all medications"* → table with name, dose, frequency, route
- *"What are all the allergies?"* → table with substance, reaction, severity
- *"List all diagnoses"* → primary and secondary conditions
- *"What are the vital signs?"* → structured vitals object
### 4. Cross-Document Reasoning
ClinIQ retrieves context from all uploaded documents simultaneously. A question about drug interactions can pull the medication list from the discharge summary, the allergy list from the intake note, and the cardiology recommendation from the consult — combining all three into a single coherent answer.
### 5. OCR Support
Supports scanned PDFs and image files (JPG, PNG, TIFF). Tesseract OCR extracts text from scanned documents automatically before indexing.
---
## Architecture
### Document Ingestion Pipeline
```
Upload (PDF / TXT / JPG / PNG / TIFF / scanned PDF)
↓
Text Extraction
├── Native PDF → pypdf (fast, text-selectable)
├── Scanned PDF → pdf2image + tesseract OCR
├── Image → tesseract OCR (upscaled to 1000px for accuracy)
└── TXT / MD → direct read
↓
Chunking — 400 character chunks with 80 character overlap
↓
Dual Indexing
├── BM25 index (rank_bm25) — keyword matching
└── Dense index (sentence-transformers/all-MiniLM-L6-v2) — semantic matching
```
### Query Pipeline — LangGraph 5-Node Agent
```
User Question
↓
[1] CLASSIFY
Rule-based classification — no LLM call
Detects query type: simple / structured / complex / comparison
↓
[2] DECOMPOSE
For complex and comparison queries only
Qwen breaks the question into 2-3 focused sub-queries
↓
[3] RETRIEVE
Runs each sub-query through hybrid retrieval:
├── BM25 score (keyword overlap)
├── Dense score (semantic similarity via cosine)
└── Score fusion → top-6 unique chunks across all documents
↓
[4] GENERATE
Qwen2.5-3B-Instruct synthesizes an answer from retrieved chunks
Structured queries → returns JSON parsed into a table
Free-text queries → returns a concise clinical answer
↓
[5] REFLECT
Heuristic grounding check — verifies answer overlaps with context
Flags potential hallucinations without an extra LLM call
↓
Streaming response → custom HTML/JS frontend (gr.Server)
```
### Drug Safety Pipeline (runs in parallel with Q&A)
```
All uploaded documents
↓
Step 1 — Regex Extraction (no LLM, instantaneous)
Scans for 20+ medication section header variants:
MEDICATIONS, Current Medications, Active Meds, Home Meds, Rx, Drug List...
Scans for 15+ allergy section header variants:
ALLERGIES, Drug Allergies, Known Allergies, Sensitivities, Adverse Reactions...
Also handles inline format: "Allergies: Penicillin, Sulfa"
Deduplicates across all documents
↓
Step 2 — LLM Safety Reasoning (Qwen2.5-3B)
Feeds clean extracted lists to the model
Asks: "are there dangerous drug-allergy or drug-drug interactions?"
Returns structured JSON: alerts with DANGER / WARNING / INFO severity
↓
Colour-coded alert panel in the UI
```
### Inference Stack
```
HF Space (CPU) Modal Cloud (GPU)
───────────────── ──────────────────────────────
gr.Server (Gradio 6.11) nvidia/cuda:12.4.0-devel image
LangGraph agent llama.cpp built with CUDA
Hybrid retriever Qwen2.5-3B-Instruct Q4_K_M GGUF
Safety checker llama-server on A10G
│ │
└──── HTTP POST ────────────────────┘
(httpx, timeout=300s)
payload: {prompt, n_predict, temperature: 0.0}
```
---
## Tech Stack
| Component | Technology |
|-----------|-----------|
| Frontend | Custom HTML/CSS/JS via `gr.Server` (Gradio 6.11) |
| Agent framework | LangGraph (StateGraph, 5 nodes) |
| Retrieval | BM25 (rank_bm25) + Dense (sentence-transformers) |
| Vector index | FAISS (faiss-cpu) |
| LLM | Qwen2.5-3B-Instruct via llama.cpp Q4_K_M |
| Inference server | llama-server (llama.cpp) on Modal A10G |
| OCR | tesseract-ocr + pdf2image + pytesseract |
| PDF parsing | pypdf |
| Trace sharing | HuggingFace Hub API (huggingface_hub) |
---
## Why a 3B Model Works Here
Clinical document extraction is a **retrieval + pattern matching** problem, not a general reasoning problem. The hard work is done by the hybrid retriever (finds the right chunks) and the two-step safety checker (regex extracts reliably, LLM only reasons on clean lists). The model's job is to synthesize well-retrieved context into a clean answer.
Qwen2.5-3B-Instruct handles this well:
- Instruction-tuned for structured output (JSON tables)
- Fast — ~2 seconds on A10G at Q4_K_M quantization
- Efficient — ~1.8GB on disk, ~$0.001 per query
- Accurate enough for fact retrieval from clinical text
---
## Model
[bartowski/Qwen2.5-3B-Instruct-GGUF](https://huggingface.co/bartowski/Qwen2.5-3B-Instruct-GGUF) — Q4_K_M quantization — Apache 2.0 license
Base model: [Qwen/Qwen2.5-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-3B-Instruct)
---
## Dataset
Demo documents are synthetic clinical notes styled after MTSamples — a discharge summary, an intake note, and a cardiology consult for two fictional patients. They contain an intentional cross-document drug-allergy conflict (Aspirin prescribed in one, Aspirin allergy in another) to demonstrate the safety checker.
Live agent traces from queries are published at [karthikmulugu08/cliniq-traces](https://huggingface.co/datasets/karthikmulugu08/cliniq-traces).
---
## Running Locally
```bash
pip install -r requirements.txt
python app.py
# visit http://localhost:7860
```
## Deploying to Modal + HF Space
```bash
# 1. Deploy the GPU inference server
pip install modal
modal setup
modal deploy modal_inference.py
# Copy the printed endpoint URL
# 2. Set Space secrets
# MODAL_ENDPOINT → URL from modal deploy
# HF_TOKEN → your HF write token
# HF_DATASET_REPO → karthikmulugu08/cliniq-traces
```
---
## Submission Links
- **Space:** https://huggingface.co/spaces/build-small-hackathon/cliniq
- **Demo Video:** https://www.loom.com/share/d72ef56097194dd08fdaa734ff64d90f
- **Social Post:** https://www.linkedin.com/posts/karthikmulugu_healthcareai-gradio-huggingface-share-7469807577778819072-dQx6/
- **Field Notes:** https://huggingface.co/blog/build-small-hackathon/cliniq-on-device-pharmacist |