knn scatter: exclusive demo (not additive)

Same pattern as models / perf / live: cachedReal accumulates real
embedding events at all times; demoActive flag gates which source
renders.
- demo on  → only synthetic clusters
- demo off → only real embeddings (replayed from cachedReal)

Cache cap 5000 points to bound memory across long sessions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Max Gorog 2026-05-08 16:36:31 -05:00
parent b6e478c578
commit ef6bc71009
2 changed files with 31 additions and 6 deletions

View file

@ -2102,8 +2102,29 @@ def train_nn(*, model, X_train, y_train, X_val, y_val,
rebuildLegend();
}
on('demo_start', loadSynthetic);
on('demo_stop', () => { points.length = 0; resetStats(); rebuildLegend(); });
// Exclusive: demo on → only synthetic clusters; demo off → only
// real embedding events. cachedReal accumulates real points
// even while demo is on, so demo_stop restores them.
let demoActive = false;
const cachedReal = [];
const REAL_CACHE_CAP = 5000;
function repaintFromReal() {
points.length = 0;
resetStats();
for (const pt of cachedReal) {
points.push(pt);
addStat(pt);
}
rebuildLegend();
}
on('demo_start', () => {
demoActive = true;
loadSynthetic();
});
on('demo_stop', () => {
demoActive = false;
repaintFromReal();
});
on('embedding', m => {
if (typeof m.x !== 'number' || typeof m.y !== 'number') return;
const pt = {
@ -2113,9 +2134,13 @@ def train_nn(*, model, X_train, y_train, X_val, y_val,
predicted: m.predicted,
cluster: typeof m.cluster === 'number' ? m.cluster : undefined,
};
points.push(pt);
addStat(pt);
rebuildLegend();
cachedReal.push(pt);
if (cachedReal.length > REAL_CACHE_CAP) cachedReal.shift();
if (!demoActive) {
points.push(pt);
addStat(pt);
rebuildLegend();
}
});
rebuildLegend();

View file

@ -1314,6 +1314,6 @@
</article>
</div>
<script src="/static/dashboard.js?v=4346accf"></script>
<script src="/static/dashboard.js?v=33d3c0e6"></script>
</body>
</html>