Translate transcripts with Opus-MT in a dedicated translation worker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
82ed34c7f9
commit
a0748b5fa6
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Transcript-translation speed on onnxruntime-node (CPU), D-WS4b.
|
||||
*
|
||||
* flock /tmp/whisper-heavy.lock bun scripts/bench-translate.ts [batchSizes…]
|
||||
*
|
||||
* Translates the reference transcripts in tests/audio_files/ with the same
|
||||
* Opus-MT loader and translation loop the browser worker uses, once per
|
||||
* batch size, and prints wall-clock time, pieces, and whether the output
|
||||
* depends on the batch size (padding). Downloads ~113 MB per direction into
|
||||
* the Transformers.js cache on first run. Not browser numbers: WASM in a
|
||||
* tab is slower than onnxruntime-node.
|
||||
*/
|
||||
|
||||
import { join } from "node:path";
|
||||
import { buildTranslationUnits, getTranslationModel, translateUnits } from "../src/lib/translation";
|
||||
import { loadOpusMt } from "../src/worker/opusMt";
|
||||
|
||||
const AUDIO = join(import.meta.dir, "../tests/audio_files");
|
||||
const batchSizes = process.argv.slice(2).map(Number).filter((n) => n > 0);
|
||||
const sizes = batchSizes.length > 0 ? batchSizes : [1, 8];
|
||||
|
||||
for (const [file, source, target] of [
|
||||
["en_alien.txt", "en", "da"],
|
||||
["da_festuge.txt", "da", "en"],
|
||||
] as const) {
|
||||
const model = getTranslationModel(source, target)!;
|
||||
const text = await Bun.file(join(AUDIO, file)).text();
|
||||
const units = buildTranslationUnits({ text, chunks: [] }).map((unit) => unit.text);
|
||||
const loadStart = performance.now();
|
||||
const translator = await loadOpusMt(model.id);
|
||||
const loadMs = performance.now() - loadStart;
|
||||
console.log(`\n${file} (${text.length} chars, ${units.length} lines) ${model.label} — load ${loadMs.toFixed(0)} ms`);
|
||||
let baseline: string[] | null = null;
|
||||
for (const batchSize of sizes) {
|
||||
let pieces = 0;
|
||||
const start = performance.now();
|
||||
const out = await translateUnits(units, {
|
||||
batchSize,
|
||||
countTokens: translator.countTokens,
|
||||
translateBatch: (batch) => translator.translateBatch(batch),
|
||||
onProgress: (_done, total) => (pieces = total),
|
||||
});
|
||||
const seconds = (performance.now() - start) / 1000;
|
||||
const same = baseline === null ? "baseline" : JSON.stringify(out) === JSON.stringify(baseline) ? "identical" : "differs";
|
||||
baseline ??= out;
|
||||
console.log(
|
||||
` batch ${String(batchSize).padStart(2)}: ${seconds.toFixed(2)} s, ${pieces} pieces, ` +
|
||||
`${(text.length / seconds).toFixed(0)} chars/s, output ${same}`,
|
||||
);
|
||||
}
|
||||
console.log(` sample: ${baseline!.slice(0, 3).join(" | ")}`);
|
||||
await translator.dispose();
|
||||
}
|
||||
+13
-2
@@ -3,7 +3,8 @@
|
||||
*
|
||||
* Emits a self-contained `dist/`:
|
||||
* 1. `bun build src/index.html` → hashed app-shell assets
|
||||
* 2. inference worker bundle at the stable /inference.worker.js URL
|
||||
* 2. inference + translate worker bundles at stable /inference.worker.js and
|
||||
* /translate.worker.js URLs
|
||||
* 3. manifest + icons + onnxruntime .wasm/.mjs (dist/ort/, offline inference)
|
||||
* 4. sw.js with the precache manifest + a content-hash version injected
|
||||
*
|
||||
@@ -53,6 +54,15 @@ const workerBuild = await Bun.build({
|
||||
});
|
||||
const workerScript = await workerBuild.outputs[0]!.text();
|
||||
await Bun.write(join(DIST, "inference.worker.js"), workerScript);
|
||||
// Transcript-translation worker (D-WS4b), same mechanism.
|
||||
const translateWorkerBuild = await Bun.build({
|
||||
entrypoints: [join(ROOT, "src/worker/translate.worker.ts")],
|
||||
target: "browser",
|
||||
format: "esm",
|
||||
minify: true,
|
||||
});
|
||||
const translateWorkerScript = await translateWorkerBuild.outputs[0]!.text();
|
||||
await Bun.write(join(DIST, "translate.worker.js"), translateWorkerScript);
|
||||
|
||||
// 3. Static PWA assets + onnxruntime wasm binaries (offline inference — the
|
||||
// worker loads them from /ort/ instead of the jsdelivr CDN). The manifest and
|
||||
@@ -89,6 +99,7 @@ const precachePaths = precachePathsFromOutputs(
|
||||
shellBuild.outputs.map((output) => relative(DIST, output.path)),
|
||||
[
|
||||
"inference.worker.js",
|
||||
"translate.worker.js",
|
||||
"manifest.webmanifest",
|
||||
...ICON_SIZES.map((size) => `icons/icon-${size}.png`),
|
||||
...fontFiles.map((name) => `fonts/${name}`),
|
||||
@@ -98,7 +109,7 @@ const fontBytes = await Promise.all(
|
||||
fontFiles.map((name) => Bun.file(join(FONTS_DIR, name)).bytes()),
|
||||
);
|
||||
const version = Bun.hash(
|
||||
JSON.stringify(precachePaths) + swSource + workerScript + fontBytes.map((b) => Bun.hash(b)).join(),
|
||||
JSON.stringify(precachePaths) + swSource + workerScript + translateWorkerScript + fontBytes.map((b) => Bun.hash(b)).join(),
|
||||
).toString(36);
|
||||
await Bun.write(join(DIST, "sw.js"), injectPrecacheManifest(swSource, precachePaths, version));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user