// Replay the REAL production clustering (diar.js clusterWindows) on cached // window embeddings, so thresholds can be tuned without re-running sherpa or the // embedding pass. Usage: // [DIAR_COS_FINE=..] [DIAR_COS_FINAL=..] node lab-eval.mjs [expect] [numSpeakers] [markers.json] import fs from 'node:fs'; import { clusterWindows } from './diar-v2.js'; const [file, expect, numSp, markerFile] = process.argv.slice(2); const d = JSON.parse(fs.readFileSync(file, 'utf8')); const ok = d.wins.filter(w => w.v).map(w => ({ ti: w.ti, start: w.s, end: w.e, vec: w.v })); const markers = markerFile ? JSON.parse(fs.readFileSync(markerFile, 'utf8')) : []; const segs = clusterWindows(d.turns, ok, +(numSp || 0), markers); const talk = new Map(), first = new Map(), last = new Map(); for (const s of segs) { talk.set(s.speaker, (talk.get(s.speaker) || 0) + (s.end - s.start)); if (!first.has(s.speaker)) first.set(s.speaker, s.start); last.set(s.speaker, s.end); } const order = [...talk.entries()].sort((a, b) => b[1] - a[1]); console.log(`RESULT ${d.model.split('/').pop()} fine=${process.env.DIAR_COS_FINE || 0.35} final=${process.env.DIAR_COS_FINAL || 0.30}` + ` → speakers=${order.length}${expect ? ` (expect ${expect})` : ''} segs=${segs.length}`); console.log(' ' + order.map(([sp, t]) => `${sp.replace('speaker_', '')}:${t.toFixed(0)}s[${(first.get(sp) / 60).toFixed(1)}-${(last.get(sp) / 60).toFixed(1)}]`).join(' '));