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