From af1f7fb56dee22ccf7f3d3b1f37beed8ac5a64ae Mon Sep 17 00:00:00 2001 From: Max Gorog Date: Fri, 8 May 2026 16:27:25 -0500 Subject: [PATCH] demo mode: backfill phase mix + knn metric (no clobber on real data) Two targeted fixes for the demo-toggle path; intentionally narrow so we don't override widgets that already work in both modes (KNN scatter, DB explorer). Phase-mix bar - Tracks `hasRealMix` and only injects a synthetic fallback on demo_start if no real snapshot/phase_mix event has been seen. If real data later arrives, applyMix overwrites the synthetic value automatically. - Synthetic numbers mirror a real production run (500/78705 episodes, ~4.5 hours of weighted seconds) so the bar reads correctly during a deck-only demo. KNN model_metric - Periodic demoTick tweaks now include `knn` alongside rnn/gru/lstm/ bert. Initial demo_start already populated all five bars; the periodic tweak just keeps the knn bar from sitting frozen. Co-Authored-By: Claude Opus 4.7 (1M context) --- training/dashboard/static/dashboard.js | 36 ++++++++++++++++++++++++-- training/dashboard/static/index.html | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/training/dashboard/static/dashboard.js b/training/dashboard/static/dashboard.js index b5e8d80..275974b 100644 --- a/training/dashboard/static/dashboard.js +++ b/training/dashboard/static/dashboard.js @@ -238,8 +238,8 @@ } // Occasionally tweak a model metric so the bars aren't static. if (Math.random() < 0.05) { - const m = ['rnn', 'gru', 'lstm', 'bert'][Math.floor(Math.random() * 4)]; - const base = { rnn: 0.872, gru: 0.911, lstm: 0.928, bert: 0.954 }[m]; + const m = ['knn', 'rnn', 'gru', 'lstm', 'bert'][Math.floor(Math.random() * 5)]; + const base = { knn: 0.736, rnn: 0.872, gru: 0.911, lstm: 0.928, bert: 0.954 }[m]; dispatch({ type: 'model_metric', model: m, accuracy: base + (Math.random() - 0.5) * 0.012 }); } } @@ -1345,8 +1345,15 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, function fmtInt(n) { return (typeof n === 'number') ? n.toLocaleString() : '—'; } + // Tracks whether real phase_mix data has arrived. demo_start uses + // this to decide whether to inject a synthetic fallback — if real + // data is already present (from snapshot or the phase_mix feeder), + // the demo toggle does NOT clobber it. + let hasRealMix = false; + function applyMix(mix) { if (!mix) return; + hasRealMix = true; const w = mix.weighted_seconds || {}; const c = mix.counts || {}; // Prefer time-weighted proportions; fall back to label counts. @@ -1378,6 +1385,31 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, on('snapshot', m => { if (m.phase_mix) applyMix(m.phase_mix); }); on('phase_mix', applyMix); + on('demo_start', () => { + // Synthetic fallback: only fires if no real phase_mix has + // arrived yet (e.g. dashboard offline, or feeder hasn't done its + // first compute). The numbers below mirror a real production run + // so the bar reads correctly during a deck-only demo. If real + // data later arrives via snapshot or phase_mix event, applyMix + // overwrites this on the spot. + if (hasRealMix) return; + applyMix({ + weighted_seconds: { + clean: 2659.1, armed: 725.7, infecting: 1607.4, + infected_running: 8308.3, dormant: 3059.3, + }, + counts: { + clean: 451, armed: 198, infecting: 379, + infected_running: 614, dormant: 223, + }, + sampled_episodes: 500, + population_episodes: 78705, + total_labels: 1865, + }); + // applyMix flipped hasRealMix to true — flip back so a future + // real arrival is still recognised as the first real one. + hasRealMix = false; + }); on('demo_stop', () => { // Demo toggle off doesn't wipe the dataset mix — the dataset is // ground truth, the demo only fakes per-event widgets. diff --git a/training/dashboard/static/index.html b/training/dashboard/static/index.html index e11ab00..2705411 100644 --- a/training/dashboard/static/index.html +++ b/training/dashboard/static/index.html @@ -1314,6 +1314,6 @@ - +