llm-semantic-visualizer / tests /saeControls.test.js
hbauzan's picture
fix(sae,compare): unstick train UI and restore GROUP_* badges
bfd323f
Raw
History Blame Contribute Delete
4.44 kB
import { describe, expect, it } from 'vitest';
import {
DEFAULT_SAE_SETTINGS,
computeActivationMetrics,
formatSaeTrainProgress,
loadSaeSettings,
resolveSaeSettings,
saveSaeSettings,
} from '../src/ui/saeControlsDefaults.js';
import {
applySaeToCompare,
cosineSimilarity,
densifyTopKActivations,
} from '../src/core/saeReplace.js';
describe('saeControlsDefaults', () => {
it('round-trips enabled + train params through localStorage', () => {
const store = new Map();
const storage = {
getItem: (k) => (store.has(k) ? store.get(k) : null),
setItem: (k, v) => { store.set(k, String(v)); },
removeItem: (k) => { store.delete(k); },
key: (i) => [...store.keys()][i] ?? null,
get length() { return store.size; },
};
const settings = resolveSaeSettings({
enabled: true,
hiddenDim: 4096,
k: 16,
epochs: 10,
lr: 0.002,
batchSize: 32,
});
saveSaeSettings(settings, storage);
const loaded = loadSaeSettings(storage);
expect(loaded.enabled).toBe(true);
expect(loaded.hiddenDim).toBe(4096);
expect(loaded.k).toBe(16);
expect(loaded.epochs).toBe(10);
expect(loaded.batchSize).toBe(32);
});
it('defaults are 8192 / k32 / 20ep and purge legacy poisoned keys', () => {
expect(DEFAULT_SAE_SETTINGS.enabled).toBe(false);
expect(DEFAULT_SAE_SETTINGS.hiddenDim).toBe(8192);
expect(DEFAULT_SAE_SETTINGS.k).toBe(32);
expect(DEFAULT_SAE_SETTINGS.epochs).toBe(20);
expect(loadSaeSettings(null).enabled).toBe(false);
const store = new Map([
['vl3d.sae.hiddenDim', '32'],
['vl3d.sae.k', '1'],
['vl3d.sae.epochs', '1'],
['vl3d.sae.enabled', 'true'],
]);
const storage = {
getItem: (k) => (store.has(k) ? store.get(k) : null),
setItem: (k, v) => { store.set(k, String(v)); },
removeItem: (k) => { store.delete(k); },
key: (i) => [...store.keys()][i] ?? null,
get length() { return store.size; },
};
const loaded = loadSaeSettings(storage);
expect(loaded.hiddenDim).toBe(8192);
expect(loaded.k).toBe(32);
expect(loaded.epochs).toBe(20);
expect(loaded.enabled).toBe(false);
expect(store.has('vl3d.sae.hiddenDim')).toBe(false);
});
it('computes L0 / sparsity from sparse rows', () => {
const acts = [
[1, 0, 0, 2],
[0, 0, 3, 0],
];
const m = computeActivationMetrics(acts);
expect(m.dim).toBe(4);
expect(m.l0).toBe(1.5);
expect(m.activeFeatures).toBe(3);
});
it('formats train progress with done / left / percent', () => {
const mid = formatSaeTrainProgress({
status: 'training',
phase_key: 'training',
message: 'Training epoch 13/50 — 38 remaining · last loss=0.012345',
current_epoch: 12,
total_epochs: 50,
remaining_epochs: 38,
percent: 24,
n_vectors: 40,
resolved_hidden: 160,
resolved_k: 16,
});
expect(mid.busy).toBe(true);
expect(mid.label).toContain('38 remaining');
expect(mid.meta).toContain('12/50 done');
expect(mid.meta).toContain('38 left');
expect(mid.meta).toContain('24%');
expect(mid.percent).toBe(24);
const prep = formatSaeTrainProgress({
status: 'training',
phase_key: 'preparing',
message: '',
current_epoch: 0,
total_epochs: 50,
});
expect(prep.label).toMatch(/Preparing/i);
expect(prep.meta).toContain('0/50 done');
expect(prep.meta).toContain('50 left');
expect(prep.indeterminate).toBe(true);
});
});
describe('saeReplace', () => {
it('recomputes compare cosine_vs_first in SAE space', () => {
const raw = {
count: 2,
items: [
{ id: 'tok_0', text: 'a', embedding: [1, 0] },
{ id: 'tok_1', text: 'b', embedding: [0, 1] },
],
};
const acts = [
[1, 0, 0],
[1, 0, 0],
];
const next = applySaeToCompare(raw, acts);
expect(next.items[0].cosine_vs_first).toBe(1);
expect(next.items[1].cosine_vs_first).toBeCloseTo(1, 5);
expect(cosineSimilarity([1, 0], [0, 1])).toBeCloseTo(0, 5);
});
it('densifies Top-K sparse encode payload', () => {
const dense = densifyTopKActivations({
format: 'topk_sparse',
indices: [[0, 2], [1, 3]],
values: [[1.5, 0.5], [2, 0]],
dimension: 4,
k: 2,
});
expect(dense).toEqual([
[1.5, 0, 0.5, 0],
[0, 2, 0, 0],
]);
});
});