@ai-sdk/cartesia 0.0.1 → 3.0.2
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 +23 -0
- package/LICENSE +13 -0
- package/README.md +47 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.js +724 -0
- package/dist/index.js.map +1 -0
- package/docs/95-cartesia.mdx +262 -0
- package/package.json +69 -7
- package/src/cartesia-api-types.ts +17 -0
- package/src/cartesia-config.ts +9 -0
- package/src/cartesia-error.ts +17 -0
- package/src/cartesia-provider.ts +190 -0
- package/src/cartesia-realtime-model-options.ts +3 -0
- package/src/cartesia-realtime-model.ts +326 -0
- package/src/cartesia-speech-api-types.ts +64 -0
- package/src/cartesia-speech-model-options.ts +40 -0
- package/src/cartesia-speech-model.ts +285 -0
- package/src/cartesia-speech-options.ts +9 -0
- package/src/cartesia-transcription-model-options.ts +13 -0
- package/src/cartesia-transcription-model.ts +157 -0
- package/src/cartesia-transcription-options.ts +1 -0
- package/src/index.ts +20 -0
- package/src/transcript-test.mp3 +0 -0
- package/src/version.ts +6 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
// src/cartesia-provider.ts
|
|
2
|
+
import {
|
|
3
|
+
NoSuchModelError
|
|
4
|
+
} from "@ai-sdk/provider";
|
|
5
|
+
import {
|
|
6
|
+
loadApiKey,
|
|
7
|
+
withUserAgentSuffix
|
|
8
|
+
} from "@ai-sdk/provider-utils";
|
|
9
|
+
|
|
10
|
+
// src/cartesia-transcription-model.ts
|
|
11
|
+
import {
|
|
12
|
+
combineHeaders,
|
|
13
|
+
convertBase64ToUint8Array,
|
|
14
|
+
createJsonResponseHandler,
|
|
15
|
+
mediaTypeToExtension,
|
|
16
|
+
parseProviderOptions,
|
|
17
|
+
postFormDataToApi,
|
|
18
|
+
serializeModelOptions,
|
|
19
|
+
WORKFLOW_SERIALIZE,
|
|
20
|
+
WORKFLOW_DESERIALIZE
|
|
21
|
+
} from "@ai-sdk/provider-utils";
|
|
22
|
+
import { z as z3 } from "zod/v4";
|
|
23
|
+
|
|
24
|
+
// src/cartesia-error.ts
|
|
25
|
+
import { z } from "zod/v4";
|
|
26
|
+
import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils";
|
|
27
|
+
var cartesiaErrorDataSchema = z.object({
|
|
28
|
+
error_code: z.string().nullish(),
|
|
29
|
+
title: z.string(),
|
|
30
|
+
message: z.string(),
|
|
31
|
+
request_id: z.string(),
|
|
32
|
+
doc_url: z.string().optional()
|
|
33
|
+
});
|
|
34
|
+
var cartesiaFailedResponseHandler = createJsonErrorResponseHandler({
|
|
35
|
+
errorSchema: cartesiaErrorDataSchema,
|
|
36
|
+
errorToMessage: (data) => `${data.title}: ${data.message}`
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// src/cartesia-transcription-model-options.ts
|
|
40
|
+
import { z as z2 } from "zod/v4";
|
|
41
|
+
var cartesiaTranscriptionModelOptionsSchema = z2.object({
|
|
42
|
+
/** The language of the audio (ISO 639-1 code). Defaults to English. */
|
|
43
|
+
language: z2.string().nullish(),
|
|
44
|
+
/** The timestamp granularities to populate. Currently only `word` is supported. */
|
|
45
|
+
timestampGranularities: z2.array(z2.enum(["word"])).nullish()
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// src/cartesia-transcription-model.ts
|
|
49
|
+
var CartesiaTranscriptionModel = class _CartesiaTranscriptionModel {
|
|
50
|
+
constructor(modelId, config) {
|
|
51
|
+
this.modelId = modelId;
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.specificationVersion = "v4";
|
|
54
|
+
}
|
|
55
|
+
get provider() {
|
|
56
|
+
return this.config.provider;
|
|
57
|
+
}
|
|
58
|
+
static [WORKFLOW_SERIALIZE](model) {
|
|
59
|
+
return serializeModelOptions({
|
|
60
|
+
modelId: model.modelId,
|
|
61
|
+
config: model.config
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
static [WORKFLOW_DESERIALIZE](options) {
|
|
65
|
+
return new _CartesiaTranscriptionModel(options.modelId, options.config);
|
|
66
|
+
}
|
|
67
|
+
async getArgs({
|
|
68
|
+
audio,
|
|
69
|
+
mediaType,
|
|
70
|
+
providerOptions
|
|
71
|
+
}) {
|
|
72
|
+
const warnings = [];
|
|
73
|
+
const cartesiaOptions = await parseProviderOptions({
|
|
74
|
+
provider: "cartesia",
|
|
75
|
+
providerOptions,
|
|
76
|
+
schema: cartesiaTranscriptionModelOptionsSchema
|
|
77
|
+
});
|
|
78
|
+
const formData = new FormData();
|
|
79
|
+
const blob = audio instanceof Uint8Array ? new Blob([audio]) : new Blob([convertBase64ToUint8Array(audio)]);
|
|
80
|
+
const fileExtension = mediaTypeToExtension(mediaType);
|
|
81
|
+
formData.append("model", this.modelId);
|
|
82
|
+
formData.append(
|
|
83
|
+
"file",
|
|
84
|
+
new File([blob], "audio", { type: mediaType }),
|
|
85
|
+
`audio.${fileExtension}`
|
|
86
|
+
);
|
|
87
|
+
if (cartesiaOptions) {
|
|
88
|
+
if (cartesiaOptions.language != null) {
|
|
89
|
+
formData.set("language", cartesiaOptions.language);
|
|
90
|
+
}
|
|
91
|
+
if (cartesiaOptions.timestampGranularities != null) {
|
|
92
|
+
for (const granularity of cartesiaOptions.timestampGranularities) {
|
|
93
|
+
formData.append("timestamp_granularities[]", granularity);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
formData,
|
|
99
|
+
warnings
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async doGenerate(options) {
|
|
103
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
104
|
+
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
|
|
105
|
+
const { formData, warnings } = await this.getArgs(options);
|
|
106
|
+
const {
|
|
107
|
+
value: response,
|
|
108
|
+
responseHeaders,
|
|
109
|
+
rawValue: rawResponse
|
|
110
|
+
} = await postFormDataToApi({
|
|
111
|
+
url: this.config.url({
|
|
112
|
+
path: "/stt",
|
|
113
|
+
modelId: this.modelId
|
|
114
|
+
}),
|
|
115
|
+
headers: combineHeaders((_e = (_d = this.config).headers) == null ? void 0 : _e.call(_d), options.headers),
|
|
116
|
+
formData,
|
|
117
|
+
failedResponseHandler: cartesiaFailedResponseHandler,
|
|
118
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
119
|
+
cartesiaTranscriptionResponseSchema
|
|
120
|
+
),
|
|
121
|
+
abortSignal: options.abortSignal,
|
|
122
|
+
fetch: this.config.fetch
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
text: response.text,
|
|
126
|
+
segments: (_g = (_f = response.words) == null ? void 0 : _f.map((word) => ({
|
|
127
|
+
text: word.word,
|
|
128
|
+
startSecond: word.start,
|
|
129
|
+
endSecond: word.end
|
|
130
|
+
}))) != null ? _g : [],
|
|
131
|
+
language: (_h = response.language) != null ? _h : void 0,
|
|
132
|
+
durationInSeconds: (_i = response.duration) != null ? _i : void 0,
|
|
133
|
+
warnings,
|
|
134
|
+
response: {
|
|
135
|
+
timestamp: currentDate,
|
|
136
|
+
modelId: this.modelId,
|
|
137
|
+
headers: responseHeaders,
|
|
138
|
+
body: rawResponse
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var cartesiaTranscriptionResponseSchema = z3.object({
|
|
144
|
+
text: z3.string(),
|
|
145
|
+
language: z3.string().nullish(),
|
|
146
|
+
duration: z3.number().nullish(),
|
|
147
|
+
words: z3.array(
|
|
148
|
+
z3.object({
|
|
149
|
+
word: z3.string(),
|
|
150
|
+
start: z3.number(),
|
|
151
|
+
end: z3.number()
|
|
152
|
+
})
|
|
153
|
+
).nullish()
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// src/cartesia-speech-model.ts
|
|
157
|
+
import {
|
|
158
|
+
combineHeaders as combineHeaders2,
|
|
159
|
+
createBinaryResponseHandler,
|
|
160
|
+
parseProviderOptions as parseProviderOptions2,
|
|
161
|
+
postJsonToApi,
|
|
162
|
+
serializeModelOptions as serializeModelOptions2,
|
|
163
|
+
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2,
|
|
164
|
+
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2
|
|
165
|
+
} from "@ai-sdk/provider-utils";
|
|
166
|
+
|
|
167
|
+
// src/cartesia-speech-model-options.ts
|
|
168
|
+
import { z as z4 } from "zod/v4";
|
|
169
|
+
var cartesiaSpeechModelOptionsSchema = z4.object({
|
|
170
|
+
/** Container format for the output audio (raw, wav, mp3). */
|
|
171
|
+
container: z4.enum(["raw", "wav", "mp3"]).nullish(),
|
|
172
|
+
/** Encoding type for the audio output (pcm_f32le, pcm_s16le, pcm_mulaw, pcm_alaw). */
|
|
173
|
+
encoding: z4.enum(["pcm_f32le", "pcm_s16le", "pcm_mulaw", "pcm_alaw"]).nullish(),
|
|
174
|
+
/** Sample rate for the output audio in Hz (e.g. 8000, 16000, 22050, 24000, 44100, 48000). */
|
|
175
|
+
sampleRate: z4.union([
|
|
176
|
+
z4.literal(8e3),
|
|
177
|
+
z4.literal(16e3),
|
|
178
|
+
z4.literal(22050),
|
|
179
|
+
z4.literal(24e3),
|
|
180
|
+
z4.literal(44100),
|
|
181
|
+
z4.literal(48e3)
|
|
182
|
+
]).nullish(),
|
|
183
|
+
/** Bitrate for mp3 output in bits per second (e.g. 32000, 64000, 128000, 192000). */
|
|
184
|
+
bitRate: z4.union([
|
|
185
|
+
z4.literal(32e3),
|
|
186
|
+
z4.literal(64e3),
|
|
187
|
+
z4.literal(96e3),
|
|
188
|
+
z4.literal(128e3),
|
|
189
|
+
z4.literal(192e3)
|
|
190
|
+
]).nullish(),
|
|
191
|
+
/** Controls the speed of the generated speech. */
|
|
192
|
+
speed: z4.number().min(0.6).max(1.5).nullish(),
|
|
193
|
+
/** The language to generate speech in (ISO 639-1 code). */
|
|
194
|
+
language: z4.string().nullish()
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// src/cartesia-speech-model.ts
|
|
198
|
+
var DEFAULT_OUTPUT_FORMAT = {
|
|
199
|
+
container: "mp3",
|
|
200
|
+
sample_rate: 44100,
|
|
201
|
+
bit_rate: 128e3
|
|
202
|
+
};
|
|
203
|
+
var OUTPUT_FORMATS = {
|
|
204
|
+
alaw: { container: "raw", encoding: "pcm_alaw", sample_rate: 8e3 },
|
|
205
|
+
mp3: DEFAULT_OUTPUT_FORMAT,
|
|
206
|
+
mulaw: { container: "raw", encoding: "pcm_mulaw", sample_rate: 8e3 },
|
|
207
|
+
pcm: { container: "raw", encoding: "pcm_f32le", sample_rate: 44100 },
|
|
208
|
+
raw: { container: "raw", encoding: "pcm_f32le", sample_rate: 44100 },
|
|
209
|
+
wav: { container: "wav", encoding: "pcm_s16le", sample_rate: 44100 }
|
|
210
|
+
};
|
|
211
|
+
var SAMPLE_RATES = [8e3, 16e3, 22050, 24e3, 44100, 48e3];
|
|
212
|
+
function isSampleRate(value) {
|
|
213
|
+
return SAMPLE_RATES.includes(value);
|
|
214
|
+
}
|
|
215
|
+
function resolveOutputFormat({
|
|
216
|
+
outputFormat,
|
|
217
|
+
providerOptions,
|
|
218
|
+
warnings
|
|
219
|
+
}) {
|
|
220
|
+
var _a, _b, _c, _d;
|
|
221
|
+
const [formatName, sampleRateText, ...extraParts] = outputFormat.toLowerCase().split("_");
|
|
222
|
+
const mapped = OUTPUT_FORMATS[formatName];
|
|
223
|
+
let resolved = mapped ? { ...mapped } : { ...DEFAULT_OUTPUT_FORMAT };
|
|
224
|
+
if (!mapped) {
|
|
225
|
+
warnings.push({
|
|
226
|
+
type: "unsupported",
|
|
227
|
+
feature: "outputFormat",
|
|
228
|
+
details: `Unknown output format "${outputFormat}". Falling back to mp3. Use providerOptions.cartesia to configure container, encoding, and sampleRate directly.`
|
|
229
|
+
});
|
|
230
|
+
} else if (sampleRateText != null) {
|
|
231
|
+
const parsedRate = Number(sampleRateText);
|
|
232
|
+
if (extraParts.length === 0 && Number.isInteger(parsedRate) && isSampleRate(parsedRate)) {
|
|
233
|
+
resolved.sample_rate = parsedRate;
|
|
234
|
+
} else {
|
|
235
|
+
warnings.push({
|
|
236
|
+
type: "unsupported",
|
|
237
|
+
feature: "outputFormat",
|
|
238
|
+
details: `Unsupported Cartesia sample rate in output format "${outputFormat}". Using ${resolved.sample_rate} Hz instead.`
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const container = (_a = providerOptions == null ? void 0 : providerOptions.container) != null ? _a : resolved.container;
|
|
243
|
+
const sampleRate = (_b = providerOptions == null ? void 0 : providerOptions.sampleRate) != null ? _b : resolved.sample_rate;
|
|
244
|
+
if (container === "mp3") {
|
|
245
|
+
if ((providerOptions == null ? void 0 : providerOptions.encoding) != null) {
|
|
246
|
+
warnings.push({
|
|
247
|
+
type: "unsupported",
|
|
248
|
+
feature: "providerOptions.cartesia.encoding",
|
|
249
|
+
details: "Cartesia MP3 output does not accept an encoding. The encoding option was ignored."
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
container,
|
|
254
|
+
sample_rate: sampleRate,
|
|
255
|
+
bit_rate: (_c = providerOptions == null ? void 0 : providerOptions.bitRate) != null ? _c : resolved.container === "mp3" ? resolved.bit_rate : 128e3
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if ((providerOptions == null ? void 0 : providerOptions.bitRate) != null) {
|
|
259
|
+
warnings.push({
|
|
260
|
+
type: "unsupported",
|
|
261
|
+
feature: "providerOptions.cartesia.bitRate",
|
|
262
|
+
details: "Cartesia raw and WAV output do not accept a bit rate. The bitRate option was ignored."
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
container,
|
|
267
|
+
encoding: (_d = providerOptions == null ? void 0 : providerOptions.encoding) != null ? _d : resolved.container === "mp3" ? container === "wav" ? "pcm_s16le" : "pcm_f32le" : resolved.encoding,
|
|
268
|
+
sample_rate: sampleRate
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
var CartesiaSpeechModel = class _CartesiaSpeechModel {
|
|
272
|
+
constructor(modelId, config) {
|
|
273
|
+
this.modelId = modelId;
|
|
274
|
+
this.config = config;
|
|
275
|
+
this.specificationVersion = "v4";
|
|
276
|
+
}
|
|
277
|
+
get provider() {
|
|
278
|
+
return this.config.provider;
|
|
279
|
+
}
|
|
280
|
+
static [WORKFLOW_SERIALIZE2](model) {
|
|
281
|
+
return serializeModelOptions2({
|
|
282
|
+
modelId: model.modelId,
|
|
283
|
+
config: model.config
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
static [WORKFLOW_DESERIALIZE2](options) {
|
|
287
|
+
return new _CartesiaSpeechModel(options.modelId, options.config);
|
|
288
|
+
}
|
|
289
|
+
async getArgs({
|
|
290
|
+
text,
|
|
291
|
+
voice,
|
|
292
|
+
outputFormat = "mp3",
|
|
293
|
+
instructions,
|
|
294
|
+
language,
|
|
295
|
+
speed,
|
|
296
|
+
providerOptions
|
|
297
|
+
}) {
|
|
298
|
+
var _a;
|
|
299
|
+
const warnings = [];
|
|
300
|
+
const cartesiaOptions = await parseProviderOptions2({
|
|
301
|
+
provider: "cartesia",
|
|
302
|
+
providerOptions,
|
|
303
|
+
schema: cartesiaSpeechModelOptionsSchema
|
|
304
|
+
});
|
|
305
|
+
if (!voice) {
|
|
306
|
+
throw new Error("Cartesia speech models require a `voice` to be set.");
|
|
307
|
+
}
|
|
308
|
+
const outputFormatObject = resolveOutputFormat({
|
|
309
|
+
outputFormat,
|
|
310
|
+
providerOptions: cartesiaOptions,
|
|
311
|
+
warnings
|
|
312
|
+
});
|
|
313
|
+
const requestBody = {
|
|
314
|
+
model_id: this.modelId,
|
|
315
|
+
transcript: text,
|
|
316
|
+
voice: {
|
|
317
|
+
mode: "id",
|
|
318
|
+
id: voice
|
|
319
|
+
},
|
|
320
|
+
output_format: outputFormatObject
|
|
321
|
+
};
|
|
322
|
+
if (language) {
|
|
323
|
+
requestBody.language = language;
|
|
324
|
+
}
|
|
325
|
+
if (cartesiaOptions) {
|
|
326
|
+
if (cartesiaOptions.language != null) {
|
|
327
|
+
requestBody.language = cartesiaOptions.language;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const resolvedSpeed = (_a = cartesiaOptions == null ? void 0 : cartesiaOptions.speed) != null ? _a : speed;
|
|
331
|
+
if (resolvedSpeed != null) {
|
|
332
|
+
if (resolvedSpeed >= 0.6 && resolvedSpeed <= 1.5) {
|
|
333
|
+
requestBody.generation_config = { speed: resolvedSpeed };
|
|
334
|
+
} else {
|
|
335
|
+
warnings.push({
|
|
336
|
+
type: "unsupported",
|
|
337
|
+
feature: "speed",
|
|
338
|
+
details: "Cartesia speed must be between 0.6 and 1.5. The speed option was ignored."
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (instructions) {
|
|
343
|
+
warnings.push({
|
|
344
|
+
type: "unsupported",
|
|
345
|
+
feature: "instructions",
|
|
346
|
+
details: `Cartesia speech models do not support instructions. Instructions parameter was ignored.`
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
requestBody,
|
|
351
|
+
warnings
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async doGenerate(options) {
|
|
355
|
+
var _a, _b, _c, _d, _e;
|
|
356
|
+
const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
|
|
357
|
+
const { requestBody, warnings } = await this.getArgs(options);
|
|
358
|
+
const {
|
|
359
|
+
value: audio,
|
|
360
|
+
responseHeaders,
|
|
361
|
+
rawValue: rawResponse
|
|
362
|
+
} = await postJsonToApi({
|
|
363
|
+
url: this.config.url({
|
|
364
|
+
path: "/tts/bytes",
|
|
365
|
+
modelId: this.modelId
|
|
366
|
+
}),
|
|
367
|
+
headers: combineHeaders2((_e = (_d = this.config).headers) == null ? void 0 : _e.call(_d), options.headers),
|
|
368
|
+
body: requestBody,
|
|
369
|
+
failedResponseHandler: cartesiaFailedResponseHandler,
|
|
370
|
+
successfulResponseHandler: createBinaryResponseHandler(),
|
|
371
|
+
abortSignal: options.abortSignal,
|
|
372
|
+
fetch: this.config.fetch
|
|
373
|
+
});
|
|
374
|
+
return {
|
|
375
|
+
audio,
|
|
376
|
+
warnings,
|
|
377
|
+
request: {
|
|
378
|
+
body: JSON.stringify(requestBody)
|
|
379
|
+
},
|
|
380
|
+
response: {
|
|
381
|
+
timestamp: currentDate,
|
|
382
|
+
modelId: this.modelId,
|
|
383
|
+
headers: responseHeaders,
|
|
384
|
+
body: rawResponse
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// src/cartesia-realtime-model.ts
|
|
391
|
+
import {
|
|
392
|
+
convertBase64ToUint8Array as convertBase64ToUint8Array2
|
|
393
|
+
} from "@ai-sdk/provider-utils";
|
|
394
|
+
var CartesiaRealtimeModel = class {
|
|
395
|
+
constructor(modelId, config) {
|
|
396
|
+
this.specificationVersion = "v4";
|
|
397
|
+
this.manualTranscript = {
|
|
398
|
+
duration: 0,
|
|
399
|
+
events: [],
|
|
400
|
+
text: ""
|
|
401
|
+
};
|
|
402
|
+
this.sessionCreated = false;
|
|
403
|
+
this.useTurnDetection = true;
|
|
404
|
+
this.modelId = modelId;
|
|
405
|
+
this.provider = config.provider;
|
|
406
|
+
this.config = config;
|
|
407
|
+
}
|
|
408
|
+
async doCreateClientSecret(options) {
|
|
409
|
+
var _a, _b, _c, _d, _e, _f;
|
|
410
|
+
const expiresIn = options.expiresAfterSeconds;
|
|
411
|
+
if (expiresIn != null && (!Number.isInteger(expiresIn) || expiresIn <= 0 || expiresIn > 3600)) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
"Cartesia realtime client secrets must expire between 1 and 3600 seconds."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const sessionConfig = options.sessionConfig;
|
|
417
|
+
const language = (_a = sessionConfig == null ? void 0 : sessionConfig.inputAudioTranscription) == null ? void 0 : _a.language;
|
|
418
|
+
if (language != null && language !== "en") {
|
|
419
|
+
throw new Error("Cartesia Ink 2 currently supports English only.");
|
|
420
|
+
}
|
|
421
|
+
const fetchFn = (_b = this.config.fetch) != null ? _b : fetch;
|
|
422
|
+
const response = await fetchFn(`${this.config.baseURL}/access-token`, {
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: {
|
|
425
|
+
...this.config.headers(),
|
|
426
|
+
"Content-Type": "application/json"
|
|
427
|
+
},
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
grants: { stt: true },
|
|
430
|
+
...expiresIn != null ? { expires_in: expiresIn } : {}
|
|
431
|
+
})
|
|
432
|
+
});
|
|
433
|
+
if (!response.ok) {
|
|
434
|
+
const text = await response.text();
|
|
435
|
+
throw new Error(
|
|
436
|
+
`Cartesia realtime client secret request failed: ${response.status} ${text}`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
const data = await response.json();
|
|
440
|
+
if (typeof data.token !== "string" || data.token.length === 0) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
"Cartesia realtime client secret response did not include a token."
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const turnDetection = sessionConfig == null ? void 0 : sessionConfig.turnDetection;
|
|
446
|
+
const useTurnDetection = turnDetection === void 0 || turnDetection !== null && turnDetection.type !== "disabled";
|
|
447
|
+
const inputAudioFormat = sessionConfig == null ? void 0 : sessionConfig.inputAudioFormat;
|
|
448
|
+
const url = new URL(
|
|
449
|
+
useTurnDetection ? `${this.config.baseURL}/stt/turns/websocket` : `${this.config.baseURL}/stt/websocket`
|
|
450
|
+
);
|
|
451
|
+
url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
|
|
452
|
+
url.searchParams.set("model", this.modelId);
|
|
453
|
+
url.searchParams.set(
|
|
454
|
+
"encoding",
|
|
455
|
+
resolveCartesiaEncoding(inputAudioFormat == null ? void 0 : inputAudioFormat.type)
|
|
456
|
+
);
|
|
457
|
+
url.searchParams.set(
|
|
458
|
+
"sample_rate",
|
|
459
|
+
String((_c = inputAudioFormat == null ? void 0 : inputAudioFormat.rate) != null ? _c : 24e3)
|
|
460
|
+
);
|
|
461
|
+
url.searchParams.set("cartesia_version", this.config.version);
|
|
462
|
+
if (!useTurnDetection && language != null) {
|
|
463
|
+
url.searchParams.set("language", language);
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
token: data.token,
|
|
467
|
+
url: url.toString(),
|
|
468
|
+
...expiresIn != null ? {
|
|
469
|
+
expiresAt: Math.floor(
|
|
470
|
+
((_f = (_e = (_d = this.config._internal) == null ? void 0 : _d.currentDate) == null ? void 0 : _e.call(_d)) != null ? _f : /* @__PURE__ */ new Date()).getTime() / 1e3
|
|
471
|
+
) + expiresIn
|
|
472
|
+
} : {}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
getWebSocketConfig(options) {
|
|
476
|
+
const url = new URL(options.url);
|
|
477
|
+
this.useTurnDetection = url.pathname.includes("/stt/turns/websocket");
|
|
478
|
+
url.searchParams.set("access_token", options.token);
|
|
479
|
+
return { url: url.toString() };
|
|
480
|
+
}
|
|
481
|
+
parseServerEvent(raw) {
|
|
482
|
+
var _a, _b, _c, _d, _e;
|
|
483
|
+
const event = raw;
|
|
484
|
+
const requestId = (_a = event.request_id) != null ? _a : "cartesia-realtime";
|
|
485
|
+
let mapped;
|
|
486
|
+
switch (event.type) {
|
|
487
|
+
case "connected":
|
|
488
|
+
this.sessionCreated = true;
|
|
489
|
+
return {
|
|
490
|
+
type: "session-created",
|
|
491
|
+
sessionId: event.request_id,
|
|
492
|
+
raw
|
|
493
|
+
};
|
|
494
|
+
case "turn.start":
|
|
495
|
+
mapped = { type: "speech-started", itemId: requestId, raw };
|
|
496
|
+
break;
|
|
497
|
+
case "turn.resume":
|
|
498
|
+
mapped = [
|
|
499
|
+
{ type: "speech-started", itemId: requestId, raw },
|
|
500
|
+
{ type: "custom", rawType: event.type, raw }
|
|
501
|
+
];
|
|
502
|
+
break;
|
|
503
|
+
case "turn.end":
|
|
504
|
+
mapped = [
|
|
505
|
+
{ type: "speech-stopped", itemId: requestId, raw },
|
|
506
|
+
{
|
|
507
|
+
type: "input-transcription-completed",
|
|
508
|
+
itemId: requestId,
|
|
509
|
+
transcript: (_b = event.transcript) != null ? _b : "",
|
|
510
|
+
raw
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
break;
|
|
514
|
+
case "transcript":
|
|
515
|
+
if (event.is_final === true) {
|
|
516
|
+
this.manualTranscript.text += (_c = event.text) != null ? _c : "";
|
|
517
|
+
this.manualTranscript.duration += (_d = event.duration) != null ? _d : 0;
|
|
518
|
+
this.manualTranscript.events.push(event);
|
|
519
|
+
}
|
|
520
|
+
mapped = { type: "custom", rawType: event.type, raw };
|
|
521
|
+
break;
|
|
522
|
+
case "flush_done":
|
|
523
|
+
mapped = this.finishManualTranscript(event, raw);
|
|
524
|
+
break;
|
|
525
|
+
case "done": {
|
|
526
|
+
if (this.manualTranscript.text.length > 0) {
|
|
527
|
+
const completed = this.finishManualTranscript(event, raw);
|
|
528
|
+
mapped = Array.isArray(completed) ? [...completed, { type: "custom", rawType: event.type, raw }] : [completed, { type: "custom", rawType: event.type, raw }];
|
|
529
|
+
} else {
|
|
530
|
+
mapped = { type: "custom", rawType: event.type, raw };
|
|
531
|
+
}
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case "error":
|
|
535
|
+
mapped = {
|
|
536
|
+
type: "error",
|
|
537
|
+
message: (_e = event.message) != null ? _e : "Unknown Cartesia realtime error",
|
|
538
|
+
code: event.error_code,
|
|
539
|
+
raw
|
|
540
|
+
};
|
|
541
|
+
break;
|
|
542
|
+
default:
|
|
543
|
+
mapped = { type: "custom", rawType: event.type, raw };
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
return this.withSessionCreated(mapped, requestId, raw);
|
|
547
|
+
}
|
|
548
|
+
serializeClientEvent(event) {
|
|
549
|
+
switch (event.type) {
|
|
550
|
+
case "session-update":
|
|
551
|
+
case "input-audio-clear":
|
|
552
|
+
case "response-create":
|
|
553
|
+
return null;
|
|
554
|
+
case "input-audio-append":
|
|
555
|
+
return convertBase64ToUint8Array2(event.audio);
|
|
556
|
+
case "input-audio-commit":
|
|
557
|
+
return this.useTurnDetection ? null : "finalize";
|
|
558
|
+
default:
|
|
559
|
+
throw new Error(
|
|
560
|
+
`Cartesia Ink 2 does not support realtime client event "${event.type}".`
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
buildSessionConfig(_config) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
finishManualTranscript(event, raw) {
|
|
568
|
+
var _a;
|
|
569
|
+
const state = this.manualTranscript;
|
|
570
|
+
this.manualTranscript = { duration: 0, events: [], text: "" };
|
|
571
|
+
if (state.text.length === 0) {
|
|
572
|
+
return { type: "custom", rawType: event.type, raw };
|
|
573
|
+
}
|
|
574
|
+
return [
|
|
575
|
+
{
|
|
576
|
+
type: "audio-committed",
|
|
577
|
+
itemId: event.request_id,
|
|
578
|
+
raw
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
type: "input-transcription-completed",
|
|
582
|
+
itemId: (_a = event.request_id) != null ? _a : "cartesia-realtime",
|
|
583
|
+
transcript: state.text,
|
|
584
|
+
raw: {
|
|
585
|
+
event,
|
|
586
|
+
transcriptEvents: state.events,
|
|
587
|
+
duration: state.duration
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
];
|
|
591
|
+
}
|
|
592
|
+
withSessionCreated(event, sessionId, raw) {
|
|
593
|
+
if (this.sessionCreated) {
|
|
594
|
+
return event;
|
|
595
|
+
}
|
|
596
|
+
this.sessionCreated = true;
|
|
597
|
+
const created = {
|
|
598
|
+
type: "session-created",
|
|
599
|
+
sessionId,
|
|
600
|
+
raw
|
|
601
|
+
};
|
|
602
|
+
return Array.isArray(event) ? [created, ...event] : [created, event];
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
function resolveCartesiaEncoding(type) {
|
|
606
|
+
switch (type) {
|
|
607
|
+
case void 0:
|
|
608
|
+
case "audio/pcm":
|
|
609
|
+
return "pcm_s16le";
|
|
610
|
+
case "audio/pcmu":
|
|
611
|
+
return "pcm_mulaw";
|
|
612
|
+
case "audio/pcma":
|
|
613
|
+
return "pcm_alaw";
|
|
614
|
+
default:
|
|
615
|
+
throw new Error(`Unsupported Cartesia realtime audio format: ${type}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/version.ts
|
|
620
|
+
var VERSION = true ? "3.0.2" : "0.0.0-test";
|
|
621
|
+
|
|
622
|
+
// src/cartesia-provider.ts
|
|
623
|
+
var CARTESIA_API_VERSION = "2026-03-01";
|
|
624
|
+
function createCartesia(options = {}) {
|
|
625
|
+
const getHeaders = () => {
|
|
626
|
+
var _a;
|
|
627
|
+
return withUserAgentSuffix(
|
|
628
|
+
{
|
|
629
|
+
Authorization: `Bearer ${loadApiKey({
|
|
630
|
+
apiKey: options.apiKey,
|
|
631
|
+
environmentVariableName: "CARTESIA_API_KEY",
|
|
632
|
+
description: "Cartesia"
|
|
633
|
+
})}`,
|
|
634
|
+
"Cartesia-Version": (_a = options.version) != null ? _a : CARTESIA_API_VERSION,
|
|
635
|
+
...options.headers
|
|
636
|
+
},
|
|
637
|
+
`ai-sdk/cartesia/${VERSION}`
|
|
638
|
+
);
|
|
639
|
+
};
|
|
640
|
+
const createTranscriptionModel = (modelId) => new CartesiaTranscriptionModel(modelId, {
|
|
641
|
+
provider: `cartesia.transcription`,
|
|
642
|
+
url: ({ path }) => `https://api.cartesia.ai${path}`,
|
|
643
|
+
headers: getHeaders,
|
|
644
|
+
fetch: options.fetch
|
|
645
|
+
});
|
|
646
|
+
const createSpeechModel = (modelId) => new CartesiaSpeechModel(modelId, {
|
|
647
|
+
provider: `cartesia.speech`,
|
|
648
|
+
url: ({ path }) => `https://api.cartesia.ai${path}`,
|
|
649
|
+
headers: getHeaders,
|
|
650
|
+
fetch: options.fetch
|
|
651
|
+
});
|
|
652
|
+
const createRealtimeModel = (modelId) => {
|
|
653
|
+
var _a;
|
|
654
|
+
return new CartesiaRealtimeModel(modelId, {
|
|
655
|
+
provider: `cartesia.realtime`,
|
|
656
|
+
baseURL: "https://api.cartesia.ai",
|
|
657
|
+
version: (_a = options.version) != null ? _a : CARTESIA_API_VERSION,
|
|
658
|
+
headers: getHeaders,
|
|
659
|
+
fetch: options.fetch
|
|
660
|
+
});
|
|
661
|
+
};
|
|
662
|
+
const experimentalRealtimeFactory = Object.assign(
|
|
663
|
+
(modelId) => createRealtimeModel(modelId),
|
|
664
|
+
{
|
|
665
|
+
getToken: async (tokenOptions) => {
|
|
666
|
+
const secret = await createRealtimeModel(
|
|
667
|
+
tokenOptions.model
|
|
668
|
+
).doCreateClientSecret({
|
|
669
|
+
sessionConfig: tokenOptions.sessionConfig,
|
|
670
|
+
expiresAfterSeconds: tokenOptions.expiresAfterSeconds
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
token: secret.token,
|
|
674
|
+
url: secret.url,
|
|
675
|
+
expiresAt: secret.expiresAt
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
);
|
|
680
|
+
const provider = function(modelId) {
|
|
681
|
+
return {
|
|
682
|
+
speech: createSpeechModel(modelId)
|
|
683
|
+
};
|
|
684
|
+
};
|
|
685
|
+
provider.specificationVersion = "v4";
|
|
686
|
+
provider.transcription = createTranscriptionModel;
|
|
687
|
+
provider.transcriptionModel = createTranscriptionModel;
|
|
688
|
+
provider.speech = createSpeechModel;
|
|
689
|
+
provider.speechModel = createSpeechModel;
|
|
690
|
+
provider.experimental_realtime = experimentalRealtimeFactory;
|
|
691
|
+
provider.languageModel = (modelId) => {
|
|
692
|
+
throw new NoSuchModelError({
|
|
693
|
+
modelId,
|
|
694
|
+
modelType: "languageModel",
|
|
695
|
+
message: "Cartesia does not provide language models"
|
|
696
|
+
});
|
|
697
|
+
};
|
|
698
|
+
provider.embeddingModel = (modelId) => {
|
|
699
|
+
throw new NoSuchModelError({
|
|
700
|
+
modelId,
|
|
701
|
+
modelType: "embeddingModel",
|
|
702
|
+
message: "Cartesia does not provide embedding models"
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
provider.textEmbeddingModel = provider.embeddingModel;
|
|
706
|
+
provider.imageModel = (modelId) => {
|
|
707
|
+
throw new NoSuchModelError({
|
|
708
|
+
modelId,
|
|
709
|
+
modelType: "imageModel",
|
|
710
|
+
message: "Cartesia does not provide image models"
|
|
711
|
+
});
|
|
712
|
+
};
|
|
713
|
+
return provider;
|
|
714
|
+
}
|
|
715
|
+
var cartesia = createCartesia();
|
|
716
|
+
export {
|
|
717
|
+
CartesiaRealtimeModel,
|
|
718
|
+
CartesiaSpeechModel,
|
|
719
|
+
CartesiaTranscriptionModel,
|
|
720
|
+
VERSION,
|
|
721
|
+
cartesia,
|
|
722
|
+
createCartesia
|
|
723
|
+
};
|
|
724
|
+
//# sourceMappingURL=index.js.map
|