@ai-sdk/elevenlabs 3.0.13 → 3.0.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @ai-sdk/elevenlabs
2
2
 
3
+ ## 3.0.15
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [0c464d9]
8
+ - Updated dependencies [c49380c]
9
+ - @ai-sdk/provider-utils@5.0.14
10
+
11
+ ## 3.0.14
12
+
13
+ ### Patch Changes
14
+
15
+ - 49fd7cd: Add streaming transcription support for ElevenLabs Scribe v2 Realtime through `experimental_streamTranscribe`.
16
+ - Updated dependencies [1e2f324]
17
+ - @ai-sdk/provider@4.0.4
18
+ - @ai-sdk/provider-utils@5.0.13
19
+
3
20
  ## 3.0.13
4
21
 
5
22
  ### Patch Changes
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # AI SDK - ElevenLabs Provider
2
2
 
3
3
  The **[ElevenLabs provider](https://ai-sdk.dev/providers/ai-sdk-providers/elevenlabs)** for the [AI SDK](https://ai-sdk.dev/docs)
4
- contains language model support for the ElevenLabs chat and completion APIs and embedding model support for the ElevenLabs embeddings API.
4
+ contains speech generation plus batch and realtime transcription support for the ElevenLabs APIs.
5
5
 
6
6
  > **Deploying to Vercel?** With Vercel's AI Gateway you can access ElevenLabs (and hundreds of models from other providers) — no additional packages, API keys, or extra cost. [Get started with AI Gateway](https://vercel.com/ai-gateway).
7
7
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _ai_sdk_provider from '@ai-sdk/provider';
2
- import { TranscriptionModelV4, ProviderV4, SpeechModelV4 } from '@ai-sdk/provider';
3
- import { FetchFunction, WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@ai-sdk/provider-utils';
2
+ import { TranscriptionModelV4, Experimental_TranscriptionModelV4StreamOptions, ProviderV4, SpeechModelV4 } from '@ai-sdk/provider';
3
+ import { FetchFunction, WebSocketConstructor, WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@ai-sdk/provider-utils';
4
4
  import { z } from 'zod/v4';
5
5
 
6
6
  type ElevenLabsConfig = {
@@ -12,9 +12,10 @@ type ElevenLabsConfig = {
12
12
  headers?: () => Record<string, string | undefined>;
13
13
  fetch?: FetchFunction;
14
14
  generateId?: () => string;
15
+ webSocket?: WebSocketConstructor;
15
16
  };
16
17
 
17
- type ElevenLabsTranscriptionModelId = 'scribe_v1' | 'scribe_v1_experimental' | 'scribe_v2' | (string & {});
18
+ type ElevenLabsTranscriptionModelId = 'scribe_v1' | 'scribe_v1_experimental' | 'scribe_v2' | 'scribe_v2_realtime' | (string & {});
18
19
 
19
20
  interface ElevenLabsTranscriptionModelConfig extends ElevenLabsConfig {
20
21
  _internal?: {
@@ -37,6 +38,7 @@ declare class ElevenLabsTranscriptionModel implements TranscriptionModelV4 {
37
38
  constructor(modelId: ElevenLabsTranscriptionModelId, config: ElevenLabsTranscriptionModelConfig);
38
39
  private getArgs;
39
40
  doGenerate(options: Parameters<TranscriptionModelV4['doGenerate']>[0]): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>>;
41
+ doStream(options: Experimental_TranscriptionModelV4StreamOptions): Promise<Awaited<ReturnType<NonNullable<TranscriptionModelV4['doStream']>>>>;
40
42
  }
41
43
 
42
44
  type ElevenLabsSpeechModelId = 'eleven_v3' | 'eleven_multilingual_v2' | 'eleven_flash_v2_5' | 'eleven_flash_v2' | 'eleven_turbo_v2_5' | 'eleven_turbo_v2' | 'eleven_monolingual_v1' | 'eleven_multilingual_v1' | (string & {});
@@ -73,6 +75,11 @@ interface ElevenLabsProviderSettings {
73
75
  * or to provide a custom fetch implementation for e.g. testing.
74
76
  */
75
77
  fetch?: FetchFunction;
78
+ /**
79
+ * Custom WebSocket implementation. Required in runtimes whose native
80
+ * WebSocket constructor does not support headers for realtime transcription.
81
+ */
82
+ webSocket?: WebSocketConstructor;
76
83
  }
77
84
  /**
78
85
  * Create an ElevenLabs provider instance.
@@ -124,6 +131,24 @@ declare const elevenLabsTranscriptionModelOptionsSchema: z.ZodObject<{
124
131
  pcm_s16le_16: "pcm_s16le_16";
125
132
  other: "other";
126
133
  }>>>>;
134
+ streaming: z.ZodOptional<z.ZodNullable<z.ZodObject<{
135
+ commitStrategy: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
136
+ manual: "manual";
137
+ vad: "vad";
138
+ }>>>;
139
+ enableLogging: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
140
+ filterBackgroundAudio: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
141
+ includeLanguageDetection: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
142
+ includeTimestamps: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
143
+ keyterms: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
144
+ minSilenceDurationMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
145
+ minSpeechDurationMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
146
+ noVerbatim: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
147
+ previousText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
148
+ secondaryLanguages: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
149
+ vadSilenceThresholdSecs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
150
+ vadThreshold: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
151
+ }, z.core.$strip>>>;
127
152
  }, z.core.$strip>;
128
153
  type ElevenLabsTranscriptionModelOptions = z.infer<typeof elevenLabsTranscriptionModelOptionsSchema>;
129
154
 
package/dist/index.js CHANGED
@@ -8,14 +8,23 @@ import {
8
8
  } from "@ai-sdk/provider-utils";
9
9
 
10
10
  // src/elevenlabs-transcription-model.ts
11
+ import {
12
+ InvalidArgumentError,
13
+ UnsupportedFunctionalityError
14
+ } from "@ai-sdk/provider";
11
15
  import {
12
16
  combineHeaders,
13
17
  convertBase64ToUint8Array,
18
+ convertToBase64,
19
+ connectToWebSocket,
14
20
  createJsonResponseHandler,
15
21
  mediaTypeToExtension,
16
22
  parseProviderOptions,
17
23
  postFormDataToApi,
24
+ safeParseJSON,
18
25
  serializeModelOptions,
26
+ toWebSocketUrl,
27
+ waitForWebSocketBufferDrain,
19
28
  WORKFLOW_SERIALIZE,
20
29
  WORKFLOW_DESERIALIZE
21
30
  } from "@ai-sdk/provider-utils";
@@ -43,10 +52,49 @@ var elevenLabsTranscriptionModelOptionsSchema = z2.object({
43
52
  numSpeakers: z2.number().int().min(1).max(32).nullish(),
44
53
  timestampsGranularity: z2.enum(["none", "word", "character"]).nullish().default("word"),
45
54
  diarize: z2.boolean().nullish().default(false),
46
- fileFormat: z2.enum(["pcm_s16le_16", "other"]).nullish().default("other")
55
+ fileFormat: z2.enum(["pcm_s16le_16", "other"]).nullish().default("other"),
56
+ streaming: z2.object({
57
+ commitStrategy: z2.enum(["manual", "vad"]).nullish(),
58
+ enableLogging: z2.boolean().nullish(),
59
+ filterBackgroundAudio: z2.boolean().nullish(),
60
+ includeLanguageDetection: z2.boolean().nullish(),
61
+ includeTimestamps: z2.boolean().nullish(),
62
+ keyterms: z2.array(z2.string().max(20)).max(50).nullish(),
63
+ minSilenceDurationMs: z2.number().int().min(50).max(2e3).nullish(),
64
+ minSpeechDurationMs: z2.number().int().min(50).max(2e3).nullish(),
65
+ noVerbatim: z2.boolean().nullish(),
66
+ previousText: z2.string().nullish(),
67
+ secondaryLanguages: z2.array(z2.string()).nullish(),
68
+ vadSilenceThresholdSecs: z2.number().min(0.3).max(3).nullish(),
69
+ vadThreshold: z2.number().min(0.1).max(0.9).nullish()
70
+ }).nullish()
47
71
  });
48
72
 
49
73
  // src/elevenlabs-transcription-model.ts
74
+ var elevenLabsRealtimeErrorTypes = /* @__PURE__ */ new Set([
75
+ "auth_error",
76
+ "chunk_size_exceeded",
77
+ "commit_throttled",
78
+ "error",
79
+ "input_error",
80
+ "insufficient_audio_activity",
81
+ "queue_overflow",
82
+ "quota_exceeded",
83
+ "rate_limited",
84
+ "resource_exhausted",
85
+ "session_time_limit_exceeded",
86
+ "transcriber_error",
87
+ "unaccepted_terms"
88
+ ]);
89
+ var elevenLabsLateFinalizationErrorTypes = /* @__PURE__ */ new Set([
90
+ "commit_throttled",
91
+ "input_error",
92
+ "insufficient_audio_activity"
93
+ ]);
94
+ var finalCommitGracePeriodMs = 250;
95
+ function isRealtimeTranscriptionModelId(modelId) {
96
+ return modelId === "scribe_v2_realtime";
97
+ }
50
98
  var ElevenLabsTranscriptionModel = class _ElevenLabsTranscriptionModel {
51
99
  constructor(modelId, config) {
52
100
  this.modelId = modelId;
@@ -77,6 +125,13 @@ var ElevenLabsTranscriptionModel = class _ElevenLabsTranscriptionModel {
77
125
  providerOptions,
78
126
  schema: elevenLabsTranscriptionModelOptionsSchema
79
127
  });
128
+ if ((elevenlabsOptions == null ? void 0 : elevenlabsOptions.streaming) != null) {
129
+ warnings.push({
130
+ type: "unsupported",
131
+ feature: "providerOptions.elevenlabs.streaming",
132
+ details: "ElevenLabs batch transcription does not support streaming options."
133
+ });
134
+ }
80
135
  const formData = new FormData();
81
136
  const blob = audio instanceof Uint8Array ? new Blob([audio]) : new Blob([convertBase64ToUint8Array(audio)]);
82
137
  formData.append("model_id", this.modelId);
@@ -112,6 +167,11 @@ var ElevenLabsTranscriptionModel = class _ElevenLabsTranscriptionModel {
112
167
  }
113
168
  async doGenerate(options) {
114
169
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
170
+ if (isRealtimeTranscriptionModelId(this.modelId)) {
171
+ throw new UnsupportedFunctionalityError({
172
+ functionality: `non-streaming transcription with ${this.modelId}`
173
+ });
174
+ }
115
175
  const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
116
176
  const { formData, warnings } = await this.getArgs(options);
117
177
  const {
@@ -153,7 +213,385 @@ var ElevenLabsTranscriptionModel = class _ElevenLabsTranscriptionModel {
153
213
  }
154
214
  };
155
215
  }
216
+ async doStream(options) {
217
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
218
+ if (!isRealtimeTranscriptionModelId(this.modelId)) {
219
+ throw new UnsupportedFunctionalityError({
220
+ functionality: `streaming transcription with ${this.modelId}`
221
+ });
222
+ }
223
+ const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
224
+ const elevenLabsOptions = await parseProviderOptions({
225
+ provider: "elevenlabs",
226
+ providerOptions: options.providerOptions,
227
+ schema: elevenLabsTranscriptionModelOptionsSchema
228
+ });
229
+ const streamingOptions = (_d = elevenLabsOptions == null ? void 0 : elevenLabsOptions.streaming) != null ? _d : void 0;
230
+ const warnings = [];
231
+ const rawElevenLabsOptions = (_f = (_e = options.providerOptions) == null ? void 0 : _e.elevenlabs) != null ? _f : {};
232
+ for (const option of [
233
+ "diarize",
234
+ "fileFormat",
235
+ "numSpeakers",
236
+ "tagAudioEvents",
237
+ "timestampsGranularity"
238
+ ]) {
239
+ if (rawElevenLabsOptions[option] != null) {
240
+ warnings.push({
241
+ type: "unsupported",
242
+ feature: `providerOptions.elevenlabs.${option}`,
243
+ details: `ElevenLabs realtime transcription does not support ${option}.`
244
+ });
245
+ }
246
+ }
247
+ if ((streamingOptions == null ? void 0 : streamingOptions.filterBackgroundAudio) === true && (streamingOptions.includeTimestamps === true || streamingOptions.includeLanguageDetection === true)) {
248
+ throw new InvalidArgumentError({
249
+ argument: "providerOptions",
250
+ message: "providerOptions.elevenlabs.streaming.filterBackgroundAudio cannot be combined with includeTimestamps or includeLanguageDetection"
251
+ });
252
+ }
253
+ const inputFormat = getElevenLabsRealtimeAudioFormat(
254
+ options.inputAudioFormat
255
+ );
256
+ const url = buildElevenLabsRealtimeTranscriptionUrl({
257
+ baseUrl: toWebSocketUrl(
258
+ this.config.url({
259
+ path: "/v1/speech-to-text/realtime",
260
+ modelId: this.modelId
261
+ })
262
+ ),
263
+ inputFormat: inputFormat.audioFormat,
264
+ languageCode: (_g = elevenLabsOptions == null ? void 0 : elevenLabsOptions.languageCode) != null ? _g : void 0,
265
+ modelId: this.modelId,
266
+ streamingOptions
267
+ });
268
+ return {
269
+ request: { body: url.toString() },
270
+ response: {
271
+ timestamp: currentDate,
272
+ modelId: this.modelId
273
+ },
274
+ stream: createElevenLabsRealtimeTranscriptionStream({
275
+ abortSignal: options.abortSignal,
276
+ audio: options.audio,
277
+ headers: combineHeaders((_i = (_h = this.config).headers) == null ? void 0 : _i.call(_h), options.headers),
278
+ includeLanguageDetection: (streamingOptions == null ? void 0 : streamingOptions.includeLanguageDetection) === true,
279
+ includeRawChunks: options.includeRawChunks,
280
+ includeTimestamps: (streamingOptions == null ? void 0 : streamingOptions.includeTimestamps) === true,
281
+ language: (_j = elevenLabsOptions == null ? void 0 : elevenLabsOptions.languageCode) != null ? _j : void 0,
282
+ previousText: (_k = streamingOptions == null ? void 0 : streamingOptions.previousText) != null ? _k : void 0,
283
+ sampleRate: inputFormat.sampleRate,
284
+ url,
285
+ warnings,
286
+ webSocket: this.config.webSocket
287
+ })
288
+ };
289
+ }
156
290
  };
291
+ function getElevenLabsRealtimeAudioFormat(inputAudioFormat) {
292
+ const type = inputAudioFormat.type.toLowerCase();
293
+ const rate = inputAudioFormat.rate;
294
+ if (type === "audio/pcmu") {
295
+ if (rate != null && rate !== 8e3) {
296
+ throw new InvalidArgumentError({
297
+ argument: "inputAudioFormat",
298
+ message: "ElevenLabs only supports audio/pcmu at 8000 Hz"
299
+ });
300
+ }
301
+ return { audioFormat: "ulaw_8000", sampleRate: 8e3 };
302
+ }
303
+ const supportedPcmRates = [8e3, 16e3, 22050, 24e3, 44100, 48e3];
304
+ const pcmRate = rate != null ? rate : 16e3;
305
+ if (type !== "audio/pcm" || !supportedPcmRates.includes(pcmRate)) {
306
+ throw new InvalidArgumentError({
307
+ argument: "inputAudioFormat",
308
+ message: "ElevenLabs realtime transcription supports audio/pcm at 8000, 16000, 22050, 24000, 44100, or 48000 Hz, and audio/pcmu at 8000 Hz"
309
+ });
310
+ }
311
+ return { audioFormat: `pcm_${pcmRate}`, sampleRate: pcmRate };
312
+ }
313
+ function buildElevenLabsRealtimeTranscriptionUrl({
314
+ baseUrl,
315
+ inputFormat,
316
+ languageCode,
317
+ modelId,
318
+ streamingOptions
319
+ }) {
320
+ var _a, _b;
321
+ const url = new URL(baseUrl);
322
+ url.searchParams.set("model_id", modelId);
323
+ url.searchParams.set("audio_format", inputFormat);
324
+ const includeDetailedCommit = (streamingOptions == null ? void 0 : streamingOptions.includeTimestamps) === true || (streamingOptions == null ? void 0 : streamingOptions.includeLanguageDetection) === true;
325
+ const parameters = {
326
+ commit_strategy: streamingOptions == null ? void 0 : streamingOptions.commitStrategy,
327
+ enable_logging: streamingOptions == null ? void 0 : streamingOptions.enableLogging,
328
+ filter_background_audio: streamingOptions == null ? void 0 : streamingOptions.filterBackgroundAudio,
329
+ include_language_detection: streamingOptions == null ? void 0 : streamingOptions.includeLanguageDetection,
330
+ include_timestamps: includeDetailedCommit ? true : streamingOptions == null ? void 0 : streamingOptions.includeTimestamps,
331
+ language_code: languageCode,
332
+ min_silence_duration_ms: streamingOptions == null ? void 0 : streamingOptions.minSilenceDurationMs,
333
+ min_speech_duration_ms: streamingOptions == null ? void 0 : streamingOptions.minSpeechDurationMs,
334
+ no_verbatim: streamingOptions == null ? void 0 : streamingOptions.noVerbatim,
335
+ vad_silence_threshold_secs: streamingOptions == null ? void 0 : streamingOptions.vadSilenceThresholdSecs,
336
+ vad_threshold: streamingOptions == null ? void 0 : streamingOptions.vadThreshold
337
+ };
338
+ for (const [key, value] of Object.entries(parameters)) {
339
+ if (value != null) {
340
+ url.searchParams.set(key, String(value));
341
+ }
342
+ }
343
+ for (const keyterm of (_a = streamingOptions == null ? void 0 : streamingOptions.keyterms) != null ? _a : []) {
344
+ url.searchParams.append("keyterms", keyterm);
345
+ }
346
+ for (const secondaryLanguage of (_b = streamingOptions == null ? void 0 : streamingOptions.secondaryLanguages) != null ? _b : []) {
347
+ url.searchParams.append("secondary_languages", secondaryLanguage);
348
+ }
349
+ return url;
350
+ }
351
+ function createElevenLabsRealtimeTranscriptionStream({
352
+ abortSignal,
353
+ audio,
354
+ headers,
355
+ includeLanguageDetection,
356
+ includeRawChunks,
357
+ includeTimestamps,
358
+ language,
359
+ previousText,
360
+ sampleRate,
361
+ url,
362
+ warnings,
363
+ webSocket
364
+ }) {
365
+ let finished = false;
366
+ let cleanup = () => {
367
+ };
368
+ return new ReadableStream({
369
+ start: (controller) => {
370
+ let audioReader;
371
+ let connection;
372
+ let detectedLanguage = language;
373
+ let endOfInput = false;
374
+ let finalCommitGracePeriod;
375
+ let receivedPostInputCommit = false;
376
+ let segmentIndex = 0;
377
+ let sessionId;
378
+ let committedEventCount = 0;
379
+ let committedEventsAtEndOfInput = 0;
380
+ let finalCommitEventCount;
381
+ let timestampedCommitCount = 0;
382
+ const expectDetailedCommit = includeTimestamps || includeLanguageDetection;
383
+ const finalSegments = [];
384
+ const finalTexts = [];
385
+ cleanup = (closeCode) => {
386
+ clearTimeout(finalCommitGracePeriod);
387
+ if (audioReader != null) {
388
+ void audioReader.cancel().catch(() => {
389
+ });
390
+ } else {
391
+ void audio.cancel().catch(() => {
392
+ });
393
+ }
394
+ connection == null ? void 0 : connection.close(closeCode);
395
+ };
396
+ const finishWithError = (error) => {
397
+ if (finished) return;
398
+ finished = true;
399
+ cleanup();
400
+ controller.error(error);
401
+ };
402
+ const finish = () => {
403
+ var _a;
404
+ if (finished) return;
405
+ finished = true;
406
+ controller.enqueue({
407
+ type: "finish",
408
+ text: finalTexts.join(" ").trim(),
409
+ segments: finalSegments,
410
+ language: detectedLanguage,
411
+ durationInSeconds: (_a = finalSegments.at(-1)) == null ? void 0 : _a.endSecond
412
+ });
413
+ controller.close();
414
+ cleanup(1e3);
415
+ };
416
+ const scheduleFinish = () => {
417
+ clearTimeout(finalCommitGracePeriod);
418
+ finalCommitGracePeriod = setTimeout(finish, finalCommitGracePeriodMs);
419
+ };
420
+ const sendAudio = async (socket) => {
421
+ audioReader = audio.getReader();
422
+ let firstChunk = true;
423
+ try {
424
+ while (true) {
425
+ const { done, value } = await audioReader.read();
426
+ if (done || finished) break;
427
+ socket.send(
428
+ JSON.stringify({
429
+ message_type: "input_audio_chunk",
430
+ audio_base_64: convertToBase64(value),
431
+ commit: false,
432
+ sample_rate: sampleRate,
433
+ ...firstChunk && previousText != null ? { previous_text: previousText } : {}
434
+ })
435
+ );
436
+ firstChunk = false;
437
+ await waitForWebSocketBufferDrain(socket);
438
+ }
439
+ } finally {
440
+ audioReader.releaseLock();
441
+ audioReader = void 0;
442
+ }
443
+ if (!finished) {
444
+ committedEventsAtEndOfInput = committedEventCount;
445
+ endOfInput = true;
446
+ socket.send(
447
+ JSON.stringify({
448
+ message_type: "input_audio_chunk",
449
+ audio_base_64: "",
450
+ commit: true,
451
+ sample_rate: sampleRate
452
+ })
453
+ );
454
+ }
455
+ };
456
+ connection = connectToWebSocket({
457
+ abortSignal,
458
+ headers,
459
+ onAbort: finishWithError,
460
+ onClose: () => {
461
+ if (finished) return;
462
+ if (endOfInput && receivedPostInputCommit) {
463
+ finish();
464
+ return;
465
+ }
466
+ finishWithError(
467
+ new Error(
468
+ "ElevenLabs realtime transcription stream closed before completion."
469
+ )
470
+ );
471
+ },
472
+ onMessageText: async (text) => {
473
+ var _a, _b, _c, _d, _e, _f, _g, _h;
474
+ const parsed = await safeParseJSON({ text });
475
+ if (!parsed.success) return;
476
+ const raw = parsed.value;
477
+ if (includeRawChunks) {
478
+ controller.enqueue({ type: "raw", rawValue: raw });
479
+ }
480
+ if (raw.message_type != null && elevenLabsRealtimeErrorTypes.has(raw.message_type)) {
481
+ if (endOfInput && (committedEventCount > 0 || timestampedCommitCount > 0) && elevenLabsLateFinalizationErrorTypes.has(raw.message_type)) {
482
+ receivedPostInputCommit = true;
483
+ finish();
484
+ return;
485
+ }
486
+ finishWithError(
487
+ new Error((_a = raw.error) != null ? _a : "ElevenLabs realtime transcription error")
488
+ );
489
+ return;
490
+ }
491
+ switch (raw.message_type) {
492
+ case "session_started": {
493
+ sessionId = raw.session_id;
494
+ controller.enqueue({ type: "stream-start", warnings });
495
+ const socket = connection == null ? void 0 : connection.socket;
496
+ if (socket == null) {
497
+ finishWithError(new Error("WebSocket is not connected."));
498
+ break;
499
+ }
500
+ void sendAudio(socket).catch(finishWithError);
501
+ break;
502
+ }
503
+ case "partial_transcript": {
504
+ controller.enqueue({
505
+ type: "transcript-partial",
506
+ id: `${sessionId != null ? sessionId : "session"}:${segmentIndex}`,
507
+ text: (_b = raw.text) != null ? _b : ""
508
+ });
509
+ break;
510
+ }
511
+ case "final_transcript":
512
+ case "committed_transcript": {
513
+ committedEventCount++;
514
+ const text2 = ((_c = raw.text) != null ? _c : "").trim();
515
+ const id = `${sessionId != null ? sessionId : "session"}:${segmentIndex}`;
516
+ if (text2.length > 0) {
517
+ finalTexts.push(text2);
518
+ segmentIndex++;
519
+ controller.enqueue({
520
+ type: "transcript-final",
521
+ id,
522
+ text: text2
523
+ });
524
+ }
525
+ if (endOfInput && committedEventCount > committedEventsAtEndOfInput) {
526
+ receivedPostInputCommit = true;
527
+ finalCommitEventCount = committedEventCount;
528
+ if (!expectDetailedCommit || raw.message_type === "final_transcript") {
529
+ scheduleFinish();
530
+ }
531
+ }
532
+ break;
533
+ }
534
+ case "final_transcript_with_timestamps":
535
+ case "committed_transcript_with_timestamps": {
536
+ const text2 = ((_d = raw.text) != null ? _d : "").trim();
537
+ const words = (_e = raw.words) != null ? _e : [];
538
+ const timestampedWords = words.filter(
539
+ (word) => typeof word.start === "number" && typeof word.end === "number"
540
+ );
541
+ detectedLanguage = (_f = raw.language_code) != null ? _f : detectedLanguage;
542
+ if (finalTexts[timestampedCommitCount] == null && text2.length > 0) {
543
+ const id = `${sessionId != null ? sessionId : "session"}:${segmentIndex++}`;
544
+ finalTexts.push(text2);
545
+ controller.enqueue({
546
+ type: "transcript-final",
547
+ id,
548
+ text: text2,
549
+ ...includeTimestamps ? {
550
+ startSecond: (_g = timestampedWords[0]) == null ? void 0 : _g.start,
551
+ endSecond: (_h = timestampedWords.at(-1)) == null ? void 0 : _h.end
552
+ } : {}
553
+ });
554
+ }
555
+ timestampedCommitCount++;
556
+ if (includeTimestamps) {
557
+ finalSegments.push(
558
+ ...timestampedWords.map((word) => {
559
+ var _a2;
560
+ return {
561
+ text: (_a2 = word.text) != null ? _a2 : "",
562
+ startSecond: word.start,
563
+ endSecond: word.end
564
+ };
565
+ })
566
+ );
567
+ }
568
+ if (endOfInput && timestampedCommitCount > committedEventsAtEndOfInput && (finalCommitEventCount == null || timestampedCommitCount >= finalCommitEventCount)) {
569
+ receivedPostInputCommit = true;
570
+ scheduleFinish();
571
+ }
572
+ break;
573
+ }
574
+ }
575
+ },
576
+ onProcessingError: finishWithError,
577
+ onSocketError: () => {
578
+ finishWithError(
579
+ new Error(
580
+ "ElevenLabs realtime transcription error." + (webSocket == null ? " Note: the native WebSocket implementation in browsers, Node.js, Deno, and Bun cannot send the xi-api-key header required by ElevenLabs. Pass a header-capable WebSocket implementation (e.g. the 'ws' package) via createElevenLabs({ webSocket })." : "")
581
+ )
582
+ );
583
+ },
584
+ url,
585
+ webSocket
586
+ });
587
+ },
588
+ cancel: () => {
589
+ if (finished) return;
590
+ finished = true;
591
+ cleanup();
592
+ }
593
+ });
594
+ }
157
595
  var elevenlabsTranscriptionResponseSchema = z3.object({
158
596
  language_code: z3.string(),
159
597
  language_probability: z3.number(),
@@ -384,7 +822,7 @@ var ElevenLabsSpeechModel = class _ElevenLabsSpeechModel {
384
822
  };
385
823
 
386
824
  // src/version.ts
387
- var VERSION = true ? "3.0.13" : "0.0.0-test";
825
+ var VERSION = true ? "3.0.15" : "0.0.0-test";
388
826
 
389
827
  // src/elevenlabs-provider.ts
390
828
  function createElevenLabs(options = {}) {
@@ -403,7 +841,8 @@ function createElevenLabs(options = {}) {
403
841
  provider: `elevenlabs.transcription`,
404
842
  url: ({ path }) => `https://api.elevenlabs.io${path}`,
405
843
  headers: getHeaders,
406
- fetch: options.fetch
844
+ fetch: options.fetch,
845
+ webSocket: options.webSocket
407
846
  });
408
847
  const createSpeechModel = (modelId) => new ElevenLabsSpeechModel(modelId, {
409
848
  provider: `elevenlabs.speech`,