Skip to content
Security
Skill

/bridging-presidio-and-spacy

Combine OpenMed clinical NLP with Microsoft Presidio, spaCy, or LangChain through OpenMed's built-in interop adapter registry (openmed.interop). Covers the lazy adapter registry (available_adapters, get_adapter, adapter_spec), the presidio/spacy/langchain pip extras, and the

BOOST
From plugin
openmed
5.7k74 skills
Install
$ npx -y skills add maziyarpanahi/openmed --skill bridging-presidio-and-spacy --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/bridging-presidio-and-spacy

Context preview

The summary Claude sees to decide when to auto-load this skill.

Combine OpenMed clinical NLP with Microsoft Presidio, spaCy, or LangChain through OpenMed's built-in interop adapter registry (openmed.interop). Covers the lazy adapter registry (available_adapters, get_adapter, adapter_spec), the presidio/spacy/langchain pip extras, and the

SKILL.md

bridging-presidio-and-spacy.SKILL.md
name: bridging-presidio-and-spacy
description: "Combine OpenMed clinical NLP with Microsoft Presidio, spaCy, or LangChain through OpenMed's built-in interop adapter registry (openmed.interop). Covers the lazy adapter registry (available_adapters, get_adapter, adapter_spec), the presidio/spacy/langchain pip extras, and the verified callables — Presidio to_canonical/from_canonical/merge_with_openmed, the spaCy openmed_deid pipeline factory, and the LangChain create_redaction_runnable. Use when the user wants to add Presidio recognizers, embed OpenMed PII detection in a spaCy pipeline, or use OpenMed de-identification as a LangChain runnable. Pairs adjacent to the OpenMed PII skills."
license: Apache-2.0
metadata:
  project: OpenMed
  category: fhir-interop
  pairs: adjacent
  version: "1.0"

Bridging Presidio, spaCy & LangChain

OpenMed interoperates with the dominant PII/NLP ecosystems through a single, **lazy** adapter registry: `openmed.interop`. Adapters live behind explicit imports, so importing `openmed` never drags in Presidio, spaCy, or LangChain — each is an optional extra you install only when you need that bridge.

When to use

Reach for a bridge when:

  • you already run **Microsoft Presidio** and want OpenMed's clinical PII recall

on top (or to feed OpenMed spans back into Presidio's anonymizer);

  • you have a **spaCy** pipeline and want OpenMed PII spans on the `Doc`;
  • you build **LangChain** chains and want to redact PHI *before* text reaches an

LLM (the on-device guardrail in front of a cloud model);

  • you need OpenMed's de-identification reachable from an existing framework

instead of rewriting the pipeline around `openmed.deidentify`.

The lazy adapter registry (verified)

import openmed.interop as interop

interop.available_adapters()
# ('cda', 'hl7v2', 'langchain', 'presidio', 'spacy')

spec = interop.adapter_spec("presidio")
# AdapterSpec(name='presidio', module='openmed.interop.presidio',
#             extra='presidio', description='Presidio RecognizerResult adapter')

mod = interop.get_adapter("presidio")        # imports openmed.interop.presidio
# Attribute access also works lazily:
openmed.interop.presidio                      # same module, imported on first touch

`available_adapters()` and `adapter_spec()` never import the adapter module, so they are safe to call for discovery even without the extra installed. `get_adapter(name)` (and attribute access) triggers the import — and the adapter's own optional dependency.

Install only the extra you need:

pip install "openmed[presidio]"     # Presidio RecognizerResult adapter
pip install "openmed[spacy]"        # spaCy openmed_deid component
pip install "openmed[langchain]"    # LangChain redaction runnable
# cda and hl7v2 adapters ship in core (no extra) — see their own skills

Presidio bridge (verified callables)

Module `openmed.interop.presidio` converts between Presidio `RecognizerResult`s and OpenMed canonical `PIIEntity`s, and merges both detectors through OpenMed's semantic-unit merger.

from openmed.interop.presidio import (
    to_canonical,        # RecognizerResult(s) -> [PIIEntity]
    from_canonical,      # [PIIEntity] -> [RecognizerResult]  (needs presidio extra)
    merge_with_openmed,  # combine OpenMed + Presidio spans, resolve overlaps
    PresidioAdapterConfig,
)
import openmed

text = "Dr. Smith called patient at 617-555-0123 on 2024-03-02."

# Presidio gives you RecognizerResults; OpenMed gives PIIEntities.
openmed_spans = openmed.extract_pii(text).entities
presidio_results = analyzer.analyze(text=text, language="en")   # your Presidio analyzer

merged = merge_with_openmed(
    openmed_spans, presidio_results, text=text,
    config=PresidioAdapterConfig(preserve_presidio_labels=True),
)
# -> de-duplicated [PIIEntity]; overlaps resolved by score, length, OpenMed-origin

Why merge instead of union: `merge_with_openmed` runs both detectors' spans through `merge_entities_with_semantic_units`, so overlapping/adjacent detections collapse into one correct span (e.g. `PHONE` from Presidio vs a partial OpenMed hit) rather than producing double redactions. Label mapping is built in (Presidio `PHONE_NUMBER` ↔ OpenMed `PHONE`, `US_SSN` ↔ `SSN`, etc.).

To push OpenMed spans into Presidio's **anonymizer**, convert back:

results = from_canonical(openmed_spans)        # [RecognizerResult]
anonymized = anonymizer.anonymize(text=text, analyzer_results=results)

spaCy bridge (verified factory)

Module `openmed.interop.spacy_component` registers a spaCy pipeline factory named **`openmed_deid`**. Add it to a pipeline and OpenMed PII spans land on the `Doc`.

import spacy
import openmed.interop.spacy_component   # registers the @Language.factory

nlp = spacy.blank("en")
nlp.add_pipe("openmed_deid", config={
    "confidence_threshold": 0.5,
    "lang": "en",
    "target": "openmed_pii",     # doc.spans key
    "merge_ents": False,         # set True to also write doc.ents
    "alignment_mode": "expand",  # char->token alignment: strict|contract|expand
})

doc = nlp("Patient John Doe, MRN 12345, seen today.")
for span in doc.spans["openmed_pii"]:
    print(span.label_, span.text)
# raw char-offset spans also available on doc._.openmed_pii

`merge_ents=True` writes the spans into `doc.ents`, resolving overlaps with spaCy's `filter_spans`. Use `OpenMedDeidComponent` / `OpenMedDeidConfig` directly if you construct the component outside `add_pipe`.

LangChain bridge (verified runnable)

Module `openmed.interop.langchain` exposes a `Runnable`-shaped redactor you drop *in front of* an LLM step so PHI never leaves the device.

from openmed.interop.langchain import (
    create_redaction_runnable, LangChainRedactionConfig,
)

redactor = create_redaction_runnable(
    config=LangChainRedactionConfig(method="mask", policy="hipaa_safe_harbor"),
    input_key="text",        # redact this key in a dict payload (optional)
Read more
Ships withopenmed

Local-first healthcare AI: clinical NER and HIPAA PII de-identification on hardware you control. 2,200+ medical models, 35 model-backed PII languages, and Python, MLX, Android and browser runtimes. Apache-2.0 SDK.

Get the whole plugin

Other skills on openmed.