Paranoid MRI Processing Pipeline: Domain Model and Clinician-in-the-Loop
The MRI Second Opinion engineering loop ingests DICOM data, generates a draft analysis in voxel-backed or metadata-fallback modes, passes safety checks and mandatory clinician review before finalization. A TypeScript orchestrator manages case state, while Python workers handle async processing. Separating orchestration from computation prevents out-of-memory errors and timeouts, logging events via Event Sourcing + Outbox.
Infrastructure Over Jupyter Scripts
In real clinics, DICOM archives often include files without extensions, custom private tags, and JPEG 2000 Lossless that crash libraries with segfaults. The ingestion worker deterministically unpacks data, validates DICOM UIDs per PS3.5 §9.1, and creates an ImagingStudyRef. For incomplete scans, it switches to metadata-fallback: pulling the clinical question from metadata or defaulting to “Routine second opinion review requested”.
Ingestion and delivery workers register in a DI container and activate via flags. This is CDS (Clinical Decision Support), not SaMD: DomainInvariantViolationError blocks finalize() without clinician review, per FDA CDS guidance section 520(o)(1)(E).
Key pipeline stages:
- Python-ingestion: Case creation from DICOM metadata.
- TypeScript-orchestrator: Initialize
MriSecondOpinionCase. - Python-processor: Draft in two modes.
- Safety policy: Shift to
AwaitingReview. - Human review: Physician finalization.
- Delivery: Structured report.
Strict Invariants in TypeScript
GenerateMriSecondOpinionUseCase applies IClinicalSafetyPolicy with flags for low confidence, significant disagreement, or insufficient data. It draws from ACR Practice Parameters (Res. 11, 2020). Physicians with Neuroradiologist or Attending Physician roles update the draft; the aggregate tracks humanReview.modifications separately from the AI draft.
State machine: 9 statuses (INGESTING → QC_REJECTED, SUBMITTED → AWAITING_REVIEW → REVIEWED → FINALIZED → DELIVERY_PENDING → DELIVERED / DELIVERY_FAILED). 4 managed by the domain aggregate, 5 by intake/delivery loops.
Separating Orchestration from Computation
TypeScript API returns HTTP 200 instantly, while async Python processors tackle GPU tasks. Contracts (port, DI token, safety policy) enable swapping adapters for multimodal engines without rebuilds. Storage: SQLite locally, PostgreSQL in production.
Reports map to HL7 FHIR R4 DiagnosticReport via ImagingStudyRef, with export to DICOM SR (SOP Class 1.2.840.10008.5.1.4.1.1.88.33). RadiologyReportSummary uses RadLex (RSNA) and SNOMED CT. Capturing edits addresses dataset shift/model drift for future retraining.
Metrics and Observability
Prometheus metrics: counters/histograms for ingestion/processing/delivery, gauges like mri_second_opinion_cases_awaiting_review (by urgency) and mri_second_opinion_review_wait_started_at_unix. Alerts on timeouts in AwaitingReview for critical cases (stroke).
Open Repo: Tests and Workbench
Standalone repo with Dockerfile, docker-compose, SECURITY.md. 176/177 tests passing (state machine, PostgreSQL, auth). Positioned as clinician-in-the-loop workflow, Research Use Only, no regulatory approvals.
Release features:
- Full case lifecycle with retries and audit.
- Split TypeScript (orchestration, validation, reviewer UI) and Python (dispatch, fallback).
- Blocks delivery without review.
- Metrics and standards export.
Key Takeaways
- Pipeline enforces human-in-the-loop at code level, blocking finalization without review.
- Metadata-fallback avoids crashes on corrupted DICOM.
- Separating compute and orchestration simplifies AI adapter swaps.
- FHIR/DICOM SR + RadLex/SNOMED ensures interoperability.
- Metrics target review times for clinical risk management.
— Editorial Team
No comments yet.