openmed README

The project's README from github.com/maziyarpanahi/openmed. As one markdown file: README.md.

OpenMed README banner with the cat mascot, lowercase wordmark, Open Cross, and the text Open-source healthcare AI, 340M+ downloads, and 10M+ installs

Your Data. Your Model. Your Hardware.

Turn clinical text into structured, de-identified insight on hardware you control.
OpenMed's core local runtime performs extraction and de-identification after required model artifacts are available. Model downloads, remote-provider adapters, telemetry-enabled paths, and user-configured integrations may use a network; review each model and dataset's terms.

PyPI package · Python 3.10+ · Model catalog · Research paper · Apache-2.0 SDK source

OpenMedKit · Apple Silicon / MLX · Android / ONNX Runtime Mobile · Browser / Transformers.js · Documentation

2,266 manifest entries  ·  33 model-backed PII languages  ·  Apache-2.0 SDK

English · 简体中文 · Español · Français · Deutsch · Italiano · Português · Nederlands · العربية · हिन्दी · తెలుగు · 日本語 · Türkçe · فارسی · Kiswahili


See it in action

This iPhone example uses OpenMed's local runtime after the required model artifacts are available:

OpenMed Scan on iPhone · on-device PII de-identification and clinical extraction via OpenMedKit
On iPhone via OpenMedKit: scan a clinical note, de-identify it, and extract clinical signals with Apple MLX processing locally in this configuration.

OpenMed redacting PII from a clinical discharge document in real time
Real-time PII de-identification: in this configured local workflow, the Nemotron Privacy Filter redacts names, addresses, IDs, and billing data from a synthetic clinical discharge packet. (All values shown are synthetic.)

30-second example

from openmed import analyze_text

result = analyze_text(
    "Patient started on imatinib for chronic myeloid leukemia.",
    model_name="disease_detection_superclinical",
)

for entity in result.entities:
    print(f"{entity.label:<12} {entity.text:<28} {entity.confidence:.2f}")
# DISEASE      chronic myeloid leukemia     0.98
# DRUG         imatinib                     0.95

A clinical NER model using the local runtime after its required artifacts are available.


Building with an agent?

Start with the consumer agent-usage guide, or load the curated llms.txt documentation index. For callable local interfaces, use the MCP server, the typed tool registry, or the command-line interface. Ready-made cross-tool procedures live in the repository skills catalog.


Why OpenMed?

Deployment consideration OpenMed SDK boundary
Core runtime Processes locally after required artifacts are available
Optional network paths Downloads, remote adapters, telemetry-enabled paths, and user integrations may use a network
Validation Deployment owners validate model and dataset terms, privacy behavior, and clinical fitness
Interfaces Python, Swift, Android, browser, and service surfaces where supported

On-device on Apple: Swift, MLX & iOS

On supported Apple hardware, OpenMed can use MLX and OpenMedKit for local processing after required artifacts are available. Model acquisition and any user-configured remote integrations remain separate network boundaries.

// Add OpenMedKit to your app
dependencies: [
    .package(url: "https://github.com/maziyarpanahi/openmed.git", from: "2.3.0"),
]

Expected result: Swift Package Manager resolves OpenMedKit and makes import OpenMedKit available to your app target.

Guides: MLX backend · OpenMedKit (Swift) · CoreML export

MLX vs CPU latency on Apple Silicon: 24 to 33 times faster
MLX on Apple Silicon: 24–33× faster than CPU PyTorch for the Privacy Filter: median latency per inference step, lower is better.

On-device on Android — Kotlin & ONNX Runtime Mobile

OpenMedKit also ships as a native Android/Kotlin library for local document intake, OCR handoff, PII redaction, and token-classification inference through ONNX Runtime Mobile. Mobile model repositories include stable tensor names, dynamic sequence axes, tokenizer files, labels, and Android-ready fp32, fp16, INT8, and optional .ort outputs.

Add the scoped JitPack repository in settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://jitpack.io")
            content { includeGroup("com.github.maziyarpanahi") }
        }
    }
}

Then use the immutable OpenMed v2.3.0 release:

dependencies {
    implementation("com.github.maziyarpanahi:openmed:v2.3.0")
}

See the Android installation guide for local builds and publishing details.

val model = OpenMedKit.fromDirectory(modelDir)
val entities = model.analyzeText("Patient Alice Nguyen was seen in cardiology.")

Guides: Android ONNX export · Android span parity · OpenMedKit Android

