Skip to main content

Entropy-gated hybrid NER for privacy-compliant PII detection

Project description

NerGuard

Entropy-Gated Hybrid NER for Privacy-Compliant PII Detection

Python PyTorch HuggingFace Ollama uv MIT License

🤗 Model on HuggingFace  ·  📦 PyPI: nerguard  ·  Open in Colab

NerGuard is a pre-ingestion privacy layer for RAG pipelines: it detects and redacts PII from text before documents are indexed, keeping sensitive data out of vector databases and LLM context windows. It runs a multilingual mDeBERTa-v3 base model for fast, high-confidence predictions, then selectively routes only uncertain spans to an LLM (OpenAI or local Ollama) for correction, typically less than 3% of tokens. A three-stage regex layer handles structured PII (credit cards, SSNs, IBANs) with deterministic validation. The result is a hybrid pipeline that matches or exceeds larger models on PII recall while remaining GDPR-auditable: every prediction carries its source, confidence score, and routing decision.

NerGuard demo

Install

pip install nerguard

The NER model (~300 MB) downloads automatically from HuggingFace on first use.

Quick start

from nerguard import Redactor

ng = Redactor(
    model_path=None,        # str  — local path or HuggingFace Hub ID for the NER model
    llm_routing=False,      # bool — enable entropy-gated LLM routing
    llm_source="openai",    # str  — "openai" or "ollama"
    llm_model="gpt-4o",     # str  — LLM model name
    typed=True,             # bool — typed placeholders ([NAME]) vs generic ([PII])
)
result = ng.redact("Hi, I'm John Smith. Email: john@acme.com")

print(result.text)
# "Hi, I'm [NAME] [NAME]. Email: [EMAIL]"

print(result.mapping)
# {"NAME_0": "John", "NAME_1": "Smith", "EMAIL_0": "john@acme.com"}

print(result.entities)
# [{"label": "GIVENNAME", "text": "John", "confidence": 0.998, "source": "base"}, ...]

Batch:

texts = [
    "Hi, I'm John Smith. Email: john@acme.com",
    "Call me at +1-800-555-0199 or find me on LinkedIn.",
]

results = [ng.redact(t) for t in texts]  # model stays cached across calls

for r in results:
    print(r.text)
# "Hi, I'm [NAME] [NAME]. Email: [EMAIL]"
# "Call me at [PHONE] or find me on LinkedIn."

# Collect all mappings
all_mappings = {k: v for r in results for k, v in r.mapping.items()}
# {"NAME_0": "John", "NAME_1": "Smith", "EMAIL_0": "john@acme.com", "PHONE_0": "+1-800-555-0199"}

LLM routing

Improves recall on ambiguous spans (phone numbers, IDs, dates) by routing uncertain predictions to an LLM.

# Cloud (requires OPENAI_API_KEY)
ng = Redactor(llm_routing=True, llm_source="openai", llm_model="gpt-4o")

# Local — no data leaves the machine (requires Ollama)
ng = Redactor(llm_routing=True, llm_source="ollama", llm_model="qwen2.5:7b")

CLI / interactive REPL

nerguard                                         # interactive REPL
nerguard --file report.txt                       # redact a file
nerguard --llm --backend ollama --model qwen2.5:7b  # with local LLM
nerguard --format rag                            # RAG-optimised output
REPL command Description
/mode [human|rag|json|generic] Switch output format
/llm Toggle LLM routing
/backend [openai|ollama] Switch LLM backend
/model NAME Set LLM model
/file PATH Redact a file
/help Show all commands

Constructor parameters

