Assembling Fhir Bundles logo

Assembling Fhir Bundles

CommunityPopular
maziyarpanahi
assembling-fhir-bundles

Package multiple FHIR R4 resources produced from OpenMed output into a single valid transaction Bundle ready to POST to an EHR, using OpenMed's verified bundle assembler openmed.clinical.exporters.fhir.to_bundle. Covers deterministic urn:uuid fullUrls, automatic in-Bundle reference rewriting, request blocks (method/url) for transaction vs batch, and conditional create. Use after exporting-to-fhir when the user has several Condition/Observation/MedicationStatement resources and wants one transaction Bundle, mentions Bundle, transaction, references, or posting to a FHIR server. Builds on exporting-to-fhir; pairs after.

Overview

Publishermaziyarpanahi
Repositoryopenmed
Skill nameassembling-fhir-bundles
Stars
5.3K
Forks
677
Bundled files
Instructions only
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.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

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

Installation

Install the Assembling Fhir Bundles 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/assembling-fhir-bundles .claude/skills/assembling-fhir-bundles
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Assembling Fhir Bundles 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 Assembling Fhir Bundles 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 Assembling Fhir Bundles 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.

Assembling FHIR Bundles

A FHIR server ingests one transaction Bundle, not loose resources, and the resources inside it must cross-reference each other (Condition.subject → Patient, Observation.encounter → Encounter, DiagnosticReport.result → Observation). OpenMed ships a deterministic, mechanical Bundle assembler — openmed.clinical.exporters.fhir.to_bundle — that wraps the resources you built in exporting-to-fhir into a valid R4 Bundle and wires up the references.

When to use

Use after you have a list of standalone resources from exporting-to-fhir and the destination is a FHIR server. Reach for it when the user says "build a Bundle", "transaction", "POST these resources", or needs internal references resolved. To check the Bundle against US Core, hand off to validating-us-core.

What OpenMed gives you (verified API)

python
from openmed.clinical.exporters.fhir import to_bundle, deterministic_fullurl

bundle = to_bundle(
    resources,                       # Sequence[Mapping] each with a resourceType
    doc_id="note-2024-03-02-001",    # seeds stable urn:uuid fullUrls
    bundle_type="transaction",       # "transaction" | "batch" | "collection" | ...
)

to_bundle does exactly three things, and never synthesises or validates:

  1. Deterministic fullUrl. Each resource gets a urn:uuid seeded by doc_id + its index, so the same input always produces byte-identical output (golden-test friendly). You can pre-compute the same urn with deterministic_fullurl(doc_id, index).
  2. Reference rewriting. Any {"reference": "ResourceType/id"} whose target is present in the Bundle is repointed at that resource's fullUrl. References to resources absent from the Bundle (e.g. a Patient removed by de-identification) are left untouched — no dangling internal refs.
  3. Request blocks. For transaction/batch bundles each entry gets a request block ({"method": "POST", "url": "<ResourceType>"}) so the server knows to create it.

It raises ValueError if a resource lacks resourceType, or if two resources share the same ResourceType/id (duplicate ids would silently corrupt the reference map).

Quick start

python
from openmed.clinical.exporters.fhir import to_bundle
from openmed.clinical.exporters.codeable_concept_simple import coding, codeable_concept

condition = {
    "resourceType": "Condition", "id": "cond-1",
    "clinicalStatus": {"coding": [{
        "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
        "code": "active"}]},
    "code": codeable_concept(
        [coding("snomed", "44054006", "Diabetes mellitus type 2")],
        text="type 2 diabetes"),
    "subject": {"reference": "Patient/patient-1"},   # internal ref, rewritten
}
medication = {
    "resourceType": "MedicationStatement", "id": "med-1", "status": "active",
    "medicationCodeableConcept": codeable_concept(
        [coding("rxnorm", "860975", "metformin 500 MG Oral Tablet")],
        text="metformin 500 mg"),
    "subject": {"reference": "Patient/patient-1"},
}
patient = {
    "resourceType": "Patient", "id": "patient-1",
    "gender": "unknown",                              # de-identified, synthetic
}

bundle = to_bundle([patient, condition, medication],
                   doc_id="demo-note", bundle_type="transaction")

Worked: the transaction Bundle

