Personalised metabolic syndrome tracker: reads the medical record, connects to wearable trends, and gives clinically-contextualised, medication-aware feedback on whether interventions are actually working.
# 1. Add your OpenAI key to .env
echo "OPENAI_API_KEY=sk-..." > .env
# 2. Start everything
./start.sh
# Opens http://localhost:5174# Backend (terminal 1)
conda activate local_llm
uvicorn backend.api.main:app --port 8001 --reload
# Frontend (terminal 2)
cd frontend && npm run dev- Metabolic Syndrome Score — ATP III criteria, 5/5 evaluated with live interactive sliders
- Biomarker Status — colour-coded cards with personalized targets (medication-aware)
- Clinical Insights — GPT-4.1-mini narrative: "is this treatment working?" per medication
- Lab Trends — recharts line charts with reference target lines
- Wearable Trends — 90-day simulated series (HR, HRV, steps, sleep)
- Interactive Input Panel — live MetS score recalculation from user-adjusted waist + BP values
Male, 48 · Metabolic syndrome · Family history: premature CAD Medications: Atorvastatin 40mg (8 wks) · Metformin 1000mg (12 wks) Story: statin working (LDL 165→95), metformin working (HbA1c 6.3→5.9), HDL still low, hs-CRP improving.
HOMA-IR = (fasting_glucose_mg/dL × fasting_insulin_μU/mL) / 405
Reference: Matthews et al., Diabetologia 1985.
Implemented in backend/engine/biomarker.py → compute_homa_ir().
Auto-computed whenever both fasting glucose and insulin readings are present.
Clinical threshold for insulin resistance: HOMA-IR ≥ 2.5.
| # | Criterion | Threshold | Note |
|---|---|---|---|
| 1 | Fasting glucose | ≥ 100 mg/dL | Latest reading used |
| 2 | Triglycerides | ≥ 150 mg/dL | Latest reading used |
| 3 | HDL-C (male) | < 40 mg/dL | Sex-specific |
| 3 | HDL-C (female) | < 50 mg/dL | Sex-specific |
| 4 | Blood pressure | ≥ 130/85 mmHg or on antihypertensive | Medication flag counts |
| 5 | Waist circumference (male) | ≥ 102 cm | ATP III |
| 5 | Waist circumference (female) | ≥ 88 cm | ATP III |
MetS positive when criteriaMet ≥ 3.
BP criterion is met if the patient is on any antihypertensive medication (ACE inhibitor, ARB, CCB, beta-blocker, thiazide) regardless of current reading — consistent with ATP III rules for treated hypertension.
Implemented in backend/engine/mets.py → evaluate_mets().
Exponentially Weighted Moving Average applied to each biomarker's time series:
μ_t = α · x_t + (1 - α) · μ_{t-1}
| Parameter | Value | When used |
|---|---|---|
| α = 0.40 | More weight to recent values | < 10 readings (sparse lab data) |
| α = 0.15 | Smoother signal | ≥ 10 readings (dense wearable data) |
Slope is estimated by ordinary least-squares linear regression. Dates are converted to fractional months to make the slope interpretable as units-per-month:
month(d) = 12 × year + month + day/30
β̂ = Σ (xᵢ - x̄)(yᵢ - ȳ) / Σ (xᵢ - x̄)²
Direction thresholds:
| Condition | Direction |
|---|---|
| ` | β̂ |
β̂ < 0 (lower-is-better biomarker) |
improving |
β̂ > 0 (lower-is-better biomarker) |
worsening |
β̂ > 0 (higher-is-better: HDL, HRV, steps, sleep) |
improving |
β̂ < 0 (higher-is-better) |
worsening |
Implemented in backend/engine/trends.py → ewma_trend(), direction_for_display().
Default population-level thresholds are overridden when the patient's medication and risk profile support a tighter evidence-based target:
| Medication class | Biomarker | Personalized target | Guideline |
|---|---|---|---|
| Statin + high CV risk ¹ | LDL-C | < 70 mg/dL | ACC/AHA 2018 |
| Statin (no high CV risk) | LDL-C | < 100 mg/dL | ACC/AHA 2018 |
| Metformin / SGLT2 / GLP-1 | HbA1c | < 7.0% | ADA 2024 |
| Any antihypertensive | Systolic BP | < 130 mmHg | ACC/AHA 2017 |
| Any antihypertensive | Diastolic BP | < 80 mmHg | ACC/AHA 2017 |
¹ High CV risk = family history of premature CAD or metabolic syndrome diagnosis.
Borderline zone: values within 15% above the target maximum are flagged borderline rather than out_of_target to reflect clinical grey zones.
Implemented in backend/engine/biomarker.py → personalized_targets().
For each medication with a tracked start date, the engine computes a before/after signal per biomarker:
1. baseline = earliest reading of the biomarker (any date)
2. current = latest reading
3. Δ = current − baseline
4. weeks_on = (today − medication.startedAt).days / 7
Interpretation rules
Lower-is-better (LDL, TG, HbA1c, glucose, BP, HOMA-IR):
Δ < 0 → improving
Δ > +1 → worsening
else → stable
Higher-is-better (HDL, HRV, steps, sleep_hours):
Δ > 0 → improving
Δ < −1 → worsening
else → stable
Implemented in backend/engine/signals.py → response_signals().
frontend/ React 19 + Vite + TypeScript + Tailwind v4 + recharts
backend/ FastAPI (local_llm conda env) + OpenAI GPT-4.1-mini
├── engine/
│ ├── mets.py ATP III scoring
│ ├── biomarker.py Status evaluation + personalized targets
│ ├── trends.py EWMA + slope regression
│ └── signals.py Medication response signals
├── config/
│ └── thresholds.py Clinical reference ranges
└── data/
└── seed.py Demo patient data generator
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Liveness probe |
| GET | /seed |
Full pre-computed demo patient data |
| POST | /narrative |
GPT-4.1-mini narrative cards (cached) |
| POST | /compute |
Live engine re-evaluation (interactive panel) |
| Variable | Required | Notes |
|---|---|---|
OPENAI_API_KEY |
Yes | For narrative generation |
Hackathon demo — not a medical device. For informational purposes only. Always consult your care team.