Skip to content

Commit 9188dd1

Browse files
Update cookbook docs (#134)
* Update cookbook docs * Update cookbooks * Minor docstring and error message fix * poetry.lock
1 parent c06979d commit 9188dd1

File tree

17 files changed

+1185
-742
lines changed

17 files changed

+1185
-742
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ First time here? Check out our [Docs](https://dotimplement.github.io/HealthChain
3434
## Why use HealthChain?
3535
- **EHR integrations are manual and time-consuming** - **HealthChainAPI** abstracts away complexities so you can focus on AI development, not learning FHIR APIs, CDS Hooks, and authentication schemes.
3636
- **Healthcare data is fragmented and complex** - **InteropEngine** handles the conversion between FHIR, CDA, and HL7v2 so you don't have to become an expert in healthcare data standards.
37-
- [**Most healthcare data is unstructured**](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6372467/) - HealthChain **Pipelines** are optimized for real-time AI and NLP applications that deal with realistic healthcare data.
37+
- [**Most healthcare data is unstructured**](https://pmc.ncbi.nlm.nih.gov/articles/PMC10566734/) - HealthChain **Pipelines** are optimized for real-time AI and NLP applications that deal with realistic healthcare data.
3838
- **Built by health tech developers, for health tech developers** - HealthChain is tech stack agnostic, modular, and easily extensible with built-in compliance and audit features.
3939

4040
## HealthChainAPI
Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import healthchain as hc
2-
2+
from healthchain.gateway import HealthChainAPI, CDSHooksService
33
from healthchain.pipeline import SummarizationPipeline
4-
from healthchain.models import CDSRequest, CDSResponse, Prefetch
5-
from healthchain.data_generators import CdsDataGenerator
64
from healthchain.sandbox.use_cases import ClinicalDecisionSupport
5+
from healthchain.models import Prefetch, CDSRequest, CDSResponse
6+
from healthchain.data_generators import CdsDataGenerator
77

88
from langchain_huggingface.llms import HuggingFaceEndpoint
99
from langchain_huggingface import ChatHuggingFace
10-
1110
from langchain_core.prompts import PromptTemplate
1211
from langchain_core.output_parsers import StrOutputParser
1312

@@ -21,47 +20,76 @@
2120

2221
def create_summarization_chain():
2322
hf = HuggingFaceEndpoint(
24-
repo_id="HuggingFaceH4/zephyr-7b-beta",
23+
repo_id="deepseek-ai/DeepSeek-R1-0528",
2524
task="text-generation",
2625
max_new_tokens=512,
2726
do_sample=False,
2827
repetition_penalty=1.03,
2928
)
29+
3030
model = ChatHuggingFace(llm=hf)
31+
3132
template = """
32-
You are a bed planner for a hospital. Provide a concise, objective summary of the input text in short bullet points separated by new lines,
33-
focusing on key actions such as appointments and medication dispense instructions, without using second or third person pronouns.\n'''{text}'''
33+
You are a discharge planning assistant for hospital operations.
34+
Provide a concise, objective summary focusing on actionable items
35+
for care coordination, including appointments, medications, and
36+
follow-up instructions. Format as bullet points with no preamble.\n'''{text}'''
3437
"""
3538
prompt = PromptTemplate.from_template(template)
39+
3640
return prompt | model | StrOutputParser()
3741

3842

39-
@hc.sandbox
43+
# Create the healthcare application
44+
app = HealthChainAPI(
45+
title="Discharge Note Summarizer",
46+
description="AI-powered discharge note summarization service",
47+
)
48+
49+
chain = create_summarization_chain()
50+
pipeline = SummarizationPipeline.load(
51+
chain, source="langchain", template_path="templates/cds_card_template.json"
52+
)
53+
54+
# Create CDS Hooks service
55+
cds = CDSHooksService()
56+
57+
58+
@cds.hook("encounter-discharge", id="discharge-summarizer")
59+
def discharge_summarizer(request: CDSRequest) -> CDSResponse:
60+
result = pipeline.process_request(request)
61+
return result
62+
63+
64+
# Register the CDS service
65+
app.register_service(cds, path="/cds")
66+
67+
68+
@hc.sandbox(api="http://localhost:8000")
4069
class DischargeNoteSummarizer(ClinicalDecisionSupport):
4170
def __init__(self):
42-
# Initialize pipeline and data generator
43-
chain = create_summarization_chain()
44-
self.pipeline = SummarizationPipeline.load(
45-
chain, source="langchain", template_path="templates/cds_card_template.json"
46-
)
71+
super().__init__(path="/cds/cds-services/discharge-summarizer")
4772
self.data_generator = CdsDataGenerator()
4873

4974
@hc.ehr(workflow="encounter-discharge")
5075
def load_data_in_client(self) -> Prefetch:
51-
# Generate synthetic FHIR data for testing
5276
data = self.data_generator.generate_prefetch(
5377
free_text_path="data/discharge_notes.csv", column_name="text"
5478
)
5579
return data
5680

57-
@hc.api
58-
def my_service(self, request: CDSRequest) -> CDSResponse:
59-
# Process the request through our pipeline
60-
result = self.pipeline.process_request(request)
61-
return result
62-
6381

6482
if __name__ == "__main__":
65-
# Start the sandbox server
83+
import uvicorn
84+
import threading
85+
86+
# Start the API server in a separate thread
87+
def start_api():
88+
uvicorn.run(app, port=8000)
89+
90+
api_thread = threading.Thread(target=start_api, daemon=True)
91+
api_thread.start()
92+
93+
# Start the sandbox
6694
summarizer = DischargeNoteSummarizer()
6795
summarizer.start_sandbox()
Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import healthchain as hc
2-
2+
from healthchain.gateway import HealthChainAPI, CDSHooksService
33
from healthchain.pipeline import SummarizationPipeline
44
from healthchain.sandbox.use_cases import ClinicalDecisionSupport
55
from healthchain.models import Prefetch, CDSRequest, CDSResponse
@@ -13,12 +13,35 @@
1313
os.environ["HUGGINGFACEHUB_API_TOKEN"] = getpass.getpass("Enter your token: ")
1414

1515

16-
@hc.sandbox
16+
# Create the healthcare application
17+
app = HealthChainAPI(
18+
title="Discharge Note Summarizer",
19+
description="AI-powered discharge note summarization service",
20+
)
21+
22+
# Initialize pipeline
23+
pipeline = SummarizationPipeline.from_model_id(
24+
"google/pegasus-xsum", source="huggingface", task="summarization"
25+
)
26+
27+
# Create CDS Hooks service
28+
cds = CDSHooksService()
29+
30+
31+
@cds.hook("encounter-discharge", id="discharge-summarizer")
32+
def discharge_summarizer(request: CDSRequest) -> CDSResponse:
33+
result = pipeline.process_request(request)
34+
return result
35+
36+
37+
# Register the CDS service
38+
app.register_service(cds, path="/cds")
39+
40+
41+
@hc.sandbox(api="http://localhost:8000")
1742
class DischargeNoteSummarizer(ClinicalDecisionSupport):
1843
def __init__(self):
19-
self.pipeline = SummarizationPipeline.from_model_id(
20-
"google/pegasus-xsum", source="huggingface", task="summarization"
21-
)
44+
super().__init__(path="/cds/cds-services/discharge-summarizer")
2245
self.data_generator = CdsDataGenerator()
2346

2447
@hc.ehr(workflow="encounter-discharge")
@@ -28,12 +51,18 @@ def load_data_in_client(self) -> Prefetch:
2851
)
2952
return data
3053

31-
@hc.api
32-
def my_service(self, request: CDSRequest) -> CDSResponse:
33-
result = self.pipeline.process_request(request)
34-
return result
35-
3654

3755
if __name__ == "__main__":
56+
import uvicorn
57+
import threading
58+
59+
# Start the API server in a separate thread
60+
def start_api():
61+
uvicorn.run(app, port=8000)
62+
63+
api_thread = threading.Thread(target=start_api, daemon=True)
64+
api_thread.start()
65+
66+
# Start the sandbox
3867
summarizer = DischargeNoteSummarizer()
3968
summarizer.start_sandbox()
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env python3
2+
"""
3+
A complete CDI service that processes clinical notes and extracts billing codes.
4+
Demonstrates FHIR-native pipelines, legacy system integration, and multi-source data handling.
5+
6+
Requirements:
7+
- pip install healthchain
8+
- pip install scispacy
9+
- pip install https://s3-us-west-2.amazonaws.com/ai2-s2-scispacy/releases/v0.5.4/en_core_sci_sm-0.5.4.tar.gz
10+
- pip install python-dotenv
11+
12+
Run:
13+
- python notereader_clinical_coding_fhir.py # Demo and start server
14+
"""
15+
16+
import os
17+
import uvicorn
18+
from datetime import datetime, timezone
19+
20+
import healthchain as hc
21+
from fhir.resources.documentreference import DocumentReference
22+
from fhir.resources.meta import Meta
23+
from spacy.tokens import Span
24+
from dotenv import load_dotenv
25+
26+
from healthchain.fhir import create_document_reference
27+
from healthchain.gateway.api import HealthChainAPI
28+
from healthchain.gateway.fhir import FHIRGateway
29+
from healthchain.gateway.soap import NoteReaderService
30+
from healthchain.io import CdaAdapter, Document
31+
from healthchain.models import CdaRequest
32+
from healthchain.pipeline.medicalcodingpipeline import MedicalCodingPipeline
33+
from healthchain.sandbox.use_cases import ClinicalDocumentation
34+
35+
36+
load_dotenv()
37+
38+
39+
BILLING_URL = (
40+
f"fhir://api.medplum.com/fhir/R4/"
41+
f"?client_id={os.environ.get('MEDPLUM_CLIENT_ID')}"
42+
f"&client_secret={os.environ.get('MEDPLUM_CLIENT_SECRET')}"
43+
f"&token_url={os.environ.get('MEDPLUM_TOKEN_URL', 'https://api.medplum.com/oauth2/token')}"
44+
f"&scope={os.environ.get('MEDPLUM_SCOPE', 'openid')}"
45+
)
46+
47+
48+
def create_pipeline():
49+
"""Build FHIR-native ML pipeline with automatic problem extraction."""
50+
pipeline = MedicalCodingPipeline.from_model_id("en_core_sci_sm", source="spacy")
51+
52+
# Add custom entity linking
53+
@pipeline.add_node(position="after", reference="SpacyNLP")
54+
def link_entities(doc: Document) -> Document:
55+
"""Add CUI codes to medical entities for problem extraction"""
56+
if not Span.has_extension("cui"):
57+
Span.set_extension("cui", default=None)
58+
59+
spacy_doc = doc.nlp.get_spacy_doc()
60+
61+
# Simple dummy linker for demo purposes
62+
dummy_linker = {
63+
"pneumonia": "233604007",
64+
"type 2 diabetes mellitus": "44054006",
65+
"congestive heart failure": "42343007",
66+
"chronic kidney disease": "431855005",
67+
"hypertension": "38341003",
68+
"community acquired pneumonia": "385093006",
69+
"ventilator associated pneumonia": "233717007",
70+
"anaphylaxis": "39579001",
71+
"delirium": "2776000",
72+
"depression": "35489007",
73+
"asthma": "195967001",
74+
"copd": "13645005",
75+
}
76+
77+
for ent in spacy_doc.ents:
78+
if ent.text.lower() in dummy_linker:
79+
ent._.cui = dummy_linker[ent.text.lower()]
80+
81+
return doc
82+
83+
return pipeline
84+
85+
86+
def create_app():
87+
"""Create production healthcare API."""
88+
pipeline = create_pipeline()
89+
cda_adapter = CdaAdapter()
90+
91+
# Modern FHIR sources
92+
fhir_gateway = FHIRGateway()
93+
fhir_gateway.add_source("billing", BILLING_URL)
94+
95+
# Legacy CDA processing
96+
note_service = NoteReaderService()
97+
98+
@note_service.method("ProcessDocument")
99+
def ai_coding_workflow(request: CdaRequest):
100+
doc = cda_adapter.parse(request)
101+
doc = pipeline(doc)
102+
103+
for condition in doc.fhir.problem_list:
104+
# Add basic provenance tracking
105+
condition.meta = Meta(
106+
source="urn:healthchain:pipeline:cdi",
107+
lastUpdated=datetime.now(timezone.utc).isoformat(),
108+
)
109+
fhir_gateway.create(condition, source="billing")
110+
111+
cda_response = cda_adapter.format(doc)
112+
113+
return cda_response
114+
115+
# Register services
116+
app = HealthChainAPI(title="Epic CDI Service with FHIR integration")
117+
app.register_gateway(fhir_gateway, path="/fhir")
118+
app.register_service(note_service, path="/notereader")
119+
120+
return app
121+
122+
123+
def create_sandbox():
124+
@hc.sandbox(api="http://localhost:8000/")
125+
class NotereaderSandbox(ClinicalDocumentation):
126+
"""Sandbox for testing clinical documentation workflows"""
127+
128+
def __init__(self):
129+
super().__init__()
130+
self.data_path = "./resources/uclh_cda.xml"
131+
132+
@hc.ehr(workflow="sign-note-inpatient")
133+
def load_clinical_document(self) -> DocumentReference:
134+
"""Load a sample CDA document for processing"""
135+
with open(self.data_path, "r") as file:
136+
xml_content = file.read()
137+
138+
return create_document_reference(
139+
data=xml_content,
140+
content_type="text/xml",
141+
description="Sample CDA document from sandbox",
142+
)
143+
144+
return NotereaderSandbox()
145+
146+
147+
# Create the app
148+
app = create_app()
149+
150+
151+
if __name__ == "__main__":
152+
import threading
153+
from time import sleep
154+
155+
# Start server
156+
def run_server():
157+
uvicorn.run(app, port=8000, log_level="warning")
158+
159+
server_thread = threading.Thread(target=run_server, daemon=True)
160+
server_thread.start()
161+
sleep(2) # Wait for startup
162+
163+
# Test sandbox
164+
sandbox = create_sandbox()
165+
sandbox.start_sandbox()
166+
167+
try:
168+
server_thread.join()
169+
except KeyboardInterrupt:
170+
pass
113 KB
Loading

0 commit comments

Comments
 (0)