demo mode: exclusive (not additive) on models / perf / live
Was: demo seeded on demo_start, then real producer events rendered on top of the synthetic bars/points/cells. Both sources visible simultaneously — visually confusing. Now: each widget tracks demoActive + a cachedReal store. - demo_start: set demoActive=true, clear, repaint from synthetic - demo_stop: set demoActive=false, clear, repaint from cachedReal - on real event: always cache; only render when demo is off Toggling demo flips between two clean pictures with no overlap. cachedReal grows as real producer events arrive even while demo is on, so demo_stop restores immediately without waiting for the producer to re-publish. Applied to: models bars, perf scatter, live detections. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a04ea60aef
commit
b6e478c578
2 changed files with 78 additions and 43 deletions
|
|
@ -1777,23 +1777,33 @@ 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;
|
||||
// Exclusive: demo on → only synthetic; demo off → only real.
|
||||
// cachedReal accumulates real model_metric events even while
|
||||
// demo is on, so toggling demo off restores the real picture
|
||||
// without waiting for the producer to re-publish.
|
||||
let demoActive = false;
|
||||
const cachedReal = new Map();
|
||||
function repaintFrom(src) {
|
||||
rows.clear(); root.innerHTML = '';
|
||||
if (src.size === 0) { emptyState(); return; }
|
||||
src.forEach((acc, model) => render(model, acc));
|
||||
}
|
||||
on('demo_start', () => {
|
||||
if (hasRealMetric) return;
|
||||
[ ['knn', 0.736], ['rnn', 0.872], ['gru', 0.911], ['lstm', 0.928], ['bert', 0.954] ]
|
||||
.forEach(([m, a]) => render(m, a));
|
||||
demoActive = true;
|
||||
const synth = new Map([
|
||||
['knn', 0.736], ['rnn', 0.872], ['gru', 0.911],
|
||||
['lstm', 0.928], ['bert', 0.954],
|
||||
]);
|
||||
repaintFrom(synth);
|
||||
});
|
||||
on('demo_stop', () => {
|
||||
// Don't wipe real data on demo toggle off.
|
||||
if (!hasRealMetric) { rows.clear(); emptyState(); }
|
||||
demoActive = false;
|
||||
repaintFrom(cachedReal);
|
||||
});
|
||||
on('model_metric', m => {
|
||||
if (!m.model || typeof m.accuracy !== 'number') return;
|
||||
hasRealMetric = true;
|
||||
render(m.model, m.accuracy);
|
||||
cachedReal.set(m.model, m.accuracy);
|
||||
if (!demoActive) render(m.model, m.accuracy);
|
||||
});
|
||||
emptyState();
|
||||
})();
|
||||
|
|
@ -2203,12 +2213,22 @@ def train_nn(*, model, X_train, y_train, X_val, y_val,
|
|||
}
|
||||
setInterval(updateStats, 500);
|
||||
|
||||
// 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;
|
||||
// Exclusive: demo on → only synthetic detections; demo off →
|
||||
// only real. The caps mean we hold the most recent N real
|
||||
// detections so toggling demo off restores them.
|
||||
let demoActive = false;
|
||||
const cachedReal = [];
|
||||
const REAL_CACHE_CAP = 600;
|
||||
function clearLanes() {
|
||||
lanes.forEach(l => l.row.remove());
|
||||
lanes.clear();
|
||||
eventTimes.length = 0;
|
||||
totalCorrect = 0; totalLabeled = 0;
|
||||
lastModel = null;
|
||||
latestEl.innerHTML = '<div class="live-latest-empty">awaiting <code>live_detection</code> events from the inference loop</div>';
|
||||
updateStats();
|
||||
}
|
||||
function paintDetection(d) {
|
||||
eventTimes.push(Date.now());
|
||||
if (d.model) lastModel = d.model;
|
||||
if (d.actual) {
|
||||
|
|
@ -2218,13 +2238,23 @@ def train_nn(*, model, X_train, y_train, X_val, y_val,
|
|||
paintCell(d);
|
||||
paintLatest(d);
|
||||
}
|
||||
function handleDetection(d) {
|
||||
if (!d.host_id || !d.predicted) return;
|
||||
paintDetection(d);
|
||||
}
|
||||
|
||||
on('live_detection', m => handleDetection(m, true));
|
||||
on('live_detection', m => {
|
||||
if (!m.host_id || !m.predicted) return;
|
||||
cachedReal.push(m);
|
||||
if (cachedReal.length > REAL_CACHE_CAP) cachedReal.shift();
|
||||
if (!demoActive) paintDetection(m);
|
||||
});
|
||||
|
||||
// Synthetic demo: 5 hosts, walk through phases, ~92% accuracy.
|
||||
let demoTimer = null;
|
||||
function demoStart() {
|
||||
if (hasRealDetection) return;
|
||||
demoActive = true;
|
||||
clearLanes();
|
||||
if (demoTimer) clearInterval(demoTimer);
|
||||
const HOSTS = [
|
||||
{ id: 'elliott-lab', profile: 'cpu-saturate', phaseIdx: 0 },
|
||||
|
|
@ -2257,16 +2287,12 @@ def train_nn(*, model, X_train, y_train, X_val, y_val,
|
|||
}, 280);
|
||||
}
|
||||
function demoStop() {
|
||||
demoActive = false;
|
||||
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;
|
||||
totalCorrect = 0; totalLabeled = 0;
|
||||
lastModel = null;
|
||||
latestEl.innerHTML = '<div class="live-latest-empty">awaiting <code>live_detection</code> events from the inference loop</div>';
|
||||
updateStats();
|
||||
clearLanes();
|
||||
// Replay cached real detections so the lanes don't sit empty
|
||||
// until the next live_detection event arrives.
|
||||
for (const m of cachedReal) paintDetection(m);
|
||||
}
|
||||
on('demo_start', demoStart);
|
||||
on('demo_stop', demoStop);
|
||||
|
|
@ -2517,24 +2543,33 @@ 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;
|
||||
// Exclusive: demo on → only synthetic; demo off → only real.
|
||||
let demoActive = false;
|
||||
const cachedReal = new Map(); // model → {latency, accuracy}
|
||||
function repaintFrom(src) {
|
||||
points.clear(); svg.innerHTML = '';
|
||||
if (src.size === 0) { emptyState(); return; }
|
||||
src.forEach((p, model) => render(model, p.latency, p.accuracy));
|
||||
}
|
||||
on('demo_start', () => {
|
||||
if (hasRealPerf) return;
|
||||
[
|
||||
{ model: 'knn', latency_us: 90, accuracy: 0.84 },
|
||||
{ model: 'rnn', latency_us: 380, accuracy: 0.87 },
|
||||
{ model: 'gru', latency_us: 520, accuracy: 0.91 },
|
||||
{ model: 'lstm', latency_us: 700, accuracy: 0.93 },
|
||||
{ model: 'bert', latency_us: 3200, accuracy: 0.95 },
|
||||
].forEach(p => render(p.model, p.latency_us, p.accuracy));
|
||||
demoActive = true;
|
||||
const synth = new Map([
|
||||
['knn', { latency: 90, accuracy: 0.84 }],
|
||||
['rnn', { latency: 380, accuracy: 0.87 }],
|
||||
['gru', { latency: 520, accuracy: 0.91 }],
|
||||
['lstm', { latency: 700, accuracy: 0.93 }],
|
||||
['bert', { latency: 3200, accuracy: 0.95 }],
|
||||
]);
|
||||
repaintFrom(synth);
|
||||
});
|
||||
on('demo_stop', () => {
|
||||
demoActive = false;
|
||||
repaintFrom(cachedReal);
|
||||
});
|
||||
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);
|
||||
cachedReal.set(m.model, { latency: m.latency_us, accuracy: m.accuracy });
|
||||
if (!demoActive) render(m.model, m.latency_us, m.accuracy);
|
||||
});
|
||||
emptyState();
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,6 @@
|
|||
</article>
|
||||
</div>
|
||||
|
||||
<script src="/static/dashboard.js?v=d4323f22"></script>
|
||||
<script src="/static/dashboard.js?v=4346accf"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue