import levenshtein from 'fast-levenshtein'; /** * Word Error Rate (WER) calculation using Levenshtein distance at word level. */ export const calculateWER = (reference: string, hypothesis: string): number => { const clean = (str: string) => str.toLowerCase().replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "").split(/\s+/).filter(w => w); const refWords = clean(reference); const hypWords = clean(hypothesis); if (refWords.length === 0) return hypWords.length > 0 ? 1 : 0; const wordMap = new Map(); let charCode = 0xE000; const getChar = (word: string) => { if (!wordMap.has(word)) wordMap.set(word, String.fromCharCode(charCode++)); return wordMap.get(word)!; }; const refChars = refWords.map(getChar).join(''); const hypChars = hypWords.map(getChar).join(''); return levenshtein.get(refChars, hypChars) / refWords.length; }; /** * Standard error formatter for catch blocks. */ export const formatError = (err: unknown): string => { if (err instanceof Error) return err.message; return String(err); };