@voiceinput/elevenlabs 0.1.0-beta.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["VoiceInputError","ELEVENLABS_SAMPLE_RATE","validateElevenLabsConfiguration","createElevenLabsRealtimeUrl","sendWithBackpressure"],"sources":["../src/index.ts"],"sourcesContent":["import { sendWithBackpressure } from \"@voiceinput/provider/transport\";\nimport {\n VoiceInputError,\n type VoiceInputProviderV1,\n type VoiceInputProviderV1CallOptions,\n type VoiceInputProviderV1Session,\n type VoiceInputProviderV1StreamPart,\n type VoiceTranscriptionOptions,\n} from \"@voiceinput/provider\";\n\nimport {\n ELEVENLABS_DEFAULT_MODEL,\n ELEVENLABS_SAMPLE_RATE,\n createElevenLabsRealtimeUrl,\n validateElevenLabsConfiguration,\n type ElevenLabsRealtimeSettings,\n type ElevenLabsSessionConfiguration,\n} from \"./session-config.js\";\n\nconst DEFAULT_REALTIME_URL =\n \"wss://api.elevenlabs.io/v1/speech-to-text/realtime\";\nconst DEFAULT_FINISH_TIMEOUT_MS = 4_000;\nconst FINISH_DRAIN_MS = 250;\n\nexport interface ElevenLabsVoiceInputProviderOptions extends ElevenLabsRealtimeSettings {\n readonly tokenEndpoint: string | URL;\n readonly model?: string;\n readonly fetch?: typeof globalThis.fetch;\n readonly webSocket?: typeof globalThis.WebSocket;\n readonly realtimeUrl?: string;\n readonly finishTimeoutMs?: number;\n}\n\nexport function elevenlabs(\n options: ElevenLabsVoiceInputProviderOptions,\n): VoiceInputProviderV1 {\n const model = factoryString(\n options.model ?? ELEVENLABS_DEFAULT_MODEL,\n \"model\",\n );\n const tokenEndpoint = factoryString(\n String(options.tokenEndpoint),\n \"tokenEndpoint\",\n );\n const realtimeUrl = factoryString(\n options.realtimeUrl ?? DEFAULT_REALTIME_URL,\n \"realtimeUrl\",\n );\n const finishTimeoutMs = positiveInteger(\n options.finishTimeoutMs ?? DEFAULT_FINISH_TIMEOUT_MS,\n \"finishTimeoutMs\",\n );\n const providerSettings: ElevenLabsRealtimeSettings = {\n ...(options.vadThreshold === undefined\n ? {}\n : { vadThreshold: options.vadThreshold }),\n ...(options.minSpeechDurationMs === undefined\n ? {}\n : { minSpeechDurationMs: options.minSpeechDurationMs }),\n ...(options.minSilenceDurationMs === undefined\n ? {}\n : { minSilenceDurationMs: options.minSilenceDurationMs }),\n ...(options.noVerbatim === undefined\n ? {}\n : { noVerbatim: options.noVerbatim }),\n ...(options.filterBackgroundAudio === undefined\n ? {}\n : { filterBackgroundAudio: options.filterBackgroundAudio }),\n };\n const validateProviderOptions = (\n transcriptionOptions: VoiceTranscriptionOptions,\n ): void => {\n try {\n validateElevenLabsConfiguration({\n model,\n ...providerSettings,\n ...transcriptionOptions,\n });\n } catch (cause) {\n if (VoiceInputError.isInstance(cause)) {\n throw cause;\n }\n throw invalidConfiguration(cause);\n }\n };\n\n validateProviderOptions({});\n\n return Object.freeze({\n specificationVersion: \"v1\" as const,\n provider: \"elevenlabs\",\n modelId: model,\n sampleRate: ELEVENLABS_SAMPLE_RATE,\n validateOptions: validateProviderOptions,\n async doOpen(callOptions: VoiceInputProviderV1CallOptions) {\n validateProviderOptions(callOptions);\n const configuration = validateElevenLabsConfiguration({\n model,\n ...providerSettings,\n ...callOptions,\n });\n return await openSession({\n abortSignal: callOptions.abortSignal,\n configuration,\n fetchImplementation: options.fetch ?? globalThis.fetch,\n finishTimeoutMs,\n realtimeUrl,\n tokenEndpoint,\n WebSocketImplementation: options.webSocket ?? globalThis.WebSocket,\n });\n },\n });\n}\n\nasync function openSession(options: {\n abortSignal: AbortSignal;\n configuration: ElevenLabsSessionConfiguration;\n fetchImplementation: typeof globalThis.fetch;\n finishTimeoutMs: number;\n realtimeUrl: string;\n tokenEndpoint: string;\n WebSocketImplementation: typeof globalThis.WebSocket;\n}): Promise<VoiceInputProviderV1Session> {\n throwIfAborted(options.abortSignal);\n requireBrowserFunction(options.fetchImplementation, \"fetch\");\n requireBrowserFunction(options.WebSocketImplementation, \"WebSocket\");\n const token = await requestToken(\n options.fetchImplementation,\n options.tokenEndpoint,\n options.configuration.model,\n options.abortSignal,\n );\n throwIfAborted(options.abortSignal);\n const socket = new options.WebSocketImplementation(\n createElevenLabsRealtimeUrl(\n options.realtimeUrl,\n token,\n options.configuration,\n ),\n );\n return await createSession(\n socket,\n options.abortSignal,\n options.finishTimeoutMs,\n );\n}\n\nasync function requestToken(\n fetchImplementation: typeof globalThis.fetch,\n tokenEndpoint: string,\n model: string,\n abortSignal: AbortSignal,\n): Promise<string> {\n let response: Response;\n try {\n response = await fetchImplementation(tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ model }),\n credentials: \"same-origin\",\n signal: abortSignal,\n });\n } catch (cause) {\n throwIfAborted(abortSignal);\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to reach the ElevenLabs token endpoint.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n });\n }\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n let value: unknown;\n try {\n value = await response.json();\n } catch (cause) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint returned invalid JSON.\",\n provider: \"elevenlabs\",\n cause,\n });\n }\n if (!isRecord(value) || !nonEmpty(value[\"token\"])) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint returned an invalid token.\",\n provider: \"elevenlabs\",\n });\n }\n return value[\"token\"];\n}\n\nasync function createSession(\n socket: WebSocket,\n abortSignal: AbortSignal,\n finishTimeoutMs: number,\n): Promise<VoiceInputProviderV1Session> {\n let controller:\n ReadableStreamDefaultController<VoiceInputProviderV1StreamPart> | undefined;\n const stream = new ReadableStream<VoiceInputProviderV1StreamPart>({\n start(value) {\n controller = value;\n },\n });\n if (controller === undefined) {\n throw new VoiceInputError({\n code: \"provider-error\",\n message: \"Unable to initialize the ElevenLabs transcript stream.\",\n provider: \"elevenlabs\",\n });\n }\n\n let hasAudio = false;\n let closed = false;\n let failed = false;\n let finishing = false;\n let finishDeferred: Deferred<void> | undefined;\n let finishSettled = false;\n let finishTimer: ReturnType<typeof setTimeout> | undefined;\n let finishDrainTimer: ReturnType<typeof setTimeout> | undefined;\n let lastInterim = \"\";\n let segmentSequence = 0;\n const segmentId = (): string => `commit:${segmentSequence}`;\n let speechActive = false;\n\n const closeSocket = (reason: \"aborted\" | \"finished\"): void => {\n if (socket.readyState === 0 || socket.readyState === 1) {\n socket.close(1000, reason);\n }\n };\n const closeStream = (): void => {\n if (closed) {\n return;\n }\n closed = true;\n abortSignal.removeEventListener(\"abort\", abort);\n controller?.close();\n };\n const settleFinish = (error?: unknown): void => {\n if (finishDeferred === undefined || finishSettled) {\n return;\n }\n finishSettled = true;\n if (finishTimer !== undefined) {\n clearTimeout(finishTimer);\n finishTimer = undefined;\n }\n if (finishDrainTimer !== undefined) {\n clearTimeout(finishDrainTimer);\n finishDrainTimer = undefined;\n }\n if (error === undefined) {\n finishDeferred.resolve(undefined);\n } else {\n finishDeferred.reject(error);\n }\n };\n const finishCleanly = (): void => {\n if (lastInterim.length > 0) {\n lastInterim = \"\";\n controller?.enqueue({\n type: \"interim\",\n text: \"\",\n segmentId: segmentId(),\n });\n }\n endSpeech();\n settleFinish();\n closeStream();\n closeSocket(\"finished\");\n };\n const scheduleFinishDrain = (): void => {\n if (!finishing || finishSettled) {\n return;\n }\n if (finishDrainTimer !== undefined) {\n clearTimeout(finishDrainTimer);\n }\n finishDrainTimer = setTimeout(finishCleanly, FINISH_DRAIN_MS);\n };\n const fail = (error: VoiceInputError): void => {\n if (closed || failed) {\n return;\n }\n failed = true;\n settleFinish(error);\n controller?.enqueue({ type: \"error\", error });\n closeStream();\n closeSocket(\"aborted\");\n };\n const startSpeech = (): void => {\n if (!speechActive) {\n speechActive = true;\n controller?.enqueue({ type: \"speech-start\" });\n }\n };\n const endSpeech = (): void => {\n if (speechActive) {\n speechActive = false;\n controller?.enqueue({ type: \"speech-end\" });\n }\n };\n const emitInterim = (text: string): void => {\n if (text.length === 0 || text === lastInterim) {\n return;\n }\n startSpeech();\n lastInterim = text;\n controller?.enqueue({ type: \"interim\", text, segmentId: segmentId() });\n };\n const commitTranscript = (text: string): void => {\n if (text.length > 0) startSpeech();\n controller?.enqueue({ type: \"final\", text, segmentId: segmentId() });\n segmentSequence += 1;\n lastInterim = \"\";\n if (!finishing) endSpeech();\n };\n const handleMessage = (event: MessageEvent): void => {\n if (closed) {\n return;\n }\n try {\n const value = JSON.parse(String(event.data)) as unknown;\n if (!isRecord(value) || !nonEmpty(value[\"message_type\"])) {\n throw new TypeError(\"ElevenLabs sent an invalid Realtime event.\");\n }\n const type = value[\"message_type\"];\n if (type === \"partial_transcript\") {\n emitInterim(readText(value));\n if (finishDrainTimer !== undefined) {\n scheduleFinishDrain();\n }\n } else if (type === \"final_transcript\") {\n // Informational only: committed_transcript closes the segment.\n if (finishDrainTimer !== undefined) {\n scheduleFinishDrain();\n }\n } else if (type === \"committed_transcript\") {\n commitTranscript(readText(value));\n if (finishing) {\n scheduleFinishDrain();\n }\n } else if (elevenLabsErrorTypes.has(type)) {\n fail(normalizeRealtimeError(value));\n }\n } catch (cause) {\n fail(\n new VoiceInputError({\n code: \"provider-error\",\n message: \"ElevenLabs sent an invalid Realtime event.\",\n provider: \"elevenlabs\",\n cause,\n }),\n );\n }\n };\n const handleClose = (event: CloseEvent): void => {\n if (closed) {\n return;\n }\n if (event.code === 1000) {\n if (finishing) {\n finishCleanly();\n return;\n }\n closeStream();\n } else {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: \"The ElevenLabs Realtime connection closed unexpectedly.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause: event,\n }),\n );\n }\n };\n function abort(): void {\n if (closed) {\n return;\n }\n settleFinish(\n abortSignal.reason ??\n new VoiceInputError({\n code: \"provider-error\",\n message: \"The ElevenLabs Realtime session was aborted.\",\n provider: \"elevenlabs\",\n }),\n );\n closeStream();\n closeSocket(\"aborted\");\n }\n\n socket.addEventListener(\"message\", handleMessage);\n socket.addEventListener(\"close\", handleClose);\n socket.addEventListener(\"error\", () => {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: \"The ElevenLabs Realtime connection failed.\",\n provider: \"elevenlabs\",\n retryable: true,\n }),\n );\n });\n abortSignal.addEventListener(\"abort\", abort, { once: true });\n await waitForOpen(socket, abortSignal);\n\n return {\n stream,\n sendAudio(chunk) {\n if (closed || finishing || chunk.length === 0) {\n return;\n }\n hasAudio = true;\n return sendWithBackpressure(\n socket,\n Math.ceil((chunk.byteLength * 4) / 3) + 160,\n abortSignal,\n \"elevenlabs\",\n () =>\n sendJson(socket, {\n message_type: \"input_audio_chunk\",\n audio_base_64: encodePcm16(chunk),\n commit: false,\n sample_rate: ELEVENLABS_SAMPLE_RATE,\n }),\n );\n },\n finish() {\n if (finishDeferred !== undefined) {\n return finishDeferred.promise;\n }\n if (closed) {\n return;\n }\n finishing = true;\n finishDeferred = createDeferred<void>();\n if (!hasAudio) {\n finishCleanly();\n return finishDeferred.promise;\n }\n try {\n sendJson(socket, {\n message_type: \"input_audio_chunk\",\n audio_base_64: \"\",\n commit: true,\n sample_rate: ELEVENLABS_SAMPLE_RATE,\n });\n } catch (cause) {\n fail(\n VoiceInputError.isInstance(cause)\n ? cause\n : new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to commit the ElevenLabs transcript.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n }),\n );\n return finishDeferred.promise;\n }\n finishTimer = setTimeout(() => {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: `ElevenLabs did not commit the final transcript within ${finishTimeoutMs}ms.`,\n provider: \"elevenlabs\",\n retryable: true,\n }),\n );\n }, finishTimeoutMs);\n return finishDeferred.promise;\n },\n abort,\n };\n}\n\nconst elevenLabsErrorTypes = new Set([\n \"auth_error\",\n \"quota_exceeded\",\n \"transcriber_error\",\n \"input_error\",\n \"invalid_request\",\n \"error\",\n \"commit_throttled\",\n \"unaccepted_terms\",\n \"rate_limited\",\n \"queue_overflow\",\n \"resource_exhausted\",\n \"session_time_limit_exceeded\",\n \"chunk_size_exceeded\",\n \"insufficient_audio_activity\",\n]);\n\nfunction normalizeRealtimeError(\n value: Record<string, unknown>,\n): VoiceInputError {\n const type = String(value[\"message_type\"]);\n const rateLimited =\n type === \"rate_limited\" ||\n type === \"quota_exceeded\" ||\n type === \"commit_throttled\";\n const retryable =\n rateLimited || type === \"queue_overflow\" || type === \"resource_exhausted\";\n const code =\n type === \"auth_error\"\n ? \"unauthorized\"\n : rateLimited\n ? \"rate-limited\"\n : type === \"input_error\" || type === \"chunk_size_exceeded\"\n ? \"audio-error\"\n : \"provider-error\";\n return new VoiceInputError({\n code,\n message:\n typeof value[\"error\"] === \"string\"\n ? value[\"error\"]\n : `ElevenLabs Realtime reported ${type}.`,\n provider: \"elevenlabs\",\n retryable,\n cause: value,\n });\n}\n\nfunction invalidConfiguration(cause: unknown): VoiceInputError {\n return new VoiceInputError({\n code: \"invalid-configuration\",\n message:\n cause instanceof Error\n ? cause.message\n : \"Invalid ElevenLabs transcription options.\",\n provider: \"elevenlabs\",\n cause,\n });\n}\n\nfunction readText(value: Record<string, unknown>): string {\n if (typeof value[\"text\"] !== \"string\") {\n throw new TypeError(\"text must be a string.\");\n }\n return value[\"text\"];\n}\n\nfunction requireBrowserFunction(value: unknown, feature: string): void {\n if (typeof value !== \"function\") {\n throw new VoiceInputError({\n code: \"unsupported-browser\",\n message: `ElevenLabs voice input requires browser ${feature} support.`,\n provider: \"elevenlabs\",\n });\n }\n}\n\nfunction factoryString(value: string, name: string): string {\n if (value.trim().length === 0) {\n throw invalidConfiguration(new TypeError(`${name} must be non-empty.`));\n }\n return value;\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw invalidConfiguration(\n new TypeError(`${name} must be a positive safe integer.`),\n );\n }\n return value;\n}\n\ninterface Deferred<T> {\n readonly promise: Promise<T>;\n resolve(value: T | PromiseLike<T>): void;\n reject(reason?: unknown): void;\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve: (value: T | PromiseLike<T>) => void = () => {};\n let reject: (reason?: unknown) => void = () => {};\n const promise = new Promise<T>((resolve_, reject_) => {\n resolve = resolve_;\n reject = reject_;\n });\n return { promise, resolve, reject };\n}\n\nasync function tokenResponseError(\n response: Response,\n): Promise<VoiceInputError> {\n const retryAfterMs = parseRetryAfter(response.headers.get(\"Retry-After\"));\n if (response.status === 401 || response.status === 403) {\n return new VoiceInputError({\n code: \"unauthorized\",\n message: \"The ElevenLabs token endpoint rejected this request.\",\n provider: \"elevenlabs\",\n });\n }\n if (response.status === 429) {\n return new VoiceInputError({\n code: \"rate-limited\",\n message: \"The ElevenLabs token endpoint rate limit was exceeded.\",\n provider: \"elevenlabs\",\n retryable: true,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n });\n }\n const safeError = await readSafeTokenError(response);\n if (safeError !== undefined) {\n return new VoiceInputError({ ...safeError, provider: \"elevenlabs\" });\n }\n return new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint did not issue a token.\",\n provider: \"elevenlabs\",\n retryable: response.status >= 500,\n });\n}\n\nasync function readSafeTokenError(response: Response): Promise<\n | {\n code: \"invalid-configuration\" | \"unsupported-feature\";\n message: string;\n }\n | undefined\n> {\n if (\n response.status !== 400 ||\n response.headers.get(\"X-VoiceInput-Error\") !== \"1\" ||\n response.headers.get(\"Content-Type\")?.split(\";\", 1)[0]?.trim() !==\n \"application/json\"\n ) {\n return undefined;\n }\n const text = await readBoundedErrorText(response);\n if (text === undefined) return undefined;\n try {\n const value = JSON.parse(text) as unknown;\n const error = isRecord(value) ? value[\"error\"] : undefined;\n if (\n !isRecord(error) ||\n (error[\"code\"] !== \"invalid-configuration\" &&\n error[\"code\"] !== \"unsupported-feature\") ||\n typeof error[\"message\"] !== \"string\" ||\n error[\"message\"].length === 0 ||\n error[\"message\"].length > 1_000\n ) {\n return undefined;\n }\n return { code: error[\"code\"], message: error[\"message\"] };\n } catch {\n return undefined;\n }\n}\n\nasync function readBoundedErrorText(\n response: Response,\n): Promise<string | undefined> {\n const reader = response.body?.getReader();\n if (reader === undefined) return undefined;\n const decoder = new TextDecoder();\n let bytesRead = 0;\n let text = \"\";\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) return text + decoder.decode();\n bytesRead += value.byteLength;\n if (bytesRead > 4_096) {\n await reader.cancel().catch(() => {});\n return undefined;\n }\n text += decoder.decode(value, { stream: true });\n }\n } catch {\n return undefined;\n }\n}\n\nfunction waitForOpen(\n socket: WebSocket,\n abortSignal: AbortSignal,\n): Promise<void> {\n if (socket.readyState === 1) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n const cleanup = (): void => {\n socket.removeEventListener(\"open\", handleOpen);\n socket.removeEventListener(\"error\", handleError);\n abortSignal.removeEventListener(\"abort\", handleAbort);\n };\n const handleOpen = (): void => {\n cleanup();\n resolve();\n };\n const handleError = (event: Event): void => {\n cleanup();\n reject(\n new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to open the ElevenLabs Realtime connection.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause: event,\n }),\n );\n };\n const handleAbort = (): void => {\n cleanup();\n reject(abortSignal.reason);\n };\n socket.addEventListener(\"open\", handleOpen, { once: true });\n socket.addEventListener(\"error\", handleError, { once: true });\n abortSignal.addEventListener(\"abort\", handleAbort, { once: true });\n });\n}\n\nfunction sendJson(socket: WebSocket, value: Record<string, unknown>): void {\n try {\n socket.send(JSON.stringify(value));\n } catch (cause) {\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to send data to ElevenLabs Realtime.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n });\n }\n}\n\nfunction encodePcm16(chunk: Int16Array): string {\n const bytes = new Uint8Array(\n chunk.buffer,\n chunk.byteOffset,\n chunk.byteLength,\n );\n let binary = \"\";\n for (let offset = 0; offset < bytes.length; offset += 8_192) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + 8_192));\n }\n return btoa(binary);\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (value === null) {\n return undefined;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.ceil(seconds * 1_000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (signal.aborted) {\n throw signal.reason;\n }\n}\n\nfunction nonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport { ELEVENLABS_DEFAULT_MODEL } from \"./session-config.js\";\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { VoiceInputProviderV1 } from "@voiceinput/provider";
2
+ //#region src/session-config.d.ts
3
+ declare const ELEVENLABS_DEFAULT_MODEL = "scribe_v2_realtime";
4
+ interface ElevenLabsRealtimeSettings {
5
+ readonly vadThreshold?: number;
6
+ readonly minSpeechDurationMs?: number;
7
+ readonly minSilenceDurationMs?: number;
8
+ readonly noVerbatim?: boolean;
9
+ readonly filterBackgroundAudio?: boolean;
10
+ }
11
+ //#endregion
12
+ //#region src/index.d.ts
13
+ interface ElevenLabsVoiceInputProviderOptions extends ElevenLabsRealtimeSettings {
14
+ readonly tokenEndpoint: string | URL;
15
+ readonly model?: string;
16
+ readonly fetch?: typeof globalThis.fetch;
17
+ readonly webSocket?: typeof globalThis.WebSocket;
18
+ readonly realtimeUrl?: string;
19
+ readonly finishTimeoutMs?: number;
20
+ }
21
+ declare function elevenlabs(options: ElevenLabsVoiceInputProviderOptions): VoiceInputProviderV1;
22
+ //#endregion
23
+ export { ELEVENLABS_DEFAULT_MODEL, ElevenLabsVoiceInputProviderOptions, elevenlabs };
24
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/session-config.ts","../src/index.ts"],"mappings":";;cAKa;UAII;WACN;WACA;WACA;WACA;WACA;;;;UCUM,4CAA4C;WAClD,wBAAwB;WACxB;WACA,eAAe,WAAW;WAC1B,mBAAmB,WAAW;WAC9B;WACA;;iBAGK,WACd,SAAS,sCACR"}
@@ -0,0 +1,24 @@
1
+ import { VoiceInputProviderV1 } from "@voiceinput/provider";
2
+ //#region src/session-config.d.ts
3
+ declare const ELEVENLABS_DEFAULT_MODEL = "scribe_v2_realtime";
4
+ interface ElevenLabsRealtimeSettings {
5
+ readonly vadThreshold?: number;
6
+ readonly minSpeechDurationMs?: number;
7
+ readonly minSilenceDurationMs?: number;
8
+ readonly noVerbatim?: boolean;
9
+ readonly filterBackgroundAudio?: boolean;
10
+ }
11
+ //#endregion
12
+ //#region src/index.d.ts
13
+ interface ElevenLabsVoiceInputProviderOptions extends ElevenLabsRealtimeSettings {
14
+ readonly tokenEndpoint: string | URL;
15
+ readonly model?: string;
16
+ readonly fetch?: typeof globalThis.fetch;
17
+ readonly webSocket?: typeof globalThis.WebSocket;
18
+ readonly realtimeUrl?: string;
19
+ readonly finishTimeoutMs?: number;
20
+ }
21
+ declare function elevenlabs(options: ElevenLabsVoiceInputProviderOptions): VoiceInputProviderV1;
22
+ //#endregion
23
+ export { ELEVENLABS_DEFAULT_MODEL, ElevenLabsVoiceInputProviderOptions, elevenlabs };
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/session-config.ts","../src/index.ts"],"mappings":";;cAKa;UAII;WACN;WACA;WACA;WACA;WACA;;;;UCUM,4CAA4C;WAClD,wBAAwB;WACxB;WACA,eAAe,WAAW;WAC1B,mBAAmB,WAAW;WAC9B;WACA;;iBAGK,WACd,SAAS,sCACR"}
package/dist/index.js ADDED
@@ -0,0 +1,525 @@
1
+ import { i as validateElevenLabsConfiguration, n as ELEVENLABS_SAMPLE_RATE, r as createElevenLabsRealtimeUrl, t as ELEVENLABS_DEFAULT_MODEL } from "./session-config-DEaQLtZY.js";
2
+ import { sendWithBackpressure } from "@voiceinput/provider/transport";
3
+ import { VoiceInputError } from "@voiceinput/provider";
4
+ //#region src/index.ts
5
+ const DEFAULT_REALTIME_URL = "wss://api.elevenlabs.io/v1/speech-to-text/realtime";
6
+ const DEFAULT_FINISH_TIMEOUT_MS = 4e3;
7
+ const FINISH_DRAIN_MS = 250;
8
+ function elevenlabs(options) {
9
+ const model = factoryString(options.model ?? "scribe_v2_realtime", "model");
10
+ const tokenEndpoint = factoryString(String(options.tokenEndpoint), "tokenEndpoint");
11
+ const realtimeUrl = factoryString(options.realtimeUrl ?? DEFAULT_REALTIME_URL, "realtimeUrl");
12
+ const finishTimeoutMs = positiveInteger(options.finishTimeoutMs ?? DEFAULT_FINISH_TIMEOUT_MS, "finishTimeoutMs");
13
+ const providerSettings = {
14
+ ...options.vadThreshold === void 0 ? {} : { vadThreshold: options.vadThreshold },
15
+ ...options.minSpeechDurationMs === void 0 ? {} : { minSpeechDurationMs: options.minSpeechDurationMs },
16
+ ...options.minSilenceDurationMs === void 0 ? {} : { minSilenceDurationMs: options.minSilenceDurationMs },
17
+ ...options.noVerbatim === void 0 ? {} : { noVerbatim: options.noVerbatim },
18
+ ...options.filterBackgroundAudio === void 0 ? {} : { filterBackgroundAudio: options.filterBackgroundAudio }
19
+ };
20
+ const validateProviderOptions = (transcriptionOptions) => {
21
+ try {
22
+ validateElevenLabsConfiguration({
23
+ model,
24
+ ...providerSettings,
25
+ ...transcriptionOptions
26
+ });
27
+ } catch (cause) {
28
+ if (VoiceInputError.isInstance(cause)) throw cause;
29
+ throw invalidConfiguration(cause);
30
+ }
31
+ };
32
+ validateProviderOptions({});
33
+ return Object.freeze({
34
+ specificationVersion: "v1",
35
+ provider: "elevenlabs",
36
+ modelId: model,
37
+ sampleRate: ELEVENLABS_SAMPLE_RATE,
38
+ validateOptions: validateProviderOptions,
39
+ async doOpen(callOptions) {
40
+ validateProviderOptions(callOptions);
41
+ const configuration = validateElevenLabsConfiguration({
42
+ model,
43
+ ...providerSettings,
44
+ ...callOptions
45
+ });
46
+ return await openSession({
47
+ abortSignal: callOptions.abortSignal,
48
+ configuration,
49
+ fetchImplementation: options.fetch ?? globalThis.fetch,
50
+ finishTimeoutMs,
51
+ realtimeUrl,
52
+ tokenEndpoint,
53
+ WebSocketImplementation: options.webSocket ?? globalThis.WebSocket
54
+ });
55
+ }
56
+ });
57
+ }
58
+ async function openSession(options) {
59
+ throwIfAborted(options.abortSignal);
60
+ requireBrowserFunction(options.fetchImplementation, "fetch");
61
+ requireBrowserFunction(options.WebSocketImplementation, "WebSocket");
62
+ const token = await requestToken(options.fetchImplementation, options.tokenEndpoint, options.configuration.model, options.abortSignal);
63
+ throwIfAborted(options.abortSignal);
64
+ return await createSession(new options.WebSocketImplementation(createElevenLabsRealtimeUrl(options.realtimeUrl, token, options.configuration)), options.abortSignal, options.finishTimeoutMs);
65
+ }
66
+ async function requestToken(fetchImplementation, tokenEndpoint, model, abortSignal) {
67
+ let response;
68
+ try {
69
+ response = await fetchImplementation(tokenEndpoint, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({ model }),
73
+ credentials: "same-origin",
74
+ signal: abortSignal
75
+ });
76
+ } catch (cause) {
77
+ throwIfAborted(abortSignal);
78
+ throw new VoiceInputError({
79
+ code: "network-error",
80
+ message: "Unable to reach the ElevenLabs token endpoint.",
81
+ provider: "elevenlabs",
82
+ retryable: true,
83
+ cause
84
+ });
85
+ }
86
+ if (!response.ok) throw await tokenResponseError(response);
87
+ let value;
88
+ try {
89
+ value = await response.json();
90
+ } catch (cause) {
91
+ throw new VoiceInputError({
92
+ code: "token-error",
93
+ message: "The ElevenLabs token endpoint returned invalid JSON.",
94
+ provider: "elevenlabs",
95
+ cause
96
+ });
97
+ }
98
+ if (!isRecord(value) || !nonEmpty(value["token"])) throw new VoiceInputError({
99
+ code: "token-error",
100
+ message: "The ElevenLabs token endpoint returned an invalid token.",
101
+ provider: "elevenlabs"
102
+ });
103
+ return value["token"];
104
+ }
105
+ async function createSession(socket, abortSignal, finishTimeoutMs) {
106
+ let controller;
107
+ const stream = new ReadableStream({ start(value) {
108
+ controller = value;
109
+ } });
110
+ if (controller === void 0) throw new VoiceInputError({
111
+ code: "provider-error",
112
+ message: "Unable to initialize the ElevenLabs transcript stream.",
113
+ provider: "elevenlabs"
114
+ });
115
+ let hasAudio = false;
116
+ let closed = false;
117
+ let failed = false;
118
+ let finishing = false;
119
+ let finishDeferred;
120
+ let finishSettled = false;
121
+ let finishTimer;
122
+ let finishDrainTimer;
123
+ let lastInterim = "";
124
+ let segmentSequence = 0;
125
+ const segmentId = () => `commit:${segmentSequence}`;
126
+ let speechActive = false;
127
+ const closeSocket = (reason) => {
128
+ if (socket.readyState === 0 || socket.readyState === 1) socket.close(1e3, reason);
129
+ };
130
+ const closeStream = () => {
131
+ if (closed) return;
132
+ closed = true;
133
+ abortSignal.removeEventListener("abort", abort);
134
+ controller?.close();
135
+ };
136
+ const settleFinish = (error) => {
137
+ if (finishDeferred === void 0 || finishSettled) return;
138
+ finishSettled = true;
139
+ if (finishTimer !== void 0) {
140
+ clearTimeout(finishTimer);
141
+ finishTimer = void 0;
142
+ }
143
+ if (finishDrainTimer !== void 0) {
144
+ clearTimeout(finishDrainTimer);
145
+ finishDrainTimer = void 0;
146
+ }
147
+ if (error === void 0) finishDeferred.resolve(void 0);
148
+ else finishDeferred.reject(error);
149
+ };
150
+ const finishCleanly = () => {
151
+ if (lastInterim.length > 0) {
152
+ lastInterim = "";
153
+ controller?.enqueue({
154
+ type: "interim",
155
+ text: "",
156
+ segmentId: segmentId()
157
+ });
158
+ }
159
+ endSpeech();
160
+ settleFinish();
161
+ closeStream();
162
+ closeSocket("finished");
163
+ };
164
+ const scheduleFinishDrain = () => {
165
+ if (!finishing || finishSettled) return;
166
+ if (finishDrainTimer !== void 0) clearTimeout(finishDrainTimer);
167
+ finishDrainTimer = setTimeout(finishCleanly, FINISH_DRAIN_MS);
168
+ };
169
+ const fail = (error) => {
170
+ if (closed || failed) return;
171
+ failed = true;
172
+ settleFinish(error);
173
+ controller?.enqueue({
174
+ type: "error",
175
+ error
176
+ });
177
+ closeStream();
178
+ closeSocket("aborted");
179
+ };
180
+ const startSpeech = () => {
181
+ if (!speechActive) {
182
+ speechActive = true;
183
+ controller?.enqueue({ type: "speech-start" });
184
+ }
185
+ };
186
+ const endSpeech = () => {
187
+ if (speechActive) {
188
+ speechActive = false;
189
+ controller?.enqueue({ type: "speech-end" });
190
+ }
191
+ };
192
+ const emitInterim = (text) => {
193
+ if (text.length === 0 || text === lastInterim) return;
194
+ startSpeech();
195
+ lastInterim = text;
196
+ controller?.enqueue({
197
+ type: "interim",
198
+ text,
199
+ segmentId: segmentId()
200
+ });
201
+ };
202
+ const commitTranscript = (text) => {
203
+ if (text.length > 0) startSpeech();
204
+ controller?.enqueue({
205
+ type: "final",
206
+ text,
207
+ segmentId: segmentId()
208
+ });
209
+ segmentSequence += 1;
210
+ lastInterim = "";
211
+ if (!finishing) endSpeech();
212
+ };
213
+ const handleMessage = (event) => {
214
+ if (closed) return;
215
+ try {
216
+ const value = JSON.parse(String(event.data));
217
+ if (!isRecord(value) || !nonEmpty(value["message_type"])) throw new TypeError("ElevenLabs sent an invalid Realtime event.");
218
+ const type = value["message_type"];
219
+ if (type === "partial_transcript") {
220
+ emitInterim(readText(value));
221
+ if (finishDrainTimer !== void 0) scheduleFinishDrain();
222
+ } else if (type === "final_transcript") {
223
+ if (finishDrainTimer !== void 0) scheduleFinishDrain();
224
+ } else if (type === "committed_transcript") {
225
+ commitTranscript(readText(value));
226
+ if (finishing) scheduleFinishDrain();
227
+ } else if (elevenLabsErrorTypes.has(type)) fail(normalizeRealtimeError(value));
228
+ } catch (cause) {
229
+ fail(new VoiceInputError({
230
+ code: "provider-error",
231
+ message: "ElevenLabs sent an invalid Realtime event.",
232
+ provider: "elevenlabs",
233
+ cause
234
+ }));
235
+ }
236
+ };
237
+ const handleClose = (event) => {
238
+ if (closed) return;
239
+ if (event.code === 1e3) {
240
+ if (finishing) {
241
+ finishCleanly();
242
+ return;
243
+ }
244
+ closeStream();
245
+ } else fail(new VoiceInputError({
246
+ code: "network-error",
247
+ message: "The ElevenLabs Realtime connection closed unexpectedly.",
248
+ provider: "elevenlabs",
249
+ retryable: true,
250
+ cause: event
251
+ }));
252
+ };
253
+ function abort() {
254
+ if (closed) return;
255
+ settleFinish(abortSignal.reason ?? new VoiceInputError({
256
+ code: "provider-error",
257
+ message: "The ElevenLabs Realtime session was aborted.",
258
+ provider: "elevenlabs"
259
+ }));
260
+ closeStream();
261
+ closeSocket("aborted");
262
+ }
263
+ socket.addEventListener("message", handleMessage);
264
+ socket.addEventListener("close", handleClose);
265
+ socket.addEventListener("error", () => {
266
+ fail(new VoiceInputError({
267
+ code: "network-error",
268
+ message: "The ElevenLabs Realtime connection failed.",
269
+ provider: "elevenlabs",
270
+ retryable: true
271
+ }));
272
+ });
273
+ abortSignal.addEventListener("abort", abort, { once: true });
274
+ await waitForOpen(socket, abortSignal);
275
+ return {
276
+ stream,
277
+ sendAudio(chunk) {
278
+ if (closed || finishing || chunk.length === 0) return;
279
+ hasAudio = true;
280
+ return sendWithBackpressure(socket, Math.ceil(chunk.byteLength * 4 / 3) + 160, abortSignal, "elevenlabs", () => sendJson(socket, {
281
+ message_type: "input_audio_chunk",
282
+ audio_base_64: encodePcm16(chunk),
283
+ commit: false,
284
+ sample_rate: ELEVENLABS_SAMPLE_RATE
285
+ }));
286
+ },
287
+ finish() {
288
+ if (finishDeferred !== void 0) return finishDeferred.promise;
289
+ if (closed) return;
290
+ finishing = true;
291
+ finishDeferred = createDeferred();
292
+ if (!hasAudio) {
293
+ finishCleanly();
294
+ return finishDeferred.promise;
295
+ }
296
+ try {
297
+ sendJson(socket, {
298
+ message_type: "input_audio_chunk",
299
+ audio_base_64: "",
300
+ commit: true,
301
+ sample_rate: ELEVENLABS_SAMPLE_RATE
302
+ });
303
+ } catch (cause) {
304
+ fail(VoiceInputError.isInstance(cause) ? cause : new VoiceInputError({
305
+ code: "network-error",
306
+ message: "Unable to commit the ElevenLabs transcript.",
307
+ provider: "elevenlabs",
308
+ retryable: true,
309
+ cause
310
+ }));
311
+ return finishDeferred.promise;
312
+ }
313
+ finishTimer = setTimeout(() => {
314
+ fail(new VoiceInputError({
315
+ code: "network-error",
316
+ message: `ElevenLabs did not commit the final transcript within ${finishTimeoutMs}ms.`,
317
+ provider: "elevenlabs",
318
+ retryable: true
319
+ }));
320
+ }, finishTimeoutMs);
321
+ return finishDeferred.promise;
322
+ },
323
+ abort
324
+ };
325
+ }
326
+ const elevenLabsErrorTypes = /* @__PURE__ */ new Set([
327
+ "auth_error",
328
+ "quota_exceeded",
329
+ "transcriber_error",
330
+ "input_error",
331
+ "invalid_request",
332
+ "error",
333
+ "commit_throttled",
334
+ "unaccepted_terms",
335
+ "rate_limited",
336
+ "queue_overflow",
337
+ "resource_exhausted",
338
+ "session_time_limit_exceeded",
339
+ "chunk_size_exceeded",
340
+ "insufficient_audio_activity"
341
+ ]);
342
+ function normalizeRealtimeError(value) {
343
+ const type = String(value["message_type"]);
344
+ const rateLimited = type === "rate_limited" || type === "quota_exceeded" || type === "commit_throttled";
345
+ const retryable = rateLimited || type === "queue_overflow" || type === "resource_exhausted";
346
+ return new VoiceInputError({
347
+ code: type === "auth_error" ? "unauthorized" : rateLimited ? "rate-limited" : type === "input_error" || type === "chunk_size_exceeded" ? "audio-error" : "provider-error",
348
+ message: typeof value["error"] === "string" ? value["error"] : `ElevenLabs Realtime reported ${type}.`,
349
+ provider: "elevenlabs",
350
+ retryable,
351
+ cause: value
352
+ });
353
+ }
354
+ function invalidConfiguration(cause) {
355
+ return new VoiceInputError({
356
+ code: "invalid-configuration",
357
+ message: cause instanceof Error ? cause.message : "Invalid ElevenLabs transcription options.",
358
+ provider: "elevenlabs",
359
+ cause
360
+ });
361
+ }
362
+ function readText(value) {
363
+ if (typeof value["text"] !== "string") throw new TypeError("text must be a string.");
364
+ return value["text"];
365
+ }
366
+ function requireBrowserFunction(value, feature) {
367
+ if (typeof value !== "function") throw new VoiceInputError({
368
+ code: "unsupported-browser",
369
+ message: `ElevenLabs voice input requires browser ${feature} support.`,
370
+ provider: "elevenlabs"
371
+ });
372
+ }
373
+ function factoryString(value, name) {
374
+ if (value.trim().length === 0) throw invalidConfiguration(/* @__PURE__ */ new TypeError(`${name} must be non-empty.`));
375
+ return value;
376
+ }
377
+ function positiveInteger(value, name) {
378
+ if (!Number.isSafeInteger(value) || value <= 0) throw invalidConfiguration(/* @__PURE__ */ new TypeError(`${name} must be a positive safe integer.`));
379
+ return value;
380
+ }
381
+ function createDeferred() {
382
+ let resolve = () => {};
383
+ let reject = () => {};
384
+ return {
385
+ promise: new Promise((resolve_, reject_) => {
386
+ resolve = resolve_;
387
+ reject = reject_;
388
+ }),
389
+ resolve,
390
+ reject
391
+ };
392
+ }
393
+ async function tokenResponseError(response) {
394
+ const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
395
+ if (response.status === 401 || response.status === 403) return new VoiceInputError({
396
+ code: "unauthorized",
397
+ message: "The ElevenLabs token endpoint rejected this request.",
398
+ provider: "elevenlabs"
399
+ });
400
+ if (response.status === 429) return new VoiceInputError({
401
+ code: "rate-limited",
402
+ message: "The ElevenLabs token endpoint rate limit was exceeded.",
403
+ provider: "elevenlabs",
404
+ retryable: true,
405
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs }
406
+ });
407
+ const safeError = await readSafeTokenError(response);
408
+ if (safeError !== void 0) return new VoiceInputError({
409
+ ...safeError,
410
+ provider: "elevenlabs"
411
+ });
412
+ return new VoiceInputError({
413
+ code: "token-error",
414
+ message: "The ElevenLabs token endpoint did not issue a token.",
415
+ provider: "elevenlabs",
416
+ retryable: response.status >= 500
417
+ });
418
+ }
419
+ async function readSafeTokenError(response) {
420
+ if (response.status !== 400 || response.headers.get("X-VoiceInput-Error") !== "1" || response.headers.get("Content-Type")?.split(";", 1)[0]?.trim() !== "application/json") return;
421
+ const text = await readBoundedErrorText(response);
422
+ if (text === void 0) return void 0;
423
+ try {
424
+ const value = JSON.parse(text);
425
+ const error = isRecord(value) ? value["error"] : void 0;
426
+ if (!isRecord(error) || error["code"] !== "invalid-configuration" && error["code"] !== "unsupported-feature" || typeof error["message"] !== "string" || error["message"].length === 0 || error["message"].length > 1e3) return;
427
+ return {
428
+ code: error["code"],
429
+ message: error["message"]
430
+ };
431
+ } catch {
432
+ return;
433
+ }
434
+ }
435
+ async function readBoundedErrorText(response) {
436
+ const reader = response.body?.getReader();
437
+ if (reader === void 0) return void 0;
438
+ const decoder = new TextDecoder();
439
+ let bytesRead = 0;
440
+ let text = "";
441
+ try {
442
+ while (true) {
443
+ const { done, value } = await reader.read();
444
+ if (done) return text + decoder.decode();
445
+ bytesRead += value.byteLength;
446
+ if (bytesRead > 4096) {
447
+ await reader.cancel().catch(() => {});
448
+ return;
449
+ }
450
+ text += decoder.decode(value, { stream: true });
451
+ }
452
+ } catch {
453
+ return;
454
+ }
455
+ }
456
+ function waitForOpen(socket, abortSignal) {
457
+ if (socket.readyState === 1) return Promise.resolve();
458
+ return new Promise((resolve, reject) => {
459
+ const cleanup = () => {
460
+ socket.removeEventListener("open", handleOpen);
461
+ socket.removeEventListener("error", handleError);
462
+ abortSignal.removeEventListener("abort", handleAbort);
463
+ };
464
+ const handleOpen = () => {
465
+ cleanup();
466
+ resolve();
467
+ };
468
+ const handleError = (event) => {
469
+ cleanup();
470
+ reject(new VoiceInputError({
471
+ code: "network-error",
472
+ message: "Unable to open the ElevenLabs Realtime connection.",
473
+ provider: "elevenlabs",
474
+ retryable: true,
475
+ cause: event
476
+ }));
477
+ };
478
+ const handleAbort = () => {
479
+ cleanup();
480
+ reject(abortSignal.reason);
481
+ };
482
+ socket.addEventListener("open", handleOpen, { once: true });
483
+ socket.addEventListener("error", handleError, { once: true });
484
+ abortSignal.addEventListener("abort", handleAbort, { once: true });
485
+ });
486
+ }
487
+ function sendJson(socket, value) {
488
+ try {
489
+ socket.send(JSON.stringify(value));
490
+ } catch (cause) {
491
+ throw new VoiceInputError({
492
+ code: "network-error",
493
+ message: "Unable to send data to ElevenLabs Realtime.",
494
+ provider: "elevenlabs",
495
+ retryable: true,
496
+ cause
497
+ });
498
+ }
499
+ }
500
+ function encodePcm16(chunk) {
501
+ const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
502
+ let binary = "";
503
+ for (let offset = 0; offset < bytes.length; offset += 8192) binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
504
+ return btoa(binary);
505
+ }
506
+ function parseRetryAfter(value) {
507
+ if (value === null) return;
508
+ const seconds = Number(value);
509
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.ceil(seconds * 1e3);
510
+ const date = Date.parse(value);
511
+ return Number.isNaN(date) ? void 0 : Math.max(0, date - Date.now());
512
+ }
513
+ function throwIfAborted(signal) {
514
+ if (signal.aborted) throw signal.reason;
515
+ }
516
+ function nonEmpty(value) {
517
+ return typeof value === "string" && value.length > 0;
518
+ }
519
+ function isRecord(value) {
520
+ return typeof value === "object" && value !== null && !Array.isArray(value);
521
+ }
522
+ //#endregion
523
+ export { ELEVENLABS_DEFAULT_MODEL, elevenlabs };
524
+
525
+ //# sourceMappingURL=index.js.map