Ingesting Clinical Documents logo

Ingesting Clinical Documents

CommunityPopular
maziyarpanahi
ingesting-clinical-documents

Turn scanned faxes, images, and CSV/CDA exports into clean text ready for OpenMed de-identification and NER, fully on-device. Use when the user has clinical documents (image scans, photographed/faxed notes, tabular CSV/TSV exports, C-CDA XML) and needs OCR or structured intake before openmed.deidentify and openmed.analyze_text, asks about openmed.multimodal, OCR engines (Tesseract / PaddleOCR), tabular redaction, or layout and reading order. Covers the verified ocr() and redact_document() entry points and the ExtractedDocument contract. Pairs before deidentifying-clinical-text and extracting-clinical-entities.

Overview

Publishermaziyarpanahi
Repositoryopenmed
Skill nameingesting-clinical-documents
Stars
5.3K
Forks
677
Bundled files
1
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by maziyarpanahi on GitHub. Read the source before you install it.

Installation

Install the Ingesting Clinical Documents AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/maziyarpanahi/openmed.git /tmp/openmed
mkdir -p .claude/skills
cp -r /tmp/openmed/skills/ingesting-clinical-documents .claude/skills/ingesting-clinical-documents
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Ingesting Clinical Documents in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Ingesting Clinical Documents on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Ingesting Clinical Documents is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Ingesting Clinical Documents

Clinical text often arrives as scanned faxes, photographed notes, CSV exports, or C-CDA XML — not plain text. openmed.multimodal converts these into a normalized ExtractedDocument (clean text + character-offset → source-location spans) so you can run de-identification and NER. It runs on-device: OCR backends are local, no document leaves the machine.

When to use

  • You have images / scanned faxes of clinical notes and need text out (OCR).
  • You have CSV/TSV patient exports that need column-aware handling.
  • You have C-CDA XML to flatten into text.
  • You are building the intake stage that feeds openmed.deidentify and openmed.analyze_text.

This is the first stage. After intake, hand off to deidentifying-clinical-text then extracting-clinical-entities.

What is supported today

redact_document dispatches by file extension. Live handlers:

InputExtensionsPath
Images / scans.png .jpg .jpeg .tif .tiff .bmp .gif .webpOCR (ocr() / image handler)
Tables.csv .tsvcolumn-aware tabular redaction
C-CDA.xml (detected as CDA)stdlib CDA adapter

PDF and DOCX have no live handler yetredact_document("x.pdf") raises UnsupportedDocumentError. Convert PDFs to page images first (or to text with your own tool) and feed the images through OCR. See references/multimodal-ingest.md for the full contract, engines, and the tabular pipeline.

Install

bash
pip install "openmed[multimodal]"      # document intake contract + image deps
pip install "openmed[ocr-paddle]"      # add the PaddleOCR engine
# Tesseract engine also needs the system binary, e.g.:  brew install tesseract

Quick start: OCR an image, then de-identify

The clean two-step intake path. ocr() lives in the submodule (it is intentionally not re-exported from openmed.multimodal):

python
from openmed.multimodal.ocr import ocr
import openmed

# 1) OCR a scanned/faxed note -> OcrResult -> ExtractedDocument -> plain text
result = ocr("fax_page.png", engine=None)   # None = auto-select an installed engine
doc    = result.to_document()                # ExtractedDocument
text   = doc.text                            # clean text for downstream OpenMed

# 2) De-identify, then run NER (privacy-first order)
deid = openmed.deidentify(text, method="mask", policy="hipaa_safe_harbor")
ner  = openmed.analyze_text(deid.deidentified_text, output_format="dict")

for ent in ner.entities:
    print(ent.label, ent.text, ent.confidence)

engine may be None (auto-select), "tesseract", "paddleocr", or an OcrEngine instance. OcrResult exposes .text and per-word boxes via .words (each OcrWord has text, bbox, confidence, page).

One-step intake + redaction with redact_document

For images, CSV/TSV, and CDA, redact_document performs intake and de-identification in a single, format-aware call, returning an already-redacted ExtractedDocument:

python
from openmed.multimodal import redact_document

# Image scan: OCR + redact in one call
doc = redact_document("fax_page.png")
print(doc.text)        # redacted text
print(doc.spans[:3])   # SourceSpan offsets -> page / bbox in the original scan

