Open infrastructure for matching trauma patients to clinical trials in real time. Pure-Python rule engine with clause-level reasoning trace and NEMSIS v3.5 ePCR adapter.
Project description
traumatrial-match
Open infrastructure for trauma trial eligibility matching. Evaluates structured trauma trial inclusion/exclusion rules against patient records in <100ms with clause-level reasoning trace.
MIT licensed. Synthetic data only — never PHI. Pure-Python core; pydantic for validation.
60-second example
from traumatrial_match import Patient, Trial, Rule, match
patient = Patient(
patient_id="P-001",
age_years=34,
sex="M",
gcs=7,
sbp_mmhg=82,
hr_bpm=128,
mechanism="blunt_mvc",
trauma_activation_level=1,
eta_minutes=4,
pregnancy_status="not_applicable",
anticoagulant_use=False,
presumed_tbi=True,
presumed_hemorrhage=True,
presumed_intracranial_hemorrhage=False,
spinal_injury_suspected=False,
)
trial = Trial(
trial_id="NCT05638581",
short_name="TROOP",
title="Trauma Resuscitation With Low-Titer Group O Whole Blood",
requires_efic=True,
inclusion=[
Rule(field="age_years", op="gte", value=15, hard=True),
Rule(field="presumed_hemorrhage", op="eq", value=True, hard=True),
Rule(field="trauma_activation_level", op="lte", value=1, hard=False),
],
exclusion=[
Rule(field="pregnancy_status", op="in",
value=["pregnant", "unknown_could_be_pregnant"], hard=True),
],
)
result = match(patient, trial)
print(result.eligible, result.confidence)
# True 1.0
for clause in result.trace:
mark = "HIT" if clause.hit else "MISS"
print(f" [{mark}] {clause.clause} patient={clause.patient_value}")
What's in the box
Patient— pydantic model for a trauma bay patient snapshot.Trial— pydantic model for a trial's structured eligibility rules.Rule— a single inclusion or exclusion clause: field + operator + value + hard/soft.MatchResult— eligibility, confidence, and a complete clause-level reasoning trace.match(patient, trial)— evaluate one patient against one trial.match_all(patient, trials)— evaluate against many; sorted eligible-first, confidence desc.from_nemsis_xml(xml_str)— convert a NEMSIS v3.5 ePCR XML into(Patient, NemsisConversionTrace). The trace records each Patient field asextracted/inferred/defaulted/skippedwith a one-line reason. v0 mapping covers ~10 high-signal eFields; everything else is honestly defaulted. Seetraumatrial_match/nemsis.py.
Operators (8)
eq, ne, gte, lte, gt, lt, in, not_in.
in and not_in require a list value; the others require a scalar.
Confidence rubric
- Any hard inclusion missed OR any hard exclusion hit →
eligible=False,confidence=0.0. - Otherwise →
eligible=True.confidence = soft_inclusion_hits / soft_inclusion_total, or1.0if no soft inclusions.
The categorical signal is eligible: bool; magnitude is confidence: float. There is no HIGH/MEDIUM/LOW enum.
Bundled corpus
The repo ships with 10 verified active trauma trials (TROOP, SWiFTCanada, ICECAP, SELECT-TBI, AEDH-MT, BOOST3, WEBSTER, FIT-BRAIN, Ketamine-TBI, ELASTIC) and 8 patient personas covering hemorrhage, TBI, anticoagulation, pregnancy exclusion, cardiac arrest, and pediatric exclusion. See trials/ and patients/.
These are hand-written from the public clinicaltrials.gov criteria. They are an approximation, not a clinical decision system. PRs welcome that improve fidelity, add trials, or extend the operator vocabulary.
Install (dev)
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"
pytest
Run the precompute script
This generates the static match payloads consumed by the Next.js demo in ../demo/.
python scripts/precompute.py
Auto-import a trial from clinicaltrials.gov
Watch a real trial become structured rules in 10 seconds. Fetches the trial from clinicaltrials.gov, sends the inclusion/exclusion text to Claude with our schema as the contract, validates the response with pydantic, and writes a engine/trials/NCT….json. If a criterion can't be expressed in our 8-operator vocabulary, it goes into _metadata.skipped_criteria instead of being silently dropped.
pip install -e ".[parse]"
export ANTHROPIC_API_KEY=sk-ant-... # or put it in a .env at the repo root
python scripts/parse_trial.py NCT05638581
python scripts/parse_trial.py NCT05638581 NCT04217551 NCT04995068 --overwrite
The schema is the constraint that keeps the LLM honest. Field types (int / bool / enum) and value ranges are injected into the system prompt, AND validated at load time by Rule._value_must_match_field_type. A hallucinated value like trauma_activation_level eq "massive_hemorrhage_protocol" (the LLM's first attempt at TROOP) fails pydantic validation, which feeds the error back to the model for a retry. Up to 3 attempts before giving up.
Always hand-review the generated JSON before committing — the LLM is good but not perfect. Look for gte vs gt off-by-ones, soft-vs-hard misclassifications, and the _metadata.skipped_criteria list for things that need a schema extension.
What this is NOT
- Not a clinical decision-support system.
- Not validated against real patient data.
- Not regulated, not certified, not BAA-able.
- Not a substitute for a research coordinator's clinical judgment.
It is a structured, testable, transparent starting point for talking about how trauma trial matching could be automated. Treat it that way.
Contributing
See ../CONTRIBUTING.md. PRs and issues welcome — particularly from trauma research coordinators, EMS data SMEs, and clinical trial operations folks who can tell us where the schema is wrong.
License
MIT. See ../LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file traumatrial_match-0.0.2.tar.gz.
File metadata
- Download URL: traumatrial_match-0.0.2.tar.gz
- Upload date:
- Size: 21.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fc94daf58534fbc662b7a15d742bea35b96383200c695ea6bc4a780e46bacb0
|
|
| MD5 |
f519cd91a1be6b3eaa2a77480e673663
|
|
| BLAKE2b-256 |
008128c112759751e056e7f08398332029829e55e5fd2b660a2cb90153526d3b
|
Provenance
The following attestation bundles were made for traumatrial_match-0.0.2.tar.gz:
Publisher:
publish-pypi.yml on jajjer/traumatrial
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
traumatrial_match-0.0.2.tar.gz -
Subject digest:
8fc94daf58534fbc662b7a15d742bea35b96383200c695ea6bc4a780e46bacb0 - Sigstore transparency entry: 1438440748
- Sigstore integration time:
-
Permalink:
jajjer/traumatrial@4d1da1a66c71fe312be02ec00ea8d4734483e6a2 -
Branch / Tag:
refs/tags/engine-v0.0.2 - Owner: https://github.com/jajjer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4d1da1a66c71fe312be02ec00ea8d4734483e6a2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file traumatrial_match-0.0.2-py3-none-any.whl.
File metadata
- Download URL: traumatrial_match-0.0.2-py3-none-any.whl
- Upload date:
- Size: 16.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31a06b962814c6410604c44831ff75dc237c9ed311729ea3d98cd738ad8c6274
|
|
| MD5 |
a3fbd860225d8295a08f6be7e53cf523
|
|
| BLAKE2b-256 |
221f14e5e6c4910e605c18af91a491e621dfe20f4d059c91649cb9696795ecd0
|
Provenance
The following attestation bundles were made for traumatrial_match-0.0.2-py3-none-any.whl:
Publisher:
publish-pypi.yml on jajjer/traumatrial
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
traumatrial_match-0.0.2-py3-none-any.whl -
Subject digest:
31a06b962814c6410604c44831ff75dc237c9ed311729ea3d98cd738ad8c6274 - Sigstore transparency entry: 1438440797
- Sigstore integration time:
-
Permalink:
jajjer/traumatrial@4d1da1a66c71fe312be02ec00ea8d4734483e6a2 -
Branch / Tag:
refs/tags/engine-v0.0.2 - Owner: https://github.com/jajjer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4d1da1a66c71fe312be02ec00ea8d4734483e6a2 -
Trigger Event:
push
-
Statement type: