@happyvertical/speech 0.110.1 → 0.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +3 -0
- package/README.md +46 -3
- package/dist/adapters/local/audio.d.ts.map +1 -1
- package/dist/adapters/local/worker.d.ts +8 -5
- package/dist/adapters/local/worker.d.ts.map +1 -1
- package/dist/adapters/openai-compatible-transcriber.d.ts +3 -0
- package/dist/adapters/openai-compatible-transcriber.d.ts.map +1 -1
- package/dist/adapters/openai-compatible.d.ts +3 -0
- package/dist/adapters/openai-compatible.d.ts.map +1 -1
- package/dist/adapters/openai-realtime.d.ts +8 -0
- package/dist/adapters/openai-realtime.d.ts.map +1 -1
- package/dist/adapters/qwen3.d.ts +6 -0
- package/dist/adapters/qwen3.d.ts.map +1 -1
- package/dist/adapters/studio-server.d.ts +11 -0
- package/dist/adapters/studio-server.d.ts.map +1 -1
- package/dist/adapters/voxtral-realtime.d.ts +8 -0
- package/dist/adapters/voxtral-realtime.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/chunks/errors-CzsR_5dE.js +122 -0
- package/dist/chunks/errors-CzsR_5dE.js.map +1 -0
- package/dist/chunks/{registry-DFSF3WoM.js → registry-DhB2589P.js} +5 -5
- package/dist/chunks/registry-DhB2589P.js.map +1 -0
- package/dist/chunks/{wav-vzrhhZbu.js → wav-PxVZJX9V.js} +15 -5
- package/dist/chunks/{wav-vzrhhZbu.js.map → wav-PxVZJX9V.js.map} +1 -1
- package/dist/chunks/{websocket-YbAfY6YI.js → websocket-DLZLLyD9.js} +2 -2
- package/dist/chunks/{websocket-YbAfY6YI.js.map → websocket-DLZLLyD9.js.map} +1 -1
- package/dist/conversation-server.js +2 -2
- package/dist/conversation.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +302 -20
- package/dist/index.js.map +1 -1
- package/dist/local.js +21 -12
- package/dist/local.js.map +1 -1
- package/dist/pcm.js +2 -2
- package/dist/shared/audio.d.ts.map +1 -1
- package/dist/shared/errors.d.ts +48 -1
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/http.d.ts +3 -0
- package/dist/shared/http.d.ts.map +1 -1
- package/dist/shared/probe.d.ts +90 -0
- package/dist/shared/probe.d.ts.map +1 -0
- package/dist/shared/streaming-factory.d.ts.map +1 -1
- package/dist/shared/streaming-types.d.ts +10 -1
- package/dist/shared/streaming-types.d.ts.map +1 -1
- package/dist/shared/types.d.ts +14 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/wav.d.ts +12 -1
- package/dist/shared/wav.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/errors-Cs0vX-mV.js +0 -64
- package/dist/chunks/errors-Cs0vX-mV.js.map +0 -1
- package/dist/chunks/registry-DFSF3WoM.js.map +0 -1
package/dist/local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { f as readEnv, h as reportSpeechUsage, o as defaultEnv, p as resolveTranscriberConfig, r as registerOptionalTranscriber, v as normalizeAudioInput, y as normalizeMimeType } from "./chunks/registry-
|
|
1
|
+
import { a as SpeechError, c as SpeechProviderUnavailableError, i as SpeechConfigurationError, o as SpeechInvalidAudioError, r as SpeechAudioTooLargeError, s as SpeechProviderError } from "./chunks/errors-CzsR_5dE.js";
|
|
2
|
+
import { f as readEnv, h as reportSpeechUsage, o as defaultEnv, p as resolveTranscriberConfig, r as registerOptionalTranscriber, v as normalizeAudioInput, y as normalizeMimeType } from "./chunks/registry-DhB2589P.js";
|
|
3
3
|
//#region src/adapters/local/env.ts
|
|
4
4
|
/**
|
|
5
5
|
* Environment configuration for the `local` transcriber. Explicit options win,
|
|
@@ -205,14 +205,14 @@ async function decodeAudioBytes(bytes, options) {
|
|
|
205
205
|
if (essence && PCM_MIME_TYPES.has(essence)) return decodeRawPcm(bytes, options);
|
|
206
206
|
const decoded = await decodeExternally(bytes, options);
|
|
207
207
|
if (decoded) return decoded;
|
|
208
|
-
throw new
|
|
208
|
+
throw new SpeechInvalidAudioError(`Cannot decode '${options.mimeType}' audio on this runtime: pass WAV or audio/pcm, or supply a decodeAudio hook (e.g. ffmpeg) for compressed formats`, ADAPTER);
|
|
209
209
|
}
|
|
210
210
|
function isRiffWave(bytes) {
|
|
211
211
|
return bytes.byteLength >= 12 && ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WAVE";
|
|
212
212
|
}
|
|
213
213
|
/** Parses a RIFF/WAVE file into per-channel Float32 samples. */
|
|
214
214
|
function decodeWav(bytes) {
|
|
215
|
-
if (!isRiffWave(bytes)) throw new
|
|
215
|
+
if (!isRiffWave(bytes)) throw new SpeechInvalidAudioError("Invalid WAV data: missing RIFF/WAVE header", ADAPTER);
|
|
216
216
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
217
217
|
let format;
|
|
218
218
|
let offset = 12;
|
|
@@ -222,7 +222,7 @@ function decodeWav(bytes) {
|
|
|
222
222
|
const body = offset + 8;
|
|
223
223
|
if (id === "fmt ") format = readWavFormat(view, body, size);
|
|
224
224
|
else if (id === "data") {
|
|
225
|
-
if (!format) throw new
|
|
225
|
+
if (!format) throw new SpeechInvalidAudioError("Invalid WAV data: data chunk before fmt chunk", ADAPTER);
|
|
226
226
|
const available = bytes.byteLength - body;
|
|
227
227
|
const length = size === 0 || size > available ? available : size;
|
|
228
228
|
return {
|
|
@@ -232,13 +232,13 @@ function decodeWav(bytes) {
|
|
|
232
232
|
}
|
|
233
233
|
offset = body + size + size % 2;
|
|
234
234
|
}
|
|
235
|
-
throw new
|
|
235
|
+
throw new SpeechInvalidAudioError("Invalid WAV data: no data chunk", ADAPTER);
|
|
236
236
|
}
|
|
237
237
|
var WAVE_FORMAT_PCM = 1;
|
|
238
238
|
var WAVE_FORMAT_IEEE_FLOAT = 3;
|
|
239
239
|
var WAVE_FORMAT_EXTENSIBLE = 65534;
|
|
240
240
|
function readWavFormat(view, body, size) {
|
|
241
|
-
if (size < 16) throw new
|
|
241
|
+
if (size < 16) throw new SpeechInvalidAudioError("Invalid WAV data: fmt chunk too short", ADAPTER);
|
|
242
242
|
let tag = view.getUint16(body, true);
|
|
243
243
|
const channels = view.getUint16(body + 2, true);
|
|
244
244
|
const sampleRate = view.getUint32(body + 4, true);
|
|
@@ -253,7 +253,7 @@ function readWavFormat(view, body, size) {
|
|
|
253
253
|
32: "f32le",
|
|
254
254
|
64: "f64le"
|
|
255
255
|
}[bitsPerSample] : void 0;
|
|
256
|
-
if (!encoding || channels < 1 || sampleRate < 1) throw new
|
|
256
|
+
if (!encoding || channels < 1 || sampleRate < 1) throw new SpeechInvalidAudioError(`Unsupported WAV encoding (format ${tag}, ${bitsPerSample}-bit, ${channels} channels); use PCM or IEEE float, or supply a decodeAudio hook`, ADAPTER);
|
|
257
257
|
return {
|
|
258
258
|
channels,
|
|
259
259
|
sampleRate,
|
|
@@ -265,8 +265,8 @@ function decodeRawPcm(bytes, options) {
|
|
|
265
265
|
const sampleRate = positiveInteger(params.rate) ?? explicitPositiveInteger(options.sampleRate, "sampleRate");
|
|
266
266
|
const channels = positiveInteger(params.channels) ?? explicitPositiveInteger(options.channels, "channels") ?? 1;
|
|
267
267
|
const encoding = (params.encoding ?? "s16le").toLowerCase();
|
|
268
|
-
if (!sampleRate) throw new
|
|
269
|
-
if (encoding !== "s16le" && encoding !== "f32le") throw new
|
|
268
|
+
if (!sampleRate) throw new SpeechInvalidAudioError("Raw PCM audio needs a sample rate: use 'audio/pcm;rate=16000' or AudioInput.sampleRate", ADAPTER);
|
|
269
|
+
if (encoding !== "s16le" && encoding !== "f32le") throw new SpeechInvalidAudioError(`Unsupported raw PCM encoding '${encoding}' (expected s16le or f32le)`, ADAPTER);
|
|
270
270
|
return {
|
|
271
271
|
samples: deinterleave(bytes, channels, encoding),
|
|
272
272
|
sampleRate
|
|
@@ -371,7 +371,7 @@ async function decodeWithAudioContext(bytes) {
|
|
|
371
371
|
try {
|
|
372
372
|
buffer = await context.decodeAudioData(copy.buffer);
|
|
373
373
|
} catch (error) {
|
|
374
|
-
throw new
|
|
374
|
+
throw new SpeechInvalidAudioError(`The browser could not decode the audio: ${errorMessage$1(error)}`, ADAPTER);
|
|
375
375
|
}
|
|
376
376
|
return {
|
|
377
377
|
samples: Array.from({ length: buffer.numberOfChannels }, (_, index) => buffer.getChannelData(index)),
|
|
@@ -397,7 +397,7 @@ function positiveInteger(value) {
|
|
|
397
397
|
/** Validates a caller-supplied `AudioInput.sampleRate`/`channels`. */
|
|
398
398
|
function explicitPositiveInteger(value, name) {
|
|
399
399
|
if (value === void 0 || value === null) return;
|
|
400
|
-
if (!Number.isInteger(value) || value < 1) throw new
|
|
400
|
+
if (!Number.isInteger(value) || value < 1) throw new SpeechInvalidAudioError(`AudioInput.${name} must be a positive integer, got ${String(value)}`, ADAPTER);
|
|
401
401
|
return value;
|
|
402
402
|
}
|
|
403
403
|
function ascii(bytes, offset, length) {
|
|
@@ -927,6 +927,12 @@ function transferList(audio) {
|
|
|
927
927
|
return [...new Set(views.map((view) => view.buffer))];
|
|
928
928
|
}
|
|
929
929
|
function serializeError(error) {
|
|
930
|
+
if (error instanceof SpeechAudioTooLargeError) return {
|
|
931
|
+
name: error.name,
|
|
932
|
+
message: error.message,
|
|
933
|
+
code: error.code,
|
|
934
|
+
maxBytes: error.maxBytes
|
|
935
|
+
};
|
|
930
936
|
if (error instanceof SpeechError) return {
|
|
931
937
|
name: error.name,
|
|
932
938
|
message: error.message,
|
|
@@ -944,7 +950,10 @@ function serializeError(error) {
|
|
|
944
950
|
function deserializeError(error) {
|
|
945
951
|
switch (error.name) {
|
|
946
952
|
case "SpeechConfigurationError": return new SpeechConfigurationError(error.message, "local");
|
|
953
|
+
case "SpeechInvalidAudioError": return new SpeechInvalidAudioError(error.message, "local");
|
|
954
|
+
case "SpeechAudioTooLargeError": return new SpeechAudioTooLargeError(error.message, error.maxBytes ?? NaN, "local");
|
|
947
955
|
case "SpeechProviderError": return new SpeechProviderError("local", error.message);
|
|
956
|
+
case "SpeechProviderUnavailableError": return new SpeechProviderUnavailableError("local", error.message);
|
|
948
957
|
case "AbortError": return new DOMException(error.message, "AbortError");
|
|
949
958
|
default: {
|
|
950
959
|
const restored = new Error(error.message);
|
package/dist/local.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.js","names":[],"sources":["../src/adapters/local/env.ts","../src/adapters/local/runtime.ts","../src/adapters/local/abort.ts","../src/adapters/local/audio.ts","../src/adapters/local/transcriber.ts","../src/adapters/local/worker.ts","../src/local.ts"],"sourcesContent":["/**\n * Environment configuration for the `local` transcriber. Explicit options win,\n * then the factory context, then `HAVE_SPEECH_TRANSCRIBER_*` variables (Node).\n */\n\nimport {\n defaultEnv,\n readEnv,\n resolveTranscriberConfig,\n} from '../../shared/env.js';\nimport type { SpeechFactoryContext } from '../../shared/factory.js';\nimport type { LocalTranscriberOptions } from './types.js';\n\n/** Local-only environment variables, in priority order. */\nexport const LOCAL_TRANSCRIBER_ENV_KEYS = {\n device: ['HAVE_SPEECH_TRANSCRIBER_DEVICE'],\n dtype: ['HAVE_SPEECH_TRANSCRIBER_DTYPE'],\n cacheDir: ['HAVE_SPEECH_TRANSCRIBER_CACHE_DIR'],\n modelHost: ['HAVE_SPEECH_TRANSCRIBER_MODEL_HOST'],\n} as const;\n\n/**\n * Merges explicit options with the environment. `model` and `maxBytes` reuse\n * the shared `HAVE_SPEECH_TRANSCRIBER_MODEL`/`_MAX_BYTES` keys.\n */\nexport function resolveLocalTranscriberOptions(\n options: LocalTranscriberOptions = {},\n context: Pick<SpeechFactoryContext, 'env'> = {},\n): LocalTranscriberOptions {\n const env = context.env ?? defaultEnv();\n const shared = resolveTranscriberConfig(\n { model: options.model, maxBytes: options.maxBytes },\n { env },\n );\n\n return {\n ...options,\n type: 'local',\n model: shared.model,\n maxBytes: shared.maxBytes,\n device:\n options.device ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.device),\n dtype: options.dtype ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.dtype),\n cacheDir:\n options.cacheDir ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.cacheDir),\n modelHost:\n options.modelHost ??\n readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.modelHost),\n };\n}\n","// biome-ignore-all lint/style/useNamingConvention: transformers.js options and outputs use snake_case and PascalCase exports.\n/**\n * Loads the optional `@huggingface/transformers` peer dependency and builds\n * `automatic-speech-recognition` pipelines from it.\n *\n * This is the only module that names the peer. It is reachable only from the\n * `@happyvertical/speech/local` entry, never from the core entry.\n */\n\nimport { SpeechConfigurationError } from '../../shared/errors.js';\nimport type {\n LocalTranscriberDevice,\n LocalTranscriberDtype,\n LocalTranscriberProgressCallback,\n LocalTransformersRuntime,\n} from './types.js';\n\nexport const TRANSFORMERS_PACKAGE = '@huggingface/transformers';\n\nexport const TRANSFORMERS_INSTALL_HINT =\n `Local transcription needs the optional peer dependency ${TRANSFORMERS_PACKAGE}. ` +\n `Install it with \\`pnpm add ${TRANSFORMERS_PACKAGE}\\` (or npm/yarn equivalent).`;\n\n/** One ASR result as returned by transformers.js. */\nexport interface AsrOutput {\n text: string;\n chunks?: Array<{ text: string; timestamp: [number, number | null] }>;\n}\n\n/** Callable `automatic-speech-recognition` pipeline (structural subset). */\nexport interface AsrPipeline {\n (\n audio: Float32Array,\n options?: Record<string, unknown>,\n ): Promise<AsrOutput | AsrOutput[]>;\n dispose?: () => Promise<void>;\n model?: {\n config?: { model_type?: string };\n generation_config?: { is_multilingual?: boolean };\n };\n}\n\n/** The parts of the transformers.js module this adapter uses. */\nexport interface TransformersModule {\n pipeline: (\n task: 'automatic-speech-recognition',\n model: string,\n options: Record<string, unknown>,\n ) => Promise<AsrPipeline>;\n env?: Record<string, unknown>;\n /** transformers.js v4 cache inspection; absent on older builds. */\n ModelRegistry?: {\n is_pipeline_cached(\n task: 'automatic-speech-recognition',\n model: string,\n options?: Record<string, unknown>,\n ): Promise<boolean>;\n };\n InterruptableStoppingCriteria?: new () => { interrupt(): void };\n}\n\n/**\n * Resolves the transformers.js module: an injected module or loader, else a\n * dynamic import of the peer. A missing peer becomes a\n * `SpeechConfigurationError` with an install hint.\n */\nexport async function loadTransformers(\n runtime: LocalTransformersRuntime | undefined,\n): Promise<TransformersModule> {\n let loaded: unknown;\n try {\n loaded =\n typeof runtime === 'function'\n ? await runtime()\n : (runtime ?? (await importTransformers()));\n } catch (error) {\n if (error instanceof SpeechConfigurationError) {\n throw error;\n }\n const configurationError = new SpeechConfigurationError(\n `${TRANSFORMERS_INSTALL_HINT} (${errorMessage(error)})`,\n 'local',\n );\n configurationError.cause = error;\n throw configurationError;\n }\n\n // Accept both the ES namespace and a CommonJS-style `default` wrapper.\n const candidate = loaded as\n | (Partial<TransformersModule> & { default?: TransformersModule })\n | undefined;\n const module =\n typeof candidate?.pipeline === 'function'\n ? (candidate as TransformersModule)\n : candidate?.default;\n\n if (typeof module?.pipeline !== 'function') {\n throw new SpeechConfigurationError(\n `${TRANSFORMERS_PACKAGE} did not export pipeline(); expected v4 or later`,\n 'local',\n );\n }\n return module;\n}\n\n/** Isolated so tests can observe whether the peer was ever imported. */\nexport function importTransformers(): Promise<unknown> {\n return import('@huggingface/transformers');\n}\n\nexport interface CreatePipelineOptions {\n model: string;\n device: LocalTranscriberDevice;\n dtype?: LocalTranscriberDtype;\n revision?: string;\n cacheDir?: string;\n onProgress?: LocalTranscriberProgressCallback;\n}\n\n/**\n * Creates the ASR pipeline. `device: 'auto'` resolves to WebGPU when a\n * browser adapter is available (retrying on WASM if WebGPU fails to\n * initialise) and to CPU elsewhere.\n */\nexport async function createAsrPipeline(\n transformers: TransformersModule,\n options: CreatePipelineOptions,\n): Promise<{ pipeline: AsrPipeline; device: string }> {\n const device =\n options.device === 'auto' ? await detectAutoDevice() : options.device;\n const build = (selected: string) =>\n transformers.pipeline('automatic-speech-recognition', options.model, {\n device: selected,\n ...(options.dtype ? { dtype: options.dtype } : {}),\n ...(options.revision ? { revision: options.revision } : {}),\n ...(options.cacheDir ? { cache_dir: options.cacheDir } : {}),\n ...(options.onProgress ? { progress_callback: options.onProgress } : {}),\n });\n\n try {\n return { pipeline: await build(device), device };\n } catch (error) {\n if (options.device === 'auto' && device === 'webgpu') {\n return { pipeline: await build('wasm'), device: 'wasm' };\n }\n throw error;\n }\n}\n\n/** `webgpu` when a browser exposes a usable adapter, `wasm` in other browsers, `cpu` in Node. */\nexport async function detectAutoDevice(): Promise<string> {\n if (isNodeRuntime()) {\n return 'cpu';\n }\n\n const gpu = (\n globalThis.navigator as\n | { gpu?: { requestAdapter(): Promise<unknown> } }\n | undefined\n )?.gpu;\n if (gpu) {\n try {\n if (await gpu.requestAdapter()) {\n return 'webgpu';\n }\n } catch {\n // No usable adapter: fall through to WASM.\n }\n }\n return 'wasm';\n}\n\nexport function isNodeRuntime(): boolean {\n return Boolean(\n (globalThis as { process?: { versions?: { node?: string } } }).process\n ?.versions?.node,\n );\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Rejects with the signal's reason as soon as it aborts, without waiting for\n * `promise` (which keeps running; its eventual result is ignored).\n */\nexport function raceAbort<T>(\n promise: Promise<T>,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (!signal) {\n return promise;\n }\n if (signal.aborted) {\n // Mark the abandoned promise handled so a later rejection is not reported.\n promise.catch(() => undefined);\n return Promise.reject(signal.reason);\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n","/**\n * Audio decoding for on-device inference: turns encoded bytes into 16 kHz\n * mono Float32 PCM, the input Whisper and Moonshine expect.\n *\n * - WAV (PCM 8/16/24/32-bit, IEEE float 32/64, WAVE_FORMAT_EXTENSIBLE) and raw\n * `audio/pcm` are decoded in-process in every runtime, including workers.\n * - Anything else goes to the caller's `decodeAudio` hook, or, in browsers,\n * to `OfflineAudioContext.decodeAudioData` (MediaRecorder WebM/Opus, Safari\n * `audio/mp4`, MP3, ...).\n */\n\nimport { normalizeMimeType } from '../../shared/audio.js';\nimport { SpeechConfigurationError } from '../../shared/errors.js';\nimport { raceAbort } from './abort.js';\nimport type { DecodedAudio, LocalAudioDecoder } from './types.js';\n\n/** Sample rate expected by Whisper and Moonshine feature extractors. */\nexport const LOCAL_SAMPLE_RATE = 16_000;\n\nconst ADAPTER = 'local';\n\nconst WAV_MIME_TYPES = new Set([\n 'audio/wav',\n 'audio/wave',\n 'audio/x-wav',\n 'audio/vnd.wave',\n]);\nconst PCM_MIME_TYPES = new Set(['audio/pcm', 'audio/x-pcm', 'audio/raw']);\n\nexport interface DecodeToPcmOptions {\n /** Full MIME type including parameters (`audio/pcm;rate=16000`). */\n mimeType: string;\n /** Sample rate for raw PCM when the MIME type has no `rate` parameter. */\n sampleRate?: number;\n /** Channel count for raw PCM when the MIME type has no `channels` parameter. */\n channels?: number;\n decodeAudio?: LocalAudioDecoder;\n signal?: AbortSignal;\n}\n\n/**\n * Decodes `bytes` and returns 16 kHz mono Float32 samples.\n *\n * Raw PCM (`audio/pcm`) is little-endian signed 16-bit by default; pass\n * `encoding=f32le` for Float32. The rate comes from the `rate` MIME parameter\n * or `sampleRate`, the channel count from `channels` (default 1).\n */\nexport async function decodeToPcm16k(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): Promise<Float32Array> {\n // Decoders (caller hooks, decodeAudioData) cannot be cancelled: stop waiting on abort.\n const decoded = await raceAbort(\n decodeAudioBytes(bytes, options),\n options.signal,\n );\n return toPcm16k(decoded);\n}\n\n/** Downmixes decoded audio to mono and resamples it to 16 kHz. */\nexport function toPcm16k(decoded: DecodedAudio): Float32Array {\n return resample(\n downmix(decoded.samples),\n decoded.sampleRate,\n LOCAL_SAMPLE_RATE,\n );\n}\n\n/**\n * True for WAV and raw PCM, which this module decodes itself in every\n * runtime, including Web Workers.\n */\nexport function isInProcessDecodable(\n bytes: Uint8Array,\n mimeType: string,\n): boolean {\n const essence = normalizeMimeType(mimeType);\n return Boolean(\n (essence && (WAV_MIME_TYPES.has(essence) || PCM_MIME_TYPES.has(essence))) ||\n isRiffWave(bytes),\n );\n}\n\n/**\n * Decodes a format without an in-process decoder through the caller's\n * `decodeAudio` hook or, in browsers, `OfflineAudioContext`. Resolves\n * `undefined` when neither is available.\n */\nexport async function decodeExternally(\n bytes: Uint8Array,\n options: Omit<DecodeToPcmOptions, 'sampleRate' | 'channels'>,\n): Promise<DecodedAudio | undefined> {\n if (options.decodeAudio) {\n const decoded = await options.decodeAudio({\n bytes,\n mimeType: options.mimeType,\n signal: options.signal,\n });\n assertDecodedAudio(decoded);\n return decoded;\n }\n\n if (hasOfflineAudioContext()) {\n return decodeWithAudioContext(bytes);\n }\n\n return undefined;\n}\n\nasync function decodeAudioBytes(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): Promise<DecodedAudio> {\n const essence = normalizeMimeType(options.mimeType);\n\n if ((essence && WAV_MIME_TYPES.has(essence)) || isRiffWave(bytes)) {\n return decodeWav(bytes);\n }\n\n if (essence && PCM_MIME_TYPES.has(essence)) {\n return decodeRawPcm(bytes, options);\n }\n\n const decoded = await decodeExternally(bytes, options);\n if (decoded) {\n return decoded;\n }\n\n throw new SpeechConfigurationError(\n `Cannot decode '${options.mimeType}' audio on this runtime: pass WAV or audio/pcm, ` +\n 'or supply a decodeAudio hook (e.g. ffmpeg) for compressed formats',\n ADAPTER,\n );\n}\n\nfunction isRiffWave(bytes: Uint8Array): boolean {\n return (\n bytes.byteLength >= 12 &&\n ascii(bytes, 0, 4) === 'RIFF' &&\n ascii(bytes, 8, 4) === 'WAVE'\n );\n}\n\n/** Parses a RIFF/WAVE file into per-channel Float32 samples. */\nexport function decodeWav(bytes: Uint8Array): DecodedAudio {\n if (!isRiffWave(bytes)) {\n throw new SpeechConfigurationError(\n 'Invalid WAV data: missing RIFF/WAVE header',\n ADAPTER,\n );\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let format: WavFormat | undefined;\n let offset = 12;\n\n while (offset + 8 <= bytes.byteLength) {\n const id = ascii(bytes, offset, 4);\n const size = view.getUint32(offset + 4, true);\n const body = offset + 8;\n\n if (id === 'fmt ') {\n format = readWavFormat(view, body, size);\n } else if (id === 'data') {\n if (!format) {\n throw new SpeechConfigurationError(\n 'Invalid WAV data: data chunk before fmt chunk',\n ADAPTER,\n );\n }\n // Streamed WAVs may leave the size as 0 or 0xFFFFFFFF; clamp to the bytes we have.\n const available = bytes.byteLength - body;\n const length = size === 0 || size > available ? available : size;\n return {\n samples: deinterleave(\n bytes.subarray(body, body + length),\n format.channels,\n format.encoding,\n ),\n sampleRate: format.sampleRate,\n };\n }\n\n offset = body + size + (size % 2);\n }\n\n throw new SpeechConfigurationError(\n 'Invalid WAV data: no data chunk',\n ADAPTER,\n );\n}\n\ntype PcmEncoding = 'u8' | 's16le' | 's24le' | 's32le' | 'f32le' | 'f64le';\n\ninterface WavFormat {\n channels: number;\n sampleRate: number;\n encoding: PcmEncoding;\n}\n\nconst WAVE_FORMAT_PCM = 1;\nconst WAVE_FORMAT_IEEE_FLOAT = 3;\nconst WAVE_FORMAT_EXTENSIBLE = 0xfffe;\n\nfunction readWavFormat(view: DataView, body: number, size: number): WavFormat {\n if (size < 16) {\n throw new SpeechConfigurationError(\n 'Invalid WAV data: fmt chunk too short',\n ADAPTER,\n );\n }\n\n let tag = view.getUint16(body, true);\n const channels = view.getUint16(body + 2, true);\n const sampleRate = view.getUint32(body + 4, true);\n const bitsPerSample = view.getUint16(body + 14, true);\n\n if (tag === WAVE_FORMAT_EXTENSIBLE && size >= 26) {\n // The first two bytes of the SubFormat GUID carry the real format tag.\n tag = view.getUint16(body + 24, true);\n }\n\n const encoding =\n tag === WAVE_FORMAT_PCM\n ? ({ 8: 'u8', 16: 's16le', 24: 's24le', 32: 's32le' } as const)[\n bitsPerSample\n ]\n : tag === WAVE_FORMAT_IEEE_FLOAT\n ? ({ 32: 'f32le', 64: 'f64le' } as const)[bitsPerSample]\n : undefined;\n\n if (!encoding || channels < 1 || sampleRate < 1) {\n throw new SpeechConfigurationError(\n `Unsupported WAV encoding (format ${tag}, ${bitsPerSample}-bit, ${channels} channels); ` +\n 'use PCM or IEEE float, or supply a decodeAudio hook',\n ADAPTER,\n );\n }\n\n return { channels, sampleRate, encoding };\n}\n\nfunction decodeRawPcm(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): DecodedAudio {\n const params = mimeParameters(options.mimeType);\n const sampleRate =\n positiveInteger(params.rate) ??\n explicitPositiveInteger(options.sampleRate, 'sampleRate');\n const channels =\n positiveInteger(params.channels) ??\n explicitPositiveInteger(options.channels, 'channels') ??\n 1;\n const encoding = (params.encoding ?? 's16le').toLowerCase();\n\n if (!sampleRate) {\n throw new SpeechConfigurationError(\n \"Raw PCM audio needs a sample rate: use 'audio/pcm;rate=16000' or AudioInput.sampleRate\",\n ADAPTER,\n );\n }\n if (encoding !== 's16le' && encoding !== 'f32le') {\n throw new SpeechConfigurationError(\n `Unsupported raw PCM encoding '${encoding}' (expected s16le or f32le)`,\n ADAPTER,\n );\n }\n\n return {\n samples: deinterleave(bytes, channels, encoding),\n sampleRate,\n };\n}\n\nconst BYTES_PER_SAMPLE: Record<PcmEncoding, number> = {\n u8: 1,\n s16le: 2,\n s24le: 3,\n s32le: 4,\n f32le: 4,\n f64le: 8,\n};\n\nfunction deinterleave(\n bytes: Uint8Array,\n channels: number,\n encoding: PcmEncoding,\n): Float32Array[] {\n const width = BYTES_PER_SAMPLE[encoding];\n const frames = Math.floor(bytes.byteLength / (width * channels));\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const output = Array.from(\n { length: channels },\n () => new Float32Array(frames),\n );\n\n for (let frame = 0; frame < frames; frame++) {\n for (let channel = 0; channel < channels; channel++) {\n const at = (frame * channels + channel) * width;\n (output[channel] as Float32Array)[frame] = readSample(view, at, encoding);\n }\n }\n\n return output;\n}\n\nfunction readSample(view: DataView, at: number, encoding: PcmEncoding): number {\n switch (encoding) {\n case 'u8':\n return (view.getUint8(at) - 128) / 128;\n case 's16le':\n return view.getInt16(at, true) / 32_768;\n case 's24le': {\n const value =\n view.getUint8(at) |\n (view.getUint8(at + 1) << 8) |\n (view.getInt8(at + 2) << 16);\n return value / 8_388_608;\n }\n case 's32le':\n return view.getInt32(at, true) / 2_147_483_648;\n case 'f32le':\n return view.getFloat32(at, true);\n case 'f64le':\n return view.getFloat64(at, true);\n }\n}\n\n/** Averages channels into one mono track. */\nexport function downmix(samples: Float32Array | Float32Array[]): Float32Array {\n if (samples instanceof Float32Array) {\n return samples;\n }\n if (samples.length === 1) {\n return samples[0] as Float32Array;\n }\n\n const length = Math.min(...samples.map((channel) => channel.length));\n const mono = new Float32Array(length);\n for (const channel of samples) {\n for (let index = 0; index < length; index++) {\n (mono as Float32Array)[index] =\n (mono[index] as number) + (channel[index] as number) / samples.length;\n }\n }\n return mono;\n}\n\n/**\n * Resamples mono audio with linear interpolation. When downsampling, a box\n * filter one source period wide is applied first to limit aliasing; this is\n * adequate for speech recognition, not for music playback.\n */\nexport function resample(\n samples: Float32Array,\n fromRate: number,\n toRate: number,\n): Float32Array {\n if (fromRate === toRate || samples.length === 0) {\n return samples;\n }\n\n const ratio = fromRate / toRate;\n const source = ratio > 1 ? boxFilter(samples, Math.round(ratio)) : samples;\n const length = Math.max(1, Math.round(samples.length / ratio));\n const output = new Float32Array(length);\n\n for (let index = 0; index < length; index++) {\n const position = index * ratio;\n const left = Math.floor(position);\n const right = Math.min(left + 1, source.length - 1);\n const fraction = position - left;\n output[index] =\n (source[left] as number) * (1 - fraction) +\n (source[right] as number) * fraction;\n }\n\n return output;\n}\n\nfunction boxFilter(samples: Float32Array, width: number): Float32Array {\n if (width <= 1) {\n return samples;\n }\n\n const output = new Float32Array(samples.length);\n const half = Math.floor(width / 2);\n let sum = 0;\n let count = 0;\n let start = 0;\n let end = 0;\n\n for (let index = 0; index < samples.length; index++) {\n const windowStart = Math.max(0, index - half);\n const windowEnd = Math.min(samples.length, index - half + width);\n while (end < windowEnd) {\n sum += samples[end] as number;\n count++;\n end++;\n }\n while (start < windowStart) {\n sum -= samples[start] as number;\n count--;\n start++;\n }\n output[index] = sum / count;\n }\n\n return output;\n}\n\nfunction hasOfflineAudioContext(): boolean {\n return typeof globalThis.OfflineAudioContext === 'function';\n}\n\n/**\n * Browser decode path. Decoding through a 16 kHz `OfflineAudioContext` makes\n * the browser resample with its own high-quality resampler. Not available in\n * Web Workers: decode on the main thread (see the worker client).\n */\nasync function decodeWithAudioContext(\n bytes: Uint8Array,\n): Promise<DecodedAudio> {\n const context = new OfflineAudioContext(1, 1, LOCAL_SAMPLE_RATE);\n // decodeAudioData detaches its input, so hand it a private copy.\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n\n let buffer: AudioBuffer;\n try {\n buffer = await context.decodeAudioData(copy.buffer);\n } catch (error) {\n throw new SpeechConfigurationError(\n `The browser could not decode the audio: ${errorMessage(error)}`,\n ADAPTER,\n );\n }\n\n const channels = Array.from({ length: buffer.numberOfChannels }, (_, index) =>\n buffer.getChannelData(index),\n );\n return { samples: channels, sampleRate: buffer.sampleRate };\n}\n\nfunction assertDecodedAudio(decoded: DecodedAudio): void {\n const channels =\n decoded?.samples instanceof Float32Array\n ? [decoded.samples]\n : decoded?.samples;\n if (\n !Array.isArray(channels) ||\n channels.length === 0 ||\n !channels.every((channel) => channel instanceof Float32Array) ||\n !(decoded.sampleRate > 0)\n ) {\n throw new SpeechConfigurationError(\n 'decodeAudio must return { samples: Float32Array | Float32Array[], sampleRate }',\n ADAPTER,\n );\n }\n}\n\nfunction mimeParameters(mimeType: string): Record<string, string> {\n const params: Record<string, string> = {};\n for (const part of mimeType.split(';').slice(1)) {\n const [key, value] = part.split('=');\n if (key && value) {\n params[key.trim().toLowerCase()] = value.trim().replace(/^\"|\"$/g, '');\n }\n }\n return params;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n const parsed = value ? Number.parseInt(value, 10) : Number.NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/** Validates a caller-supplied `AudioInput.sampleRate`/`channels`. */\nfunction explicitPositiveInteger(\n value: number | undefined,\n name: 'sampleRate' | 'channels',\n): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n if (!Number.isInteger(value) || value < 1) {\n throw new SpeechConfigurationError(\n `AudioInput.${name} must be a positive integer, got ${String(value)}`,\n ADAPTER,\n );\n }\n return value;\n}\n\nfunction ascii(bytes: Uint8Array, offset: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(offset, offset + length));\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../../shared/audio.js';\nimport {\n SpeechConfigurationError,\n SpeechProviderError,\n} from '../../shared/errors.js';\nimport type {\n AudioInput,\n AudioSource,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n TranscriptSegment,\n WordTiming,\n} from '../../shared/types.js';\nimport {\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../../shared/usage.js';\nimport { raceAbort } from './abort.js';\nimport { decodeToPcm16k, LOCAL_SAMPLE_RATE } from './audio.js';\nimport {\n type AsrOutput,\n type AsrPipeline,\n createAsrPipeline,\n detectAutoDevice,\n loadTransformers,\n type TransformersModule,\n} from './runtime.js';\nimport type { LocalTranscriberOptions } from './types.js';\n\n/** Small multilingual Whisper (~80 MB at the default q8 quantisation). */\nexport const DEFAULT_LOCAL_MODEL = 'onnx-community/whisper-base';\n\nconst DEFAULT_CHUNK_LENGTH_SECONDS = 30;\nconst WHISPER_WINDOW_SECONDS = 30;\nconst WHISPER_MODEL_TYPES = new Set(['whisper', 'lite-whisper']);\n\ninterface LoadedPipeline {\n pipeline: AsrPipeline;\n device: string;\n}\n\n/**\n * On-device speech-to-text with transformers.js (Whisper or Moonshine ONNX).\n * Runs on WebGPU/WASM in browsers and onnxruntime-node in Node; no audio\n * leaves the device and no API key is involved.\n */\nexport class LocalTranscriber implements Transcriber {\n readonly type = 'local' as const;\n\n private readonly options: LocalTranscriberOptions;\n private readonly model: string;\n private readonly maxBytes: number;\n private readonly onUsage?: SpeechUsageCallback;\n private runtime?: Promise<TransformersModule>;\n private readonly pipelines = new Map<string, Promise<LoadedPipeline>>();\n /** Serialises inference: ONNX sessions (especially WebGPU) run one call at a time. */\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(options: LocalTranscriberOptions = {}) {\n this.options = options;\n this.model = options.model?.trim() || DEFAULT_LOCAL_MODEL;\n this.maxBytes = options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES;\n this.onUsage = options.onUsage;\n }\n\n /**\n * Downloads (first time) and initialises the model so the first\n * `transcribe()` is fast. Progress goes to `onProgress`.\n */\n async preload(model?: string, signal?: AbortSignal): Promise<void> {\n await raceAbort(this.loadPipeline(model?.trim() || this.model), signal);\n }\n\n /**\n * Whether every file `model` needs is already in the model cache (Cache\n * Storage in browsers), so a `preload()` would download nothing. Resolves\n * `false` when the runtime cannot tell. Does not load the model.\n */\n async isCached(model?: string): Promise<boolean> {\n try {\n const transformers = await this.loadRuntime();\n if (!transformers.ModelRegistry) {\n return false;\n }\n const { dtype, revision, cacheDir } = this.options;\n // Cached files depend on the device (transformers.js picks per-device\n // dtypes), so resolve it exactly as loadPipeline does.\n const requested = this.options.device ?? 'auto';\n const device =\n requested === 'auto' ? await detectAutoDevice() : requested;\n return await transformers.ModelRegistry.is_pipeline_cached(\n 'automatic-speech-recognition',\n model?.trim() || this.model,\n {\n device,\n ...(dtype ? { dtype } : {}),\n ...(revision ? { revision } : {}),\n ...(cacheDir ? { cache_dir: cacheDir } : {}),\n },\n );\n } catch {\n return false;\n }\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const { signal } = request;\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes ?? this.maxBytes,\n adapter: this.type,\n signal,\n });\n const format = pcmFormat(request.audio);\n const samples = await decodeToPcm16k(\n new Uint8Array(await audio.blob.arrayBuffer()),\n {\n mimeType: audio.mimeType,\n sampleRate: format.sampleRate,\n channels: format.channels,\n decodeAudio: this.options.decodeAudio,\n signal,\n },\n );\n signal?.throwIfAborted();\n\n const model = request.model?.trim() || this.model;\n const loaded = await raceAbort(this.loadPipeline(model), signal);\n const audioSeconds = samples.length / LOCAL_SAMPLE_RATE;\n // The queue tracks the real inference; the caller stops waiting on abort.\n const output = await raceAbort(\n this.enqueue(() =>\n this.run(loaded.pipeline, samples, audioSeconds, request, model),\n ),\n signal,\n );\n\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.type,\n model,\n audioSeconds,\n bytes: audio.bytes,\n };\n const result: TranscriptResult = {\n ...mapAsrOutput(\n output,\n request.timestampGranularities?.includes('word') ?? false,\n ),\n language: request.language,\n durationSeconds: audioSeconds,\n provider: this.type,\n model,\n raw: output,\n usage,\n };\n\n await reportSpeechUsage(usage, this.onUsage, request.onUsage);\n return result;\n }\n\n /** Waits for in-flight inference, then releases every loaded model session. */\n async dispose(): Promise<void> {\n await this.queue;\n const loaded = [...this.pipelines.values()];\n this.pipelines.clear();\n await Promise.all(\n loaded.map(async (entry) => {\n const { pipeline } = await entry.catch(() => ({ pipeline: undefined }));\n await pipeline?.dispose?.();\n }),\n );\n }\n\n private async run(\n pipeline: AsrPipeline,\n samples: Float32Array,\n audioSeconds: number,\n request: TranscriptionRequest,\n model: string,\n ): Promise<AsrOutput> {\n const { signal } = request;\n signal?.throwIfAborted();\n\n const transformers = await this.loadRuntime();\n const options: Record<string, unknown> = {};\n const isWhisper = WHISPER_MODEL_TYPES.has(\n pipeline.model?.config?.model_type ?? 'whisper',\n );\n\n if (isWhisper) {\n const granularities = request.timestampGranularities ?? [];\n if (granularities.includes('word')) {\n options.return_timestamps = 'word';\n } else if (granularities.includes('segment')) {\n options.return_timestamps = true;\n }\n const chunkLength =\n this.options.chunkLengthSeconds ?? DEFAULT_CHUNK_LENGTH_SECONDS;\n if (chunkLength > 0 && audioSeconds > WHISPER_WINDOW_SECONDS) {\n options.chunk_length_s = chunkLength;\n }\n // English-only Whisper (`*.en`) rejects `language` and `task` outright.\n if (request.language && !isEnglishOnly(pipeline, model)) {\n options.language = request.language;\n options.task = 'transcribe';\n }\n }\n\n // Stops generation between tokens when the caller aborts.\n let stopping: { interrupt(): void } | undefined;\n if (signal && transformers.InterruptableStoppingCriteria) {\n stopping = new transformers.InterruptableStoppingCriteria();\n options.stopping_criteria = stopping;\n }\n const onAbort = () => stopping?.interrupt();\n signal?.addEventListener('abort', onAbort, { once: true });\n\n try {\n const output = await pipeline(samples, options);\n return Array.isArray(output) ? (output[0] ?? { text: '' }) : output;\n } catch (error) {\n if (signal?.aborted) {\n throw signal.reason;\n }\n const hint =\n options.return_timestamps === 'word' &&\n /cross attentions/i.test(errorMessage(error))\n ? ' (word timestamps need a model exported with cross attentions, e.g. onnx-community/whisper-base_timestamped)'\n : '';\n throw new SpeechProviderError(\n this.type,\n `Local transcription failed: ${errorMessage(error)}${hint}`,\n { cause: error },\n );\n } finally {\n signal?.removeEventListener('abort', onAbort);\n }\n }\n\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const next = this.queue.then(task, task);\n this.queue = next.catch(() => undefined);\n return next;\n }\n\n private loadRuntime(): Promise<TransformersModule> {\n if (!this.runtime) {\n const { transformers, configureEnv, modelHost } = this.options;\n this.runtime = loadTransformers(transformers).then((module) => {\n if (module.env) {\n if (modelHost) {\n module.env.remoteHost = modelHost;\n }\n configureEnv?.(module.env);\n }\n return module;\n });\n // A failed load (e.g. missing peer) is retried on the next call.\n this.runtime.catch(() => {\n this.runtime = undefined;\n });\n }\n return this.runtime;\n }\n\n private loadPipeline(model: string): Promise<LoadedPipeline> {\n let loaded = this.pipelines.get(model);\n if (!loaded) {\n loaded = this.loadRuntime().then((transformers) =>\n createAsrPipeline(transformers, {\n model,\n device: this.options.device ?? 'auto',\n dtype: this.options.dtype,\n revision: this.options.revision,\n cacheDir: this.options.cacheDir,\n onProgress: this.options.onProgress,\n }).catch((error: unknown) => {\n if (error instanceof SpeechConfigurationError) {\n throw error;\n }\n throw new SpeechProviderError(\n this.type,\n `Failed to load local model '${model}': ${errorMessage(error)}`,\n { cause: error },\n );\n }),\n );\n this.pipelines.set(model, loaded);\n loaded.catch(() => {\n if (this.pipelines.get(model) === loaded) {\n this.pipelines.delete(model);\n }\n });\n }\n return loaded;\n }\n}\n\n/**\n * Maps transformers.js chunks onto `words` (when word timestamps were\n * requested) or `segments`.\n */\nexport function mapAsrOutput(\n output: AsrOutput,\n wordLevel: boolean,\n): Pick<TranscriptResult, 'text' | 'segments' | 'words'> {\n const result: Pick<TranscriptResult, 'text' | 'segments' | 'words'> = {\n text: (output.text ?? '').trim(),\n };\n const chunks = output.chunks;\n if (!chunks?.length) {\n return result;\n }\n\n if (wordLevel) {\n result.words = chunks\n .filter((chunk) => chunk.text.trim())\n .map(\n (chunk): WordTiming => ({\n word: chunk.text.trim(),\n startSeconds: chunk.timestamp[0],\n endSeconds: chunk.timestamp[1] ?? chunk.timestamp[0],\n }),\n );\n } else {\n result.segments = chunks.map(\n (chunk): TranscriptSegment => ({\n text: chunk.text.trim(),\n startSeconds: chunk.timestamp[0],\n endSeconds: chunk.timestamp[1] ?? undefined,\n }),\n );\n }\n return result;\n}\n\n/** Whether this Whisper only transcribes English (it rejects `language`/`task`). */\nfunction isEnglishOnly(pipeline: AsrPipeline, model: string): boolean {\n const multilingual = pipeline.model?.generation_config?.is_multilingual;\n if (typeof multilingual === 'boolean') {\n return !multilingual;\n }\n return /\\.en(?:$|[-_])/i.test(model);\n}\n\nfunction pcmFormat(audio: AudioInput | AudioSource): {\n sampleRate?: number;\n channels?: number;\n} {\n if (\n audio &&\n typeof audio === 'object' &&\n 'data' in audio &&\n !(audio instanceof Uint8Array)\n ) {\n return { sampleRate: audio.sampleRate, channels: audio.channels };\n }\n return {};\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Web Worker support for the `local` transcriber.\n *\n * Inference runs in a worker (`serveLocalTranscriber`) so long recordings never\n * block the UI thread. The main-thread client (`LocalTranscriberWorkerClient`)\n * transfers WAV and raw PCM bytes to the worker, which parses, downmixes, and\n * resamples them there. Only formats that need `AudioContext` (which workers\n * lack) or the client's `decodeAudio` hook are decoded on the main thread;\n * their Float32 channels are then transferred and resampled in the worker.\n */\n\nimport {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../../shared/audio.js';\nimport {\n SpeechConfigurationError,\n SpeechError,\n SpeechProviderError,\n} from '../../shared/errors.js';\nimport type {\n AudioInput,\n TimestampGranularity,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from '../../shared/types.js';\nimport {\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../../shared/usage.js';\nimport { raceAbort } from './abort.js';\nimport {\n decodeExternally,\n isInProcessDecodable,\n LOCAL_SAMPLE_RATE,\n toPcm16k,\n} from './audio.js';\nimport { resolveLocalTranscriberOptions } from './env.js';\nimport { LocalTranscriber } from './transcriber.js';\nimport type {\n LocalAudioDecoder,\n LocalTranscriberOptions,\n LocalTranscriberProgress,\n LocalTranscriberProgressCallback,\n} from './types.js';\n\n/** Minimal `postMessage` endpoint: a `Worker`, `DedicatedWorkerGlobalScope`, or `MessagePort`. */\nexport interface LocalWorkerEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(\n type: 'message',\n listener: (event: MessageEvent) => void,\n ): void;\n removeEventListener(\n type: 'message',\n listener: (event: MessageEvent) => void,\n ): void;\n}\n\n/** Audio sent to the worker. Its buffers are transferred, not copied. */\nexport type LocalWorkerAudio =\n | {\n /** WAV or raw PCM bytes (or any format the worker's `decodeAudio` handles). */\n kind: 'encoded';\n bytes: Uint8Array;\n mimeType: string;\n sampleRate?: number;\n channels?: number;\n }\n | {\n /** Channels decoded on the main thread; the worker downmixes and resamples. */\n kind: 'decoded';\n samples: Float32Array[];\n sampleRate: number;\n };\n\n/** Messages from the client to the worker. */\nexport type LocalWorkerRequest =\n | {\n kind: 'transcribe';\n id: number;\n audio: LocalWorkerAudio;\n model?: string;\n language?: string;\n timestampGranularities?: TimestampGranularity[];\n }\n | { kind: 'preload'; id: number; model?: string }\n | { kind: 'isCached'; id: number; model?: string }\n | { kind: 'abort'; id: number };\n\n/** Messages from the worker to the client. */\nexport type LocalWorkerResponse =\n | {\n kind: 'result';\n id: number;\n result?: TranscriptResult;\n /** Answer to an `isCached` request. */\n cached?: boolean;\n }\n | {\n kind: 'error';\n id: number;\n error: { name: string; message: string; code?: string };\n }\n | { kind: 'progress'; progress: LocalTranscriberProgress };\n\ninterface WorkerReply {\n result?: TranscriptResult;\n cached?: boolean;\n}\n\nconst PCM_MIME_TYPE = `audio/pcm;rate=${LOCAL_SAMPLE_RATE};encoding=f32le`;\n\n/**\n * Runs a {@link LocalTranscriber} inside a worker and answers client\n * requests. Call it from your worker module:\n *\n * ```ts\n * // transcriber.worker.ts\n * import { serveLocalTranscriber } from '@happyvertical/speech/local';\n * serveLocalTranscriber({ model: 'onnx-community/whisper-base' });\n * ```\n *\n * Returns a function that stops listening and disposes the model.\n */\nexport function serveLocalTranscriber(\n options: LocalTranscriberOptions = {},\n endpoint: LocalWorkerEndpoint = globalThis as unknown as LocalWorkerEndpoint,\n): () => Promise<void> {\n const userProgress = options.onProgress;\n const transcriber = new LocalTranscriber({\n ...resolveLocalTranscriberOptions(options),\n // The client already enforced its byte limit on the encoded input.\n maxBytes: Number.POSITIVE_INFINITY,\n onProgress: (progress) => {\n userProgress?.(progress);\n endpoint.postMessage({\n kind: 'progress',\n progress: toCloneable(progress),\n } satisfies LocalWorkerResponse);\n },\n });\n const pending = new Map<number, AbortController>();\n\n const reply = (message: LocalWorkerResponse) => endpoint.postMessage(message);\n const listener = (event: MessageEvent) => {\n const message = event.data as LocalWorkerRequest;\n if (!message || typeof message !== 'object') {\n return;\n }\n\n if (message.kind === 'abort') {\n pending.get(message.id)?.abort();\n return;\n }\n\n const controller = new AbortController();\n pending.set(message.id, controller);\n const work =\n message.kind === 'isCached'\n ? transcriber.isCached(message.model).then((cached) => {\n reply({ kind: 'result', id: message.id, cached });\n })\n : message.kind === 'preload'\n ? transcriber.preload(message.model, controller.signal).then(() => {\n reply({ kind: 'result', id: message.id });\n })\n : Promise.resolve()\n .then(() =>\n transcriber.transcribe({\n audio: toAudioInput(message.audio),\n model: message.model,\n language: message.language,\n timestampGranularities: message.timestampGranularities,\n signal: controller.signal,\n }),\n )\n .then((result) => {\n reply({ kind: 'result', id: message.id, result });\n });\n\n work\n .catch((error: unknown) => {\n reply({ kind: 'error', id: message.id, error: serializeError(error) });\n })\n .finally(() => pending.delete(message.id));\n };\n\n endpoint.addEventListener('message', listener);\n return async () => {\n endpoint.removeEventListener('message', listener);\n for (const controller of pending.values()) {\n controller.abort();\n }\n await transcriber.dispose();\n };\n}\n\nexport interface LocalTranscriberWorkerClientOptions {\n /** Encoded input byte limit, checked on the main thread. Default 25 MB. */\n maxBytes?: number;\n /**\n * Main-thread decoder for formats other than WAV and raw PCM. Formats with\n * neither this hook nor `AudioContext` go to the worker's own `decodeAudio`.\n */\n decodeAudio?: LocalAudioDecoder;\n /** Model download/load progress forwarded from the worker. */\n onProgress?: LocalTranscriberProgressCallback;\n /** Called with usage after every successful transcription. */\n onUsage?: SpeechUsageCallback;\n}\n\n/**\n * Main-thread {@link Transcriber} that delegates inference to a worker running\n * {@link serveLocalTranscriber}.\n *\n * ```ts\n * const worker = new Worker(new URL('./transcriber.worker.ts', import.meta.url), { type: 'module' });\n * const transcriber = new LocalTranscriberWorkerClient(worker, { onProgress });\n * const { text } = await transcriber.transcribe({ audio: recordingBlob });\n * ```\n */\nexport class LocalTranscriberWorkerClient implements Transcriber {\n readonly type = 'local' as const;\n\n private readonly endpoint: LocalWorkerEndpoint;\n private readonly options: LocalTranscriberWorkerClientOptions;\n private readonly calls = new Map<\n number,\n {\n resolve: (response: WorkerReply) => void;\n reject: (error: unknown) => void;\n }\n >();\n private nextId = 1;\n private closed = false;\n /** Aborted by `close()` to reject calls still buffering or decoding. */\n private readonly closing = new AbortController();\n private readonly listener = (event: MessageEvent) =>\n this.onMessage(event.data as LocalWorkerResponse);\n\n constructor(\n endpoint: LocalWorkerEndpoint,\n options: LocalTranscriberWorkerClientOptions = {},\n ) {\n this.endpoint = endpoint;\n this.options = options;\n endpoint.addEventListener('message', this.listener);\n }\n\n /** Whether the worker finds every file for `model` in the model cache. */\n async isCached(model?: string): Promise<boolean> {\n try {\n const { cached } = await this.call(\n { kind: 'isCached', model },\n undefined,\n );\n return cached === true;\n } catch {\n // Same contract as LocalTranscriber.isCached: unknown means false.\n return false;\n }\n }\n\n /** Loads the model in the worker ahead of the first transcription. */\n async preload(model?: string, signal?: AbortSignal): Promise<void> {\n await this.call({ kind: 'preload', model }, signal);\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n this.assertOpen();\n const { signal } = request;\n // Buffering and main-thread decoding count as in flight: close() rejects them.\n const { audio, bytes } = await raceAbort(\n this.prepare(request),\n this.closing.signal,\n );\n\n const { result } = await this.call(\n {\n kind: 'transcribe',\n audio,\n model: request.model,\n language: request.language,\n timestampGranularities: request.timestampGranularities,\n },\n signal,\n transferList(audio),\n );\n if (!result) {\n throw new SpeechProviderError(this.type, 'Worker returned no result');\n }\n\n const usage: SpeechUsage = {\n ...(result.usage ?? { operation: 'transcription', provider: this.type }),\n bytes,\n };\n await reportSpeechUsage(usage, this.options.onUsage, request.onUsage);\n return { ...result, usage };\n }\n\n /**\n * Stops listening, rejects in-flight and later calls, and asks the worker to\n * abort posted work. Does not terminate the worker.\n */\n close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.endpoint.removeEventListener('message', this.listener);\n this.closing.abort(closedError());\n for (const [id, call] of this.calls) {\n this.endpoint.postMessage({\n kind: 'abort',\n id,\n } satisfies LocalWorkerRequest);\n call.reject(closedError());\n }\n this.calls.clear();\n }\n\n /** Buffers the input and, only when the worker cannot, decodes it here. */\n private async prepare(\n request: TranscriptionRequest,\n ): Promise<{ audio: LocalWorkerAudio; bytes: number }> {\n const { signal } = request;\n const normalized = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes:\n request.maxBytes ?? this.options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES,\n adapter: this.type,\n signal,\n });\n const bytes = new Uint8Array(await normalized.blob.arrayBuffer());\n\n if (!isInProcessDecodable(bytes, normalized.mimeType)) {\n // Decoders (caller hooks, decodeAudioData) cannot be cancelled: stop waiting on abort.\n const decoded = await raceAbort(\n decodeExternally(bytes, {\n mimeType: normalized.mimeType,\n decodeAudio: this.options.decodeAudio,\n signal,\n }),\n signal,\n );\n if (decoded) {\n const channels =\n decoded.samples instanceof Float32Array\n ? [decoded.samples]\n : decoded.samples;\n return {\n audio: {\n kind: 'decoded',\n samples: channels.map(ownedCopy),\n sampleRate: decoded.sampleRate,\n },\n bytes: normalized.bytes,\n };\n }\n }\n\n const wrapped =\n typeof request.audio === 'object' &&\n 'data' in request.audio &&\n !(request.audio instanceof Uint8Array)\n ? request.audio\n : undefined;\n return {\n audio: {\n kind: 'encoded',\n bytes: ownedCopy(bytes),\n mimeType: normalized.mimeType,\n sampleRate: wrapped?.sampleRate,\n channels: wrapped?.channels,\n },\n bytes: normalized.bytes,\n };\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw closedError();\n }\n }\n\n private call(\n message:\n | Omit<Extract<LocalWorkerRequest, { kind: 'transcribe' }>, 'id'>\n | Omit<Extract<LocalWorkerRequest, { kind: 'preload' }>, 'id'>\n | Omit<Extract<LocalWorkerRequest, { kind: 'isCached' }>, 'id'>,\n signal: AbortSignal | undefined,\n transfer: Transferable[] = [],\n ): Promise<WorkerReply> {\n this.assertOpen();\n signal?.throwIfAborted();\n const id = this.nextId++;\n\n return new Promise((resolve, reject) => {\n const onAbort = () => {\n this.calls.delete(id);\n this.endpoint.postMessage({ kind: 'abort', id });\n reject(signal?.reason);\n };\n const settle = () => signal?.removeEventListener('abort', onAbort);\n this.calls.set(id, {\n resolve: (response) => {\n settle();\n resolve(response);\n },\n reject: (error) => {\n settle();\n reject(error);\n },\n });\n signal?.addEventListener('abort', onAbort, { once: true });\n this.endpoint.postMessage({ ...message, id }, transfer);\n });\n }\n\n private onMessage(message: LocalWorkerResponse): void {\n if (!message || typeof message !== 'object') {\n return;\n }\n if (message.kind === 'progress') {\n this.options.onProgress?.(message.progress);\n return;\n }\n\n const call = this.calls.get(message.id);\n if (!call) {\n return;\n }\n this.calls.delete(message.id);\n if (message.kind === 'result') {\n call.resolve({ result: message.result, cached: message.cached });\n } else {\n call.reject(deserializeError(message.error));\n }\n }\n}\n\nfunction closedError(): SpeechConfigurationError {\n return new SpeechConfigurationError('Worker client closed', 'local');\n}\n\n/** Rebuilds the transcriber input from a worker message, resampling decoded channels here. */\nfunction toAudioInput(audio: LocalWorkerAudio): AudioInput {\n if (audio.kind === 'decoded') {\n const pcm = toPcm16k({\n samples: audio.samples,\n sampleRate: audio.sampleRate,\n });\n return {\n data: new Uint8Array(pcm.buffer, pcm.byteOffset, pcm.byteLength),\n mimeType: PCM_MIME_TYPE,\n };\n }\n return {\n data: audio.bytes,\n mimeType: audio.mimeType,\n sampleRate: audio.sampleRate,\n channels: audio.channels,\n };\n}\n\n/**\n * Returns `view` when it spans a whole ordinary `ArrayBuffer` (safe to\n * transfer), otherwise a copy. Views of a larger buffer would transfer\n * unrelated bytes, and a `SharedArrayBuffer` cannot be transferred at all.\n */\nfunction ownedCopy<T extends Uint8Array | Float32Array>(view: T): T {\n const { buffer } = view;\n return isPlainArrayBuffer(buffer) &&\n view.byteOffset === 0 &&\n view.byteLength === buffer.byteLength\n ? view\n : (view.slice() as T);\n}\n\nfunction isPlainArrayBuffer(buffer: ArrayBufferLike): buffer is ArrayBuffer {\n // Tag check rather than instanceof: buffers may come from another realm.\n return Object.prototype.toString.call(buffer) === '[object ArrayBuffer]';\n}\n\n/** Distinct buffers to transfer (two channels may share one). */\nfunction transferList(audio: LocalWorkerAudio): ArrayBuffer[] {\n const views = audio.kind === 'decoded' ? audio.samples : [audio.bytes];\n return [...new Set(views.map((view) => view.buffer as ArrayBuffer))];\n}\n\nfunction serializeError(error: unknown): {\n name: string;\n message: string;\n code?: string;\n} {\n if (error instanceof SpeechError) {\n return { name: error.name, message: error.message, code: error.code };\n }\n if (error instanceof Error) {\n return { name: error.name, message: error.message };\n }\n return { name: 'Error', message: String(error) };\n}\n\nfunction deserializeError(error: {\n name: string;\n message: string;\n code?: string;\n}): Error {\n switch (error.name) {\n case 'SpeechConfigurationError':\n return new SpeechConfigurationError(error.message, 'local');\n case 'SpeechProviderError':\n return new SpeechProviderError('local', error.message);\n case 'AbortError':\n return new DOMException(error.message, 'AbortError');\n default: {\n const restored = new Error(error.message);\n restored.name = error.name;\n return restored;\n }\n }\n}\n\n/** Progress objects cross `postMessage`, so drop anything not cloneable. */\nfunction toCloneable(\n progress: LocalTranscriberProgress,\n): LocalTranscriberProgress {\n const clone: LocalTranscriberProgress = { status: progress.status };\n for (const [key, value] of Object.entries(progress)) {\n if (\n value === null ||\n ['string', 'number', 'boolean'].includes(typeof value)\n ) {\n clone[key] = value;\n }\n }\n return clone;\n}\n","/**\n * @happyvertical/speech/local\n *\n * On-device speech-to-text with transformers.js (`@huggingface/transformers`,\n * an optional peer dependency loaded on first use). Importing this entry\n * registers `type: 'local'` with `getTranscriber()` and `getSpeech()`.\n *\n * @example\n * ```typescript\n * import { getTranscriber } from '@happyvertical/speech';\n * import '@happyvertical/speech/local';\n *\n * const transcriber = await getTranscriber({ type: 'local' });\n * const { text } = await transcriber.transcribe({ audio: wavBytes, mimeType: 'audio/wav' });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { resolveLocalTranscriberOptions } from './adapters/local/env.js';\nimport { loadTransformers } from './adapters/local/runtime.js';\nimport { LocalTranscriber } from './adapters/local/transcriber.js';\nimport type { LocalTranscriberOptions } from './adapters/local/types.js';\nimport type { SpeechFactoryContext } from './shared/factory.js';\nimport { registerOptionalTranscriber } from './shared/registry.js';\n\nexport {\n decodeToPcm16k,\n LOCAL_SAMPLE_RATE,\n} from './adapters/local/audio.js';\nexport { LOCAL_TRANSCRIBER_ENV_KEYS } from './adapters/local/env.js';\nexport {\n TRANSFORMERS_INSTALL_HINT,\n TRANSFORMERS_PACKAGE,\n} from './adapters/local/runtime.js';\nexport {\n DEFAULT_LOCAL_MODEL,\n LocalTranscriber,\n} from './adapters/local/transcriber.js';\nexport type {\n DecodedAudio,\n LocalAudioDecoder,\n LocalTranscriberDevice,\n LocalTranscriberDtype,\n LocalTranscriberOptions,\n LocalTranscriberProgress,\n LocalTranscriberProgressCallback,\n LocalTranscriberSettings,\n LocalTransformersRuntime,\n} from './adapters/local/types.js';\nexport {\n LocalTranscriberWorkerClient,\n type LocalTranscriberWorkerClientOptions,\n type LocalWorkerAudio,\n type LocalWorkerEndpoint,\n type LocalWorkerRequest,\n type LocalWorkerResponse,\n serveLocalTranscriber,\n} from './adapters/local/worker.js';\n\n/**\n * Creates an on-device transcriber. Explicit options win over\n * `HAVE_SPEECH_TRANSCRIBER_*` environment variables (see\n * `LOCAL_TRANSCRIBER_ENV_KEYS`). The model loads on first use.\n */\nexport function createLocalTranscriber(\n options: LocalTranscriberOptions = {},\n context: Pick<SpeechFactoryContext, 'env'> = {},\n): LocalTranscriber {\n return new LocalTranscriber(resolveLocalTranscriberOptions(options, context));\n}\n\n/**\n * Resolves `true` when `@huggingface/transformers` can be imported in this\n * runtime. This imports the runtime, so call it only when you intend to use it.\n */\nexport async function isLocalTranscriberAvailable(): Promise<boolean> {\n try {\n await loadTransformers(undefined);\n return true;\n } catch {\n return false;\n }\n}\n\nregisterOptionalTranscriber('local', (options, context) =>\n createLocalTranscriber(\n { ...options, type: 'local' } as LocalTranscriberOptions,\n context,\n ),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local.js","names":[],"sources":["../src/adapters/local/env.ts","../src/adapters/local/runtime.ts","../src/adapters/local/abort.ts","../src/adapters/local/audio.ts","../src/adapters/local/transcriber.ts","../src/adapters/local/worker.ts","../src/local.ts"],"sourcesContent":["/**\n * Environment configuration for the `local` transcriber. Explicit options win,\n * then the factory context, then `HAVE_SPEECH_TRANSCRIBER_*` variables (Node).\n */\n\nimport {\n defaultEnv,\n readEnv,\n resolveTranscriberConfig,\n} from '../../shared/env.js';\nimport type { SpeechFactoryContext } from '../../shared/factory.js';\nimport type { LocalTranscriberOptions } from './types.js';\n\n/** Local-only environment variables, in priority order. */\nexport const LOCAL_TRANSCRIBER_ENV_KEYS = {\n device: ['HAVE_SPEECH_TRANSCRIBER_DEVICE'],\n dtype: ['HAVE_SPEECH_TRANSCRIBER_DTYPE'],\n cacheDir: ['HAVE_SPEECH_TRANSCRIBER_CACHE_DIR'],\n modelHost: ['HAVE_SPEECH_TRANSCRIBER_MODEL_HOST'],\n} as const;\n\n/**\n * Merges explicit options with the environment. `model` and `maxBytes` reuse\n * the shared `HAVE_SPEECH_TRANSCRIBER_MODEL`/`_MAX_BYTES` keys.\n */\nexport function resolveLocalTranscriberOptions(\n options: LocalTranscriberOptions = {},\n context: Pick<SpeechFactoryContext, 'env'> = {},\n): LocalTranscriberOptions {\n const env = context.env ?? defaultEnv();\n const shared = resolveTranscriberConfig(\n { model: options.model, maxBytes: options.maxBytes },\n { env },\n );\n\n return {\n ...options,\n type: 'local',\n model: shared.model,\n maxBytes: shared.maxBytes,\n device:\n options.device ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.device),\n dtype: options.dtype ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.dtype),\n cacheDir:\n options.cacheDir ?? readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.cacheDir),\n modelHost:\n options.modelHost ??\n readEnv(env, ...LOCAL_TRANSCRIBER_ENV_KEYS.modelHost),\n };\n}\n","// biome-ignore-all lint/style/useNamingConvention: transformers.js options and outputs use snake_case and PascalCase exports.\n/**\n * Loads the optional `@huggingface/transformers` peer dependency and builds\n * `automatic-speech-recognition` pipelines from it.\n *\n * This is the only module that names the peer. It is reachable only from the\n * `@happyvertical/speech/local` entry, never from the core entry.\n */\n\nimport { SpeechConfigurationError } from '../../shared/errors.js';\nimport type {\n LocalTranscriberDevice,\n LocalTranscriberDtype,\n LocalTranscriberProgressCallback,\n LocalTransformersRuntime,\n} from './types.js';\n\nexport const TRANSFORMERS_PACKAGE = '@huggingface/transformers';\n\nexport const TRANSFORMERS_INSTALL_HINT =\n `Local transcription needs the optional peer dependency ${TRANSFORMERS_PACKAGE}. ` +\n `Install it with \\`pnpm add ${TRANSFORMERS_PACKAGE}\\` (or npm/yarn equivalent).`;\n\n/** One ASR result as returned by transformers.js. */\nexport interface AsrOutput {\n text: string;\n chunks?: Array<{ text: string; timestamp: [number, number | null] }>;\n}\n\n/** Callable `automatic-speech-recognition` pipeline (structural subset). */\nexport interface AsrPipeline {\n (\n audio: Float32Array,\n options?: Record<string, unknown>,\n ): Promise<AsrOutput | AsrOutput[]>;\n dispose?: () => Promise<void>;\n model?: {\n config?: { model_type?: string };\n generation_config?: { is_multilingual?: boolean };\n };\n}\n\n/** The parts of the transformers.js module this adapter uses. */\nexport interface TransformersModule {\n pipeline: (\n task: 'automatic-speech-recognition',\n model: string,\n options: Record<string, unknown>,\n ) => Promise<AsrPipeline>;\n env?: Record<string, unknown>;\n /** transformers.js v4 cache inspection; absent on older builds. */\n ModelRegistry?: {\n is_pipeline_cached(\n task: 'automatic-speech-recognition',\n model: string,\n options?: Record<string, unknown>,\n ): Promise<boolean>;\n };\n InterruptableStoppingCriteria?: new () => { interrupt(): void };\n}\n\n/**\n * Resolves the transformers.js module: an injected module or loader, else a\n * dynamic import of the peer. A missing peer becomes a\n * `SpeechConfigurationError` with an install hint.\n */\nexport async function loadTransformers(\n runtime: LocalTransformersRuntime | undefined,\n): Promise<TransformersModule> {\n let loaded: unknown;\n try {\n loaded =\n typeof runtime === 'function'\n ? await runtime()\n : (runtime ?? (await importTransformers()));\n } catch (error) {\n if (error instanceof SpeechConfigurationError) {\n throw error;\n }\n const configurationError = new SpeechConfigurationError(\n `${TRANSFORMERS_INSTALL_HINT} (${errorMessage(error)})`,\n 'local',\n );\n configurationError.cause = error;\n throw configurationError;\n }\n\n // Accept both the ES namespace and a CommonJS-style `default` wrapper.\n const candidate = loaded as\n | (Partial<TransformersModule> & { default?: TransformersModule })\n | undefined;\n const module =\n typeof candidate?.pipeline === 'function'\n ? (candidate as TransformersModule)\n : candidate?.default;\n\n if (typeof module?.pipeline !== 'function') {\n throw new SpeechConfigurationError(\n `${TRANSFORMERS_PACKAGE} did not export pipeline(); expected v4 or later`,\n 'local',\n );\n }\n return module;\n}\n\n/** Isolated so tests can observe whether the peer was ever imported. */\nexport function importTransformers(): Promise<unknown> {\n return import('@huggingface/transformers');\n}\n\nexport interface CreatePipelineOptions {\n model: string;\n device: LocalTranscriberDevice;\n dtype?: LocalTranscriberDtype;\n revision?: string;\n cacheDir?: string;\n onProgress?: LocalTranscriberProgressCallback;\n}\n\n/**\n * Creates the ASR pipeline. `device: 'auto'` resolves to WebGPU when a\n * browser adapter is available (retrying on WASM if WebGPU fails to\n * initialise) and to CPU elsewhere.\n */\nexport async function createAsrPipeline(\n transformers: TransformersModule,\n options: CreatePipelineOptions,\n): Promise<{ pipeline: AsrPipeline; device: string }> {\n const device =\n options.device === 'auto' ? await detectAutoDevice() : options.device;\n const build = (selected: string) =>\n transformers.pipeline('automatic-speech-recognition', options.model, {\n device: selected,\n ...(options.dtype ? { dtype: options.dtype } : {}),\n ...(options.revision ? { revision: options.revision } : {}),\n ...(options.cacheDir ? { cache_dir: options.cacheDir } : {}),\n ...(options.onProgress ? { progress_callback: options.onProgress } : {}),\n });\n\n try {\n return { pipeline: await build(device), device };\n } catch (error) {\n if (options.device === 'auto' && device === 'webgpu') {\n return { pipeline: await build('wasm'), device: 'wasm' };\n }\n throw error;\n }\n}\n\n/** `webgpu` when a browser exposes a usable adapter, `wasm` in other browsers, `cpu` in Node. */\nexport async function detectAutoDevice(): Promise<string> {\n if (isNodeRuntime()) {\n return 'cpu';\n }\n\n const gpu = (\n globalThis.navigator as\n | { gpu?: { requestAdapter(): Promise<unknown> } }\n | undefined\n )?.gpu;\n if (gpu) {\n try {\n if (await gpu.requestAdapter()) {\n return 'webgpu';\n }\n } catch {\n // No usable adapter: fall through to WASM.\n }\n }\n return 'wasm';\n}\n\nexport function isNodeRuntime(): boolean {\n return Boolean(\n (globalThis as { process?: { versions?: { node?: string } } }).process\n ?.versions?.node,\n );\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Rejects with the signal's reason as soon as it aborts, without waiting for\n * `promise` (which keeps running; its eventual result is ignored).\n */\nexport function raceAbort<T>(\n promise: Promise<T>,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (!signal) {\n return promise;\n }\n if (signal.aborted) {\n // Mark the abandoned promise handled so a later rejection is not reported.\n promise.catch(() => undefined);\n return Promise.reject(signal.reason);\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n","/**\n * Audio decoding for on-device inference: turns encoded bytes into 16 kHz\n * mono Float32 PCM, the input Whisper and Moonshine expect.\n *\n * - WAV (PCM 8/16/24/32-bit, IEEE float 32/64, WAVE_FORMAT_EXTENSIBLE) and raw\n * `audio/pcm` are decoded in-process in every runtime, including workers.\n * - Anything else goes to the caller's `decodeAudio` hook, or, in browsers,\n * to `OfflineAudioContext.decodeAudioData` (MediaRecorder WebM/Opus, Safari\n * `audio/mp4`, MP3, ...).\n */\n\nimport { normalizeMimeType } from '../../shared/audio.js';\nimport {\n SpeechConfigurationError,\n SpeechInvalidAudioError,\n} from '../../shared/errors.js';\nimport { raceAbort } from './abort.js';\nimport type { DecodedAudio, LocalAudioDecoder } from './types.js';\n\n/** Sample rate expected by Whisper and Moonshine feature extractors. */\nexport const LOCAL_SAMPLE_RATE = 16_000;\n\nconst ADAPTER = 'local';\n\nconst WAV_MIME_TYPES = new Set([\n 'audio/wav',\n 'audio/wave',\n 'audio/x-wav',\n 'audio/vnd.wave',\n]);\nconst PCM_MIME_TYPES = new Set(['audio/pcm', 'audio/x-pcm', 'audio/raw']);\n\nexport interface DecodeToPcmOptions {\n /** Full MIME type including parameters (`audio/pcm;rate=16000`). */\n mimeType: string;\n /** Sample rate for raw PCM when the MIME type has no `rate` parameter. */\n sampleRate?: number;\n /** Channel count for raw PCM when the MIME type has no `channels` parameter. */\n channels?: number;\n decodeAudio?: LocalAudioDecoder;\n signal?: AbortSignal;\n}\n\n/**\n * Decodes `bytes` and returns 16 kHz mono Float32 samples.\n *\n * Raw PCM (`audio/pcm`) is little-endian signed 16-bit by default; pass\n * `encoding=f32le` for Float32. The rate comes from the `rate` MIME parameter\n * or `sampleRate`, the channel count from `channels` (default 1).\n */\nexport async function decodeToPcm16k(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): Promise<Float32Array> {\n // Decoders (caller hooks, decodeAudioData) cannot be cancelled: stop waiting on abort.\n const decoded = await raceAbort(\n decodeAudioBytes(bytes, options),\n options.signal,\n );\n return toPcm16k(decoded);\n}\n\n/** Downmixes decoded audio to mono and resamples it to 16 kHz. */\nexport function toPcm16k(decoded: DecodedAudio): Float32Array {\n return resample(\n downmix(decoded.samples),\n decoded.sampleRate,\n LOCAL_SAMPLE_RATE,\n );\n}\n\n/**\n * True for WAV and raw PCM, which this module decodes itself in every\n * runtime, including Web Workers.\n */\nexport function isInProcessDecodable(\n bytes: Uint8Array,\n mimeType: string,\n): boolean {\n const essence = normalizeMimeType(mimeType);\n return Boolean(\n (essence && (WAV_MIME_TYPES.has(essence) || PCM_MIME_TYPES.has(essence))) ||\n isRiffWave(bytes),\n );\n}\n\n/**\n * Decodes a format without an in-process decoder through the caller's\n * `decodeAudio` hook or, in browsers, `OfflineAudioContext`. Resolves\n * `undefined` when neither is available.\n */\nexport async function decodeExternally(\n bytes: Uint8Array,\n options: Omit<DecodeToPcmOptions, 'sampleRate' | 'channels'>,\n): Promise<DecodedAudio | undefined> {\n if (options.decodeAudio) {\n const decoded = await options.decodeAudio({\n bytes,\n mimeType: options.mimeType,\n signal: options.signal,\n });\n assertDecodedAudio(decoded);\n return decoded;\n }\n\n if (hasOfflineAudioContext()) {\n return decodeWithAudioContext(bytes);\n }\n\n return undefined;\n}\n\nasync function decodeAudioBytes(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): Promise<DecodedAudio> {\n const essence = normalizeMimeType(options.mimeType);\n\n if ((essence && WAV_MIME_TYPES.has(essence)) || isRiffWave(bytes)) {\n return decodeWav(bytes);\n }\n\n if (essence && PCM_MIME_TYPES.has(essence)) {\n return decodeRawPcm(bytes, options);\n }\n\n const decoded = await decodeExternally(bytes, options);\n if (decoded) {\n return decoded;\n }\n\n throw new SpeechInvalidAudioError(\n `Cannot decode '${options.mimeType}' audio on this runtime: pass WAV or audio/pcm, ` +\n 'or supply a decodeAudio hook (e.g. ffmpeg) for compressed formats',\n ADAPTER,\n );\n}\n\nfunction isRiffWave(bytes: Uint8Array): boolean {\n return (\n bytes.byteLength >= 12 &&\n ascii(bytes, 0, 4) === 'RIFF' &&\n ascii(bytes, 8, 4) === 'WAVE'\n );\n}\n\n/** Parses a RIFF/WAVE file into per-channel Float32 samples. */\nexport function decodeWav(bytes: Uint8Array): DecodedAudio {\n if (!isRiffWave(bytes)) {\n throw new SpeechInvalidAudioError(\n 'Invalid WAV data: missing RIFF/WAVE header',\n ADAPTER,\n );\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let format: WavFormat | undefined;\n let offset = 12;\n\n while (offset + 8 <= bytes.byteLength) {\n const id = ascii(bytes, offset, 4);\n const size = view.getUint32(offset + 4, true);\n const body = offset + 8;\n\n if (id === 'fmt ') {\n format = readWavFormat(view, body, size);\n } else if (id === 'data') {\n if (!format) {\n throw new SpeechInvalidAudioError(\n 'Invalid WAV data: data chunk before fmt chunk',\n ADAPTER,\n );\n }\n // Streamed WAVs may leave the size as 0 or 0xFFFFFFFF; clamp to the bytes we have.\n const available = bytes.byteLength - body;\n const length = size === 0 || size > available ? available : size;\n return {\n samples: deinterleave(\n bytes.subarray(body, body + length),\n format.channels,\n format.encoding,\n ),\n sampleRate: format.sampleRate,\n };\n }\n\n offset = body + size + (size % 2);\n }\n\n throw new SpeechInvalidAudioError('Invalid WAV data: no data chunk', ADAPTER);\n}\n\ntype PcmEncoding = 'u8' | 's16le' | 's24le' | 's32le' | 'f32le' | 'f64le';\n\ninterface WavFormat {\n channels: number;\n sampleRate: number;\n encoding: PcmEncoding;\n}\n\nconst WAVE_FORMAT_PCM = 1;\nconst WAVE_FORMAT_IEEE_FLOAT = 3;\nconst WAVE_FORMAT_EXTENSIBLE = 0xfffe;\n\nfunction readWavFormat(view: DataView, body: number, size: number): WavFormat {\n if (size < 16) {\n throw new SpeechInvalidAudioError(\n 'Invalid WAV data: fmt chunk too short',\n ADAPTER,\n );\n }\n\n let tag = view.getUint16(body, true);\n const channels = view.getUint16(body + 2, true);\n const sampleRate = view.getUint32(body + 4, true);\n const bitsPerSample = view.getUint16(body + 14, true);\n\n if (tag === WAVE_FORMAT_EXTENSIBLE && size >= 26) {\n // The first two bytes of the SubFormat GUID carry the real format tag.\n tag = view.getUint16(body + 24, true);\n }\n\n const encoding =\n tag === WAVE_FORMAT_PCM\n ? ({ 8: 'u8', 16: 's16le', 24: 's24le', 32: 's32le' } as const)[\n bitsPerSample\n ]\n : tag === WAVE_FORMAT_IEEE_FLOAT\n ? ({ 32: 'f32le', 64: 'f64le' } as const)[bitsPerSample]\n : undefined;\n\n if (!encoding || channels < 1 || sampleRate < 1) {\n throw new SpeechInvalidAudioError(\n `Unsupported WAV encoding (format ${tag}, ${bitsPerSample}-bit, ${channels} channels); ` +\n 'use PCM or IEEE float, or supply a decodeAudio hook',\n ADAPTER,\n );\n }\n\n return { channels, sampleRate, encoding };\n}\n\nfunction decodeRawPcm(\n bytes: Uint8Array,\n options: DecodeToPcmOptions,\n): DecodedAudio {\n const params = mimeParameters(options.mimeType);\n const sampleRate =\n positiveInteger(params.rate) ??\n explicitPositiveInteger(options.sampleRate, 'sampleRate');\n const channels =\n positiveInteger(params.channels) ??\n explicitPositiveInteger(options.channels, 'channels') ??\n 1;\n const encoding = (params.encoding ?? 's16le').toLowerCase();\n\n if (!sampleRate) {\n throw new SpeechInvalidAudioError(\n \"Raw PCM audio needs a sample rate: use 'audio/pcm;rate=16000' or AudioInput.sampleRate\",\n ADAPTER,\n );\n }\n if (encoding !== 's16le' && encoding !== 'f32le') {\n throw new SpeechInvalidAudioError(\n `Unsupported raw PCM encoding '${encoding}' (expected s16le or f32le)`,\n ADAPTER,\n );\n }\n\n return {\n samples: deinterleave(bytes, channels, encoding),\n sampleRate,\n };\n}\n\nconst BYTES_PER_SAMPLE: Record<PcmEncoding, number> = {\n u8: 1,\n s16le: 2,\n s24le: 3,\n s32le: 4,\n f32le: 4,\n f64le: 8,\n};\n\nfunction deinterleave(\n bytes: Uint8Array,\n channels: number,\n encoding: PcmEncoding,\n): Float32Array[] {\n const width = BYTES_PER_SAMPLE[encoding];\n const frames = Math.floor(bytes.byteLength / (width * channels));\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const output = Array.from(\n { length: channels },\n () => new Float32Array(frames),\n );\n\n for (let frame = 0; frame < frames; frame++) {\n for (let channel = 0; channel < channels; channel++) {\n const at = (frame * channels + channel) * width;\n (output[channel] as Float32Array)[frame] = readSample(view, at, encoding);\n }\n }\n\n return output;\n}\n\nfunction readSample(view: DataView, at: number, encoding: PcmEncoding): number {\n switch (encoding) {\n case 'u8':\n return (view.getUint8(at) - 128) / 128;\n case 's16le':\n return view.getInt16(at, true) / 32_768;\n case 's24le': {\n const value =\n view.getUint8(at) |\n (view.getUint8(at + 1) << 8) |\n (view.getInt8(at + 2) << 16);\n return value / 8_388_608;\n }\n case 's32le':\n return view.getInt32(at, true) / 2_147_483_648;\n case 'f32le':\n return view.getFloat32(at, true);\n case 'f64le':\n return view.getFloat64(at, true);\n }\n}\n\n/** Averages channels into one mono track. */\nexport function downmix(samples: Float32Array | Float32Array[]): Float32Array {\n if (samples instanceof Float32Array) {\n return samples;\n }\n if (samples.length === 1) {\n return samples[0] as Float32Array;\n }\n\n const length = Math.min(...samples.map((channel) => channel.length));\n const mono = new Float32Array(length);\n for (const channel of samples) {\n for (let index = 0; index < length; index++) {\n (mono as Float32Array)[index] =\n (mono[index] as number) + (channel[index] as number) / samples.length;\n }\n }\n return mono;\n}\n\n/**\n * Resamples mono audio with linear interpolation. When downsampling, a box\n * filter one source period wide is applied first to limit aliasing; this is\n * adequate for speech recognition, not for music playback.\n */\nexport function resample(\n samples: Float32Array,\n fromRate: number,\n toRate: number,\n): Float32Array {\n if (fromRate === toRate || samples.length === 0) {\n return samples;\n }\n\n const ratio = fromRate / toRate;\n const source = ratio > 1 ? boxFilter(samples, Math.round(ratio)) : samples;\n const length = Math.max(1, Math.round(samples.length / ratio));\n const output = new Float32Array(length);\n\n for (let index = 0; index < length; index++) {\n const position = index * ratio;\n const left = Math.floor(position);\n const right = Math.min(left + 1, source.length - 1);\n const fraction = position - left;\n output[index] =\n (source[left] as number) * (1 - fraction) +\n (source[right] as number) * fraction;\n }\n\n return output;\n}\n\nfunction boxFilter(samples: Float32Array, width: number): Float32Array {\n if (width <= 1) {\n return samples;\n }\n\n const output = new Float32Array(samples.length);\n const half = Math.floor(width / 2);\n let sum = 0;\n let count = 0;\n let start = 0;\n let end = 0;\n\n for (let index = 0; index < samples.length; index++) {\n const windowStart = Math.max(0, index - half);\n const windowEnd = Math.min(samples.length, index - half + width);\n while (end < windowEnd) {\n sum += samples[end] as number;\n count++;\n end++;\n }\n while (start < windowStart) {\n sum -= samples[start] as number;\n count--;\n start++;\n }\n output[index] = sum / count;\n }\n\n return output;\n}\n\nfunction hasOfflineAudioContext(): boolean {\n return typeof globalThis.OfflineAudioContext === 'function';\n}\n\n/**\n * Browser decode path. Decoding through a 16 kHz `OfflineAudioContext` makes\n * the browser resample with its own high-quality resampler. Not available in\n * Web Workers: decode on the main thread (see the worker client).\n */\nasync function decodeWithAudioContext(\n bytes: Uint8Array,\n): Promise<DecodedAudio> {\n const context = new OfflineAudioContext(1, 1, LOCAL_SAMPLE_RATE);\n // decodeAudioData detaches its input, so hand it a private copy.\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n\n let buffer: AudioBuffer;\n try {\n buffer = await context.decodeAudioData(copy.buffer);\n } catch (error) {\n throw new SpeechInvalidAudioError(\n `The browser could not decode the audio: ${errorMessage(error)}`,\n ADAPTER,\n );\n }\n\n const channels = Array.from({ length: buffer.numberOfChannels }, (_, index) =>\n buffer.getChannelData(index),\n );\n return { samples: channels, sampleRate: buffer.sampleRate };\n}\n\nfunction assertDecodedAudio(decoded: DecodedAudio): void {\n const channels =\n decoded?.samples instanceof Float32Array\n ? [decoded.samples]\n : decoded?.samples;\n if (\n !Array.isArray(channels) ||\n channels.length === 0 ||\n !channels.every((channel) => channel instanceof Float32Array) ||\n !(decoded.sampleRate > 0)\n ) {\n throw new SpeechConfigurationError(\n 'decodeAudio must return { samples: Float32Array | Float32Array[], sampleRate }',\n ADAPTER,\n );\n }\n}\n\nfunction mimeParameters(mimeType: string): Record<string, string> {\n const params: Record<string, string> = {};\n for (const part of mimeType.split(';').slice(1)) {\n const [key, value] = part.split('=');\n if (key && value) {\n params[key.trim().toLowerCase()] = value.trim().replace(/^\"|\"$/g, '');\n }\n }\n return params;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n const parsed = value ? Number.parseInt(value, 10) : Number.NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/** Validates a caller-supplied `AudioInput.sampleRate`/`channels`. */\nfunction explicitPositiveInteger(\n value: number | undefined,\n name: 'sampleRate' | 'channels',\n): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n if (!Number.isInteger(value) || value < 1) {\n throw new SpeechInvalidAudioError(\n `AudioInput.${name} must be a positive integer, got ${String(value)}`,\n ADAPTER,\n );\n }\n return value;\n}\n\nfunction ascii(bytes: Uint8Array, offset: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(offset, offset + length));\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../../shared/audio.js';\nimport {\n SpeechConfigurationError,\n SpeechProviderError,\n} from '../../shared/errors.js';\nimport type {\n AudioInput,\n AudioSource,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n TranscriptSegment,\n WordTiming,\n} from '../../shared/types.js';\nimport {\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../../shared/usage.js';\nimport { raceAbort } from './abort.js';\nimport { decodeToPcm16k, LOCAL_SAMPLE_RATE } from './audio.js';\nimport {\n type AsrOutput,\n type AsrPipeline,\n createAsrPipeline,\n detectAutoDevice,\n loadTransformers,\n type TransformersModule,\n} from './runtime.js';\nimport type { LocalTranscriberOptions } from './types.js';\n\n/** Small multilingual Whisper (~80 MB at the default q8 quantisation). */\nexport const DEFAULT_LOCAL_MODEL = 'onnx-community/whisper-base';\n\nconst DEFAULT_CHUNK_LENGTH_SECONDS = 30;\nconst WHISPER_WINDOW_SECONDS = 30;\nconst WHISPER_MODEL_TYPES = new Set(['whisper', 'lite-whisper']);\n\ninterface LoadedPipeline {\n pipeline: AsrPipeline;\n device: string;\n}\n\n/**\n * On-device speech-to-text with transformers.js (Whisper or Moonshine ONNX).\n * Runs on WebGPU/WASM in browsers and onnxruntime-node in Node; no audio\n * leaves the device and no API key is involved.\n */\nexport class LocalTranscriber implements Transcriber {\n readonly type = 'local' as const;\n\n private readonly options: LocalTranscriberOptions;\n private readonly model: string;\n private readonly maxBytes: number;\n private readonly onUsage?: SpeechUsageCallback;\n private runtime?: Promise<TransformersModule>;\n private readonly pipelines = new Map<string, Promise<LoadedPipeline>>();\n /** Serialises inference: ONNX sessions (especially WebGPU) run one call at a time. */\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(options: LocalTranscriberOptions = {}) {\n this.options = options;\n this.model = options.model?.trim() || DEFAULT_LOCAL_MODEL;\n this.maxBytes = options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES;\n this.onUsage = options.onUsage;\n }\n\n /**\n * Downloads (first time) and initialises the model so the first\n * `transcribe()` is fast. Progress goes to `onProgress`.\n */\n async preload(model?: string, signal?: AbortSignal): Promise<void> {\n await raceAbort(this.loadPipeline(model?.trim() || this.model), signal);\n }\n\n /**\n * Whether every file `model` needs is already in the model cache (Cache\n * Storage in browsers), so a `preload()` would download nothing. Resolves\n * `false` when the runtime cannot tell. Does not load the model.\n */\n async isCached(model?: string): Promise<boolean> {\n try {\n const transformers = await this.loadRuntime();\n if (!transformers.ModelRegistry) {\n return false;\n }\n const { dtype, revision, cacheDir } = this.options;\n // Cached files depend on the device (transformers.js picks per-device\n // dtypes), so resolve it exactly as loadPipeline does.\n const requested = this.options.device ?? 'auto';\n const device =\n requested === 'auto' ? await detectAutoDevice() : requested;\n return await transformers.ModelRegistry.is_pipeline_cached(\n 'automatic-speech-recognition',\n model?.trim() || this.model,\n {\n device,\n ...(dtype ? { dtype } : {}),\n ...(revision ? { revision } : {}),\n ...(cacheDir ? { cache_dir: cacheDir } : {}),\n },\n );\n } catch {\n return false;\n }\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n const { signal } = request;\n const audio = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes: request.maxBytes ?? this.maxBytes,\n adapter: this.type,\n signal,\n });\n const format = pcmFormat(request.audio);\n const samples = await decodeToPcm16k(\n new Uint8Array(await audio.blob.arrayBuffer()),\n {\n mimeType: audio.mimeType,\n sampleRate: format.sampleRate,\n channels: format.channels,\n decodeAudio: this.options.decodeAudio,\n signal,\n },\n );\n signal?.throwIfAborted();\n\n const model = request.model?.trim() || this.model;\n const loaded = await raceAbort(this.loadPipeline(model), signal);\n const audioSeconds = samples.length / LOCAL_SAMPLE_RATE;\n // The queue tracks the real inference; the caller stops waiting on abort.\n const output = await raceAbort(\n this.enqueue(() =>\n this.run(loaded.pipeline, samples, audioSeconds, request, model),\n ),\n signal,\n );\n\n const usage: SpeechUsage = {\n operation: 'transcription',\n provider: this.type,\n model,\n audioSeconds,\n bytes: audio.bytes,\n };\n const result: TranscriptResult = {\n ...mapAsrOutput(\n output,\n request.timestampGranularities?.includes('word') ?? false,\n ),\n language: request.language,\n durationSeconds: audioSeconds,\n provider: this.type,\n model,\n raw: output,\n usage,\n };\n\n await reportSpeechUsage(usage, this.onUsage, request.onUsage);\n return result;\n }\n\n /** Waits for in-flight inference, then releases every loaded model session. */\n async dispose(): Promise<void> {\n await this.queue;\n const loaded = [...this.pipelines.values()];\n this.pipelines.clear();\n await Promise.all(\n loaded.map(async (entry) => {\n const { pipeline } = await entry.catch(() => ({ pipeline: undefined }));\n await pipeline?.dispose?.();\n }),\n );\n }\n\n private async run(\n pipeline: AsrPipeline,\n samples: Float32Array,\n audioSeconds: number,\n request: TranscriptionRequest,\n model: string,\n ): Promise<AsrOutput> {\n const { signal } = request;\n signal?.throwIfAborted();\n\n const transformers = await this.loadRuntime();\n const options: Record<string, unknown> = {};\n const isWhisper = WHISPER_MODEL_TYPES.has(\n pipeline.model?.config?.model_type ?? 'whisper',\n );\n\n if (isWhisper) {\n const granularities = request.timestampGranularities ?? [];\n if (granularities.includes('word')) {\n options.return_timestamps = 'word';\n } else if (granularities.includes('segment')) {\n options.return_timestamps = true;\n }\n const chunkLength =\n this.options.chunkLengthSeconds ?? DEFAULT_CHUNK_LENGTH_SECONDS;\n if (chunkLength > 0 && audioSeconds > WHISPER_WINDOW_SECONDS) {\n options.chunk_length_s = chunkLength;\n }\n // English-only Whisper (`*.en`) rejects `language` and `task` outright.\n if (request.language && !isEnglishOnly(pipeline, model)) {\n options.language = request.language;\n options.task = 'transcribe';\n }\n }\n\n // Stops generation between tokens when the caller aborts.\n let stopping: { interrupt(): void } | undefined;\n if (signal && transformers.InterruptableStoppingCriteria) {\n stopping = new transformers.InterruptableStoppingCriteria();\n options.stopping_criteria = stopping;\n }\n const onAbort = () => stopping?.interrupt();\n signal?.addEventListener('abort', onAbort, { once: true });\n\n try {\n const output = await pipeline(samples, options);\n return Array.isArray(output) ? (output[0] ?? { text: '' }) : output;\n } catch (error) {\n if (signal?.aborted) {\n throw signal.reason;\n }\n const hint =\n options.return_timestamps === 'word' &&\n /cross attentions/i.test(errorMessage(error))\n ? ' (word timestamps need a model exported with cross attentions, e.g. onnx-community/whisper-base_timestamped)'\n : '';\n throw new SpeechProviderError(\n this.type,\n `Local transcription failed: ${errorMessage(error)}${hint}`,\n { cause: error },\n );\n } finally {\n signal?.removeEventListener('abort', onAbort);\n }\n }\n\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const next = this.queue.then(task, task);\n this.queue = next.catch(() => undefined);\n return next;\n }\n\n private loadRuntime(): Promise<TransformersModule> {\n if (!this.runtime) {\n const { transformers, configureEnv, modelHost } = this.options;\n this.runtime = loadTransformers(transformers).then((module) => {\n if (module.env) {\n if (modelHost) {\n module.env.remoteHost = modelHost;\n }\n configureEnv?.(module.env);\n }\n return module;\n });\n // A failed load (e.g. missing peer) is retried on the next call.\n this.runtime.catch(() => {\n this.runtime = undefined;\n });\n }\n return this.runtime;\n }\n\n private loadPipeline(model: string): Promise<LoadedPipeline> {\n let loaded = this.pipelines.get(model);\n if (!loaded) {\n loaded = this.loadRuntime().then((transformers) =>\n createAsrPipeline(transformers, {\n model,\n device: this.options.device ?? 'auto',\n dtype: this.options.dtype,\n revision: this.options.revision,\n cacheDir: this.options.cacheDir,\n onProgress: this.options.onProgress,\n }).catch((error: unknown) => {\n if (error instanceof SpeechConfigurationError) {\n throw error;\n }\n throw new SpeechProviderError(\n this.type,\n `Failed to load local model '${model}': ${errorMessage(error)}`,\n { cause: error },\n );\n }),\n );\n this.pipelines.set(model, loaded);\n loaded.catch(() => {\n if (this.pipelines.get(model) === loaded) {\n this.pipelines.delete(model);\n }\n });\n }\n return loaded;\n }\n}\n\n/**\n * Maps transformers.js chunks onto `words` (when word timestamps were\n * requested) or `segments`.\n */\nexport function mapAsrOutput(\n output: AsrOutput,\n wordLevel: boolean,\n): Pick<TranscriptResult, 'text' | 'segments' | 'words'> {\n const result: Pick<TranscriptResult, 'text' | 'segments' | 'words'> = {\n text: (output.text ?? '').trim(),\n };\n const chunks = output.chunks;\n if (!chunks?.length) {\n return result;\n }\n\n if (wordLevel) {\n result.words = chunks\n .filter((chunk) => chunk.text.trim())\n .map(\n (chunk): WordTiming => ({\n word: chunk.text.trim(),\n startSeconds: chunk.timestamp[0],\n endSeconds: chunk.timestamp[1] ?? chunk.timestamp[0],\n }),\n );\n } else {\n result.segments = chunks.map(\n (chunk): TranscriptSegment => ({\n text: chunk.text.trim(),\n startSeconds: chunk.timestamp[0],\n endSeconds: chunk.timestamp[1] ?? undefined,\n }),\n );\n }\n return result;\n}\n\n/** Whether this Whisper only transcribes English (it rejects `language`/`task`). */\nfunction isEnglishOnly(pipeline: AsrPipeline, model: string): boolean {\n const multilingual = pipeline.model?.generation_config?.is_multilingual;\n if (typeof multilingual === 'boolean') {\n return !multilingual;\n }\n return /\\.en(?:$|[-_])/i.test(model);\n}\n\nfunction pcmFormat(audio: AudioInput | AudioSource): {\n sampleRate?: number;\n channels?: number;\n} {\n if (\n audio &&\n typeof audio === 'object' &&\n 'data' in audio &&\n !(audio instanceof Uint8Array)\n ) {\n return { sampleRate: audio.sampleRate, channels: audio.channels };\n }\n return {};\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Web Worker support for the `local` transcriber.\n *\n * Inference runs in a worker (`serveLocalTranscriber`) so long recordings never\n * block the UI thread. The main-thread client (`LocalTranscriberWorkerClient`)\n * transfers WAV and raw PCM bytes to the worker, which parses, downmixes, and\n * resamples them there. Only formats that need `AudioContext` (which workers\n * lack) or the client's `decodeAudio` hook are decoded on the main thread;\n * their Float32 channels are then transferred and resampled in the worker.\n */\n\nimport {\n DEFAULT_MAX_AUDIO_BYTES,\n normalizeAudioInput,\n} from '../../shared/audio.js';\nimport {\n SpeechAudioTooLargeError,\n SpeechConfigurationError,\n SpeechError,\n SpeechInvalidAudioError,\n SpeechProviderError,\n SpeechProviderUnavailableError,\n} from '../../shared/errors.js';\nimport type {\n AudioInput,\n TimestampGranularity,\n Transcriber,\n TranscriptionRequest,\n TranscriptResult,\n} from '../../shared/types.js';\nimport {\n reportSpeechUsage,\n type SpeechUsage,\n type SpeechUsageCallback,\n} from '../../shared/usage.js';\nimport { raceAbort } from './abort.js';\nimport {\n decodeExternally,\n isInProcessDecodable,\n LOCAL_SAMPLE_RATE,\n toPcm16k,\n} from './audio.js';\nimport { resolveLocalTranscriberOptions } from './env.js';\nimport { LocalTranscriber } from './transcriber.js';\nimport type {\n LocalAudioDecoder,\n LocalTranscriberOptions,\n LocalTranscriberProgress,\n LocalTranscriberProgressCallback,\n} from './types.js';\n\n/** Minimal `postMessage` endpoint: a `Worker`, `DedicatedWorkerGlobalScope`, or `MessagePort`. */\nexport interface LocalWorkerEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(\n type: 'message',\n listener: (event: MessageEvent) => void,\n ): void;\n removeEventListener(\n type: 'message',\n listener: (event: MessageEvent) => void,\n ): void;\n}\n\n/** Audio sent to the worker. Its buffers are transferred, not copied. */\nexport type LocalWorkerAudio =\n | {\n /** WAV or raw PCM bytes (or any format the worker's `decodeAudio` handles). */\n kind: 'encoded';\n bytes: Uint8Array;\n mimeType: string;\n sampleRate?: number;\n channels?: number;\n }\n | {\n /** Channels decoded on the main thread; the worker downmixes and resamples. */\n kind: 'decoded';\n samples: Float32Array[];\n sampleRate: number;\n };\n\n/** Messages from the client to the worker. */\nexport type LocalWorkerRequest =\n | {\n kind: 'transcribe';\n id: number;\n audio: LocalWorkerAudio;\n model?: string;\n language?: string;\n timestampGranularities?: TimestampGranularity[];\n }\n | { kind: 'preload'; id: number; model?: string }\n | { kind: 'isCached'; id: number; model?: string }\n | { kind: 'abort'; id: number };\n\n/** Messages from the worker to the client. */\nexport type LocalWorkerResponse =\n | {\n kind: 'result';\n id: number;\n result?: TranscriptResult;\n /** Answer to an `isCached` request. */\n cached?: boolean;\n }\n | {\n kind: 'error';\n id: number;\n error: SerializedError;\n }\n | { kind: 'progress'; progress: LocalTranscriberProgress };\n\ninterface WorkerReply {\n result?: TranscriptResult;\n cached?: boolean;\n}\n\nconst PCM_MIME_TYPE = `audio/pcm;rate=${LOCAL_SAMPLE_RATE};encoding=f32le`;\n\n/**\n * Runs a {@link LocalTranscriber} inside a worker and answers client\n * requests. Call it from your worker module:\n *\n * ```ts\n * // transcriber.worker.ts\n * import { serveLocalTranscriber } from '@happyvertical/speech/local';\n * serveLocalTranscriber({ model: 'onnx-community/whisper-base' });\n * ```\n *\n * Returns a function that stops listening and disposes the model.\n */\nexport function serveLocalTranscriber(\n options: LocalTranscriberOptions = {},\n endpoint: LocalWorkerEndpoint = globalThis as unknown as LocalWorkerEndpoint,\n): () => Promise<void> {\n const userProgress = options.onProgress;\n const transcriber = new LocalTranscriber({\n ...resolveLocalTranscriberOptions(options),\n // The client already enforced its byte limit on the encoded input.\n maxBytes: Number.POSITIVE_INFINITY,\n onProgress: (progress) => {\n userProgress?.(progress);\n endpoint.postMessage({\n kind: 'progress',\n progress: toCloneable(progress),\n } satisfies LocalWorkerResponse);\n },\n });\n const pending = new Map<number, AbortController>();\n\n const reply = (message: LocalWorkerResponse) => endpoint.postMessage(message);\n const listener = (event: MessageEvent) => {\n const message = event.data as LocalWorkerRequest;\n if (!message || typeof message !== 'object') {\n return;\n }\n\n if (message.kind === 'abort') {\n pending.get(message.id)?.abort();\n return;\n }\n\n const controller = new AbortController();\n pending.set(message.id, controller);\n const work =\n message.kind === 'isCached'\n ? transcriber.isCached(message.model).then((cached) => {\n reply({ kind: 'result', id: message.id, cached });\n })\n : message.kind === 'preload'\n ? transcriber.preload(message.model, controller.signal).then(() => {\n reply({ kind: 'result', id: message.id });\n })\n : Promise.resolve()\n .then(() =>\n transcriber.transcribe({\n audio: toAudioInput(message.audio),\n model: message.model,\n language: message.language,\n timestampGranularities: message.timestampGranularities,\n signal: controller.signal,\n }),\n )\n .then((result) => {\n reply({ kind: 'result', id: message.id, result });\n });\n\n work\n .catch((error: unknown) => {\n reply({ kind: 'error', id: message.id, error: serializeError(error) });\n })\n .finally(() => pending.delete(message.id));\n };\n\n endpoint.addEventListener('message', listener);\n return async () => {\n endpoint.removeEventListener('message', listener);\n for (const controller of pending.values()) {\n controller.abort();\n }\n await transcriber.dispose();\n };\n}\n\nexport interface LocalTranscriberWorkerClientOptions {\n /** Encoded input byte limit, checked on the main thread. Default 25 MB. */\n maxBytes?: number;\n /**\n * Main-thread decoder for formats other than WAV and raw PCM. Formats with\n * neither this hook nor `AudioContext` go to the worker's own `decodeAudio`.\n */\n decodeAudio?: LocalAudioDecoder;\n /** Model download/load progress forwarded from the worker. */\n onProgress?: LocalTranscriberProgressCallback;\n /** Called with usage after every successful transcription. */\n onUsage?: SpeechUsageCallback;\n}\n\n/**\n * Main-thread {@link Transcriber} that delegates inference to a worker running\n * {@link serveLocalTranscriber}.\n *\n * ```ts\n * const worker = new Worker(new URL('./transcriber.worker.ts', import.meta.url), { type: 'module' });\n * const transcriber = new LocalTranscriberWorkerClient(worker, { onProgress });\n * const { text } = await transcriber.transcribe({ audio: recordingBlob });\n * ```\n */\nexport class LocalTranscriberWorkerClient implements Transcriber {\n readonly type = 'local' as const;\n\n private readonly endpoint: LocalWorkerEndpoint;\n private readonly options: LocalTranscriberWorkerClientOptions;\n private readonly calls = new Map<\n number,\n {\n resolve: (response: WorkerReply) => void;\n reject: (error: unknown) => void;\n }\n >();\n private nextId = 1;\n private closed = false;\n /** Aborted by `close()` to reject calls still buffering or decoding. */\n private readonly closing = new AbortController();\n private readonly listener = (event: MessageEvent) =>\n this.onMessage(event.data as LocalWorkerResponse);\n\n constructor(\n endpoint: LocalWorkerEndpoint,\n options: LocalTranscriberWorkerClientOptions = {},\n ) {\n this.endpoint = endpoint;\n this.options = options;\n endpoint.addEventListener('message', this.listener);\n }\n\n /** Whether the worker finds every file for `model` in the model cache. */\n async isCached(model?: string): Promise<boolean> {\n try {\n const { cached } = await this.call(\n { kind: 'isCached', model },\n undefined,\n );\n return cached === true;\n } catch {\n // Same contract as LocalTranscriber.isCached: unknown means false.\n return false;\n }\n }\n\n /** Loads the model in the worker ahead of the first transcription. */\n async preload(model?: string, signal?: AbortSignal): Promise<void> {\n await this.call({ kind: 'preload', model }, signal);\n }\n\n async transcribe(request: TranscriptionRequest): Promise<TranscriptResult> {\n this.assertOpen();\n const { signal } = request;\n // Buffering and main-thread decoding count as in flight: close() rejects them.\n const { audio, bytes } = await raceAbort(\n this.prepare(request),\n this.closing.signal,\n );\n\n const { result } = await this.call(\n {\n kind: 'transcribe',\n audio,\n model: request.model,\n language: request.language,\n timestampGranularities: request.timestampGranularities,\n },\n signal,\n transferList(audio),\n );\n if (!result) {\n throw new SpeechProviderError(this.type, 'Worker returned no result');\n }\n\n const usage: SpeechUsage = {\n ...(result.usage ?? { operation: 'transcription', provider: this.type }),\n bytes,\n };\n await reportSpeechUsage(usage, this.options.onUsage, request.onUsage);\n return { ...result, usage };\n }\n\n /**\n * Stops listening, rejects in-flight and later calls, and asks the worker to\n * abort posted work. Does not terminate the worker.\n */\n close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.endpoint.removeEventListener('message', this.listener);\n this.closing.abort(closedError());\n for (const [id, call] of this.calls) {\n this.endpoint.postMessage({\n kind: 'abort',\n id,\n } satisfies LocalWorkerRequest);\n call.reject(closedError());\n }\n this.calls.clear();\n }\n\n /** Buffers the input and, only when the worker cannot, decodes it here. */\n private async prepare(\n request: TranscriptionRequest,\n ): Promise<{ audio: LocalWorkerAudio; bytes: number }> {\n const { signal } = request;\n const normalized = await normalizeAudioInput(request.audio, {\n mimeType: request.mimeType,\n maxBytes:\n request.maxBytes ?? this.options.maxBytes ?? DEFAULT_MAX_AUDIO_BYTES,\n adapter: this.type,\n signal,\n });\n const bytes = new Uint8Array(await normalized.blob.arrayBuffer());\n\n if (!isInProcessDecodable(bytes, normalized.mimeType)) {\n // Decoders (caller hooks, decodeAudioData) cannot be cancelled: stop waiting on abort.\n const decoded = await raceAbort(\n decodeExternally(bytes, {\n mimeType: normalized.mimeType,\n decodeAudio: this.options.decodeAudio,\n signal,\n }),\n signal,\n );\n if (decoded) {\n const channels =\n decoded.samples instanceof Float32Array\n ? [decoded.samples]\n : decoded.samples;\n return {\n audio: {\n kind: 'decoded',\n samples: channels.map(ownedCopy),\n sampleRate: decoded.sampleRate,\n },\n bytes: normalized.bytes,\n };\n }\n }\n\n const wrapped =\n typeof request.audio === 'object' &&\n 'data' in request.audio &&\n !(request.audio instanceof Uint8Array)\n ? request.audio\n : undefined;\n return {\n audio: {\n kind: 'encoded',\n bytes: ownedCopy(bytes),\n mimeType: normalized.mimeType,\n sampleRate: wrapped?.sampleRate,\n channels: wrapped?.channels,\n },\n bytes: normalized.bytes,\n };\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw closedError();\n }\n }\n\n private call(\n message:\n | Omit<Extract<LocalWorkerRequest, { kind: 'transcribe' }>, 'id'>\n | Omit<Extract<LocalWorkerRequest, { kind: 'preload' }>, 'id'>\n | Omit<Extract<LocalWorkerRequest, { kind: 'isCached' }>, 'id'>,\n signal: AbortSignal | undefined,\n transfer: Transferable[] = [],\n ): Promise<WorkerReply> {\n this.assertOpen();\n signal?.throwIfAborted();\n const id = this.nextId++;\n\n return new Promise((resolve, reject) => {\n const onAbort = () => {\n this.calls.delete(id);\n this.endpoint.postMessage({ kind: 'abort', id });\n reject(signal?.reason);\n };\n const settle = () => signal?.removeEventListener('abort', onAbort);\n this.calls.set(id, {\n resolve: (response) => {\n settle();\n resolve(response);\n },\n reject: (error) => {\n settle();\n reject(error);\n },\n });\n signal?.addEventListener('abort', onAbort, { once: true });\n this.endpoint.postMessage({ ...message, id }, transfer);\n });\n }\n\n private onMessage(message: LocalWorkerResponse): void {\n if (!message || typeof message !== 'object') {\n return;\n }\n if (message.kind === 'progress') {\n this.options.onProgress?.(message.progress);\n return;\n }\n\n const call = this.calls.get(message.id);\n if (!call) {\n return;\n }\n this.calls.delete(message.id);\n if (message.kind === 'result') {\n call.resolve({ result: message.result, cached: message.cached });\n } else {\n call.reject(deserializeError(message.error));\n }\n }\n}\n\nfunction closedError(): SpeechConfigurationError {\n return new SpeechConfigurationError('Worker client closed', 'local');\n}\n\n/** Rebuilds the transcriber input from a worker message, resampling decoded channels here. */\nfunction toAudioInput(audio: LocalWorkerAudio): AudioInput {\n if (audio.kind === 'decoded') {\n const pcm = toPcm16k({\n samples: audio.samples,\n sampleRate: audio.sampleRate,\n });\n return {\n data: new Uint8Array(pcm.buffer, pcm.byteOffset, pcm.byteLength),\n mimeType: PCM_MIME_TYPE,\n };\n }\n return {\n data: audio.bytes,\n mimeType: audio.mimeType,\n sampleRate: audio.sampleRate,\n channels: audio.channels,\n };\n}\n\n/**\n * Returns `view` when it spans a whole ordinary `ArrayBuffer` (safe to\n * transfer), otherwise a copy. Views of a larger buffer would transfer\n * unrelated bytes, and a `SharedArrayBuffer` cannot be transferred at all.\n */\nfunction ownedCopy<T extends Uint8Array | Float32Array>(view: T): T {\n const { buffer } = view;\n return isPlainArrayBuffer(buffer) &&\n view.byteOffset === 0 &&\n view.byteLength === buffer.byteLength\n ? view\n : (view.slice() as T);\n}\n\nfunction isPlainArrayBuffer(buffer: ArrayBufferLike): buffer is ArrayBuffer {\n // Tag check rather than instanceof: buffers may come from another realm.\n return Object.prototype.toString.call(buffer) === '[object ArrayBuffer]';\n}\n\n/** Distinct buffers to transfer (two channels may share one). */\nfunction transferList(audio: LocalWorkerAudio): ArrayBuffer[] {\n const views = audio.kind === 'decoded' ? audio.samples : [audio.bytes];\n return [...new Set(views.map((view) => view.buffer as ArrayBuffer))];\n}\n\ninterface SerializedError {\n name: string;\n message: string;\n code?: string;\n maxBytes?: number;\n}\n\nfunction serializeError(error: unknown): SerializedError {\n if (error instanceof SpeechAudioTooLargeError) {\n return {\n name: error.name,\n message: error.message,\n code: error.code,\n maxBytes: error.maxBytes,\n };\n }\n if (error instanceof SpeechError) {\n return { name: error.name, message: error.message, code: error.code };\n }\n if (error instanceof Error) {\n return { name: error.name, message: error.message };\n }\n return { name: 'Error', message: String(error) };\n}\n\nfunction deserializeError(error: SerializedError): Error {\n switch (error.name) {\n case 'SpeechConfigurationError':\n return new SpeechConfigurationError(error.message, 'local');\n case 'SpeechInvalidAudioError':\n return new SpeechInvalidAudioError(error.message, 'local');\n case 'SpeechAudioTooLargeError':\n return new SpeechAudioTooLargeError(\n error.message,\n error.maxBytes ?? Number.NaN,\n 'local',\n );\n case 'SpeechProviderError':\n return new SpeechProviderError('local', error.message);\n case 'SpeechProviderUnavailableError':\n return new SpeechProviderUnavailableError('local', error.message);\n case 'AbortError':\n return new DOMException(error.message, 'AbortError');\n default: {\n const restored = new Error(error.message);\n restored.name = error.name;\n return restored;\n }\n }\n}\n\n/** Progress objects cross `postMessage`, so drop anything not cloneable. */\nfunction toCloneable(\n progress: LocalTranscriberProgress,\n): LocalTranscriberProgress {\n const clone: LocalTranscriberProgress = { status: progress.status };\n for (const [key, value] of Object.entries(progress)) {\n if (\n value === null ||\n ['string', 'number', 'boolean'].includes(typeof value)\n ) {\n clone[key] = value;\n }\n }\n return clone;\n}\n","/**\n * @happyvertical/speech/local\n *\n * On-device speech-to-text with transformers.js (`@huggingface/transformers`,\n * an optional peer dependency loaded on first use). Importing this entry\n * registers `type: 'local'` with `getTranscriber()` and `getSpeech()`.\n *\n * @example\n * ```typescript\n * import { getTranscriber } from '@happyvertical/speech';\n * import '@happyvertical/speech/local';\n *\n * const transcriber = await getTranscriber({ type: 'local' });\n * const { text } = await transcriber.transcribe({ audio: wavBytes, mimeType: 'audio/wav' });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { resolveLocalTranscriberOptions } from './adapters/local/env.js';\nimport { loadTransformers } from './adapters/local/runtime.js';\nimport { LocalTranscriber } from './adapters/local/transcriber.js';\nimport type { LocalTranscriberOptions } from './adapters/local/types.js';\nimport type { SpeechFactoryContext } from './shared/factory.js';\nimport { registerOptionalTranscriber } from './shared/registry.js';\n\nexport {\n decodeToPcm16k,\n LOCAL_SAMPLE_RATE,\n} from './adapters/local/audio.js';\nexport { LOCAL_TRANSCRIBER_ENV_KEYS } from './adapters/local/env.js';\nexport {\n TRANSFORMERS_INSTALL_HINT,\n TRANSFORMERS_PACKAGE,\n} from './adapters/local/runtime.js';\nexport {\n DEFAULT_LOCAL_MODEL,\n LocalTranscriber,\n} from './adapters/local/transcriber.js';\nexport type {\n DecodedAudio,\n LocalAudioDecoder,\n LocalTranscriberDevice,\n LocalTranscriberDtype,\n LocalTranscriberOptions,\n LocalTranscriberProgress,\n LocalTranscriberProgressCallback,\n LocalTranscriberSettings,\n LocalTransformersRuntime,\n} from './adapters/local/types.js';\nexport {\n LocalTranscriberWorkerClient,\n type LocalTranscriberWorkerClientOptions,\n type LocalWorkerAudio,\n type LocalWorkerEndpoint,\n type LocalWorkerRequest,\n type LocalWorkerResponse,\n serveLocalTranscriber,\n} from './adapters/local/worker.js';\n\n/**\n * Creates an on-device transcriber. Explicit options win over\n * `HAVE_SPEECH_TRANSCRIBER_*` environment variables (see\n * `LOCAL_TRANSCRIBER_ENV_KEYS`). The model loads on first use.\n */\nexport function createLocalTranscriber(\n options: LocalTranscriberOptions = {},\n context: Pick<SpeechFactoryContext, 'env'> = {},\n): LocalTranscriber {\n return new LocalTranscriber(resolveLocalTranscriberOptions(options, context));\n}\n\n/**\n * Resolves `true` when `@huggingface/transformers` can be imported in this\n * runtime. This imports the runtime, so call it only when you intend to use it.\n */\nexport async function isLocalTranscriberAvailable(): Promise<boolean> {\n try {\n await loadTransformers(undefined);\n return true;\n } catch {\n return false;\n }\n}\n\nregisterOptionalTranscriber('local', (options, context) =>\n createLocalTranscriber(\n { ...options, type: 'local' } as LocalTranscriberOptions,\n context,\n ),\n);\n"],"mappings":"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"}
|
package/dist/pcm.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as VAD_DEFAULTS, c as createVadSegmenter, d as MAX_RESAMPLE_OUTPUT_SAMPLES, f as resampleMono, i as pcm16ToFloat32, l as frameEnergy, n as float32ToPcm16, o as VAD_FRAME_MS, r as parseWavPcm16, s as createUtteranceCollector, t as encodeWavPcm16, u as resolveVadOptions } from "./chunks/wav-
|
|
1
|
+
import { l as WavFormatError } from "./chunks/errors-CzsR_5dE.js";
|
|
2
|
+
import { a as VAD_DEFAULTS, c as createVadSegmenter, d as MAX_RESAMPLE_OUTPUT_SAMPLES, f as resampleMono, i as pcm16ToFloat32, l as frameEnergy, n as float32ToPcm16, o as VAD_FRAME_MS, r as parseWavPcm16, s as createUtteranceCollector, t as encodeWavPcm16, u as resolveVadOptions } from "./chunks/wav-PxVZJX9V.js";
|
|
3
3
|
export { MAX_RESAMPLE_OUTPUT_SAMPLES, VAD_DEFAULTS, VAD_FRAME_MS, WavFormatError, createUtteranceCollector, createVadSegmenter, encodeWavPcm16, float32ToPcm16, frameEnergy, parseWavPcm16, pcm16ToFloat32, resampleMono, resolveVadOptions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/shared/audio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/shared/audio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AAsBxD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,MAAM,GAAG,SAAS,CAGpB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,MAAM,GAAG,SAAS,CAGpB;AAED,MAAM,WAAW,qBAAqB;IACpC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,UAAU,GAAG,WAAW,EAC/B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAuD1B"}
|
package/dist/shared/errors.d.ts
CHANGED
|
@@ -3,23 +3,70 @@ export declare class SpeechError extends Error {
|
|
|
3
3
|
adapter?: string | undefined;
|
|
4
4
|
constructor(message: string, code: string, adapter?: string | undefined);
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* A request the adapter refused before calling the provider: missing or
|
|
8
|
+
* invalid setup, and (through its subclasses) audio that is too large or
|
|
9
|
+
* cannot be decoded. Retrying the same request will not help.
|
|
10
|
+
*/
|
|
6
11
|
export declare class SpeechConfigurationError extends SpeechError {
|
|
12
|
+
constructor(message: string, adapter?: string, code?: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The audio exceeds the adapter's `maxBytes` (code `SPEECH_AUDIO_TOO_LARGE`).
|
|
16
|
+
* Extends {@link SpeechConfigurationError}, so existing `instanceof` checks
|
|
17
|
+
* still match. A provider's own HTTP 413 stays a {@link SpeechProviderError}
|
|
18
|
+
* with `status: 413`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class SpeechAudioTooLargeError extends SpeechConfigurationError {
|
|
21
|
+
/** The limit that was exceeded, in bytes. */
|
|
22
|
+
readonly maxBytes: number;
|
|
23
|
+
constructor(message: string, maxBytes: number, adapter?: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The audio is empty, malformed, or in a format this adapter cannot decode
|
|
27
|
+
* (code `SPEECH_INVALID_AUDIO`). Extends {@link SpeechConfigurationError}, so
|
|
28
|
+
* existing `instanceof` checks still match. `WavFormatError` from
|
|
29
|
+
* `@happyvertical/speech/pcm` is separate and keeps its own `reason`.
|
|
30
|
+
*/
|
|
31
|
+
export declare class SpeechInvalidAudioError extends SpeechConfigurationError {
|
|
7
32
|
constructor(message: string, adapter?: string);
|
|
8
33
|
}
|
|
9
34
|
export declare class InvalidSpeechAdapterError extends SpeechError {
|
|
10
35
|
constructor(type: string, kind: string);
|
|
11
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The provider failed the request: an HTTP error status, an unusable
|
|
39
|
+
* response, or (subclass {@link SpeechProviderUnavailableError}) no response
|
|
40
|
+
* at all.
|
|
41
|
+
*/
|
|
12
42
|
export declare class SpeechProviderError extends SpeechError {
|
|
13
43
|
readonly status?: number;
|
|
14
44
|
readonly responseBody?: string;
|
|
15
45
|
/** Parsed `Retry-After` delay in milliseconds, when the provider sent one. */
|
|
16
46
|
readonly retryAfterMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the same request may succeed if sent again later: a 429 or 5xx
|
|
49
|
+
* status, or an unreachable provider. The adapters' own automatic retry
|
|
50
|
+
* covers 429/5xx only.
|
|
51
|
+
*/
|
|
52
|
+
readonly retryable: boolean;
|
|
17
53
|
constructor(adapter: string, message: string, options?: {
|
|
18
54
|
status?: number;
|
|
19
55
|
responseBody?: string;
|
|
20
56
|
retryAfterMs?: number;
|
|
21
57
|
cause?: unknown;
|
|
22
|
-
});
|
|
58
|
+
}, code?: string);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The provider could not be reached: the request failed before any HTTP
|
|
62
|
+
* response (DNS, refused or reset connection, TLS). Code
|
|
63
|
+
* `SPEECH_PROVIDER_UNAVAILABLE`, `retryable: true`, no `status`; the
|
|
64
|
+
* underlying error is `cause`. Extends {@link SpeechProviderError}. A caller
|
|
65
|
+
* abort or the adapter's `timeoutMs` still surfaces as the abort error.
|
|
66
|
+
*/
|
|
67
|
+
export declare class SpeechProviderUnavailableError extends SpeechProviderError {
|
|
68
|
+
readonly retryable = true;
|
|
69
|
+
constructor(adapter: string, message: string, cause?: unknown);
|
|
23
70
|
}
|
|
24
71
|
/** Why a WAV buffer was rejected by `parseWavPcm16`, or an encode input refused. */
|
|
25
72
|
export type WavFormatErrorReason = 'not_riff' | 'truncated' | 'malformed_chunk' | 'missing_fmt' | 'duplicate_fmt' | 'data_before_fmt' | 'missing_data' | 'empty_data' | 'unsupported_format' | 'unsupported_bits' | 'inconsistent_header' | 'misaligned_data' | 'rate_mismatch' | 'channel_mismatch' | 'invalid_argument';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAG3B,IAAI,EAAE,MAAM;IACZ,OAAO,CAAC,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,YAAA;CAK1B;AAED,qBAAa,wBAAyB,SAAQ,WAAW;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAG3B,IAAI,EAAE,MAAM;IACZ,OAAO,CAAC,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,YAAA;CAK1B;AAED;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,WAAW;gBAErD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,IAAI,SAA+B;CAKtC;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,wBAAwB;IACpE,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAKhE;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,wBAAwB;gBACvD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAI9C;AAED,qBAAa,yBAA0B,SAAQ,WAAW;gBAC5C,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAIvC;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAG1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;KACZ,EACN,IAAI,SAA0B;CAcjC;AAED;;;;;;GAMG;AACH,qBAAa,8BAA+B,SAAQ,mBAAmB;IACrE,SAAkB,SAAS,QAAQ;gBAEvB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI9D;AAED,oFAAoF;AACpF,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,YAAY,GACZ,oBAAoB,GACpB,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAC7C,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;gBAE1B,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM;CAK1D;AAED,kEAAkE;AAClE,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,WAAW,CAAC;AAEhB,sEAAsE;AACtE,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;gBAE3B,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAQ5E"}
|
package/dist/shared/http.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HttpProbeTarget, type SpeechProbeOptions, type SpeechProbeResult } from './probe.js';
|
|
1
2
|
import { type SpeechRetryOptions } from './retry.js';
|
|
2
3
|
import type { HttpSpeechOptions, SpeechFetch, SynthesisRequest, SynthesizedSpeech, TranscriptResult, TranscriptSegment, WordTiming } from './types.js';
|
|
3
4
|
/**
|
|
@@ -28,6 +29,8 @@ export declare abstract class HttpSpeechAdapter {
|
|
|
28
29
|
protected readonly headers?: HeadersInit;
|
|
29
30
|
protected readonly timeoutMs?: number;
|
|
30
31
|
constructor(options: HttpSpeechOptions);
|
|
32
|
+
/** Runs a readiness probe with this adapter's fetch, auth and headers. */
|
|
33
|
+
protected probeEndpoint(target: Omit<HttpProbeTarget, 'fetch' | 'apiKey' | 'headers' | 'timeoutMs'>, options?: SpeechProbeOptions): Promise<SpeechProbeResult>;
|
|
31
34
|
/**
|
|
32
35
|
* POSTs to `path` (relative to `baseUrl`, or an absolute URL). The optional
|
|
33
36
|
* `retry` policy retries 429/5xx responses; `timeoutMs` applies per attempt.
|