The same ONNX model on Python CPU

from openmed import OnnxModel

model = OnnxModel.from_pretrained(
    "OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-onnx-android"
)
entities = model("Patient Alice Nguyen was seen in cardiology.")

The same ONNX model in the browser

npm install openmed @huggingface/transformers
import { loadOnnxModel } from "openmed";

const model = await loadOnnxModel(
  "OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-onnx-android",
);
const entities = await model("Patient Alice Nguyen was seen in cardiology.");

How it works

flowchart LR
    A["Clinical text"] --> B["OpenMed<br/>(local-first)"]
    B --> C["Medical entities"]
    B --> D["PII detected"]
    B --> E["De-identified text"]
    style B fill:#0D6E6E,stroke:#0A5656,stroke-width:2px,color:#ffffff
    style C fill:#D6EBEB,stroke:#0D6E6E,color:#0E1116
    style D fill:#F7DCD8,stroke:#C5453A,color:#0E1116
    style E fill:#F5E27A,stroke:#A9A088,color:#0E1116

Rendered result: a local clinical-text pipeline that returns medical entities, PII findings, and de-identified text without sending data to a cloud API.


Agent Skills — build with OpenMed from your coding agent

The skills/ catalog ships portable Agent Skills for on-device de-identification, clinical NER, FHIR export, evaluation, and the healthcare workflows around them. The same SKILL.md folders work in Claude Code, OpenAI Codex, OpenCode, and compatible agents; the installer uses each client's skills directory plus the cross-client ~/.agents/skills convention.

git clone https://github.com/maziyarpanahi/openmed && cd openmed
./install-skills.sh          # installs for Claude Code, Codex, OpenCode, and ~/.agents/skills

Then ask your agent for the pipeline using synthetic placeholders:

Build a local OpenMed pipeline that de-identifies a synthetic discharge note and extracts medication entities.

It loads the right skills and produces an on-device pipeline (openmed.deidentify(...) → openmed.analyze_text(...)). After the one-time model download, run that pipeline locally on real notes; do not paste real PHI into a cloud-hosted agent prompt. See the skills catalog and 30-second example.


Quick start

# Core + Hugging Face runtime (Linux, macOS, Windows; CPU or CUDA)
pip install --upgrade "openmed[hf]"

# Add the REST service
pip install --upgrade "openmed[hf,service]"

# Apple Silicon acceleration (MLX)
pip install --upgrade "openmed[mlx]"

Expected result:

Successfully installed openmed-...

Python API

from openmed import analyze_text

result = analyze_text(
  "Patient received 75mg "
  "clopidogrel for NSTEMI.",
  model_name=
  "pharma_detection_superclinical",
)
print([(e.label, e.text) for e in result.entities])

Example output:

[('DRUG', 'clopidogrel'), ('CONDITION', 'NSTEMI')]

REST service

uvicorn openmed.service.app:app \
  --host 0.0.0.0 --port 8080

Example output:

INFO:     Uvicorn running on http://0.0.0.0:8080
GET /health -> 200 OK

GET /health POST /analyze POST /pii/extract POST /pii/deidentify

Batch

from openmed import BatchProcessor

p = BatchProcessor(
  model_name=
  "disease_detection_superclinical",
  group_entities=True,
)
results = p.process_texts([...])
print(len(results), sum(len(r.entities) for r in results))
print([(e.label, e.text) for e in results[0].entities[:1]])

Example output:

3 7
[('DISEASE', 'leukemia')]

Browser / WebGPU

Package ONNX token-classification exports for in-browser inference through Transformers.js:

python -m openmed.onnx.convert \
  --model dslim/bert-base-NER \
  --output dist/example-onnx \
  --include-transformersjs

Example output:

Exported Transformers.js bundle to dist/example-onnx
import { pipeline } from "@huggingface/transformers";

const detector = await pipeline(
  "token-classification",
  "/models/openmed-pii/transformersjs",
  { device: "webgpu" },
);
const entities = await detector("Patient Casey Example called 212-555-0198.");
console.log(entities.slice(0, 2));

Example output:

[
  { entity: "NAME", word: "Casey Example", score: 0.99 },
  { entity: "PHONE", word: "212-555-0198", score: 0.98 },
]

Transformers.js export guide

Offline / air-gapped? Point model_name (or model_id) at a local directory and OpenMed loads it without contacting the Hugging Face Hub:

from openmed import OpenMedConfig, analyze_text

