@ai-sdk/gateway 4.0.122 → 4.0.124
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 +19 -0
- package/dist/index.d.ts +54 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +335 -129
- package/dist/index.js.map +1 -1
- package/docs/00-ai-gateway.mdx +63 -0
- package/package.json +3 -3
- package/src/gateway-image-model-settings.ts +0 -1
- package/src/gateway-language-model-settings.ts +0 -7
- package/src/gateway-provider.ts +71 -7
- package/src/gateway-realtime-auth.ts +21 -3
- package/src/gateway-speech-model.ts +305 -1
- package/src/gateway-stream-errors.ts +71 -0
- package/src/gateway-transcription-model.ts +18 -72
- package/src/index.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { EXPERIMENTAL_TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE, EXPERIMENTAL_TRANSCRIPTION_STREAM_START_FRAME_TYPE, WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertUint8ArrayToBase64, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonLinesResponseHandler, createJsonResponseHandler, createProviderExecutedToolFactory, experimental_parseTranscriptionStreamPart, getErrorMessage, getFromApi, lazySchema, loadOptionalSetting, normalizeBatchRequestCounts, normalizeHeaders, parseJsonEventStream, parseProviderOptions, postJsonToApi, resolve, safeValidateTypes, secureJsonParse, serializeModelOptions, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema } from "@ai-sdk/provider-utils";
|
|
1
|
+
import { EXPERIMENTAL_SPEECH_STREAM_START_FRAME_TYPE, EXPERIMENTAL_SPEECH_STREAM_TEXT_DELTA_FRAME_TYPE, EXPERIMENTAL_SPEECH_STREAM_TEXT_DONE_FRAME_TYPE, EXPERIMENTAL_TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE, EXPERIMENTAL_TRANSCRIPTION_STREAM_START_FRAME_TYPE, WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertUint8ArrayToBase64, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonLinesResponseHandler, createJsonResponseHandler, createProviderExecutedToolFactory, experimental_parseSpeechStreamPart, experimental_parseTranscriptionStreamPart, getErrorMessage, getFromApi, lazySchema, loadOptionalSetting, normalizeBatchRequestCounts, normalizeHeaders, parseJsonEventStream, parseProviderOptions, postJsonToApi, resolve, safeValidateTypes, secureJsonParse, serializeModelOptions, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema } from "@ai-sdk/provider-utils";
|
|
2
2
|
import { any, array, boolean, discriminatedUnion, enum as enum$1, json, literal, number, object, record, string, union, unknown } from "zod/v4";
|
|
3
3
|
import { APICallError, InvalidArgumentError, UnsupportedFunctionalityError, getErrorMessage as getErrorMessage$1 } from "@ai-sdk/provider";
|
|
4
4
|
import { getContext, getVercelOidcToken } from "@vercel/oidc";
|
|
5
5
|
//#region src/gateway-realtime-auth.ts
|
|
6
6
|
/**
|
|
7
|
-
* Shared WebSocket subprotocol contract for AI Gateway realtime
|
|
8
|
-
* transcription auth.
|
|
7
|
+
* Shared WebSocket subprotocol contract for AI Gateway realtime, streaming
|
|
8
|
+
* transcription, and streaming speech auth.
|
|
9
9
|
*
|
|
10
10
|
* The browser `WebSocket` API cannot set request headers, so the Gateway auth
|
|
11
11
|
* (bearer) token is carried through the `Sec-WebSocket-Protocol` handshake
|
|
@@ -14,7 +14,8 @@ import { getContext, getVercelOidcToken } from "@vercel/oidc";
|
|
|
14
14
|
*
|
|
15
15
|
* This module is the single source of truth for that contract so the client and
|
|
16
16
|
* the Gateway server can't drift: the client encodes values with
|
|
17
|
-
* `getGatewayRealtimeProtocols` / `getGatewayTranscriptionProtocols
|
|
17
|
+
* `getGatewayRealtimeProtocols` / `getGatewayTranscriptionProtocols` /
|
|
18
|
+
* `getGatewaySpeechProtocols`, and the
|
|
18
19
|
* Gateway server decodes them with `getGatewayRealtimeAuthToken` /
|
|
19
20
|
* `getGatewayRealtimeTeamIdOrSlug`.
|
|
20
21
|
*
|
|
@@ -35,6 +36,11 @@ const GATEWAY_REALTIME_SUBPROTOCOL = "ai-gateway-realtime.v1";
|
|
|
35
36
|
* purpose as `GATEWAY_REALTIME_SUBPROTOCOL`).
|
|
36
37
|
*/
|
|
37
38
|
const GATEWAY_TRANSCRIPTION_SUBPROTOCOL = "ai-gateway-transcription.v1";
|
|
39
|
+
/**
|
|
40
|
+
* Marker subprotocol for streaming speech handshakes (same negotiation
|
|
41
|
+
* purpose as `GATEWAY_REALTIME_SUBPROTOCOL`).
|
|
42
|
+
*/
|
|
43
|
+
const GATEWAY_SPEECH_SUBPROTOCOL = "ai-gateway-speech.v1";
|
|
38
44
|
/** Subprotocol prefix that carries the Gateway auth (bearer) token. */
|
|
39
45
|
const GATEWAY_AUTH_SUBPROTOCOL_PREFIX = "ai-gateway-auth.";
|
|
40
46
|
/** Subprotocol prefix that carries optional Vercel team scoping. */
|
|
@@ -53,6 +59,13 @@ function getGatewayRealtimeProtocols(token, options) {
|
|
|
53
59
|
function getGatewayTranscriptionProtocols(token, options) {
|
|
54
60
|
return buildGatewayProtocols(GATEWAY_TRANSCRIPTION_SUBPROTOCOL, token, options);
|
|
55
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Client-side: `getGatewayRealtimeProtocols`, but with the streaming speech
|
|
64
|
+
* marker subprotocol.
|
|
65
|
+
*/
|
|
66
|
+
function getGatewaySpeechProtocols(token, options) {
|
|
67
|
+
return buildGatewayProtocols(GATEWAY_SPEECH_SUBPROTOCOL, token, options);
|
|
68
|
+
}
|
|
56
69
|
function buildGatewayProtocols(marker, token, options) {
|
|
57
70
|
const protocols = [marker, `${GATEWAY_AUTH_SUBPROTOCOL_PREFIX}${token}`];
|
|
58
71
|
if (options?.teamIdOrSlug) protocols.push(`${GATEWAY_TEAM_SUBPROTOCOL_PREFIX}${encodeSubprotocolValue(options.teamIdOrSlug)}`);
|
|
@@ -2044,93 +2057,47 @@ const gatewayRerankingResponseSchema = lazySchema(() => zodSchema(z.object({
|
|
|
2044
2057
|
providerMetadata: z.record(z.string(), z.record(z.string(), z.unknown())).optional()
|
|
2045
2058
|
})));
|
|
2046
2059
|
//#endregion
|
|
2047
|
-
//#region src/gateway-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
successfulResponseHandler: createJsonResponseHandler(gatewaySpeechResponseSchema),
|
|
2073
|
-
failedResponseHandler: createJsonErrorResponseHandler({
|
|
2074
|
-
errorSchema: z.any(),
|
|
2075
|
-
errorToMessage: (data) => getErrorMessage(data) ?? "unknown error"
|
|
2076
|
-
}),
|
|
2077
|
-
...abortSignal && { abortSignal },
|
|
2078
|
-
fetch: this.config.fetch
|
|
2079
|
-
});
|
|
2080
|
-
return {
|
|
2081
|
-
audio: responseBody.audio,
|
|
2082
|
-
warnings: responseBody.warnings ?? [],
|
|
2083
|
-
...responseBody.usage != null && { usage: responseBody.usage },
|
|
2084
|
-
providerMetadata: responseBody.providerMetadata,
|
|
2085
|
-
response: {
|
|
2086
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
2087
|
-
modelId: this.modelId,
|
|
2088
|
-
headers: responseHeaders,
|
|
2089
|
-
body: rawValue
|
|
2090
|
-
}
|
|
2091
|
-
};
|
|
2092
|
-
} catch (error) {
|
|
2093
|
-
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders ?? {}));
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
getUrl() {
|
|
2097
|
-
return `${this.config.baseURL}/speech-model`;
|
|
2098
|
-
}
|
|
2099
|
-
getModelConfigHeaders() {
|
|
2100
|
-
return {
|
|
2101
|
-
"ai-speech-model-specification-version": "4",
|
|
2102
|
-
"ai-model-id": this.modelId
|
|
2103
|
-
};
|
|
2104
|
-
}
|
|
2060
|
+
//#region src/gateway-stream-errors.ts
|
|
2061
|
+
/**
|
|
2062
|
+
* `asGatewayError` is async while the WebSocket event handlers are
|
|
2063
|
+
* synchronous, hence this helper.
|
|
2064
|
+
*/
|
|
2065
|
+
async function errorControllerWithGatewayError(controller, error, authMethod) {
|
|
2066
|
+
controller.error(await asGatewayError(error, authMethod));
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Extract a human-readable message from an `error` stream part's payload for
|
|
2070
|
+
* the terminal close error.
|
|
2071
|
+
*/
|
|
2072
|
+
function getServerErrorMessage(error) {
|
|
2073
|
+
if (error != null && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
2074
|
+
return getErrorMessage$1(error);
|
|
2075
|
+
}
|
|
2076
|
+
/** Canonical status codes for server error-part types (no HTTP status exists on the WebSocket). */
|
|
2077
|
+
const SERVER_ERROR_STATUS_CODES = {
|
|
2078
|
+
authentication_error: 401,
|
|
2079
|
+
failed_dependency: 424,
|
|
2080
|
+
forbidden: 403,
|
|
2081
|
+
internal_server_error: 500,
|
|
2082
|
+
invalid_request_error: 400,
|
|
2083
|
+
model_not_found: 404,
|
|
2084
|
+
rate_limit_exceeded: 429
|
|
2105
2085
|
};
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
message: z.string()
|
|
2122
|
-
}),
|
|
2123
|
-
z.object({
|
|
2124
|
-
type: z.literal("other"),
|
|
2125
|
-
message: z.string()
|
|
2126
|
-
})
|
|
2127
|
-
]);
|
|
2128
|
-
const gatewaySpeechResponseSchema = lazySchema(() => zodSchema(z.object({
|
|
2129
|
-
audio: z.string(),
|
|
2130
|
-
warnings: z.array(gatewaySpeechWarningSchema()).optional(),
|
|
2131
|
-
usage: z.record(z.string(), z.json()).optional(),
|
|
2132
|
-
providerMetadata: z.record(z.string(), providerMetadataEntrySchema$1()).optional()
|
|
2133
|
-
})));
|
|
2086
|
+
/**
|
|
2087
|
+
* Maps a server error-part payload (`{ message, type }`) to the public
|
|
2088
|
+
* Gateway error class for its type; unknown shapes keep the generic message.
|
|
2089
|
+
*/
|
|
2090
|
+
async function createErrorFromServerErrorPart(error, authMethod, streamLabel = "transcription") {
|
|
2091
|
+
if (typeof error === "object" && error != null && "message" in error && typeof error.message === "string" && "type" in error && typeof error.type === "string" && error.type in SERVER_ERROR_STATUS_CODES) return createGatewayErrorFromResponse({
|
|
2092
|
+
response: { error: {
|
|
2093
|
+
message: error.message,
|
|
2094
|
+
type: error.type
|
|
2095
|
+
} },
|
|
2096
|
+
statusCode: SERVER_ERROR_STATUS_CODES[error.type],
|
|
2097
|
+
authMethod
|
|
2098
|
+
});
|
|
2099
|
+
return /* @__PURE__ */ new Error(`AI Gateway ${streamLabel} stream failed: ${getServerErrorMessage(error)}`);
|
|
2100
|
+
}
|
|
2134
2101
|
//#endregion
|
|
2135
2102
|
//#region src/gateway-transcription-model.ts
|
|
2136
2103
|
var GatewayTranscriptionModel = class {
|
|
@@ -2252,11 +2219,11 @@ function toGatewayTranscriptionUrl(baseURL, modelId) {
|
|
|
2252
2219
|
* case-insensitive because `combineHeaders` does not normalize casing and
|
|
2253
2220
|
* callers can pass arbitrary casing via the `headers` option.
|
|
2254
2221
|
*/
|
|
2255
|
-
function getProtocolsFromHeaders(headers) {
|
|
2222
|
+
function getProtocolsFromHeaders(headers, { marker = GATEWAY_TRANSCRIPTION_SUBPROTOCOL, getProtocols = getGatewayTranscriptionProtocols } = {}) {
|
|
2256
2223
|
const normalizedHeaders = normalizeHeaders(headers);
|
|
2257
2224
|
const authorization = normalizedHeaders.authorization;
|
|
2258
2225
|
const token = authorization?.startsWith("Bearer ") ? authorization.slice(7) : void 0;
|
|
2259
|
-
return token == null ? [
|
|
2226
|
+
return token == null ? [marker] : getProtocols(token, { teamIdOrSlug: normalizedHeaders[VERCEL_AI_GATEWAY_TEAM_HEADER] });
|
|
2260
2227
|
}
|
|
2261
2228
|
/** Audio frames stay under server frame-size limits (envelope rule 7). */
|
|
2262
2229
|
const MAX_AUDIO_FRAME_BYTES = 65536;
|
|
@@ -2363,7 +2330,7 @@ function createGatewayTranscriptionStream({ webSocket, url, protocols, headers,
|
|
|
2363
2330
|
}
|
|
2364
2331
|
});
|
|
2365
2332
|
}
|
|
2366
|
-
const providerMetadataEntrySchema = () => z.object({}).catchall(z.unknown());
|
|
2333
|
+
const providerMetadataEntrySchema$1 = () => z.object({}).catchall(z.unknown());
|
|
2367
2334
|
const gatewayTranscriptionWarningSchema = () => z.discriminatedUnion("type", [
|
|
2368
2335
|
z.object({
|
|
2369
2336
|
type: z.literal("unsupported"),
|
|
@@ -2396,48 +2363,274 @@ const gatewayTranscriptionResponseSchema = lazySchema(() => zodSchema(z.object({
|
|
|
2396
2363
|
durationInSeconds: z.number().nullish(),
|
|
2397
2364
|
warnings: z.array(gatewayTranscriptionWarningSchema()).optional(),
|
|
2398
2365
|
usage: z.record(z.string(), z.json()).optional(),
|
|
2399
|
-
providerMetadata: z.record(z.string(), providerMetadataEntrySchema()).optional()
|
|
2366
|
+
providerMetadata: z.record(z.string(), providerMetadataEntrySchema$1()).optional()
|
|
2400
2367
|
})));
|
|
2368
|
+
//#endregion
|
|
2369
|
+
//#region src/gateway-speech-model.ts
|
|
2370
|
+
var GatewaySpeechModel = class {
|
|
2371
|
+
constructor(modelId, config) {
|
|
2372
|
+
this.modelId = modelId;
|
|
2373
|
+
this.config = config;
|
|
2374
|
+
this.specificationVersion = "v4";
|
|
2375
|
+
}
|
|
2376
|
+
get provider() {
|
|
2377
|
+
return this.config.provider;
|
|
2378
|
+
}
|
|
2379
|
+
async doGenerate({ text, voice, outputFormat, instructions, speed, language, providerOptions, headers, abortSignal }) {
|
|
2380
|
+
const resolvedHeaders = this.config.headers ? await resolve(this.config.headers) : void 0;
|
|
2381
|
+
try {
|
|
2382
|
+
const { responseHeaders, value: responseBody, rawValue } = await postJsonToApi({
|
|
2383
|
+
url: this.getUrl(),
|
|
2384
|
+
headers: combineHeaders(resolvedHeaders, headers ?? {}, this.getModelConfigHeaders(), await resolve(this.config.o11yHeaders)),
|
|
2385
|
+
body: {
|
|
2386
|
+
text,
|
|
2387
|
+
...voice && { voice },
|
|
2388
|
+
...outputFormat && { outputFormat },
|
|
2389
|
+
...instructions && { instructions },
|
|
2390
|
+
...speed != null && { speed },
|
|
2391
|
+
...language && { language },
|
|
2392
|
+
...providerOptions && { providerOptions }
|
|
2393
|
+
},
|
|
2394
|
+
successfulResponseHandler: createJsonResponseHandler(gatewaySpeechResponseSchema),
|
|
2395
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
2396
|
+
errorSchema: z.any(),
|
|
2397
|
+
errorToMessage: (data) => getErrorMessage(data) ?? "unknown error"
|
|
2398
|
+
}),
|
|
2399
|
+
...abortSignal && { abortSignal },
|
|
2400
|
+
fetch: this.config.fetch
|
|
2401
|
+
});
|
|
2402
|
+
return {
|
|
2403
|
+
audio: responseBody.audio,
|
|
2404
|
+
warnings: responseBody.warnings ?? [],
|
|
2405
|
+
...responseBody.usage != null && { usage: responseBody.usage },
|
|
2406
|
+
providerMetadata: responseBody.providerMetadata,
|
|
2407
|
+
response: {
|
|
2408
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2409
|
+
modelId: this.modelId,
|
|
2410
|
+
headers: responseHeaders,
|
|
2411
|
+
body: rawValue
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
} catch (error) {
|
|
2415
|
+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders ?? {}));
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
async doStream(options) {
|
|
2419
|
+
const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
|
|
2420
|
+
const headers = combineHeaders(await resolve(this.config.headers ?? {}), options.headers ?? {}, this.getModelConfigHeaders(), await resolve(this.config.o11yHeaders));
|
|
2421
|
+
const authMethod = await parseAuthMethod(headers);
|
|
2422
|
+
const startFrame = {
|
|
2423
|
+
type: EXPERIMENTAL_SPEECH_STREAM_START_FRAME_TYPE,
|
|
2424
|
+
...options.voice != null && { voice: options.voice },
|
|
2425
|
+
...options.outputFormat != null && { outputFormat: options.outputFormat },
|
|
2426
|
+
...options.instructions != null && { instructions: options.instructions },
|
|
2427
|
+
...options.speed != null && { speed: options.speed },
|
|
2428
|
+
...options.language != null && { language: options.language },
|
|
2429
|
+
...options.providerOptions != null && { providerOptions: options.providerOptions },
|
|
2430
|
+
...options.includeRawChunks != null && { includeRawChunks: options.includeRawChunks }
|
|
2431
|
+
};
|
|
2432
|
+
return {
|
|
2433
|
+
stream: createGatewaySpeechStream({
|
|
2434
|
+
webSocket: this.config.webSocket,
|
|
2435
|
+
url: toGatewaySpeechUrl(this.config.baseURL, this.modelId),
|
|
2436
|
+
protocols: getProtocolsFromHeaders(headers, {
|
|
2437
|
+
marker: GATEWAY_SPEECH_SUBPROTOCOL,
|
|
2438
|
+
getProtocols: getGatewaySpeechProtocols
|
|
2439
|
+
}),
|
|
2440
|
+
headers,
|
|
2441
|
+
startFrame,
|
|
2442
|
+
text: options.text,
|
|
2443
|
+
abortSignal: options.abortSignal,
|
|
2444
|
+
authMethod
|
|
2445
|
+
}),
|
|
2446
|
+
request: { body: startFrame },
|
|
2447
|
+
response: {
|
|
2448
|
+
timestamp: currentDate,
|
|
2449
|
+
modelId: this.modelId
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
getUrl() {
|
|
2454
|
+
return `${this.config.baseURL}/speech-model`;
|
|
2455
|
+
}
|
|
2456
|
+
getModelConfigHeaders() {
|
|
2457
|
+
return {
|
|
2458
|
+
"ai-speech-model-specification-version": "4",
|
|
2459
|
+
"ai-model-id": this.modelId
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2401
2463
|
/**
|
|
2402
|
-
*
|
|
2403
|
-
*
|
|
2464
|
+
* Gateway streaming speech WebSocket URL: HTTP(S) base upgraded to WS(S),
|
|
2465
|
+
* model id in `?ai-model-id=` (browser `WebSocket` cannot set headers).
|
|
2404
2466
|
*/
|
|
2405
|
-
|
|
2406
|
-
|
|
2467
|
+
function toGatewaySpeechUrl(baseURL, modelId) {
|
|
2468
|
+
const url = new URL(`${baseURL.replace(/^http/, "ws")}/speech-model`);
|
|
2469
|
+
url.searchParams.set("ai-model-id", modelId);
|
|
2470
|
+
return url.toString();
|
|
2407
2471
|
}
|
|
2408
2472
|
/**
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2473
|
+
* Keeps text-delta frames under 64 KiB (envelope rule 7) even when JSON
|
|
2474
|
+
* escapes every code unit as `\uXXXX`.
|
|
2411
2475
|
*/
|
|
2412
|
-
|
|
2413
|
-
if (error != null && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
2414
|
-
return getErrorMessage$1(error);
|
|
2415
|
-
}
|
|
2416
|
-
/** Canonical status codes for server error-part types (no HTTP status exists on the WebSocket). */
|
|
2417
|
-
const SERVER_ERROR_STATUS_CODES = {
|
|
2418
|
-
authentication_error: 401,
|
|
2419
|
-
failed_dependency: 424,
|
|
2420
|
-
forbidden: 403,
|
|
2421
|
-
internal_server_error: 500,
|
|
2422
|
-
invalid_request_error: 400,
|
|
2423
|
-
model_not_found: 404,
|
|
2424
|
-
rate_limit_exceeded: 429
|
|
2425
|
-
};
|
|
2476
|
+
const MAX_TEXT_DELTA_CODE_UNITS = 8192;
|
|
2426
2477
|
/**
|
|
2427
|
-
*
|
|
2428
|
-
*
|
|
2478
|
+
* Splits a long text delta into pieces of at most `MAX_TEXT_DELTA_CODE_UNITS`
|
|
2479
|
+
* UTF-16 code units without separating a surrogate pair.
|
|
2429
2480
|
*/
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2481
|
+
function splitTextDelta(delta) {
|
|
2482
|
+
const pieces = [];
|
|
2483
|
+
let offset = 0;
|
|
2484
|
+
while (offset < delta.length) {
|
|
2485
|
+
let end = Math.min(offset + MAX_TEXT_DELTA_CODE_UNITS, delta.length);
|
|
2486
|
+
const lastCodeUnit = delta.charCodeAt(end - 1);
|
|
2487
|
+
if (end < delta.length && lastCodeUnit >= 55296 && lastCodeUnit <= 56319) end--;
|
|
2488
|
+
pieces.push(delta.slice(offset, end));
|
|
2489
|
+
offset = end;
|
|
2490
|
+
}
|
|
2491
|
+
return pieces;
|
|
2492
|
+
}
|
|
2493
|
+
function createGatewaySpeechStream({ webSocket, url, protocols, headers, startFrame, text, abortSignal, authMethod }) {
|
|
2494
|
+
let finished = false;
|
|
2495
|
+
let cleanup = () => {};
|
|
2496
|
+
return new ReadableStream({
|
|
2497
|
+
start: (controller) => {
|
|
2498
|
+
let textReader;
|
|
2499
|
+
let hasServerErrorPart = false;
|
|
2500
|
+
let lastServerError;
|
|
2501
|
+
let textStopped = false;
|
|
2502
|
+
let connection;
|
|
2503
|
+
const cancelText = () => {
|
|
2504
|
+
if (textReader != null) {
|
|
2505
|
+
textReader.cancel().catch(() => {});
|
|
2506
|
+
textReader = void 0;
|
|
2507
|
+
} else text.cancel().catch(() => {});
|
|
2508
|
+
};
|
|
2509
|
+
cleanup = (closeCode) => {
|
|
2510
|
+
cancelText();
|
|
2511
|
+
connection?.close(closeCode);
|
|
2512
|
+
};
|
|
2513
|
+
const finishWithError = (error) => {
|
|
2514
|
+
if (finished) return;
|
|
2515
|
+
finished = true;
|
|
2516
|
+
cleanup();
|
|
2517
|
+
errorControllerWithGatewayError(controller, error, authMethod);
|
|
2518
|
+
};
|
|
2519
|
+
const sendText = async (socket) => {
|
|
2520
|
+
const reader = text.getReader();
|
|
2521
|
+
textReader = reader;
|
|
2522
|
+
try {
|
|
2523
|
+
while (true) {
|
|
2524
|
+
const { done, value } = await reader.read();
|
|
2525
|
+
if (done || finished || textStopped) break;
|
|
2526
|
+
let first = true;
|
|
2527
|
+
const pieces = splitTextDelta(value.text);
|
|
2528
|
+
if (pieces.length === 0 && (value.voice != null || value.providerOptions != null)) pieces.push("");
|
|
2529
|
+
for (const delta of pieces) {
|
|
2530
|
+
if (finished || textStopped) break;
|
|
2531
|
+
const frame = {
|
|
2532
|
+
type: EXPERIMENTAL_SPEECH_STREAM_TEXT_DELTA_FRAME_TYPE,
|
|
2533
|
+
delta,
|
|
2534
|
+
...value.voice != null && { voice: value.voice },
|
|
2535
|
+
...first && value.providerOptions != null && { providerOptions: value.providerOptions }
|
|
2536
|
+
};
|
|
2537
|
+
first = false;
|
|
2538
|
+
socket.send(JSON.stringify(frame));
|
|
2539
|
+
await waitForWebSocketBufferDrain(socket);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
} finally {
|
|
2543
|
+
reader.releaseLock();
|
|
2544
|
+
if (textReader === reader) textReader = void 0;
|
|
2545
|
+
}
|
|
2546
|
+
if (!finished && !textStopped) socket.send(JSON.stringify({ type: EXPERIMENTAL_SPEECH_STREAM_TEXT_DONE_FRAME_TYPE }));
|
|
2547
|
+
};
|
|
2548
|
+
connection = connectToWebSocket({
|
|
2549
|
+
url,
|
|
2550
|
+
protocols,
|
|
2551
|
+
headers,
|
|
2552
|
+
webSocket,
|
|
2553
|
+
abortSignal,
|
|
2554
|
+
onAbort: (reason) => {
|
|
2555
|
+
if (finished) return;
|
|
2556
|
+
finished = true;
|
|
2557
|
+
cleanup();
|
|
2558
|
+
controller.error(reason);
|
|
2559
|
+
},
|
|
2560
|
+
onProcessingError: finishWithError,
|
|
2561
|
+
onOpen: (socket) => {
|
|
2562
|
+
socket.send(JSON.stringify(startFrame));
|
|
2563
|
+
sendText(socket).catch(finishWithError);
|
|
2564
|
+
},
|
|
2565
|
+
onMessageText: async (messageText) => {
|
|
2566
|
+
if (finished) return;
|
|
2567
|
+
const part = experimental_parseSpeechStreamPart(messageText);
|
|
2568
|
+
if (part == null) return;
|
|
2569
|
+
if (part.type === "finish") {
|
|
2570
|
+
finished = true;
|
|
2571
|
+
controller.enqueue(part);
|
|
2572
|
+
controller.close();
|
|
2573
|
+
cleanup(1e3);
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
if (part.type === "error") {
|
|
2577
|
+
hasServerErrorPart = true;
|
|
2578
|
+
lastServerError = part.error;
|
|
2579
|
+
textStopped = true;
|
|
2580
|
+
cancelText();
|
|
2581
|
+
part.error = await createErrorFromServerErrorPart(part.error, authMethod, "speech");
|
|
2582
|
+
if (finished) return;
|
|
2583
|
+
}
|
|
2584
|
+
controller.enqueue(part);
|
|
2585
|
+
},
|
|
2586
|
+
onSocketError: () => {
|
|
2587
|
+
finishWithError(/* @__PURE__ */ new Error("Connection error on AI Gateway speech stream"));
|
|
2588
|
+
},
|
|
2589
|
+
onClose: ({ code, reason }) => {
|
|
2590
|
+
if (hasServerErrorPart) {
|
|
2591
|
+
if (finished) return;
|
|
2592
|
+
createErrorFromServerErrorPart(lastServerError, authMethod, "speech").then(finishWithError);
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
finishWithError(/* @__PURE__ */ new Error("AI Gateway speech stream closed before a finish part was received" + (code != null ? ` (code ${code}${reason ? `: ${reason}` : ""})` : "") + "."));
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
},
|
|
2599
|
+
cancel: () => {
|
|
2600
|
+
if (finished) return;
|
|
2601
|
+
finished = true;
|
|
2602
|
+
cleanup();
|
|
2603
|
+
}
|
|
2438
2604
|
});
|
|
2439
|
-
return /* @__PURE__ */ new Error(`AI Gateway transcription stream failed: ${getServerErrorMessage(error)}`);
|
|
2440
2605
|
}
|
|
2606
|
+
const providerMetadataEntrySchema = () => z.object({}).catchall(z.unknown());
|
|
2607
|
+
const gatewaySpeechWarningSchema = () => z.discriminatedUnion("type", [
|
|
2608
|
+
z.object({
|
|
2609
|
+
type: z.literal("unsupported"),
|
|
2610
|
+
feature: z.string(),
|
|
2611
|
+
details: z.string().optional()
|
|
2612
|
+
}),
|
|
2613
|
+
z.object({
|
|
2614
|
+
type: z.literal("compatibility"),
|
|
2615
|
+
feature: z.string(),
|
|
2616
|
+
details: z.string().optional()
|
|
2617
|
+
}),
|
|
2618
|
+
z.object({
|
|
2619
|
+
type: z.literal("deprecated"),
|
|
2620
|
+
setting: z.string(),
|
|
2621
|
+
message: z.string()
|
|
2622
|
+
}),
|
|
2623
|
+
z.object({
|
|
2624
|
+
type: z.literal("other"),
|
|
2625
|
+
message: z.string()
|
|
2626
|
+
})
|
|
2627
|
+
]);
|
|
2628
|
+
const gatewaySpeechResponseSchema = lazySchema(() => zodSchema(z.object({
|
|
2629
|
+
audio: z.string(),
|
|
2630
|
+
warnings: z.array(gatewaySpeechWarningSchema()).optional(),
|
|
2631
|
+
usage: z.record(z.string(), z.json()).optional(),
|
|
2632
|
+
providerMetadata: z.record(z.string(), providerMetadataEntrySchema()).optional()
|
|
2633
|
+
})));
|
|
2441
2634
|
//#endregion
|
|
2442
2635
|
//#region src/gateway-realtime-model.ts
|
|
2443
2636
|
/**
|
|
@@ -3078,7 +3271,7 @@ async function getVercelRequestId() {
|
|
|
3078
3271
|
}
|
|
3079
3272
|
//#endregion
|
|
3080
3273
|
//#region src/version.ts
|
|
3081
|
-
const VERSION = "4.0.
|
|
3274
|
+
const VERSION = "4.0.124";
|
|
3082
3275
|
//#endregion
|
|
3083
3276
|
//#region src/gateway-provider.ts
|
|
3084
3277
|
const AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
|
|
@@ -3315,11 +3508,24 @@ function createGateway(options = {}) {
|
|
|
3315
3508
|
baseURL,
|
|
3316
3509
|
headers: getHeaders,
|
|
3317
3510
|
fetch: options.fetch,
|
|
3318
|
-
o11yHeaders: createO11yHeaders()
|
|
3511
|
+
o11yHeaders: createO11yHeaders(),
|
|
3512
|
+
webSocket: options.webSocket
|
|
3319
3513
|
});
|
|
3320
3514
|
};
|
|
3321
3515
|
provider.speechModel = createSpeechModel;
|
|
3322
3516
|
provider.speech = createSpeechModel;
|
|
3517
|
+
provider.experimental_speech = Object.assign((modelId) => createSpeechModel(modelId), { getToken: async (tokenOptions) => {
|
|
3518
|
+
const secret = await mintClientSecret({
|
|
3519
|
+
modelId: tokenOptions.model,
|
|
3520
|
+
routeKind: "speech",
|
|
3521
|
+
...tokenOptions.expiresAfterSeconds != null && { expiresAfterSeconds: tokenOptions.expiresAfterSeconds }
|
|
3522
|
+
});
|
|
3523
|
+
return {
|
|
3524
|
+
token: secret.token,
|
|
3525
|
+
url: toGatewaySpeechUrl(baseURL, tokenOptions.model),
|
|
3526
|
+
...secret.expiresAt != null && { expiresAt: secret.expiresAt }
|
|
3527
|
+
};
|
|
3528
|
+
} });
|
|
3323
3529
|
const createTranscriptionModel = (modelId) => {
|
|
3324
3530
|
return new GatewayTranscriptionModel(modelId, {
|
|
3325
3531
|
provider: "gateway",
|
|
@@ -3382,9 +3588,9 @@ async function getGatewayAuthToken(options) {
|
|
|
3382
3588
|
};
|
|
3383
3589
|
}
|
|
3384
3590
|
function assertGatewayClientSecretServerEnvironment() {
|
|
3385
|
-
if (typeof globalThis.window !== "undefined") throw new Error("AI Gateway client secrets must be minted server-side: minting needs your Gateway credential, which must never reach the browser. Call gateway.experimental_realtime.getToken() or gateway.
|
|
3591
|
+
if (typeof globalThis.window !== "undefined") throw new Error("AI Gateway client secrets must be minted server-side: minting needs your Gateway credential, which must never reach the browser. Call gateway.experimental_realtime.getToken(), gateway.experimental_transcription.getToken(), or gateway.experimental_speech.getToken() from your server and pass the returned token to the client.");
|
|
3386
3592
|
}
|
|
3387
3593
|
//#endregion
|
|
3388
|
-
export { GATEWAY_AUTH_SUBPROTOCOL_PREFIX, GATEWAY_REALTIME_SUBPROTOCOL, GATEWAY_TEAM_SUBPROTOCOL_PREFIX, GATEWAY_TRANSCRIPTION_SUBPROTOCOL, GatewayAuthenticationError, GatewayError, GatewayFailedDependencyError, GatewayForbiddenError, GatewayInternalServerError, GatewayInvalidRequestError, GatewayModelNotFoundError, GatewayNotFoundError, GatewayRateLimitError, GatewayResponseError, VERSION, createGateway, createGateway as createGatewayProvider, gateway, getGatewayRealtimeAuthToken, getGatewayRealtimeProtocols, getGatewayRealtimeTeamIdOrSlug, getGatewayTranscriptionProtocols };
|
|
3594
|
+
export { GATEWAY_AUTH_SUBPROTOCOL_PREFIX, GATEWAY_REALTIME_SUBPROTOCOL, GATEWAY_SPEECH_SUBPROTOCOL, GATEWAY_TEAM_SUBPROTOCOL_PREFIX, GATEWAY_TRANSCRIPTION_SUBPROTOCOL, GatewayAuthenticationError, GatewayError, GatewayFailedDependencyError, GatewayForbiddenError, GatewayInternalServerError, GatewayInvalidRequestError, GatewayModelNotFoundError, GatewayNotFoundError, GatewayRateLimitError, GatewayResponseError, VERSION, createGateway, createGateway as createGatewayProvider, gateway, getGatewayRealtimeAuthToken, getGatewayRealtimeProtocols, getGatewayRealtimeTeamIdOrSlug, getGatewaySpeechProtocols, getGatewayTranscriptionProtocols };
|
|
3389
3595
|
|
|
3390
3596
|
//# sourceMappingURL=index.js.map
|