@ai-sdk/elevenlabs 3.0.12 → 3.0.14
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 +17 -0
- package/README.md +1 -1
- package/dist/index.d.ts +28 -3
- package/dist/index.js +442 -3
- package/dist/index.js.map +1 -1
- package/docs/90-elevenlabs.mdx +81 -4
- package/package.json +3 -3
- package/src/elevenlabs-config.ts +5 -1
- package/src/elevenlabs-provider.ts +8 -0
- package/src/elevenlabs-transcription-model-options.ts +17 -0
- package/src/elevenlabs-transcription-model.ts +562 -2
- package/src/elevenlabs-transcription-options.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @ai-sdk/elevenlabs
|
|
2
2
|
|
|
3
|
+
## 3.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 49fd7cd: Add streaming transcription support for ElevenLabs Scribe v2 Realtime through `experimental_streamTranscribe`.
|
|
8
|
+
- Updated dependencies [1e2f324]
|
|
9
|
+
- @ai-sdk/provider@4.0.4
|
|
10
|
+
- @ai-sdk/provider-utils@5.0.13
|
|
11
|
+
|
|
12
|
+
## 3.0.13
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [02ffdcb]
|
|
17
|
+
- Updated dependencies [76cb673]
|
|
18
|
+
- @ai-sdk/provider-utils@5.0.12
|
|
19
|
+
|
|
3
20
|
## 3.0.12
|
|
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
|
|
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.
|
|
825
|
+
var VERSION = true ? "3.0.14" : "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`,
|