result = analyze_text(
    "Patient presents with chronic myeloid leukemia and Type 2 diabetes.",
    model_id="./models/OpenMed-NER-DiseaseDetect-SuperClinical-434M",
    config=OpenMedConfig(device="cpu"),
)
for entity in result.entities:
    print(f"{entity.label:<12} {entity.text:<28} {entity.confidence:.2f}")

Example output:

DISEASE      chronic myeloid leukemia     0.98
DISEASE      Type 2 diabetes              0.96

Because model_id points to a local directory, this example does not contact the Hugging Face Hub or any external model provider.


Models

A curated registry of specialized medical NER models; browse the full catalog.

Model Specialization Entity types Size
disease_detection_superclinical Disease & conditions DISEASE, CONDITION, DIAGNOSIS 434M
pharma_detection_superclinical Drugs & medications DRUG, MEDICATION, TREATMENT 434M
pii_superclinical_large PII & de-identification NAME, DATE, SSN, PHONE, EMAIL, ADDRESS 434M
anatomy_detection_electramed Anatomy & body parts ANATOMY, ORGAN, BODY_PART 109M
gene_detection_genecorpus Genes & proteins GENE, PROTEIN 109M

Privacy: PII detection & de-identification

from openmed import extract_pii, deidentify

text = "Patient: John Doe, DOB: 01/15/1970, SSN: 123-45-6789"

# Extract PII with smart merging (prevents tokenization fragmentation)
result = extract_pii(text, model_name="pii_superclinical_large", use_smart_merging=True)
print([(e.label, e.text) for e in result.entities])

# De-identify with the method you need
print(deidentify(text, method="mask").deidentified_text)
print(deidentify(text, method="replace").deidentified_text)
print(deidentify(text, method="hash").deidentified_text)
print(deidentify(text, method="shift_dates", date_shift_days=180).deidentified_text)

Example output:

[('NAME', 'John Doe'), ('DATE', '01/15/1970'), ('SSN', '123-45-6789')]
Patient: [NAME], DOB: [DATE], SSN: [SSN]
Patient: Emily Chen, DOB: 03/22/1985, SSN: 456-78-9012
Patient: 6b8f...c4a1, DOB: 48b1...91de, SSN: 3f13...e912
Patient: John Doe, DOB: 07/14/1970, SSN: 123-45-6789
Batch PII processing throughput: up to 3.3x on CPU and 2.2x on MLX
Batch processing: up to 3.3× higher throughput on CPU and 2.2× on MLX vs. one document at a time.

Complete PII notebook · Smart merging · Anonymization quickstart

Privacy Filter family: three model families on the OpenAI Privacy Filter architecture

Same model code (gpt-oss-style sparse-MoE transformer with local attention, sink tokens, RoPE+YaRN, tiktoken o200k_base), different training data. All route through the same extract_pii() / deidentify() API; only model_name= changes. openai/privacy-filter is a Hugging Face model identifier for local weights; using it here does not call the OpenAI API.

Variant PyTorch (CPU + CUDA) MLX (Apple Silicon) MLX 8-bit
OpenAI Privacy Filter openai/privacy-filter OpenMed/privacy-filter-mlx …-mlx-8bit
Nemotron-PII fine-tune OpenMed/privacy-filter-nemotron …-nemotron-mlx …-nemotron-mlx-8bit
OpenMed Multilingual OpenMed/privacy-filter-multilingual …-multilingual-mlx …-multilingual-mlx-8bit
from openmed import extract_pii

text = "Patient Sarah Connor (DOB: 03/15/1985) at MRN 4471882."

variants = {
    "baseline": extract_pii(text, model_name="openai/privacy-filter"),
    "nemotron": extract_pii(text, model_name="OpenMed/privacy-filter-nemotron"),
    "mlx": extract_pii(text, model_name="OpenMed/privacy-filter-mlx"),
}
print([(e.label, e.text) for e in variants["baseline"].entities])

Example output:

[('NAME', 'Sarah Connor'), ('DATE', '03/15/1985'), ('ID', '4471882')]

On non-Apple-Silicon hosts, MLX model names are automatically substituted with the matching PyTorch checkpoint (with a one-time warning): reuse one model name where a matching backend artifact is available. See Privacy Filter architecture & backend routing.


Multilingual PII (36 supported routes; 33 model-backed)

