Use Cases
Clinical Decision Support (CDS)
CDS Hooks is an HL7 published specification for clinical decision support. For more information please consult the official documentation.
When | Where | What you receive | What you send back |
---|---|---|---|
Triggered at certain events during a clinician's workflow, e.g. when a patient record is opened. | EHR | The context of the event and FHIR resources that are requested by your service. e.g. patient ID, Encounter and Patient . |
βCardsβ displaying text, actionable suggestions, or links to launch a SMART app from within the workflow. |
Each workflow has associated context
and prefetch
FHIR resource returned from it.
If you use the DataGenerator
, a pre-configured list of FHIR resources is randomly generated and placed in the prefetch
field of a CDSRequest
.
Current implemented workflows:
Workflow | Implementation Completeness | Generated Synthetic Resources |
---|---|---|
patient-view | Patient , Encounter (Future: MedicationStatement , AllergyIntolerance ) |
|
encounter-discharge | Patient , Encounter , Procedure , MedicationRequest , Optional DocumentReference |
|
order-sign | Partial | Future: MedicationRequest , ProcedureRequest , ServiceRequest |
order-select | Partial | Future: MedicationRequest , ProcedureRequest , ServiceRequest |
ClinicalDecisionSupport
functions receive CdsRequest
and return a list of Card
.
class Card(BaseModel):
"""
Cards can provide a combination of information (for reading), suggested actions
(to be applied if a user selects them), and links (to launch an app if the user selects them).
The CDS Client decides how to display cards, but this specification recommends displaying suggestions
using buttons, and links using underlined text.
https://cds-hooks.org/specification/current/#card-attributes
"""
summary: str = Field(..., max_length=140)
indicator: IndicatorEnum
source: Source
uuid: Optional[str] = None
detail: Optional[str] = None
suggestions: Optional[List[Suggestion]] = None
selectionBehavior: Optional[SelectionBehaviorEnum] = None
overrideReasons: Optional[List[SimpleCoding]] = None
links: Optional[List[Link]] = None
The DataGenerator
returns data represented as CdsFhirData
, which contains a Bundle
of FHIR resources in the prefetch
field.
class CdsFhirData(BaseModel):
"""
Data model for CDS FHIR data, this matches the expected fields in CDSRequests
"""
context: Dict = Field(default={})
prefetch: Bundle
Example CDSRequest
{
"hookInstance" : "23f1a303-991f-4118-86c5-11d99a39222e",
"fhirServer" : "https://fhir.example.org",
"hook" : "patient-view",
"context" : {
"patientId" : "1288992",
"userId" : "Practitioner/example"
},
"prefetch" : {
"patientToGreet" : {
"resourceType" : "Patient",
"gender" : "male",
"birthDate" : "1925-12-23",
"id" : "1288992",
"active" : true
}
}
}
Example CDSResponse
{
"summary": "Bilirubin: Based on the age of this patient consider overlaying bilirubin [Mass/volume] results over a time-based risk chart",
"indicator": "info",
"detail": "The focus of this app is to reduce the incidence of severe hyperbilirubinemia and bilirubin encephalopathy while minimizing the risks of unintended harm such as maternal anxiety, decreased breastfeeding, and unnecessary costs or treatment.",
"source": {
"label": "Intermountain",
"url": null
},
"links": [
{
"label": "Bilirubin SMART app",
"url": "https://example.com/launch",
"type": "smart"
}
]
}
Implemented FHIR Resources
Patient
Encounter
Procedure
MedicationRequest
Example Sandbox Usage
A CDS sandbox which uses gpt-4o
to summarise patient information from synthetically generated FHIR resources received from the patient-view
CDS hook.
import healthchain as hc
from healthchain.use_cases import ClinicalDecisionSupport
from healthchain.data_generators import CdsDataGenerator
from healthchain.models import Card, CdsFhirData, CDSRequest
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from typing import List
@hc.sandbox
class CdsSandbox(ClinicalDecisionSupport):
def __init__(self):
self.chain = self._init_llm_chain()
self.data_generator = CdsDataGenerator()
def _init_llm_chain(self):
prompt = PromptTemplate.from_template(
"Extract conditions from the FHIR resource below and summarize in one sentence using simple language \n'''{text}'''"
)
model = ChatOpenAI(model="gpt-4o")
parser = StrOutputParser()
chain = prompt | model | parser
return chain
@hc.ehr(workflow="patient-view")
def load_data_in_client(self) -> CdsFhirData:
data = self.data_generator.generate()
return data
@hc.api
def my_service(self, request: CDSRequest) -> List[Card]:
result = self.chain.invoke(str(request.prefetch))
return Card(
summary="Patient summary",
indicator="info",
source={"label": "openai"},
detail=result,
)
Clinical Documentation
The ClinicalDocumentation
use case implements a real-time Clinical Documentation Improvement (CDI) service. It currently implements the Epic-integrated NoteReader CDI specification, which communicates with a third-party natural language processing (NLP) engine to analyse clinical notes and extract structured data. It helps convert free-text medical documentation into coded information that can be used for billing, quality reporting, and clinical decision support.
ClinicalDocumentation
communicates using CDAs (Clinical Document Architecture). CDAs are standardised electronic documents for exchanging clinical information. 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. CDAs typically uses a SOAP-based communication protocol (unfortunately).
Note that NoteReader is a vendor-specific component (Epic). This particular note-based workflow is one type of CDI service. Different EHR vendors will have different support for third-party CDI services.
When | Where | What you receive | What you send back |
---|---|---|---|
Triggered when a clinician opts in to a CDI functionality and signs or pends a note after writing it. | Specific modules in EHR where clinical documentation takes place, such as NoteReader in Epic. | A CDA document which contains continuity of care data and free-text data, e.g. a patient's problem list and the progress note that the clinician has entered in the EHR. | A CDA document which contains additional structured data extracted and returned by your CDI service. |
In HealthChain, the workflow is named sign-note-inpatient
or sign-note-outpatient
. We support parsing of problems, medications, and allergies sections, though some of the data fields may be limited. We plan to implement additional CDI services and workflows for different vendor specifications.
A ClinicalDocumentation
function receives and returns CcdData
:
class CcdData(BaseModel):
"""
Data model for CCD (Continuity of Care Document) that can be converted to CDA.
"""
problems: Optional[List[ProblemConcept]] = None
allergies: Optional[List[AllergyConcept]] = None
medications: Optional[List[MedicationConcept]] = None
note: Optional[Union[Dict, str]] = None
cda_xml: Optional[str] = None
The DataGenerator
currently does not support synthetic CDA data - we're working on it! At the moment, you can only load and return a pre-existing CDA from your sandbox api function.
Example CdaRequest
<?xml version="1.0" encoding="UTF-8"?>
<ClinicalDocument xmlns="urn:hl7-org:v3">
<typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
<templateId root="2.16.840.1.113883.10.20.22.1.2"/>
<id root="2.16.840.1.113883.19.5.99999.1"/>
<code code="34133-9" displayName="Summarization of Episode Note" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>CDA Document with Problem List and Progress Note</title>
<effectiveTime value="20240712"/>
<confidentialityCode code="N" codeSystem="2.16.840.1.113883.5.25"/>
<languageCode code="en-US"/>
<component>
<structuredBody>
<!-- Problem List Section -->
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.5.1"/>
<code code="11450-4" codeSystem="2.16.840.1.113883.6.1" displayName="Problem List"/>
<title>Problems</title>
<text>
<list>
<item>Hypertension</item>
</list>
</text>
<entry>
<act classCode="ACT" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.3"/>
<id root="ab1791b0-5c71-11db-b0de-0800200c9a66"/>
<code code="CONC" codeSystem="2.16.840.1.113883.5.6"/>
<statusCode code="active"/>
<effectiveTime>
<low value="20240712"/>
</effectiveTime>
<entryRelationship typeCode="SUBJ">
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.4"/>
<id root="ab1791b0-5c71-11db-b0de-0800200c9a66"/>
<code code="55607006" displayName="Problem" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
<text>Hypertension</text>
<statusCode code="completed"/>
<effectiveTime>
<low value="20240712"/>
</effectiveTime>
<value xsi:type="CD" code="59621000" displayName="Essential hypertension" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
</observation>
</entryRelationship>
</act>
</entry>
</section>
</component>
<!-- Progress Note Section -->
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.65"/>
<code code="11506-3" codeSystem="2.16.840.1.113883.6.1" displayName="Progress Note"/>
<title>Progress Note</title>
<text>
<paragraph>Patient's blood pressure remains elevated. Discussed lifestyle modifications and medication adherence. Started Lisinopril 10 mg daily for hypertension management. Will follow up in 3 months to assess response to treatment.</paragraph>
</text>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>
Example CdaResponse
<?xml version="1.0" encoding="UTF-8"?>
<ClinicalDocument xmlns="urn:hl7-org:v3">
<typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
<templateId root="2.16.840.1.113883.10.20.22.1.2"/>
<id root="2.16.840.1.113883.19.5.99999.1"/>
<code code="34133-9" displayName="Summarization of Episode Note" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>CDA Document with Problem List, Medication, and Progress Note</title>
<effectiveTime value="20240712"/>
<confidentialityCode code="N" codeSystem="2.16.840.1.113883.5.25"/>
<languageCode code="en-US"/>
<component>
<structuredBody>
<!-- Problem List Section -->
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.5.1"/>
<code code="11450-4" codeSystem="2.16.840.1.113883.6.1" displayName="Problem List"/>
<title>Problems</title>
<text>
<list>
<item>Hypertension</item>
</list>
</text>
<entry>
<act classCode="ACT" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.3"/>
<id root="ab1791b0-5c71-11db-b0de-0800200c9a66"/>
<code code="CONC" codeSystem="2.16.840.1.113883.5.6"/>
<statusCode code="active"/>
<effectiveTime>
<low value="20240712"/>
</effectiveTime>
<entryRelationship typeCode="SUBJ">
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.4"/>
<id root="ab1791b0-5c71-11db-b0de-0800200c9a66"/>
<code code="55607006" displayName="Problem" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
<text>Hypertension</text>
<statusCode code="completed"/>
<effectiveTime>
<low value="20240712"/>
</effectiveTime>
<value xsi:type="CD" code="59621000" displayName="Essential hypertension" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
</observation>
</entryRelationship>
</act>
</entry>
</section>
</component>
<!-- Medications Section -->
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.1.1"/>
<code code="10160-0" codeSystem="2.16.840.1.113883.6.1" displayName="History of medication use"/>
<title>Medications</title>
<text>
<list>
<item>Lisinopril 10 mg oral tablet, once daily</item>
</list>
</text>
<entry>
<substanceAdministration classCode="SBADM" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.16"/>
<id root="cdbd33f0-6cde-11db-9fe1-0800200c9a66"/>
<statusCode code="active"/>
<effectiveTime xsi:type="IVL_TS">
<low value="20240712"/>
</effectiveTime>
<routeCode code="PO" codeSystem="2.16.840.1.113883.5.112" displayName="Oral"/>
<doseQuantity value="1"/>
<administrationUnitCode code="C48542" displayName="Tablet" codeSystem="2.16.840.1.113883.3.26.1.1"/>
<consumable>
<manufacturedProduct classCode="MANU">
<templateId root="2.16.840.1.113883.10.20.22.4.23"/>
<manufacturedMaterial>
<code code="197884" codeSystem="2.16.840.1.113883.6.88" displayName="Lisinopril 10 MG Oral Tablet">
<originalText>Lisinopril 10 mg oral tablet</originalText>
</code>
</manufacturedMaterial>
</manufacturedProduct>
</consumable>
<entryRelationship typeCode="SUBJ">
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.22.4.20"/>
<code code="33999-4" codeSystem="2.16.840.1.113883.6.1" displayName="Indication"/>
<value xsi:type="CD" code="59621000" displayName="Essential hypertension" codeSystem="2.16.840.1.113883.6.96"/>
</observation>
</entryRelationship>
</substanceAdministration>
</entry>
</section>
</component>
<!-- Progress Note Section -->
<component>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.65"/>
<code code="11506-3" codeSystem="2.16.840.1.113883.6.1" displayName="Progress Note"/>
<title>Progress Note</title>
<text>
<paragraph>Patient's blood pressure remains elevated. Discussed lifestyle modifications and medication adherence. Started Lisinopril 10 mg daily for hypertension management. Will follow up in 3 months to assess response to treatment.</paragraph>
</text>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>
Implemented CDA Sections
- Problems
- Medications (including information on dosage, frequency, duration, route)
- Allergies (including information on severity, reaction and type of allergen)
- Progress Note (free-text)
Example Sandbox Usage
A sandbox example of NoteReader clinical documentation improvement which extracts problems, medications, and allergies entries from the progress note section of a pre-configured CDA document.
import healthchain as hc
from healthchain.use_cases import ClinicalDocumentation
from healthchain.models import (
CcdData,
AllergyConcept,
Concept,
MedicationConcept,
ProblemConcept,
Quantity,
)
@hc.sandbox
class NotereaderSandbox(ClinicalDocumentation):
def __init__(self):
self.cda_path = "./resources/uclh_cda.xml"
@hc.ehr(workflow="sign-note-inpatient")
def load_data_in_client(self) -> CcdData:
with open(self.cda_path, "r") as file:
xml_string = file.read()
return CcdData(cda_xml=xml_string)
@hc.api
def my_service(self, ccd_data: CcdData) -> CcdData:
# Apply extraction method from ccd_data.note
new_problem = ProblemConcept(
code="38341003",
code_system="2.16.840.1.113883.6.96",
code_system_name="SNOMED CT",
display_name="Hypertension",
)
new_allergy = AllergyConcept(
code="70618",
code_system="2.16.840.1.113883.6.96",
code_system_name="SNOMED CT",
display_name="Allergy to peanuts",
)
new_medication = MedicationConcept(
code="197361",
code_system="2.16.840.1.113883.6.88",
code_system_name="RxNorm",
display_name="Lisinopril 10 MG Oral Tablet",
dosage=Quantity(value=10, unit="mg"),
route=Concept(
code="26643006",
code_system="2.16.840.1.113883.6.96",
code_system_name="SNOMED CT",
display_name="Oral",
),
)
ccd_data.problems = [new_problem]
ccd_data.allergies = [new_allergy]
ccd_data.medications = [new_medication]
return ccd_data