Redactor(
    model_path=None,        # str  — local path or HuggingFace Hub ID for the NER model
    llm_routing=False,      # bool — enable entropy-gated LLM routing
    llm_source="openai",    # str  — "openai" or "ollama"
    llm_model="gpt-4o",     # str  — LLM model name
    typed=True,             # bool — typed placeholders ([NAME]) vs generic ([PII])
)
Parameter Type Default Description
model_path str HuggingFace auto-download Local filesystem path or HuggingFace Hub ID for the NER model. Omit to download exdsgift/NerGuard-0.3B automatically on first use.
llm_routing bool False Enable entropy-gated LLM routing. When True, spans where the base model is uncertain are re-evaluated by the LLM. Improves recall on ambiguous tokens (phone numbers, dates, IDs) at the cost of extra latency.
llm_source str "openai" LLM backend to use when llm_routing=True. "openai" calls the OpenAI API (requires OPENAI_API_KEY); "ollama" runs inference locally via Ollama (no data leaves the machine).
llm_model str "gpt-4o" Model name passed to the selected LLM backend. Examples: "gpt-4o", "gpt-4o-mini" for OpenAI; "qwen2.5:7b", "llama3.1:8b" for Ollama. Only used when llm_routing=True.
typed bool True Controls placeholder style. True → typed placeholders such as [NAME], [EMAIL], [PHONE] (preserves semantic context for downstream LLMs). False → every entity becomes [PII] regardless of type (maximum compression, no semantic signal).

RedactResult fields

ng.redact(text) returns a RedactResult dataclass with three fields:

Field Type Description
text str Redacted text with placeholders replacing PII spans.
entities list[dict] One dict per detected entity, with keys: label (entity type), text (original value), start/end (char offsets), confidence (0–1), source ("base" or "llm").
mapping dict[str, str] Maps each placeholder instance to its original value, keyed as "<LABEL>_<index>" (e.g. "NAME_0", "EMAIL_0"). Useful for auditing or selective de-redaction.
result = ng.redact("Hi, I'm John Smith. Email: john@acme.com")

result.text
# "Hi, I'm [NAME] [NAME]. Email: [EMAIL]"

result.mapping
# {"NAME_0": "John", "NAME_1": "Smith", "EMAIL_0": "john@acme.com"}

result.entities
# [
#   {"label": "GIVENNAME", "text": "John",          "start": 8,  "end": 12, "confidence": 0.998, "source": "base"},
#   {"label": "SURNAME",   "text": "Smith",         "start": 13, "end": 18, "confidence": 0.995, "source": "base"},
#   {"label": "EMAIL",     "text": "john@acme.com", "start": 27, "end": 40, "confidence": 0.991, "source": "base"},
# ]

Detected entity types

GIVENNAME · SURNAME · EMAIL · TELEPHONENUM · SOCIALNUM · CREDITCARDNUMBER · IBAN · PASSPORTNUM · IDCARDNUM · DRIVERLICENSENUM · TAXNUM · STREET · BUILDINGNUM · CITY · ZIPCODE · DATE · TIME · AGE · SEX · TITLE

Links

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nerguard-1.0.2.tar.gz (2.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nerguard-1.0.2-py3-none-any.whl (241.5 kB view details)

Uploaded Python 3

File details

Details for the file nerguard-1.0.2.tar.gz.

File metadata

  • Download URL: nerguard-1.0.2.tar.gz
  • Upload date:
  • Size: 2.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"20.04","id":"focal","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nerguard-1.0.2.tar.gz
Algorithm Hash digest
SHA256 58cd6275d0d3b54051d369b32b9685b83fbc41e34f243d89de6cba150c996e73
MD5 400652c0cb93cadc4268b33c75df345c
BLAKE2b-256 8292d2d3f3c60181ea8b038782f3b39ee7ec1415b7e07cca03e2ec7a2eb35a2d

See more details on using hashes here.

File details

Details for the file nerguard-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: nerguard-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 241.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"20.04","id":"focal","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nerguard-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 113063134d3dbb8190b5f35684b6c2cc9ef4218eedb157c75c77aca034ef01eb
MD5 c50b8d7c60278a8131fbe2e73444395e
BLAKE2b-256 a9605cb0c66b19a1a1d10c63093745e429b03d60423ce3ea9ba16cd681b690d4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page