From a04ea60aef0b0a8086410bc2bd03da535eb0c68d Mon Sep 17 00:00:00 2001 From: Max Gorog Date: Fri, 8 May 2026 16:32:55 -0500 Subject: [PATCH] demo mode: never overwrite real data on perf / models / live MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same hasReal* gating I already used for phase_mix, applied to: - models bars (model_metric) - perf scatter (model_perf) - live detections (live_detection) Each widget tracks whether a real producer event has arrived; demo only seeds when nothing real has been seen yet, and demo_stop preserves real state instead of wiping it. demoTick is now a no-op — periodic model_metric jitter was overwriting real values mid-stream. Per-widget one-shot seeding on demo_start (gated by hasReal*) is enough. Co-Authored-By: Claude Opus 4.7 (1M context) --- training/dashboard/static/dashboard.js | 55 +++++++++++++++----------- training/dashboard/static/index.html | 2 +- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/training/dashboard/static/dashboard.js b/training/dashboard/static/dashboard.js index 0d5b233..e7555fe 100644 --- a/training/dashboard/static/dashboard.js +++ b/training/dashboard/static/dashboard.js @@ -224,20 +224,12 @@ const demoBtn = document.getElementById('demo-btn'); let demoTimer = null; let demoActive = false; - // Demo mode is intentionally narrow: it does NOT synthesize - // `episode` events (those would clobber the real ingest counter, - // host bars, and database explorer — all of which work fine in or - // out of demo mode), and it does NOT synthesize `phase` events - // (the baseline scene reads dataset-derived `phase_mix` instead). - // The only periodic side-effect is occasional model_metric jitter - // so the bars don't sit frozen during a long talk. - function demoTick() { - if (Math.random() < 0.05) { - 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 }); - } - } + // Demo mode is intentionally narrow. Real data wins everywhere — + // demo only seeds widgets that haven't received any real producer + // output yet. demoTick is now a no-op; the per-widget demo_start + // handlers do the (one-shot) seeding, so we don't drown out real + // data with periodic synthetic noise. + function demoTick() {} function setDemo(active) { if (demoActive === active) return; demoActive = active; @@ -1785,19 +1777,22 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, r.fill.style.width = (visible * 100).toFixed(1) + '%'; r.acc.textContent = accuracy.toFixed(3); } + // Real data wins. Demo only fills in when no real model_metric + // has arrived yet — once the producer publishes for any model, + // demo never overwrites it. + let hasRealMetric = false; on('demo_start', () => { - // KNN sits below the recurrent/transformer family; it memorizes - // the train host's feature space and generalizes worse than a - // model that learned temporal structure. Bar visible-scale starts - // at 0.5 so the real cross-host F1 (~0.43) reads as 0% — that's - // honest, just visually flat. Demo value here is the in-distribution - // ballpark for a healthier display. + if (hasRealMetric) return; [ ['knn', 0.736], ['rnn', 0.872], ['gru', 0.911], ['lstm', 0.928], ['bert', 0.954] ] .forEach(([m, a]) => render(m, a)); }); - on('demo_stop', () => { rows.clear(); emptyState(); }); + on('demo_stop', () => { + // Don't wipe real data on demo toggle off. + if (!hasRealMetric) { rows.clear(); emptyState(); } + }); on('model_metric', m => { if (!m.model || typeof m.accuracy !== 'number') return; + hasRealMetric = true; render(m.model, m.accuracy); }); emptyState(); @@ -2208,8 +2203,12 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, } setInterval(updateStats, 500); - function handleDetection(d) { + // Track whether real live_detection events have arrived. Demo + // only fills in when nothing real is flowing — never overwrites. + let hasRealDetection = false; + function handleDetection(d, fromReal) { if (!d.host_id || !d.predicted) return; + if (fromReal) hasRealDetection = true; eventTimes.push(Date.now()); if (d.model) lastModel = d.model; if (d.actual) { @@ -2220,11 +2219,12 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, paintLatest(d); } - on('live_detection', handleDetection); + on('live_detection', m => handleDetection(m, true)); // Synthetic demo: 5 hosts, walk through phases, ~92% accuracy. let demoTimer = null; function demoStart() { + if (hasRealDetection) return; if (demoTimer) clearInterval(demoTimer); const HOSTS = [ { id: 'elliott-lab', profile: 'cpu-saturate', phaseIdx: 0 }, @@ -2258,6 +2258,8 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, } function demoStop() { if (demoTimer) { clearInterval(demoTimer); demoTimer = null; } + // Don't wipe real data on demo toggle off. + if (hasRealDetection) return; lanes.forEach(l => l.row.remove()); lanes.clear(); eventTimes.length = 0; @@ -2515,7 +2517,11 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, rec.g.querySelector('text').textContent = model; repaintLabels(); } + // Real data wins. Demo only fills in when no real model_perf has + // arrived yet. + let hasRealPerf = false; on('demo_start', () => { + if (hasRealPerf) return; [ { model: 'knn', latency_us: 90, accuracy: 0.84 }, { model: 'rnn', latency_us: 380, accuracy: 0.87 }, @@ -2524,9 +2530,10 @@ def train_nn(*, model, X_train, y_train, X_val, y_val, { model: 'bert', latency_us: 3200, accuracy: 0.95 }, ].forEach(p => render(p.model, p.latency_us, p.accuracy)); }); - on('demo_stop', emptyState); + on('demo_stop', () => { if (!hasRealPerf) emptyState(); }); on('model_perf', m => { if (!m.model || typeof m.latency_us !== 'number' || typeof m.accuracy !== 'number') return; + hasRealPerf = true; render(m.model, m.latency_us, m.accuracy); }); emptyState(); diff --git a/training/dashboard/static/index.html b/training/dashboard/static/index.html index 1b96fdc..f8f4885 100644 --- a/training/dashboard/static/index.html +++ b/training/dashboard/static/index.html @@ -1314,6 +1314,6 @@ - +