Church Translate, as it runs at the campuses
Live sermon translation into 100+ languages, running entirely on one Mac per campus: audio in, voice detection, Whisper on the GPU, NLLB-200 in its own process, and a built-in web server the congregation reads from on their phones. No audio leaves the building. This is the source only. What used to sit beside it — 14 GB of speech and translation models, 8 GB of voice bundles, 6 GB of natural voices, the Electron caches, and the app data holding the operator PIN and every service transcript — is machine state and downloaded assets, and stays in the NAS backup where it belongs. The vendored node, cloudflared and Windows ffmpeg binaries are out too (533 MB, re-fetched for packaging); the browser-side runtime beside them is kept, so a checkout still serves the congregation page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/* Measure the hallucination and what actually stops it. Runs the same model,
|
||||
dtype and cache the app uses, in a plain node process like mt-service.js. */
|
||||
import { pipeline, env } from '@huggingface/transformers';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
env.allowLocalModels = false;
|
||||
env.cacheDir = path.join(os.homedir(), 'Library/Application Support/Church Translate Models');
|
||||
|
||||
const t0 = Date.now();
|
||||
const pipe = await pipeline('translation', 'Xenova/nllb-200-distilled-600M', { dtype: 'fp32' });
|
||||
console.log('loaded in ' + ((Date.now() - t0) / 1000).toFixed(1) + 's\n');
|
||||
|
||||
/* The exact lines from the operator's screenshot, plus stress cases. */
|
||||
const CASES = [
|
||||
'Donald Trump.',
|
||||
'Hello, hello.',
|
||||
'His name was John.',
|
||||
'Open up to the book, Matthew.',
|
||||
'Today we are going to talk about Peter in the Bible.',
|
||||
'Turn with me to John chapter 3.',
|
||||
'Amen.',
|
||||
'Jesus wept.',
|
||||
'Paul wrote to the church at Corinth.',
|
||||
'How many countries in the world?',
|
||||
'And God said, let there be light, and there was light.'
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
{ name: 'current (no params)', opts: {} },
|
||||
{ name: 'cap 2x+8', opts: null, cap: (n) => Math.ceil(n * 2) + 8 },
|
||||
{ name: 'cap 2x+8 +nrng3', opts: { no_repeat_ngram_size: 3 }, cap: (n) => Math.ceil(n * 2) + 8 },
|
||||
{ name: 'beams2 cap2x+8', opts: { num_beams: 2, no_repeat_ngram_size: 3 }, cap: (n) => Math.ceil(n * 2) + 8 }
|
||||
];
|
||||
|
||||
const tok = pipe.tokenizer;
|
||||
for (const cfg of CONFIGS) {
|
||||
console.log('=== ' + cfg.name + ' ===');
|
||||
let total = 0;
|
||||
for (const text of CASES) {
|
||||
const nIn = tok(text).input_ids.dims.at(-1);
|
||||
const opts = Object.assign({ src_lang: 'eng_Latn', tgt_lang: 'spa_Latn' }, cfg.opts || {});
|
||||
if (cfg.cap) opts.max_new_tokens = cfg.cap(nIn);
|
||||
const s = Date.now();
|
||||
const out = await pipe(text, opts);
|
||||
const ms = Date.now() - s;
|
||||
total += ms;
|
||||
const got = (Array.isArray(out) ? out[0] : out).translation_text.trim();
|
||||
const flag = (!/[??]/.test(text) && /[??]/.test(got)) ? ' <-- INVENTED A QUESTION' : '';
|
||||
console.log(' ' + String(ms).padStart(5) + 'ms in' + String(nIn).padStart(3) + ' ' + got + flag);
|
||||
}
|
||||
console.log(' total ' + total + 'ms\n');
|
||||
}
|
||||
Reference in New Issue
Block a user