json
{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "fullUrl": "urn:uuid:6f1c...e2",
      "resource": { "resourceType": "Patient", "id": "patient-1", "gender": "unknown" },
      "request": { "method": "POST", "url": "Patient" }
    },
    {
      "fullUrl": "urn:uuid:9a3b...77",
      "resource": {
        "resourceType": "Condition", "id": "cond-1",
        "subject": { "reference": "urn:uuid:6f1c...e2" }
      },
      "request": { "method": "POST", "url": "Condition" }
    },
    {
      "fullUrl": "urn:uuid:c0d4...19",
      "resource": {
        "resourceType": "MedicationStatement", "id": "med-1",
        "subject": { "reference": "urn:uuid:6f1c...e2" }
      },
      "request": { "method": "POST", "url": "MedicationStatement" }
    }
  ]
}

Note Condition.subject and MedicationStatement.subject were rewritten from "Patient/patient-1" to the Patient entry's fullUrl — that is what makes the transaction resolvable in a single POST.

Workflow

  1. Build resources with exporting-to-fhir; give each a unique id.
  2. Put any resource you reference inside the same Bundle — including the Patient — so the reference resolves. References to resources you intend to be already on the server (e.g. an existing Patient) are left as literal "Patient/<id>"; resolve those with conditional create (below).
  3. Call to_bundle(resources, doc_id=<stable>, bundle_type="transaction").
  4. POST the whole Bundle to the server base: POST [base] {Bundle}.
  5. Validate first against US Core (validating-us-core).

Conditional create (don't duplicate an existing Patient)

to_bundle writes POST <ResourceType> request blocks. To make a transaction idempotent, post-process the entry's request to add an ifNoneExist query so the server reuses an existing match instead of creating a duplicate:

python
for entry in bundle["entry"]:
    if entry["resource"]["resourceType"] == "Patient":
        entry["request"]["ifNoneExist"] = "identifier=http://hospital.example|MRN-REDACTED"

The server creates the Patient only if no match exists; otherwise it links the references to the existing one. PUT with a known id is the alternative for true upserts.

Hand-off to / from OpenMed

  • From OpenMed: the resource list comes from exporting-to-fhir, which in turn comes from openmed.analyze_text. Keep doc_id stable per source document so re-running the pipeline yields the same Bundle.
  • De-identify a built Bundle: openmed.interop.fhir_operations.de_identify_bundle(bundle) walks every entry's free text + XHTML narrative and de-identifies it while preserving Bundle type, entry order, fullUrls, request blocks, and references — codes, systems, and temporal values are never altered. Use it as a final safety pass before transmission if any narrative might carry PHI.
  • OperationOutcome: a transaction either fully succeeds or fully fails; the server returns a Bundle of responses (or an OperationOutcome on error). Surface those to the user; for your own pre-flight findings use to_operation_outcome(...) from the same package.

Edge cases & gotchas

  • Resources without an id are valid but unreferenceable — nothing can point at them and they will not be reference-rewrite targets.
  • Duplicate ResourceType/id raises. This is intentional: a duplicate id would silently overwrite an entry in the reference map and corrupt cross-references. Make ids unique.
  • External references are left alone. Only references whose target is in the Bundle are rewritten; a "Patient/existing-123" you mean to resolve on the server stays literal — pair it with ifNoneExist or a PUT.
  • transaction vs batch. transaction is atomic (all-or-nothing, server resolves urn:uuid references); batch is independent per-entry and does not guarantee reference resolution. Use transaction when entries reference each other.
  • collection/document bundles get no request blocks (only transaction/batch do) — correct, since they are not meant to be POSTed for creation.
  • The assembler does not validate profiles. Run validating-us-core before submission.

Standards & references

Frequently asked questions

What does the Assembling Fhir Bundles AI skill do?

Package multiple FHIR R4 resources produced from OpenMed output into a single valid transaction Bundle ready to POST to an EHR, using OpenMed's verified bundle assembler openmed.clinical.exporters.fhir.to_bundle. Covers deterministic urn:uuid fullUrls, automatic in-Bundle reference rewriting, request blocks (method/url) for transaction vs batch, and conditional create. Use after exporting-to-fhir when the user has several Condition/Observation/MedicationStatement resources and wants one transaction Bundle, mentions Bundle, transaction, references, or posting to a FHIR server. Builds on expo...

Why use Assembling Fhir Bundles on TypingMind?

Because you install it once and use it with any model. Assembling Fhir Bundles 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 Assembling Fhir Bundles in TypingMind?

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

Which AI models can use Assembling Fhir Bundles?

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 Assembling Fhir Bundles?

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

Is the Assembling Fhir Bundles 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 👇