Decodes pipe-delimited HL7 v2.x messages (ADT, ORU, MDM, ORM) into structured segments/fields/components and surfaces OBX-5 and NTE-3 free-text narrative for OpenMed. Use before OpenMed processing when ingesting HL7 v2 feeds from an interface engine, lab/results system, or ADT stream and you need the embedded clinical note text de-identified and analyzed. Flatten OBX/NTE text then call openmed.deidentify and openmed.analyze_text; segment-aware redaction is available via openmed.interop.hl7v2. Trigger keywords: HL7, HL7 v2, ADT, ORU, OBX, MSH, PID, pipe-delimited, interface engine, Mirth, lab results.
72
88%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
HL7 v2.x is the workhorse of hospital interfacing — ADT (admit/discharge/ transfer), ORU (observation results), MDM (document management), and ORM (orders) messages flow continuously between EHR, lab, radiology, and ancillary systems. The clinical narrative you want for NLP is buried in OBX-5 (observation value) and NTE-3 (notes/comments) fields, wrapped in a pipe-and-caret encoding. This skill decodes that envelope and hands the free text to OpenMed.
openmed.analyze_text.A message is segments separated by \r (carriage return). Each segment is
3-letter-named, then fields split by |, components by ^,
repetitions by ~, sub-components by &, with \ as escape. The
encoding characters are declared in MSH-1 (the field separator) and
MSH-2 (^~\&). Field positions are one-based, and MSH is special:
MSH-1 is the separator, so MSH-2 is the first real field.
MSH|^~\&|LAB|HOSP|EHR|HOSP|20240302101500||ORU^R01|MSG0001|P|2.5
PID|1||MRN12345^^^HOSP^MR||DOE^JANE^Q||19700115|F|||1 FAKE ST^^SPRINGFIELD^IL^62704
OBR|1||ORD9|CBC^Complete Blood Count
OBX|1|TX|IMPRESSION||Mild leukocytosis; clinically correlate.||||||F
NTE|1||Patient reports fatigue x1 week. Dr. Smith notified.Parse the envelope and pull narrative from OBX-5 / NTE-3, then hand off:
import openmed
from openmed.interop.hl7v2 import parse_hl7v2
raw = open("results.hl7", encoding="utf-8").read()
msg = parse_hl7v2(raw) # -> HL7Message (segments preserved)
narrative_chunks = []
for seg in msg.segments:
if seg.name == "OBX":
# OBX-2 is the value type; OBX-5 is the observation value.
value_type = seg.get_field(2)
if value_type in {"TX", "FT", "CE", "ST"}:
narrative_chunks.append(seg.get_field(5) or "")
elif seg.name == "NTE":
narrative_chunks.append(seg.get_field(3) or "")
# Decode component delimiters into plain text before NLP.
flat = "\n".join(c.replace("^", " ").replace("&", " ") for c in narrative_chunks if c)
# Hand the narrative to OpenMed.
deid = openmed.deidentify(flat, method="replace", policy="hipaa_safe_harbor")
result = openmed.analyze_text(deid.text, output_format="dict")HL7Segment.get_field(position) uses one-based HL7 positions and returns
None for absent fields. HL7Message.segment_names() lists segments in order.
When you need to redact the entire message (structured PID/NK1/GT1 fields and OBX/NTE free text) while preserving HL7 framing, use the bundled redactor instead of hand-rolling it:
from openmed.interop.hl7v2 import redact_hl7v2
safe = redact_hl7v2("results.hl7") # path or message text
# PID-3 hashed, PID-5 name surrogated, PID-7 DOB date-shifted, OBX-5/NTE-3
# free text masked via openmed.deidentify — delimiters and segment order kept.redact_hl7v2 applies DEFAULT_FIELD_MAP (PID, PD1, NK1, GT1, IN1/IN2, OBX,
NTE). Extend or override it with field_map={("ZPS", 4): {"action": "hash"}}
for site-specific Z-segments, and pass date_shift_days= for a fixed,
interval-preserving shift.
\r, \r\n, or MLLP framing
(\x0b…\x1c\r). parse_hl7v2 auto-detects the segment separator; strip
MLLP control bytes before parsing.|^~\&. The adapter derives the
delimiter set from MSH-1/MSH-2 (HL7V2Encoding.from_msh_segment).~) and components (^).openmed.deidentify →
openmed.analyze_text.openmed.interop.hl7v2 provides parse_hl7v2,
redact_hl7v2, HL7Message, HL7Segment, HL7V2Encoding, HL7FieldRule,
and DEFAULT_FIELD_MAP for segment-aware de-id that preserves message
framing. It is parse-and-redact only — not a conformance validator.redact_hl7v2 hashes PID-3).\x0b
(start) and \x1c\r (end). Strip these before parse_hl7v2.\F\, \S\, \T\, \R\, \E\ encode literal
delimiters, and \.br\ is a line break inside OBX text. Unescape before NLP.TX, FT, ST, CE); numeric (NM) and coded-only values are not
free text. The default redactor restricts free-text redaction to FT/TX.Z* segments often carry extra PHI; add explicit
field_map rules — they are not in the default map.80da98c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.