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
Metadata
Release files for medguard-llm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| medguard_llm-0.1.0.tar.gz | 56.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| medguard_llm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.5 kB
Release files / medguard_llm-0.1.0.tar.gz
| Download URL | medguard_llm-0.1.0.tar.gz |
|---|---|
| Size | 56.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
48afc132423871ab391fe10dd9f84e45f3632c7162ea1c5c3821491e253cd198
|
|
BLAKE2b-256 checksum How to use checksums |
da0b4cf6a297f2f66abdc5bc6ced2b8bcff91b9871c398f7a17beba2baf7da6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 15, 2026.
Transparency logRelease files / medguard_llm-0.1.0-py3-none-any.whl
| Download URL | medguard_llm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 53.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
71524b8bf0b81b293ececdcf8c53e43c2a331d9d74179d7de67924b738a63885
|
|
BLAKE2b-256 checksum How to use checksums |
6d18519f5452ee879097f19909b395e120ba1bcb0d0208ead7267943142633dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 15, 2026.
Transparency log