All projects

Deployed · July 2025 – present

OmniDiag

Explainable clinical decision support

0.802

AUC, coronary module: leave-one-hospital-out across four hospitals (95% CI 0.675–0.887)

0.752

AUC, dysglycaemia screening: held-out 2017–18 NHANES cycle, n = 4,099

0.91

coronary sensitivity on the held-out hospital, with "uncertain" counted as referral

Problem

A clinical decision support system for resident and general physicians. Two modules ship. The coronary module ranks patients a clinician has already referred for catheterisation by the probability of a stenosis above 50%; it is not a general-population screen. The dysglycaemia module screens adults with no diabetes diagnosis for HbA1c ≥ 5.7% from 20 first-visit measurements. Each returns refer, no referral or uncertain, with a calibrated interval and the reasons behind it. It supports a clinician's decision; it does not diagnose.

Role & Approach

Built solo, end to end. Coronary: a spline GLM on seven pre-stress-test inputs from four UCI hospitals, calibrated with Venn-Abers, with a Mondrian conformal decision by sex and class. It is validated leave-one-hospital-out and on an external Tehran cohort, and its SHAP values are computed exactly in closed form. Dysglycaemia: an Explainable Boosting Machine trained on NHANES 2007–2018 (n = 26,904), Platt-calibrated, with conformal decisions by age band. Its contributions are the model, and every request checks that they sum to the score. Neither module publishes a threshold or risk bands, because the probability's meaning does not transfer between hospitals. Around the models: a DiCE-inspired counterfactual engine with a clinical firewall; human-in-the-loop review where doctors label uncertain cases and an admin retrains a candidate that a person must promote; a spaCy clinical-notes parser; rule-based and LLM reports; JWT and RBAC, Redis caching and rate limits. Adding a disease within a registered model family takes a YAML config, a schema and weights, with no routing or API changes.

Tech Stack

PythonFastAPISpline-GLM + Venn-AbersConformal predictionInterpretML (EBM)SHAPscikit-learnOptunaLightGBMspaCyMLflowPrometheusReact 18 / ViteHugging Face SpacesVercelDockerKubernetes / HelmPostgreSQLRedisGitHub Actions CI

Result

Deployed on Hugging Face Spaces, with the frontend on Vercel. Coronary AUC is 0.802 leave-one-hospital-out (95% CI 0.675–0.887). The prediction interval for a new hospital runs from 0.36 to 0.97, and the project says so instead of hiding it. Dysglycaemia AUC is 0.752 on the held-out 2017–2018 cycle, with a calibration slope of 0.952. The costs are published next to the numbers: about 40% of coronary patients land in "uncertain", and 14.1% of dysglycaemic patients receive "no referral". Prometheus metrics are live, and MLflow records each build's provenance. No clinician has reviewed the screens yet; that is the declared next step before any real use.

Media

OmniDiag coronary module in Engineering Mode: the result panel reads Refer — confirmatory testing recommended, with the calibrated interval 85.9%–87.1% and a SHAP explanation below
Coronary module, referral: the decision, its calibrated interval, and the SHAP reasons behind it.
OmniDiag coronary module: the result panel reads Uncertain — refer for further evaluation, in amber, with the calibrated interval 71.4%–73.3%
"Uncertain" is a third decision, not a middle risk band. It counts as a referral everywhere.
OmniDiag dysglycaemia screening: No referral indicated at 3.6%, with a next clinical step and a warning that no referral is not a clearance
Dysglycaemia, no referral: the panel states that 14.1% of truly dysglycaemic patients get this answer.
OmniDiag Clinical Insights: horizontal contribution bars per feature for a dysglycaemia screening, red raising and green lowering the estimate
Per-feature contributions read straight from the EBM. They sum exactly to the score.
To add — Demo video (2–5 min)
To add — Screenshot — Admin dashboard