Medical Information Extraction with LangExtract — turn unstructured clinical notes, discharge summaries, and radiology reports into structured, traceable JSON with precise source grounding.
Clinical NLP is hard: free-text notes are messy, and LLMs often hallucinate or drift from the source. LangExtract addresses this by:
- Precise source grounding — every extracted entity maps to exact character spans in the original text.
- Few-shot prompting — you guide the model with high-quality examples; no fine-tuning required.
- Structured, traceable output — JSON + evidence snippets + optional HTML visualization.
ClinicalExtract wraps LangExtract with a clinical-focused schema (medications, diagnoses, procedures, labs, symptoms, adverse events, demographics) and a Streamlit app. You can run Ollama locally or use Gemini, OpenAI, or Anthropic in the cloud.
| Area | Description |
|---|---|
| Providers | Ollama (local), Gemini, OpenAI GPT-4o / GPT-4o-mini, Anthropic Claude |
| Navigation | Multipage Streamlit app (Extract, Evaluate, History) |
| Input | Paste text, single .txt / .pdf / .docx, batch multi-file upload, or built-in samples |
| Caching | Repeated extractions with the same settings are cached (@st.cache_data) to avoid redundant LLM calls |
| Evidence | Grouped view by medication_group / lab_group plus ungrouped “Other” |
| History | Dedicated History page for past runs with JSON/Excel/FHIR re-export |
| FHIR | Export extracted entities as a FHIR R4 Bundle JSON |
| Evaluation | evaluate.py runs extraction against gold JSON and writes eval_results/eval_results.json |
git clone https://github.com/AdithyaReddyGeeda/Medical-Extraction-Langextract.git
cd Medical-Extraction-Langextract
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Optional: copy .env.example to .env and add API keys
cp .env.example .env
streamlit run app.pyOpen http://localhost:8501. Use the left navigation to switch between Extract, Evaluate, and History. On the Extract page, choose input mode, pick a provider/model in the sidebar, then run Extract (or Extract All for batch mode).
Run the full app in one command (no Python setup needed):
# Build and start
docker compose up --build
# Open in browser
open http://localhost:8501To also run a local Ollama instance alongside the app:
docker compose --profile ollama up --build
# Then pull a model:
docker exec -it ollama ollama pull qwen2.5-coder:32b-instructEnvironment variables (API keys) are loaded from your .env file automatically.
Run unit tests:
python -m pytest -qOr use the Makefile shortcut:
make testSet keys in .env or your shell (see .env.example):
| Variable | Used for |
|---|---|
LANGEXTRACT_API_KEY |
Gemini (LangExtract) |
OPENAI_API_KEY |
OpenAI |
ANTHROPIC_API_KEY |
Anthropic |
Ollama does not require API keys; ensure the server is running (e.g. ollama serve) and models are pulled.
flowchart LR
A[Clinical Text] --> B[LangExtract]
B --> C[Chunking]
C --> D[LLM + Few-shot]
D --> E[Structured JSON]
D --> F[Spans / Evidence]
E --> G[Streamlit UI]
F --> G
F --> H[HTML Viz]
- Input: Raw text or file (discharge summary, progress note, radiology report).
- Chunking: Long documents are split; LangExtract handles this natively.
- Extraction: LLM runs with your prompt + few-shot examples; outputs entities + character spans.
- Output: Structured JSON, tables, optional HTML visualization, and grouped evidence.
# Install Ollama from https://ollama.com
ollama pull qwen2.5-coder:32b-instruct
# or: ollama pull llama3.1:70b
# or: ollama pull gemma2:27b
ollama serve # if not already runningIn the app sidebar, choose Ollama (local) and the model you pulled.
Place a gold label file next to each sample note: for samples/note.txt, add samples/note.json (list of extractions with class and text).
Run the full pipeline (extracts, writes *_pred.json, scores):
python evaluate.py --provider gemini --model gemini-2.5-flashUseful flags:
| Flag | Default | Meaning |
|---|---|---|
--samples |
samples/ |
Directory of .txt files |
--output |
eval_results/ |
Where eval_results.json is written |
--provider |
gemini |
gemini, openai, anthropic, ollama |
--model |
gemini-2.5-flash |
Model id |
--match |
partial |
partial or exact span matching |
--skip-extraction |
off | Score existing *_pred.json only |
The report includes per-file aggregate metrics, overall aggregate, and a per-class precision / recall / F1 breakdown.
You can run evaluation either from the Evaluate page (in-browser) or via CLI (evaluate.py).
After extraction, go to Structured output and use Download FHIR R4 Bundle to export a JSON FHIR Bundle.
You can also re-export FHIR from previous runs on the History page.
.
├── app.py # Streamlit multipage entrypoint
├── evaluate.py # CLI evaluation (runs extractor + metrics)
├── extractor.py # LangExtract wiring, clinical schema, few-shots
├── pages/
│ ├── extract.py # Main extraction UI
│ ├── evaluate.py # In-browser evaluation UI
│ └── history.py # Session history browser
├── utils/
│ ├── visualization.py # HTML viz helpers
│ ├── eval.py # Metrics, load_gold, per-class evaluation
│ ├── export.py # Excel export helper
│ └── fhir.py # FHIR R4 Bundle mapping
├── samples/ # .txt snippets + optional matching .json gold
├── tests/ # Pytest unit tests (core logic)
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── Makefile
├── requirements.txt
├── .env.example
├── README.md
├── UPGRADE.md
├── .gitignore
└── LICENSE
- Not for clinical decision-making — extraction is for structuring and search; always verify with a clinician.
- Negation / temporality — the schema does not fully model “denied”, “history of”, or time expressions everywhere.
- Relation extraction — medication→indication and similar links are partially modeled via attributes; full relation graphs are a stretch goal.
- Languages — examples and prompts are English-only.
If you use LangExtract in research or production, please cite:
- LangExtract: GitHub | PyPI
- Medical extraction research: Goel et al., "LLMs Accelerate Annotation for Medical Information Extraction", ML4H, 2023. arXiv:2312.02296
For dependency bumps, schema improvements, and production options, see UPGRADE.md.