Skip to content

Persona Quickstarts

The Quick Start walks through installation in one linear path. This page splits that path into three short, copy-paste tracks so you land on the right first command for your role, then hands you off to the deeper guide.

Pick the track that matches how you'll use OpenMed:

Persona Goal Jump to
Researcher De-identify a corpus and measure PHI leakage Researcher
App developer Call de-identification from a service or agent App developer
Data engineer Redact files, directories, and datasets at scale Data engineer

To generate the matching config, offline defaults, README, and starter script instead of assembling them by hand, use the openmed init project scaffold.

Not a medical device

OpenMed is a research and engineering toolkit for text de-identification and named-entity recognition. It is not a medical device and does not provide clinical decisions, diagnosis, or treatment advice. De-identification is probabilistic: review residual-risk output and validate against your own compliance requirements before releasing data. Every example below uses synthetic, non-PHI text.

All tracks assume a working install. The minimal setup is:

curl -LsSf https://astral.sh/uv/install.sh | sh   # install uv (skip if present)
uv venv --python 3.11
source .venv/bin/activate
uv pip install "openmed[hf]"                      # core + Hugging Face models
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uv venv --python 3.11
.venv\Scripts\Activate.ps1
uv pip install "openmed[hf]"                      # core + Hugging Face models

The first model-backed call may download model artifacts. Clinical text is still processed locally; after warming the cache, use local-only offline mode to block outbound connections during inference.


Researcher

Goal: run entity extraction and de-identification over clinical notes, then quantify how much PHI leaked through.

1. Extract entities from a synthetic note

extract_pii() returns a PredictionResult whose entities carry the detected text, canonical label, confidence, and character offsets.

from openmed import extract_pii

note = "Patient Casey Example (MRN 00123) called from 555-0100 on 01/15/2020."

result = extract_pii(note)
for entity in result.entities:
    print(f"{entity.label:>10}  {entity.text!r}  [{entity.start}:{entity.end}]")

Reach for analyze_text() when you want the clinical NER models (diseases, medications, findings) instead of PII labels:

from openmed import analyze_text

clinical = analyze_text(
    "Metastatic breast cancer treated with paclitaxel and trastuzumab.",
    model_name="disease_detection_superclinical",
)
print(clinical.entities[0])

2. De-identify the note

from openmed import deidentify

redacted = deidentify(note, method="mask")
print(redacted.deidentified_text)
# Detected spans are replaced with labels such as [first_name] and [phone_number].

3. Measure PHI leakage

For a de-identification study, compare your gold PHI spans against the spans the model predicted. compute_leakage_rate() reports the character-weighted fraction of gold PHI not covered by a same-label prediction — the lower the better.

from openmed.eval import compute_leakage_rate

gold = [
    {"start": 8, "end": 21, "label": "NAME"},    # "Casey Example"
    {"start": 46, "end": 54, "label": "PHONE"},   # "555-0100"
]
predicted = [
    {"start": 8, "end": 21, "label": "NAME"},     # phone missed on purpose
]

metrics = compute_leakage_rate(gold, predicted, source_text=note)
print(f"Overall PHI leakage: {metrics.overall:.1%}")
print(f"Missed phone leakage: {metrics.by_label['PHONE']:.1%}")

Next steps: the full method matrix (mask, remove, replace, hash, shift_dates), reversible mappings, and longitudinal patient-keyed date shifting live in PII Anonymization. For the metric bundle and release gates, see Eval Harness & Metrics.


App developer

Goal: de-identify text from an application or agent without embedding the models in your own request path.

Option A — call the library directly

If your service is already Python, one call is enough:

from openmed import deidentify

redacted = deidentify("Contact Jordan Sample at jordan.sample@example.org.")
print(redacted.deidentified_text)
# Contact [first_name] [last_name] at [email].

Option B — run the REST service

Install the service extra and start the API:

uv pip install "openmed[hf,service]"
uvicorn openmed.service.app:app --host 127.0.0.1 --port 8080

Call POST /pii/deidentify with any HTTP client:

curl -s http://127.0.0.1:8080/pii/deidentify \
  -H "Content-Type: application/json" \
  -d '{"text": "Contact Jordan Sample at jordan.sample@example.org.", "method": "mask"}'

Or use the typed Python client bundled with the service extra:

from openmed.service.client import OpenMedClient

with OpenMedClient("http://127.0.0.1:8080", timeout=300.0) as client:
    redacted = client.deidentify(
        "Contact Jordan Sample at jordan.sample@example.org.",
        method="mask",
    )
    print(redacted["deidentified_text"])

Option C — expose OpenMed to an AI agent over MCP

Install the MCP extra and start the Model Context Protocol server. It exposes an openmed_deidentify tool (plus openmed_extract_pii and openmed_analyze_text) over stdio, so agent frameworks can redact PHI as a tool call:

uv pip install "openmed[hf,mcp]"
python -m openmed.mcp.server                       # stdio transport (default)

For a local agent runtime that connects over HTTP, keep the server on loopback:

python -m openmed.mcp.server --transport streamable-http --host 127.0.0.1 --port 8081

These service examples deliberately bind to loopback. Do not expose them to an untrusted network without an authenticated, TLS-terminating boundary.

Next steps: health checks, model warm-pool controls, request batching, the privacy gateway, and the full endpoint reference are in REST Service.


Data engineer

Goal: de-identify many files or dataset rows with progress reporting and PHI-free aggregate summaries — no per-record boilerplate.

1. Process a directory of notes

BatchProcessor reuses one warmed model across the batch and keeps going when a single record fails. It returns de-identified text in memory and does not overwrite the source files:

from pathlib import Path
from tempfile import TemporaryDirectory

from openmed import BatchProcessor

processor = BatchProcessor(
    operation="deidentify",
    model_name="pii_detection",
    method="mask",
    batch_size=16,
    confidence_threshold=0.7,
    continue_on_error=True,
)

with TemporaryDirectory() as temp_dir:
    notes_dir = Path(temp_dir)
    (notes_dir / "note-1.txt").write_text(
        "Patient Casey Example called from 555-0100.", encoding="utf-8"
    )
    (notes_dir / "note-2.txt").write_text(
        "Email Jordan Sample at jordan.sample@example.org.", encoding="utf-8"
    )

    result = processor.process_directory(
        notes_dir, pattern="*.txt", recursive=True
    )

    redacted_texts = [
        item.result.deidentified_text for item in result.get_successful_results()
    ]
    print(f"Processed {len(redacted_texts)}/{result.total_items} files")
    print(f"Failures: {result.failed_items}")

2. Redact specific columns of a dataset

For CSV or JSONL input, redact_dataset() de-identifies only the free-text columns you name and returns an aggregate, PHI-free audit summary. Parquet uses the same API after installing the columnar extra with uv pip install "openmed[hf,columnar]":

from openmed import redact_dataset

result = redact_dataset(
    "notes.csv",
    text_columns=["note", "comment"],
    output_path="notes.redacted.csv",
    policy="strict_no_leak",
)

# Aggregate counts only — total spans, per-label counts, residual-leakage estimate.
print(result.summary.to_dict())

The source dataset is never overwritten: output_path receives the redacted rows, while the summary remains in memory unless you explicitly persist it.

The same path is available from the console entry point:

openmed redact-dataset notes.csv \
  --text-columns note,comment \
  --policy strict_no_leak \
  --output notes.redacted.csv

Next steps: streaming iteration, progress callbacks, batch PII extraction, and result export are covered in Batch Processing.