Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,27 +163,32 @@ result = nlp(Document("Patient has a history of heart attack and high blood pres
print(f"Entities: {result.nlp.get_entities()}")
```

#### Adding connectors
Connectors give your pipelines the ability to interface with EHRs.
#### Working with healthcare data formats
Adapters handle conversion between healthcare formats (CDA, FHIR) and internal Document objects for seamless EHR integration.

```python
from healthchain.io import CdaConnector
from healthchain.io import CdaAdapter, Document
from healthchain.models import CdaRequest

cda_connector = CdaConnector()
adapter = CdaAdapter()

pipeline.add_input(cda_connector)
pipeline.add_output(cda_connector)
# Parse healthcare data into Document
cda_request = CdaRequest(document="<CDA XML content>")
doc = adapter.parse(cda_request)

pipe = pipeline.build()
# Process with your pipeline
processed_doc = nlp_pipeline(doc)

cda_data = CdaRequest(document="<CDA XML content>")
output = pipe(cda_data)
# output: CdsResponse model
# Access extracted clinical data
print(f"Problems: {processed_doc.fhir.problem_list}")
print(f"Medications: {processed_doc.fhir.medication_list}")

# Convert back to healthcare format
response = adapter.format(processed_doc)
```

### Using pre-built pipelines
Pre-built pipelines are use case specific end-to-end workflows that already have connectors and models built-in.
Pre-built pipelines are use case specific end-to-end workflows optimized for common healthcare AI tasks.

```python
from healthchain.pipeline import MedicalCodingPipeline
Expand All @@ -194,11 +199,17 @@ pipeline = MedicalCodingPipeline.from_model_id(
model="blaze999/Medical-NER", task="token-classification", source="huggingface"
)

# Or load from local model
pipeline = MedicalCodingPipeline.from_local_model("./path/to/model", source="spacy")

cda_data = CdaRequest(document="<CDA XML content>")
output = pipeline(cda_data)
# Simple end-to-end processing
cda_request = CdaRequest(document="<CDA XML content>")
response = pipeline.process_request(cda_request)

# Or manual control for document access
from healthchain.io import CdaAdapter
adapter = CdaAdapter()
doc = adapter.parse(cda_request)
doc = pipeline(doc)
# Access: doc.fhir.problem_list, doc.fhir.medication_list
response = adapter.format(doc)
```

## Interoperability
Expand Down
2 changes: 1 addition & 1 deletion cookbook/cds_discharge_summarizer_hf_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def load_data_in_client(self) -> Prefetch:
@hc.api
def my_service(self, request: CDSRequest) -> CDSResponse:
# Process the request through our pipeline
result = self.pipeline(request)
result = self.pipeline.process_request(request)
return result


Expand Down
2 changes: 1 addition & 1 deletion cookbook/cds_discharge_summarizer_hf_trf.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def load_data_in_client(self) -> Prefetch:

@hc.api
def my_service(self, request: CDSRequest) -> CDSResponse:
result = self.pipeline(request)
result = self.pipeline.process_request(request)
return result


Expand Down
4 changes: 4 additions & 0 deletions docs/api/adapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Adapters

::: healthchain.io.adapters.cdaadapter
::: healthchain.io.adapters.cdsfhiradapter
5 changes: 0 additions & 5 deletions docs/api/connectors.md

This file was deleted.

8 changes: 4 additions & 4 deletions docs/cookbook/cds_sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ If you are using a chat model, we recommend you initialize the pipeline with the

=== "Non-chat model"
```python
from healthchain.pipelines import SummarizationPipeline
from healthchain.pipeline import SummarizationPipeline

pipeline = SummarizationPipeline.from_model_id(
"google/pegasus-xsum", source="huggingface", task="summarization"
Expand All @@ -45,7 +45,7 @@ If you are using a chat model, we recommend you initialize the pipeline with the

=== "Chat model"
```python
from healthchain.pipelines import SummarizationPipeline
from healthchain.pipeline import SummarizationPipeline

from langchain_huggingface.llms import HuggingFaceEndpoint
from langchain_huggingface import ChatHuggingFace
Expand Down Expand Up @@ -96,7 +96,7 @@ class DischargeNoteSummarizer(ClinicalDecisionSupport):

@hc.api
def my_service(self, request: CDSRequest) -> CDSResponse:
result = self.pipeline(request)
result = self.pipeline.process_request(request)
return result
```

Expand Down Expand Up @@ -147,7 +147,7 @@ class DischargeNoteSummarizer(ClinicalDecisionSupport):

@hc.api
def my_service(self, request: CDSRequest) -> CDSResponse:
result = self.pipeline(request)
result = self.pipeline.process_request(request)
return result

@hc.ehr(workflow="encounter-discharge")
Expand Down
4 changes: 2 additions & 2 deletions docs/cookbook/notereader_sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import healthchain as hc
from healthchain.io import Document
from healthchain.models.requests import CdaRequest
from healthchain.models.responses import CdaResponse
from healthchain.pipeline.medicalcodingpipeline import MedicalCodingPipeline
from healthchain.pipeline import MedicalCodingPipeline
from healthchain.sandbox.use_cases import ClinicalDocumentation
from healthchain.fhir import create_document_reference

Expand Down Expand Up @@ -64,7 +64,7 @@ class NotereaderSandbox(ClinicalDocumentation):

@hc.api
def my_service(self, request: CdaRequest) -> CdaResponse:
result = self.pipeline(request)
result = self.pipeline.process_request(request)

return result

Expand Down
25 changes: 13 additions & 12 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,30 +102,31 @@ doc = Document("Patient presents with hypertension.")
output = pipe(doc)
```

Let's go one step further! You can use [Connectors](./reference/pipeline/connectors/connectors.md) to work directly with [CDA](https://www.hl7.org.uk/standards/hl7-standards/cda-clinical-document-architecture/) and [FHIR](https://hl7.org/fhir/) data received from healthcare system APIs. Add Connectors to your pipeline with the `.add_input()` and `.add_output()` methods.
Let's go one step further! You can use [Adapters](./reference/pipeline/adapters/adapters.md) to work directly with [CDA](https://www.hl7.org.uk/standards/hl7-standards/cda-clinical-document-architecture/) and [FHIR](https://hl7.org/fhir/) data received from healthcare system APIs. Adapters handle format conversion while keeping your pipeline pure ML processing.

```python
from healthchain.pipeline import Pipeline
from healthchain.pipeline.components import SpacyNLP
from healthchain.io import CdaConnector
from healthchain.io import CdaAdapter
from healthchain.models import CdaRequest

pipeline = Pipeline()
cda_connector = CdaConnector()

pipeline.add_input(cda_connector)
pipeline.add_node(SpacyNLP.from_model_id("en_core_sci_sm"))
pipeline.add_output(cda_connector)

pipe = pipeline.build()

cda_data = CdaRequest(document="<CDA XML content>")
output = pipe(cda_data)
# Use adapter for format conversion
adapter = CdaAdapter()
cda_request = CdaRequest(document="<CDA XML content>")

# Parse, process, format
doc = adapter.parse(cda_request)
processed_doc = pipe(doc)
output = adapter.format(processed_doc)
```

#### 3. Use Prebuilt Pipelines

Prebuilt pipelines are pre-configured collections of Components, Models, and Connectors. They are built for specific use cases, offering the highest level of abstraction. This is the easiest way to get started if you already know the use case you want to build for.
Prebuilt pipelines are pre-configured collections of Components and Models optimized for specific healthcare AI use cases. They offer the highest level of abstraction and are the easiest way to get started.

For a full list of available prebuilt pipelines and details on how to configure and customize them, see the [Pipelines](./reference/pipeline/pipeline.md) documentation page.

Expand All @@ -143,8 +144,8 @@ pipeline = MedicalCodingPipeline.from_model_id("facebook/bart-large-cnn", source
# Or load from local model
pipeline = MedicalCodingPipeline.from_local_model("./path/to/model", source="spacy")

cda_data = CdaRequest(document="<CDA XML content>")
output = pipeline(cda_data)
cda_request = CdaRequest(document="<CDA XML content>")
output = pipeline.process_request(cda_request)
```

### Interoperability 🔄
Expand Down
56 changes: 56 additions & 0 deletions docs/reference/gateway/cdshooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,59 @@ When registered with HealthChainAPI, the following endpoints are automatically c
- `MedicationRequest`

For more information, see the [official CDS Hooks documentation](https://cds-hooks.org/).

## Advanced Workflow Example

This example demonstrates how to build a custom CDS Hooks workflow that performs advanced clinical analysis and generates tailored decision support cards. By combining adapters and a custom pipeline, you can process incoming FHIR data, apply your own logic (such as risk assessment), and return dynamic CDS cards to the EHR.

```python
from healthchain.io import CdsFhirAdapter, Document
from healthchain.pipeline import Pipeline
from healthchain.pipeline.components import CdsCardCreator
from healthchain.models import CDSRequest, CDSResponse
from healthchain.gateway import HealthChainAPI, CDSHooksService

# Build custom pipeline with analysis and card creation
pipeline = Pipeline([Document])

@pipeline.add_node
def analyze_patient_data(doc: Document) -> Document:
"""Custom function to analyze patient data and document content"""
# Access FHIR prefetch resources
patient = doc.fhir.get_prefetch_resources("patient")
document_ref = doc.fhir.get_prefetch_resources("document")

# Perform custom analysis
if patient:
age = 2024 - int(patient.birthDate[:4]) # Simple age calculation
if age > 65:
doc._custom_analysis = {"high_risk": True, "reason": "Age > 65"}
else:
doc._custom_analysis = {"high_risk": False}
return doc

# Add card creator to format output
pipeline.add_node(CdsCardCreator(
template='{"summary": "Risk Assessment", "detail": "Patient risk level: {{ high_risk }}"}'
))

pipe = pipeline.build()

# Set up CDS service with custom workflow
app = HealthChainAPI()
cds = CDSHooksService()

@cds.hook("encounter-discharge", id="risk-assessment")
def assess_patient_risk(request: CDSRequest) -> CDSResponse:
# Use adapter for explicit format conversion
adapter = CdsFhirAdapter()

# Manual conversion with full document access
doc = adapter.parse(request) # CDSRequest → Document
processed_doc = pipe(doc) # Custom analysis + card creation

# Convert back to CDS response
return adapter.format(processed_doc) # Document → CDSResponse

app.register_service(cds, path="/cds")
```
14 changes: 10 additions & 4 deletions docs/reference/gateway/soap_cda.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ The SOAP/CDA protocol enables real-time Clinical Documentation Improvement (CDI)

## Overview

Clinical Documentation workflows communicate using [CDA (Clinical Document Architecture)](https://www.hl7.org.uk/standards/hl7-standards/cda-clinical-document-architecture/). CDAs are standardized electronic documents for exchanging clinical information between different healthcare systems. They provide a common structure for capturing and sharing patient data like medical history, medications, and care plans between different healthcare systems and providers. Think of it as a collaborative Google Doc that you can add, amend, and remove entries from.
Clinical Documentation workflows communicate using [CDA (Clinical Document Architecture)](https://www.hl7.org.uk/standards/hl7-standards/cda-clinical-document-architecture/). CDAs are standardized electronic documents for exchanging clinical information between different healthcare systems. They provide a common structure for capturing and sharing patient data like medical history, medications, and care plans between different healthcare systems and providers. Think of it as a collaborative Google Doc that you can add, amend, and remove entries from. CDA support is currently limited to [Epic systems](https://open.epic.com/clinical/ehrtoehr), but we plan to add support for other IHE SOAP/CDA services in the future.

### Epic NoteReader CDI

The Epic NoteReader CDI is a SOAP/CDA-based NLP service that extracts structured data from clinical notes. Like CDS Hooks, it operates in real-time and is triggered when a clinician opts into CDI functionality and signs or pends a note.

The primary use case for Epic NoteReader is to convert free-text medical documentation into coded information that can be used for billing, quality reporting, continuity of care, and clinical decision support at the point-of-care ([case study](https://www.researchsquare.com/article/rs-4925228/v1)).

It is a vendor-specific component (Epic), but we plan to add support for other IHE SOAP/CDA services in the future.

| When | Where | What you receive | What you send back |
| :-------- | :-----| :-------------------------- |----------------------------|
| Triggered when a clinician opts in to CDI functionality and signs or pends a note | EHR documentation modules (e.g. NoteReader in Epic) | A CDA document containing continuity of care data and free-text clinical notes | A CDA document with additional structured data extracted by your CDI service |


### CDA Services

CDA services facilitate the [exchange of clinical information between different healthcare systems](https://gkc.himss.org/resource-environmental-scan/care-everywhere) and are governed by the [IHE](https://www.ihe.net/uploadedFiles/Documents/PCC/IHE_PCC_Suppl_CDA_Content_Modules.pdf) standard. The Epic HIE (Health Information Exchange) platform is [CareEverywhere](https://www.epic.com/careeverywhere/).


## HealthChainAPI Integration

Use the `NoteReaderService` with HealthChainAPI to handle SOAP/CDA workflows:
Expand Down Expand Up @@ -154,4 +160,4 @@ The response includes additional structured sections extracted from the clinical
| Gateway Receives | `CdaRequest` | Processed by your service |
| Gateway Returns | Your processed result | `CdaResponse` |

You can use the [CdaConnector](../pipeline/connectors/cdaconnector.md) to handle conversion between CDA documents and HealthChain pipeline data containers.
You can use the [CdaAdapter](../pipeline/adapters/cdaadapter.md) to handle conversion between CDA documents and HealthChain pipeline data containers.
84 changes: 84 additions & 0 deletions docs/reference/pipeline/adapters/adapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Adapters

Adapters handle conversion between healthcare data formats (CDA, FHIR) and HealthChain's internal `Document` objects. They enable clean separation between ML processing logic and healthcare format handling, making your pipelines more maintainable and testable.

Unlike the legacy connector pattern, adapters are used explicitly and provide clear control over data flow.

## Available adapters

Adapters parse data from specific healthcare formats into FHIR resources and store them in a `Document` container for processing.

([Document API Reference](../../../api/containers.md#healthchain.io.containers.document.Document))

| Adapter | Input Format | Output Format | FHIR Resources | Document Access |
|---------|--------------|---------------|----------------|-----------------|
| [**CdaAdapter**](cdaadapter.md) | `CdaRequest` | `CdaResponse` | [**DocumentReference**](https://www.hl7.org/fhir/documentreference.html) | `Document.text`, `Document.fhir.problem_list`, `Document.fhir.medication_list`, `Document.fhir.allergy_list` |
| [**CdsFhirAdapter**](cdsfhiradapter.md) | `CDSRequest` | `CDSResponse` | [**Any FHIR Resource**](https://www.hl7.org/fhir/resourcelist.html) | `Document.fhir.get_prefetch_resources()` |

## Use Cases
Each adapter is designed for specific healthcare integration scenarios.

| Adapter | Use Case | Protocol |
|---------|----------|----------|
| `CdaAdapter` | [**Clinical Documentation**](../../gateway/soap_cda.md) | SOAP/CDA |
| `CdsFhirAdapter` | [**Clinical Decision Support**](../../gateway/cdshooks.md) | CDS Hooks/FHIR |

## Usage Patterns

### 1. Simple End-to-End Processing

Use prebuilt pipelines with the `process_request()` method for straightforward workflows:

```python
from healthchain.pipeline import MedicalCodingPipeline
from healthchain.models import CdaRequest

pipeline = MedicalCodingPipeline.from_model_id("en_core_sci_sm", source="spacy")
cda_request = CdaRequest(document="<CDA XML content>")

# Adapter used internally
response = pipeline.process_request(cda_request)
```

### 2. Manual Adapter Control (Document Access)

Use adapters `parse()` and `format()` methods directly when you need access to the intermediate `Document` object:

```python
from healthchain.io import CdaAdapter
from healthchain.pipeline import MedicalCodingPipeline
from healthchain.models import CdaRequest

pipeline = MedicalCodingPipeline.from_model_id("en_core_sci_sm", source="spacy")
adapter = CdaAdapter()

cda_request = CdaRequest(document="<CDA XML content>")

# Manual adapter control
doc = adapter.parse(cda_request) # CdaRequest → Document
doc = pipeline(doc) # Document → Document (pure ML)

# Access extracted clinical data
print(f"Problems: {doc.fhir.problem_list}")
print(f"Medications: {doc.fhir.medication_list}")
print(f"Allergies: {doc.fhir.allergy_list}")

# Convert back to healthcare format
response = adapter.format(doc) # Document → CdaResponse
```

## Adapter Configuration

### Custom Interop Engine

Both CDA and CDS adapters can be configured with custom interoperability engines. By default, the adapter uses the built-in InteropEngine with default CDA templates.

```python
from healthchain.io import CdaAdapter
from healthchain.interop import create_engine

# Custom engine with specific configuration
custom_engine = create_engine(config_dir="/path/to/custom/config")
adapter = CdaAdapter(engine=custom_engine)
```
For more information on the InteropEngine, see the [InteropEngine documentation](../../interop/interop.md).
Loading