@ai-sdk/gateway 4.0.121 → 4.0.123
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 +30 -0
- package/dist/index.d.ts +137 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +377 -145
- package/dist/index.js.map +1 -1
- package/docs/00-ai-gateway.mdx +93 -11
- package/package.json +3 -3
- package/src/gateway-decision-model-settings.ts +2 -0
- package/src/gateway-language-model-settings.ts +2 -0
- package/src/gateway-provider.ts +71 -7
- package/src/gateway-realtime-auth.ts +21 -3
- package/src/gateway-realtime-model-settings.ts +1 -1
- package/src/gateway-reranking-model-settings.ts +2 -0
- package/src/gateway-speech-model-settings.ts +1 -0
- package/src/gateway-speech-model.ts +305 -1
- package/src/gateway-stream-errors.ts +71 -0
- package/src/gateway-transcription-model-settings.ts +2 -0
- package/src/gateway-transcription-model.ts +18 -72
- package/src/index.ts +2 -0
- package/src/tool/parallel-search.ts +158 -46
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
|
/**
|
|
@@ -2697,39 +2890,65 @@ const exaSearchToolFactory = createProviderExecutedToolFactory({
|
|
|
2697
2890
|
const exaSearch = (config = {}) => exaSearchToolFactory(config);
|
|
2698
2891
|
//#endregion
|
|
2699
2892
|
//#region src/tool/parallel-search.ts
|
|
2893
|
+
const parallelSearchModes = [
|
|
2894
|
+
"turbo",
|
|
2895
|
+
"fast",
|
|
2896
|
+
"basic",
|
|
2897
|
+
"advanced",
|
|
2898
|
+
"one-shot",
|
|
2899
|
+
"agentic"
|
|
2900
|
+
];
|
|
2700
2901
|
const parallelSearchInputSchema = lazySchema(() => zodSchema(z.object({
|
|
2701
2902
|
objective: z.string().describe("Natural-language description of the web research goal, including source or freshness guidance and broader context from the task. Maximum 5000 characters."),
|
|
2702
|
-
search_queries: z.array(z.string()).optional().describe("
|
|
2703
|
-
mode: z.enum(
|
|
2903
|
+
search_queries: z.array(z.string()).optional().describe("Provide 2-3 concise keyword queries, 3-6 words each (max 5, 200 chars each). Strongly recommended; the search runs on these queries."),
|
|
2904
|
+
mode: z.enum(parallelSearchModes).optional().describe("Search mode: \"turbo\" and \"fast\" return quickly with shorter excerpts, \"basic\" is balanced (default), \"advanced\" is the most thorough. \"turbo\" supports English and Japanese only. \"one-shot\" and \"agentic\" are deprecated aliases of \"basic\" and \"advanced\"."),
|
|
2704
2905
|
max_results: z.number().optional().describe("Maximum number of results to return (1-20). Defaults to 10 if not specified."),
|
|
2705
2906
|
source_policy: z.object({
|
|
2706
|
-
include_domains: z.array(z.string()).optional().describe("
|
|
2707
|
-
exclude_domains: z.array(z.string()).optional().describe("
|
|
2708
|
-
after_date: z.string().optional().describe("Only include results published after this date. Use an ISO 8601 calendar date formatted YYYY-MM-DD (e.g. 2025-01-01); do not include a time.")
|
|
2907
|
+
include_domains: z.array(z.string()).optional().describe("Domains or domain/path prefixes to restrict results to, without scheme or port. Example: ['wikipedia.org', 'docs.python.org/3']."),
|
|
2908
|
+
exclude_domains: z.array(z.string()).optional().describe("Domains or domain/path prefixes to exclude, without scheme or port. Example: ['reddit.com']."),
|
|
2909
|
+
after_date: z.string().optional().describe("Only include results published on or after this date. Use an ISO 8601 calendar date formatted YYYY-MM-DD (e.g. 2025-01-01); do not include a time.")
|
|
2709
2910
|
}).optional().describe("Source policy for controlling which domains to include/exclude and freshness."),
|
|
2710
2911
|
excerpts: z.object({
|
|
2711
2912
|
max_chars_per_result: z.number().optional().describe("Maximum characters per result."),
|
|
2712
|
-
max_chars_total: z.number().optional().describe("
|
|
2913
|
+
max_chars_total: z.number().optional().describe("Deprecated: use the top-level max_chars_total.")
|
|
2713
2914
|
}).optional().describe("Excerpt configuration for controlling result length."),
|
|
2714
|
-
|
|
2915
|
+
max_chars_total: z.number().optional().describe("Maximum total characters across all result excerpts."),
|
|
2916
|
+
fetch_policy: z.object({
|
|
2917
|
+
max_age_seconds: z.number().optional().describe("Maximum age of cached content in seconds before a live fetch, minimum 600."),
|
|
2918
|
+
timeout_seconds: z.number().optional().describe("Timeout in seconds for a live fetch when content is not cached. Capped by the gateway tool timeout."),
|
|
2919
|
+
disable_cache_fallback: z.boolean().optional().describe("Return an error instead of older cached content when a live fetch fails.")
|
|
2920
|
+
}).optional().describe("Fetch policy for controlling content freshness."),
|
|
2921
|
+
location: z.string().optional().describe("ISO 3166-1 alpha-2 country code to localize results, e.g. \"us\".")
|
|
2715
2922
|
})));
|
|
2716
2923
|
const parallelSearchOutputSchema = lazySchema(() => zodSchema(z.union([z.object({
|
|
2717
|
-
|
|
2924
|
+
search_id: z.string(),
|
|
2718
2925
|
results: z.array(z.object({
|
|
2719
2926
|
url: z.string(),
|
|
2720
|
-
title: z.string(),
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2927
|
+
title: z.string().nullable().optional(),
|
|
2928
|
+
publish_date: z.string().nullable().optional(),
|
|
2929
|
+
excerpts: z.array(z.string())
|
|
2930
|
+
})),
|
|
2931
|
+
warnings: z.array(z.object({
|
|
2932
|
+
type: z.string(),
|
|
2933
|
+
message: z.string(),
|
|
2934
|
+
detail: z.record(z.string(), z.unknown()).nullable().optional()
|
|
2935
|
+
})).nullable().optional(),
|
|
2936
|
+
usage: z.array(z.object({
|
|
2937
|
+
name: z.string(),
|
|
2938
|
+
count: z.number().optional()
|
|
2939
|
+
})).nullable().optional(),
|
|
2940
|
+
session_id: z.string().optional()
|
|
2725
2941
|
}), z.object({
|
|
2726
2942
|
error: z.enum([
|
|
2727
2943
|
"api_error",
|
|
2944
|
+
"configuration_error",
|
|
2945
|
+
"execution_error",
|
|
2946
|
+
"gateway_rate_limit",
|
|
2947
|
+
"invalid_input",
|
|
2728
2948
|
"rate_limit",
|
|
2729
2949
|
"timeout",
|
|
2730
|
-
"
|
|
2731
|
-
"
|
|
2732
|
-
"unknown"
|
|
2950
|
+
"unknown",
|
|
2951
|
+
"unknown_tool"
|
|
2733
2952
|
]),
|
|
2734
2953
|
statusCode: z.number().optional(),
|
|
2735
2954
|
message: z.string()
|
|
@@ -3052,7 +3271,7 @@ async function getVercelRequestId() {
|
|
|
3052
3271
|
}
|
|
3053
3272
|
//#endregion
|
|
3054
3273
|
//#region src/version.ts
|
|
3055
|
-
const VERSION = "4.0.
|
|
3274
|
+
const VERSION = "4.0.123";
|
|
3056
3275
|
//#endregion
|
|
3057
3276
|
//#region src/gateway-provider.ts
|
|
3058
3277
|
const AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
|
|
@@ -3289,11 +3508,24 @@ function createGateway(options = {}) {
|
|
|
3289
3508
|
baseURL,
|
|
3290
3509
|
headers: getHeaders,
|
|
3291
3510
|
fetch: options.fetch,
|
|
3292
|
-
o11yHeaders: createO11yHeaders()
|
|
3511
|
+
o11yHeaders: createO11yHeaders(),
|
|
3512
|
+
webSocket: options.webSocket
|
|
3293
3513
|
});
|
|
3294
3514
|
};
|
|
3295
3515
|
provider.speechModel = createSpeechModel;
|
|
3296
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
|
+
} });
|
|
3297
3529
|
const createTranscriptionModel = (modelId) => {
|
|
3298
3530
|
return new GatewayTranscriptionModel(modelId, {
|
|
3299
3531
|
provider: "gateway",
|
|
@@ -3356,9 +3588,9 @@ async function getGatewayAuthToken(options) {
|
|
|
3356
3588
|
};
|
|
3357
3589
|
}
|
|
3358
3590
|
function assertGatewayClientSecretServerEnvironment() {
|
|
3359
|
-
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.");
|
|
3360
3592
|
}
|
|
3361
3593
|
//#endregion
|
|
3362
|
-
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 };
|
|
3363
3595
|
|
|
3364
3596
|
//# sourceMappingURL=index.js.map
|