Transcription: don't pass task/language to English-only models
CI / test (push) Successful in 18s
CI / deploy-web (push) Successful in 38s
CI / build-apk (push) Successful in 6m40s

Whisper ".en" models reject `task`/`language`; transformers.js throws
"Cannot specify `task` or `language` for an English-only model" if either
is passed — and the web engine passed `task: 'transcribe'` (plus a
possibly-undefined `language`) unconditionally. Since the default model is
tiny.en, web/PWA transcription failed for every default user.

Only add `task`/`language` for multilingual models now (translation also
requires a multilingual model), and never pass an explicit `undefined`
language (which trips the same check). Mirror the same gate on native
(whisper.rn) so a .en model is never asked to do a language/translate it
can't.

tsc clean, 279 tests pass, web export OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 09:39:24 +02:00
parent 06519faea2
commit e3ed03a471
2 changed files with 19 additions and 6 deletions
+6 -2
View File
@@ -196,11 +196,15 @@ export const engine: TranscriptionEngine = {
// Float32Array whose `.buffer` is exactly these samples.
const pcm = audio.samples.slice();
// English-only models (".en") only do English — only pass language/translate
// for multilingual models (mirrors the web engine; keeps whisper.cpp from
// being asked to do something a .en model can't).
const multilingual = MODELS[opts.modelId].multilingual;
// transcribeData returns { stop, promise }; await the promise.
const { promise } = ctx.transcribeData(pcm.buffer, {
// language undefined => whisper.rn auto-detects ('auto').
language: opts.language,
translate: opts.translate ?? false,
...(multilingual && opts.language ? { language: opts.language } : {}),
...(multilingual ? { translate: opts.translate ?? false } : {}),
});
const result = await promise;
+13 -4
View File
@@ -117,13 +117,22 @@ export const engine: TranscriptionEngine = {
const asr = loaded.get(opts.modelId);
if (!asr) throw new Error(`Model "${opts.modelId}" is not loaded; call loadModel() first.`);
const out = await asr(audio.samples, {
// English-only Whisper models (".en") reject `language`/`task` — transformers.js
// throws "Cannot specify `task` or `language` for an English-only model" if
// either is passed. Only multilingual models accept them (translation also
// requires a multilingual model). So we add those keys ONLY when multilingual,
// and never pass an explicit `undefined` language (which trips the same check).
const genOpts: Record<string, unknown> = {
return_timestamps: true,
// One window at a time; 30s matches Whisper's frame so it won't re-chunk.
chunk_length_s: 30,
language: opts.language,
task: opts.translate ? 'translate' : 'transcribe',
});
};
if (MODELS[opts.modelId].multilingual) {
genOpts.task = opts.translate ? 'translate' : 'transcribe';
if (opts.language) genOpts.language = opts.language;
}
const out = await asr(audio.samples, genOpts);
const segments: Segment[] = [];
for (const c of out.chunks ?? []) {