Summary
The TriageEngine in src/medex/medical/triage.py is a complete 567-line rule-based ESI triage engine that is never called by the API. The /api/v1/triage/assess endpoint in run_api.py ignores the engine entirely — it sends a free-text prompt to the LLM, attempts fragile regex parsing of the response, and returns hardcoded empty arrays for red_flags and vital_concerns. This makes triage non-functional.
Beyond wiring, a clinical audit against the ESI Handbook v4 (Gilboy et al.) reveals significant protocol deviations that must be corrected for the engine to produce clinically valid assessments.
Current Behavior
- User fills in triage form (chief complaint, vitals, pain level) → UI POSTs to
/api/v1/triage/assess
- Endpoint converts structured vitals to a text string, sends to
medex_app.query() (generic LLM call)
- Attempts
re.search(r"esi[:\s-]*(\d)", response_text) to extract ESI level — fails because the LLM responds in Spanish and rarely outputs "ESI" literally
- Falls back to keyword matching (
"resucitación", "crítico") — equally unreliable
red_flags: [] and vital_concerns: [] are always hardcoded empty
- Response takes 5-30 seconds (LLM round-trip) for what should be an instantaneous deterministic assessment
Expected Behavior
- Endpoint instantiates
TriageEngine and calls engine.assess() directly
- Returns structured
TriageAssessment.to_dict() with real red flags, discriminators, disposition recommendations
- Assessment is deterministic, instantaneous, and clinically aligned with ESI v4
Technical Tasks
A. Wire TriageEngine to API Endpoint
B. Clinical Hardening — ESI v4 Alignment
-
Fix ESI algorithm flow — The real ESI algorithm verifies vital signs in "danger zone" after estimating ≥2 resources (ESI 3), and upgrades to ESI 2 if vitals are abnormal. The current engine does not perform this post-resource vital sign recheck. Ref: ESI Handbook v4, Decision Point D.
-
Align vital sign thresholds to ESI danger zone:
| Vital Sign |
Current (Critical) |
ESI Danger Zone |
| Heart Rate |
>150 / <40 |
>100 / <50 |
| Systolic BP |
<80 |
<90 |
| SpO₂ |
<88% |
<92% |
| Resp Rate |
>35 / <8 |
>20 / <10 |
-
GCS 9-13 → ESI 2 — Currently only GCS <9 triggers Level 1. GCS 9-13 (altered sensorium, responds to stimuli) should classify as ESI 2 per protocol.
-
Pain ≥7 as independent ESI 2 discriminator — ESI: "high risk / confused / lethargic / disoriented OR severe pain/distress." Currently pain_scale is only checked inside _check_high_risk_vitals. Should be an independent Decision Point B check.
-
Resource estimation: clinical heuristics, not patient keywords — The current engine searches the patient's text for words like "radiografía" or "laboratorio." The patient doesn't know what resources they need. Implement clinical heuristic mapping:
- Headache + fever → labs + imaging = 2 resources = ESI 3
- Laceration → suture = 1 resource = ESI 4
- Sore throat (no red flags, normal vitals) → 0 resources = ESI 5
-
Improve red flag matching robustness — Current if flag in text misses "me duele el pecho" (only matches "dolor de pecho"). Add synonym lists, partial matching, or at minimum expand the sets.
-
Age-adjusted vital sign thresholds — HR 140 in a 2-year-old is normal; in a 70-year-old it's critical. At minimum add adult vs. pediatric threshold sets.
-
Missing ESI 2 presentations — Add: acute psychiatric danger to self/others, sexual assault, fever + immunosuppression, acute abdomen in >65y, obstetric emergencies (eclampsia, ectopic, abruption).
Files Affected
| File |
Change |
run_api.py (L696-747) |
Replace LLM call with TriageEngine |
src/medex/medical/triage.py |
Clinical hardening (thresholds, flow, resource heuristics) |
src/medex/medical/models.py |
Add age-based threshold support to VitalSigns if needed |
src/medex/medical/service.py (L228) |
Fix is_emergency() signature |
tests/ |
New tests validating ESI classification against known clinical scenarios |
Clinical References
- Gilboy N, Tanabe T, Travers D, Rosenau AM. Emergency Severity Index (ESI): A Triage Tool for Emergency Department Care. Version 4. AHRQ Publication No. 12-0014. 2012.
- ESI Implementation Handbook, Chapter 2: The ESI Algorithm.
Summary
The
TriageEngineinsrc/medex/medical/triage.pyis a complete 567-line rule-based ESI triage engine that is never called by the API. The/api/v1/triage/assessendpoint inrun_api.pyignores the engine entirely — it sends a free-text prompt to the LLM, attempts fragile regex parsing of the response, and returns hardcoded empty arrays forred_flagsandvital_concerns. This makes triage non-functional.Beyond wiring, a clinical audit against the ESI Handbook v4 (Gilboy et al.) reveals significant protocol deviations that must be corrected for the engine to produce clinically valid assessments.
Current Behavior
/api/v1/triage/assessmedex_app.query()(generic LLM call)re.search(r"esi[:\s-]*(\d)", response_text)to extract ESI level — fails because the LLM responds in Spanish and rarely outputs "ESI" literally"resucitación","crítico") — equally unreliablered_flags: []andvital_concerns: []are always hardcoded emptyExpected Behavior
TriageEngineand callsengine.assess()directlyTriageAssessment.to_dict()with real red flags, discriminators, disposition recommendationsTechnical Tasks
A. Wire TriageEngine to API Endpoint
/api/v1/triage/assess(run_api.pyL696-747) with directTriageEngine.assess()callVitalSignsdataclass fields:bp_systolic→blood_pressure_systolicbp_diastolic→blood_pressure_diastolicspo2→oxygen_saturationTriageAssessment.to_dict()(includesred_flags,discriminators,disposition,recommended_action)MedicalService.is_emergency()signature mismatch — callsTriageEngine.is_emergency(chief_complaint=..., vital_signs=...)but actual signature is(self, text: str)B. Clinical Hardening — ESI v4 Alignment
Fix ESI algorithm flow — The real ESI algorithm verifies vital signs in "danger zone" after estimating ≥2 resources (ESI 3), and upgrades to ESI 2 if vitals are abnormal. The current engine does not perform this post-resource vital sign recheck. Ref: ESI Handbook v4, Decision Point D.
Align vital sign thresholds to ESI danger zone:
GCS 9-13 → ESI 2 — Currently only GCS <9 triggers Level 1. GCS 9-13 (altered sensorium, responds to stimuli) should classify as ESI 2 per protocol.
Pain ≥7 as independent ESI 2 discriminator — ESI: "high risk / confused / lethargic / disoriented OR severe pain/distress." Currently pain_scale is only checked inside
_check_high_risk_vitals. Should be an independent Decision Point B check.Resource estimation: clinical heuristics, not patient keywords — The current engine searches the patient's text for words like "radiografía" or "laboratorio." The patient doesn't know what resources they need. Implement clinical heuristic mapping:
Improve red flag matching robustness — Current
if flag in textmisses"me duele el pecho"(only matches"dolor de pecho"). Add synonym lists, partial matching, or at minimum expand the sets.Age-adjusted vital sign thresholds — HR 140 in a 2-year-old is normal; in a 70-year-old it's critical. At minimum add adult vs. pediatric threshold sets.
Missing ESI 2 presentations — Add: acute psychiatric danger to self/others, sexual assault, fever + immunosuppression, acute abdomen in >65y, obstetric emergencies (eclampsia, ectopic, abruption).
Files Affected
run_api.py(L696-747)src/medex/medical/triage.pysrc/medex/medical/models.pysrc/medex/medical/service.py(L228)is_emergency()signaturetests/Clinical References