Healthcare-specific LLM guardrails middleware for clinical safety
Project description
Medguard
Healthcare-specific LLM guardrails middleware for clinical safety.
Wraps any LLM (Anthropic, OpenAI, Ollama) with five safety layers before and after each inference call:
| Layer | What it does |
|---|---|
| PHI Detection | Detects and redacts SSN, DOB, phone, email, MRN, and labeled names |
| Clinical Scope | Blocks or warns when queries fall outside medical scope (legal, financial) |
| Drug Safety | Checks drug-drug interactions and contraindications via OpenFDA + RxNorm |
| Hallucination Detection | Flags fake drug names, impossible dosages, and overconfident claims |
| Output Annotation | Inlines [WARNING: ...] markers on flagged response spans |
Install
pip install medguard # core only
pip install "medguard[anthropic]" # + Anthropic LLM
pip install "medguard[anthropic,openai]" # + OpenAI-compatible
pip install "medguard[anthropic,openai,nlp]" # + Presidio PHI engine
Quick start
from medguard import MedGuard
mg = MedGuard()
# Check text without calling an LLM
result = mg.check("Patient SSN: 123-45-6789 is taking warfarin and aspirin.")
print(result.phi_result.phi_detected) # True
print(result.phi_result.processed) # "Patient SSN: [REDACTED] is taking ..."
print(result.drug_result.highest_severity) # InteractionSeverity.HIGH
# Full guardrailed LLM chat
import asyncio
response = asyncio.run(mg.achat("What are side effects of metformin?"))
Run the API server
medguard serve # default port 8080
medguard serve --port 9090 # custom port
python -m medguard serve # alternative
Endpoints:
GET /v1/health dependency status (RxNorm, OpenFDA)
POST /v1/chat guardrailed LLM chat (stream: true supported)
POST /v1/check/phi standalone PHI detection / redaction
POST /v1/check/drug-interactions standalone drug interaction check
GET /docs Swagger UI
Example requests
# PHI redaction
curl -X POST http://localhost:8080/v1/check/phi \
-H "Content-Type: application/json" \
-d '{"text": "Patient John Smith, SSN: 123-45-6789", "mode": "redact"}'
# Drug interaction check
curl -X POST http://localhost:8080/v1/check/drug-interactions \
-H "Content-Type: application/json" \
-d '{"drugs": ["warfarin", "aspirin"]}'
# Guardrailed chat
curl -X POST http://localhost:8080/v1/chat \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "What is the max dose of ibuprofen?"}]}'
Docker
ANTHROPIC_API_KEY=sk-... docker compose -f docker/docker-compose.yml up
All guardrails are configurable via environment variables:
MEDGUARD_GUARDRAILS__PHI_DETECTION__MODE=block # redact | flag | block
MEDGUARD_GUARDRAILS__DRUG_SAFETY__SEVERITY_THRESHOLD=high
MEDGUARD_GUARDRAILS__SCOPE_ENFORCEMENT__ACTION=block # warn | block
MEDGUARD_LLM__PROVIDER=openai
MEDGUARD_LLM__MODEL=gpt-4o
Configuration
Config is loaded from ~/.medguard/config.json (auto-created on first run).
{
"guardrails": {
"phi_detection": { "enabled": true, "mode": "redact", "engine": "regex" },
"drug_safety": { "enabled": true, "severity_threshold": "moderate" },
"scope_enforcement": { "enabled": true, "action": "warn" },
"hallucination_detection":{ "enabled": true, "confidence_threshold": 0.7 }
},
"llm": {
"provider": "anthropic",
"model": "claude-haiku-4-5-20251001",
"api_key_env": "ANTHROPIC_API_KEY"
},
"api": { "host": "0.0.0.0", "port": 8080 }
}
PHI engine options:
"regex"— zero-dependency, ships with the library (default)"presidio"— Microsoft Presidio + spaCy, higher recall (pip install "medguard[nlp]")
Architecture
User input
│
▼
[PHI Detection] ──── redact / block
│
[Scope Enforcement] ── warn / block
│
[Drug Safety Check] ── warn / block ◄── OpenFDA API + RxNorm + static table
│
▼
LLM
│
▼
[Hallucination Detection] ── flag / block ◄── RxNorm + SNOMED bundle
│
▼
Annotated response
Each guardrail runs in an isolated try/except — an API timeout in drug safety never blocks the full request.
NeMo Guardrails integration
from nemoguardrails import RailsConfig, LLMRails
from medguard import MedGuard
from medguard.integrations.nemo import MEDICAL_COLANG_CONFIG
mg = MedGuard()
config = RailsConfig.from_content(colang_content=MEDICAL_COLANG_CONFIG)
rails = LLMRails(config)
for name, action in mg.as_nemo_actions().items():
rails.register_action(action, name=name)
Extending medguard
Every guardrail implements a Protocol interface. Register custom engines via entry points:
# In your package's pyproject.toml
[project.entry-points."medguard.phi_engines"]
my_engine = "my_package:MyPHIEngine"
[project.entry-points."medguard.interaction_sources"]
drugbank = "medguard_drugbank:DrugBankClient"
Then select it in config: "phi_detection": {"engine": "my_engine"}.
Contribution targets:
- New drug interaction sources (
InteractionSourceProtocol) - New PHI engines (
PHIEngineProtocol) - Curated drug interaction data (
medguard/knowledge/data/drug_interactions.csv) - Clinical Colang flows (
medguard/integrations/nemo.py) - Language-specific medical fact checkers
Development
git clone https://github.com/sarvanithin/Medguard
cd Medguard
pip install -e ".[anthropic,dev]"
pytest tests/ -m "not integration" # unit tests (no network)
pytest tests/ -m integration # hits real OpenFDA / RxNorm APIs
Data sources
| Source | Used for |
|---|---|
| RxNorm API | Drug name normalization |
| OpenFDA Drug Labels | Interaction + contraindication text |
| OpenFDA Adverse Events | Adverse event counts |
| Bundled SNOMED-CT subset | Medical terminology validation |
| Curated static table | Highest-risk drug pairs (offline fallback) |
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file medguard_llm-0.1.0.tar.gz.
File metadata
- Download URL: medguard_llm-0.1.0.tar.gz
- Upload date:
- Size: 56.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48afc132423871ab391fe10dd9f84e45f3632c7162ea1c5c3821491e253cd198
|
|
| MD5 |
3e010212295924c9e924c96ac3bcb42a
|
|
| BLAKE2b-256 |
da0b4cf6a297f2f66abdc5bc6ced2b8bcff91b9871c398f7a17beba2baf7da6d
|
Provenance
The following attestation bundles were made for medguard_llm-0.1.0.tar.gz:
Publisher:
publish.yml on sarvanithin/Medguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
medguard_llm-0.1.0.tar.gz -
Subject digest:
48afc132423871ab391fe10dd9f84e45f3632c7162ea1c5c3821491e253cd198 - Sigstore transparency entry: 1108481432
- Sigstore integration time:
-
Permalink:
sarvanithin/Medguard@77fb03137af15c1c53c6264eec1bd9d6c9ad3bf2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sarvanithin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@77fb03137af15c1c53c6264eec1bd9d6c9ad3bf2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file medguard_llm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: medguard_llm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 53.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71524b8bf0b81b293ececdcf8c53e43c2a331d9d74179d7de67924b738a63885
|
|
| MD5 |
7d541e0eff119bf2531666b15b4d244d
|
|
| BLAKE2b-256 |
6d18519f5452ee879097f19909b395e120ba1bcb0d0208ead7267943142633dd
|
Provenance
The following attestation bundles were made for medguard_llm-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on sarvanithin/Medguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
medguard_llm-0.1.0-py3-none-any.whl -
Subject digest:
71524b8bf0b81b293ececdcf8c53e43c2a331d9d74179d7de67924b738a63885 - Sigstore transparency entry: 1108481444
- Sigstore integration time:
-
Permalink:
sarvanithin/Medguard@77fb03137af15c1c53c6264eec1bd9d6c9ad3bf2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sarvanithin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@77fb03137af15c1c53c6264eec1bd9d6c9ad3bf2 -
Trigger Event:
release
-
Statement type: