Skip to main content

Migrating from Microsoft Presidio in 10 Minutes

Microsoft Presidio is a solid open-source PII detection SDK, but it was built as a batch NLP toolkit, not a real-time LLM traffic gateway. Bolting it in front of a streaming chat completion endpoint means loading spaCy/PyTorch models (1GB+ RAM) and paying 50-150ms of blocking latency per request โ€” enough to visibly stall Server-Sent Events streaming.

LLM-Shield-Proxy is a drop-in reverse proxy: point your existing OPENAI_BASE_URL (or equivalent) at it, and it transparently redacts PII/PHI/secrets in both directions โ€” without you touching your Presidio AnalyzerEngine/AnonymizerEngine call sites at all.

This guide gets you from "Presidio in the request path" to "LLM-Shield-Proxy in front of your LLM provider" in about 10 minutes.

Why Teams Migrateโ€‹

Microsoft Presidio (in-process)LLM-Shield-Proxy (sidecar proxy)
Deployment modelPython library called inline in your appReverse proxy / sidecar โ€” zero app-code coupling
Memory footprint1GB+ RAM (spaCy/PyTorch NLP pipeline)<85 MB RAM (compiled regex + Shannon entropy engine)
Per-request latency50-150ms (blocks the request)<6 ยตs Tier-2 entropy scan; full payload redaction in low-single-digit ms
Streaming (SSE) supportNot native โ€” requires buffering the full responseNative sliding-window SSE rehydration, sub-millisecond overhead per chunk
Secret detectionRequires custom recognizersBuilt-in Tier-2 Shannon-entropy scanner for unformatted API keys/hashes/tokens
Audit trailNone built-inWORM SHA-256 hash-chained, Ed25519-signed audit receipts out of the box

Step 1: The 1-Line Python SDK Changeโ€‹

If your code currently looks like this:

from openai import OpenAI

client = OpenAI(
api_key="sk-...",
base_url="https://api.openai.com/v1",
)

Change the base_url to point at your LLM-Shield-Proxy instance. That's the entire code change โ€” no Presidio AnalyzerEngine() / AnonymizerEngine() calls to rip out of your request path, no custom recognizers to port:

from openai import OpenAI

client = OpenAI(
api_key="sk-...",
base_url="http://localhost:8000", # <- was https://api.openai.com/v1
)

Everything else โ€” streaming, tool calls, function calling, retries โ€” behaves exactly as before. The proxy redacts PII on the way out and transparently rehydrates it on the way back in, so your application code never sees masked tokens.

Step 2: Docker Compose Drop-Inโ€‹

If Presidio was running as its own container (or an in-process dependency) in front of your LLM calls, replace it with the LLM-Shield-Proxy sidecar:

services:
# Remove: presidio-analyzer, presidio-anonymizer containers/dependencies

# Add: LLM-Shield-Proxy sidecar
llm-shield-proxy:
image: ninadphalak/llm-shield-proxy:latest
ports:
- "8000:8000"
environment:
- UPSTREAM_BASE_URL=https://api.openai.com
- OPENAI_API_KEY=${OPENAI_API_KEY}
# Optional: enable Tier 3 contextual NER for free-text names/orgs (adds ~45-65MB RAM)
# - ENABLE_TIER3_ONNX_NER=true
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz')\" || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s

Then point your application at http://llm-shield-proxy:8000 instead of the Presidio service.

Step 3: Entity Mappingโ€‹

Presidio's recognizers and LLM-Shield-Proxy's detection cascade cover overlapping ground using different entity names. If you have compliance policies or dashboards keyed on Presidio entity types, use this table to re-map them:

Presidio EntityLLM-Shield-Proxy EntityDetection Tier
PERSONPERSONTier 3 (Contextual NER, ONNX)
EMAIL_ADDRESSEMAILTier 1 (Regex)
PHONE_NUMBERPHONETier 1 (Regex)
US_SSNSSNTier 1 (Regex)
CREDIT_CARDCREDIT_CARDTier 1 (Regex)
IP_ADDRESSIP_ADDRESSTier 1 (Regex)
MEDICAL_LICENSEMRNTier 1 (Regex)
CRYPTO / custom API-key recognizersAWS_API_KEY, GITHUB_PAT, JWT_TOKENTier 1 (Regex)
Custom high-entropy-secret recognizersSECRET_KEYTier 2 (Shannon Entropy)
NRP, LOCATION, ORGANIZATION (via custom recognizers)Contextual NER coverageTier 3 (Contextual NER, opt-in)

Notes:

  • Tier 1 and Tier 2 (regex + Shannon entropy) ship enabled by default in the base pip install llm-shield-proxy package โ€” this is what gives the <85 MB / <6 ยตs numbers below.
  • Tier 3 (ENABLE_TIER3_ONNX_NER=true, pip install "llm-shield-proxy[ner]") adds a quantized ONNX BERT-NER model for conversational entities (names, organizations) that Presidio would normally catch via spaCy โ€” at a fraction of Presidio's memory cost.
  • LLM-Shield-Proxy additionally detects unformatted high-entropy secrets (raw API keys, hashes, tokens) via Shannon entropy analysis โ€” a class of secret Presidio's pattern-based recognizers typically miss unless you hand-write a regex for every key format.

Benchmark Calloutโ€‹

Numbers below are from LLM-Shield-Proxy's own reproducible benchmark suite, re-run automatically on every push via .github/workflows/benchmark.yml so the claims stay independently checkable rather than a one-time marketing snapshot:

MetricMicrosoft Presidio (spaCy/PyTorch)LLM-Shield-Proxy
Resident memory1GB โ€“ 2GB RAM<85 MB RAM
Per-request latency overhead50 โ€“ 150 ms<6 ยตs (Tier-2 entropy scan)
Streaming (SSE) supportRequires full-response bufferingNative sub-millisecond sliding-window rehydration

What You Keep, What You Gainโ€‹

You keep: your existing LLM client code (openai, anthropic, LangChain, LiteLLM โ€” anything that takes a base_url), your provider API keys, your prompts.

You gain: a zero-egress redaction layer with a <85 MB footprint, native SSE streaming support, a WORM SHA-256 hash-chained and Ed25519-signed audit trail for every redaction decision, and NIST OSCAL-formatted compliance evidence you can export in one command (llm-shield-proxy compliance-report --framework=hipaa).

Next Stepsโ€‹