diff --git a/src/lib/transcription/engineImpl.web.ts b/src/lib/transcription/engineImpl.web.ts index 0099af6..7beea30 100644 --- a/src/lib/transcription/engineImpl.web.ts +++ b/src/lib/transcription/engineImpl.web.ts @@ -40,7 +40,9 @@ interface AsrOutput { type AsrPipeline = (audio: Float32Array, opts: Record) => Promise; interface PipelineOptions { device?: string; - dtype?: string; + // A single dtype for all sub-models, or a per-file map (encoder_model, + // decoder_model_merged, …) — Whisper's decoder needs special handling on WASM. + dtype?: string | Record; progress_callback?: (e: { status?: string; progress?: number }) => void; } interface TransformersModule { @@ -111,10 +113,17 @@ export const engine: TranscriptionEngine = { const { pipeline } = await lib(); const webgpu = (await resolveBackend()) === 'webgpu'; const asr = await pipeline('automatic-speech-recognition', MODELS[modelId].webRepo, { - // WebGPU + fp16 when available; otherwise 8-bit weights on WASM, which - // stays small to download and runs acceptably on a plain CPU. device: webgpu ? 'webgpu' : 'wasm', - dtype: webgpu ? 'fp16' : 'q8', + // WebGPU: fp16 (fast, small). WASM: an explicit fp32 decoder. The default + // quantized decoders (q8/q4) use `MatMulNBits` ops that onnxruntime-web + // (transformers.js 4.2.0) FAILS to load on the WASM backend with + // "Missing required scale" — verified across Xenova + onnx-community + // repos. fp32 avoids those ops and actually runs on any CPU (the WASM + // path is what mobile and no-WebGPU devices use). Larger download, but it + // works; q8 simply does not load on WASM here. + dtype: webgpu + ? 'fp16' + : { encoder_model: 'fp32', decoder_model_merged: 'fp32' }, progress_callback: (e) => { if (e.status === 'progress' && e.progress != null) onProgress?.(e.progress / 100); },