Extraction and de-identification support 36 supported PII language codes: am, ar, as, bn, cs, da, de, el, en, es, fa, fr, he, hi, id, it, ja, ko, mr, nl, no, or, pt, ro, ru, sv, sw, ta, te, th, tr, uk, vi, xh, zh, and zu, with the registry-backed PII model catalog in total. Russian routing currently uses a documented multilingual default-model placeholder. Bengali, Chinese, and Tamil have dedicated registry entries. An optional, user-configured Indic NER family accepts four additional routes (gu, kn, ml, and pa) and can also serve Assamese, Bengali, Hindi, Marathi, Odia, Tamil, and Telugu. Set OPENMED_INDIC_NER_MODEL; OpenMed never bundles or automatically selects those optional weights. OpenMed also includes validator-backed national-ID coverage for addit

openmed (maziyarpanahi/openmed)

Searching Database…

5.7K ·GitHub
这是什么?OpenMed 是一个本地优先的医疗健康 AI 软件开发工具包:提供临床命名实体识别(NER)和符合 HIPAA 要求的个人身份信息(PII)去标识化,全部在你自己的硬件上运行,包含 2200 多个医学模型,支持 Python、MLX、Android 和浏览器等多种运行环境。
这是我写代码时要调用的库,还是一个做好的应用?是一个 SDK,不是点点鼠标就能用的应用:包含一个 Python 包、面向 Apple 平台的 Swift OpenMedKit、面向 Android 的 Kotlin 库、通过 Transformers.js 提供的浏览器运行时,另外还有 REST 服务、MCP 服务器和命令行工具等使用方式。
我怎么把它跑起来?用 'pip install openmed' 安装 Python 包 — Apple Silicon 上使用 [mlx] 这个可选依赖。Swift 应用通过 Swift Package Manager 引入 OpenMedKit;Android 则从 JitPack 添加。模型文件会在第一次使用时自动下载一次。
它能在我的笔记本电脑上跑吗,还是需要特殊硬件?Python 核心在普通 CPU 或 CUDA 上都能运行;Apple Silicon 可以启用 MLX 后端,并有 CoreML 作为后备;Android 使用 ONNX Runtime Mobile;浏览器方案跑在 WebGPU 上。具体支持情况因模型文件而异。
基于这个 SDK 开发,就自动算符合 HIPAA 合规了吗?不是。它的 Safe Harbor 对齐配置可以覆盖 18 类标识符,但 README 明确指出仍需要专家对部署进行审查,单靠这个 SDK 并不能确立合规。
如果部署的地方完全断网(物理隔离)怎么办?可以 — 把 modelname 或 modelid 指向本地目录,OpenMed 就能在不联系 Hugging Face Hub 或任何外部服务的情况下加载模型。
我的临床数据会离开这台机器吗?只要所需的模型文件就位,核心运行时的提取和去标识化全部在本地完成。只有主动开启的功能才会联网:下载、远程适配器、开启了遥测的路径,以及用户自行配置的集成。
SDK 是免费的,那模型本身呢?SDK 源码采用 Apache-2.0 许可证,但模型和数据集的条款各不相同 — README 建议部署负责人针对自己的使用场景逐一核实每个模型、许可证和数据集。
这些医学 NER 模型到底能从病历里识别出什么?精选目录收录了 2266 条清单条目:包括疾病/病症/诊断、药物和药品、解剖部位、基因和蛋白质的识别器,还有 PII 模型 — 参数规模从 109M 到 434M 不等。
PII 去标识化支持哪些语言?支持 36 个 PII 语言代码,其中 33 个有模型支持 — 包括英语、德语、俄语、中文、日语、阿拉伯语、印地语、斯瓦希里语等。孟加拉语、中文和泰米尔语有专门的注册条目,还有一组可选的 Indic NER 系列额外提供四种途径。
在 Apple Silicon 上,MLX 后端能快多少?对于 Privacy Filter 系列模型,Apple Silicon 上的 MLX 比 CPU 上的 PyTorch 快 24 到 33 倍。与逐篇处理相比,批量处理在 CPU 上最多提升 3.3 倍吞吐量,在 MLX 上提升 2.2 倍。
我能从 Claude Code 或其他编程智能体搭建处理流程吗?可以。技能目录提供可移植的 Agent Skills,涵盖本地去标识化、临床 NER、FHIR 导出和评估 — 同样的 SKILL.md 文件夹可以在 Claude Code、OpenAI Codex、OpenCode 以及兼容的智能体中使用。
Size
100.7 MB
Stars
5.7K
Age
1y
Commits · 90 Days