# CSV export: per-column classification (direct id / quasi-id / safe) + redaction
table_doc = redact_document("patients.csv")
print(table_doc.text)

Use redact_document when you want OpenMed to own intake and redaction (especially for tables, where redaction is column-scoped, not free-text NER). Use the ocr()to_document()deidentify path when you want to control the de-identification method, policy, or mapping yourself.

Tabular CSV/TSV redaction

CSV columns get classified before any cell is touched, so a free-text NER pass is not run blindly over structured data:

python
from openmed.multimodal import read_table, redact_table

view = read_table("patients.csv")            # TableView with column decisions
for col in view.columns:
    print(col.name, "->", col.assigned_class, col.action, col.canonical_label)

redacted = redact_table("patients.csv", keep_year=True)
print(redacted.text)            # redacted CSV
for entry in redacted.manifest: # PHI-SAFE audit: counts/actions per column, no raw values
    print(entry)

redact_table(...) returns a RedactedTable with .text, .headers, .rows, .columns, and a PHI-safe .manifest (no raw cell values). See references/multimodal-ingest.md for column classes and actions.

Preserve layout / reading order and map back to the source

Every ExtractedDocument keeps character offset → source location. After detecting PHI on doc.text, project a span's offset back to its page and bounding box:

python
from openmed.multimodal.ocr import ocr
import openmed

doc  = ocr("fax_page.png").to_document()
deid = openmed.deidentify(doc.text, method="mask")

for ent in deid.pii_entities:
    loc = doc.location_at(ent.start)   # SourceSpan or None
    if loc is not None:
        print(ent.label, "page", loc.page, "bbox", loc.bbox)

This lets you redact pixels on the original scan, not just the extracted text.

Hand-off to / from OpenMed

  • To deidentifying-clinical-text: pass doc.text to openmed.deidentify(...) with a policy profile; this is the required next stage for PHI.
  • To extracting-clinical-entities: run openmed.analyze_text on the redacted text, not raw OCR output.
  • From file conversion (out-of-process): for PDFs/DOCX, render to page images with your own tool, then OCR those images through this skill.

Edge cases & gotchas

  • ocr() is imported from the submodule: from openmed.multimodal.ocr import ocr. It is deliberately not re-exported from openmed.multimodal.
  • No PDF/DOCX handler yet: redact_document raises UnsupportedDocumentError for them. Rasterize to images first.
  • OCR needs a backend: install [ocr-paddle] for PaddleOCR, or the system Tesseract binary for pytesseract. Missing backends raise MissingDependencyError with an install hint.
  • OCR is noisy: misreads lower downstream recall. Prefer higher-DPI scans; inspect OcrWord.confidence to flag low-quality pages.
  • Tables are not free text: redact_table redacts per column classification — don't run whole-table NER and expect structured columns to be handled correctly.
  • No raw PHI in artifacts: the table manifest and any logs record counts/actions/labels, never raw values. Keep OCR intermediates on-device and out of logs.
  • Local-first: OCR engines run locally; do not send scans to a cloud OCR API in a PHI workflow.

Standards & references

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Ingesting Clinical Documents AI skill do?

Turn scanned faxes, images, and CSV/CDA exports into clean text ready for OpenMed de-identification and NER, fully on-device. Use when the user has clinical documents (image scans, photographed/faxed notes, tabular CSV/TSV exports, C-CDA XML) and needs OCR or structured intake before openmed.deidentify and openmed.analyze_text, asks about openmed.multimodal, OCR engines (Tesseract / PaddleOCR), tabular redaction, or layout and reading order. Covers the verified ocr() and redact_document() entry points and the ExtractedDocument contract. Pairs before deidentifying-clinical-text and extractin...

Why use Ingesting Clinical Documents on TypingMind?

Because you install it once and use it with any model. Ingesting Clinical Documents is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Ingesting Clinical Documents in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/maziyarpanahi/openmed/tree/master/skills/ingesting-clinical-documents. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Ingesting Clinical Documents?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Ingesting Clinical Documents?

As many as you like. As long as a model supports skills, you can use Ingesting Clinical Documents with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Ingesting Clinical Documents AI skill free?

Yes. It is published on GitHub by maziyarpanahi under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