@ai-sdk/gateway 4.0.64 → 4.0.68

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.
@@ -207,6 +207,12 @@ const { text } = await generateText({
207
207
 
208
208
  AI Gateway language models can also be used in the `streamText` function and support structured data generation with [`Output`](/docs/reference/ai-sdk-core/output) (see [AI SDK Core](/docs/ai-sdk-core)).
209
209
 
210
+ When an upstream provider reports a well-formed error after streaming has
211
+ started, AI SDK Core exposes it as a
212
+ [`StreamProviderError`](/docs/reference/ai-sdk-errors/ai-stream-provider-error).
213
+ Use its `type`, `code`, `statusCode`, and `isRetryable` metadata to classify
214
+ the failure without inspecting the Gateway's serialized error payload.
215
+
210
216
  ## Text Batches
211
217
 
212
218
  <Note type="warning">
@@ -1017,9 +1023,9 @@ The Tako Search tool supports these optional configuration options:
1017
1023
  - **includeContents** _boolean_ - Inline each page's extracted full text in `content`. **articleContentMaxChars** caps it, defaulting to 30,000 characters (maximum 1,000,000).
1018
1024
  - **sources.data** _object_ - Configure data results:
1019
1025
  - **count** _number_ - Maximum data results, 1-20. Defaults to 5.
1020
- - **includeContents** _boolean_ - Inline each card's underlying rows in `content.dataset` as typed, unit-labeled columns.
1026
+ - **includeContents** _boolean_ - Inline each card's underlying rows in `content.dataset` as typed, unit-labeled columns. This adds a data surcharge based on the row count and dataset source. To estimate cost, first search with `includeContents: false` and inspect `cards.content.export_pricing`. Because this applies to every returned card, use `count` and `maxRows` to control cost.
1021
1027
  - **contentFormat** _'json_compact' | 'json_records' | 'csv' | 'card_json'_ - Serialization for inlined card data. Defaults to `'json_compact'`.
1022
- - **maxRows** _number_ - Row cap for inlined card data. Omit to use your account's default inline cap (20 rows on the standard plan); maximum 2,000.
1028
+ - **maxRows** _number_ - Row cap per result. Omit to use the allowance in `cards.content.export_pricing`. A data surcharge applies per 1,000 exported rows; lower values reduce cost.
1023
1029
  - **nodeIds** _string[]_ - Data Graph node IDs to prioritize. Up to 20.
1024
1030
  - **strict** _boolean_ - Only return cards matching `nodeIds`. Requires at least one `nodeIds` value.
1025
1031
  - **location** _object_ - End-user `{ latitude, longitude }` coordinates for localized results.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ai-sdk/gateway",
3
3
  "private": false,
4
- "version": "4.0.64",
4
+ "version": "4.0.68",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "sideEffects": false,
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@ai-sdk/provider": "4.0.8",
34
- "@ai-sdk/provider-utils": "5.0.30",
34
+ "@ai-sdk/provider-utils": "5.0.33",
35
35
  "@vercel/oidc": "3.2.0"
36
36
  },
37
37
  "devDependencies": {
@@ -59,6 +59,11 @@ export async function asGatewayError(
59
59
  defaultMessage: 'Gateway request failed',
60
60
  cause: error,
61
61
  authMethod,
62
+ isRetryable:
63
+ error.isRetryable &&
64
+ (error.statusCode == null || error.statusCode < 400)
65
+ ? true
66
+ : undefined,
62
67
  });
63
68
  }
64
69
 
@@ -27,12 +27,14 @@ export async function createGatewayErrorFromResponse({
27
27
  defaultMessage = 'Gateway request failed',
28
28
  cause,
29
29
  authMethod,
30
+ isRetryable,
30
31
  }: {
31
32
  response: unknown;
32
33
  statusCode: number;
33
34
  defaultMessage?: string;
34
35
  cause?: unknown;
35
36
  authMethod?: 'api-key' | 'oidc';
37
+ isRetryable?: boolean;
36
38
  }): Promise<GatewayError> {
37
39
  const parseResult = await safeValidateTypes({
38
40
  value: response,
@@ -55,6 +57,7 @@ export async function createGatewayErrorFromResponse({
55
57
  validationError: parseResult.error,
56
58
  cause,
57
59
  generationId: rawGenerationId,
60
+ isRetryable,
58
61
  });
59
62
  }
60
63
 
@@ -23,6 +23,7 @@ export class GatewayResponseError extends GatewayError {
23
23
  validationError,
24
24
  cause,
25
25
  generationId,
26
+ isRetryable,
26
27
  }: {
27
28
  message?: string;
28
29
  statusCode?: number;
@@ -30,8 +31,9 @@ export class GatewayResponseError extends GatewayError {
30
31
  validationError?: TypeValidationError;
31
32
  cause?: unknown;
32
33
  generationId?: string;
34
+ isRetryable?: boolean;
33
35
  } = {}) {
34
- super({ message, statusCode, cause, generationId });
36
+ super({ message, statusCode, cause, generationId, isRetryable });
35
37
  this.response = response;
36
38
  this.validationError = validationError;
37
39
  }
@@ -13,6 +13,7 @@ export type GatewayImageModelId =
13
13
  | 'bytedance/seedream-4.5'
14
14
  | 'bytedance/seedream-5.0-lite'
15
15
  | 'bytedance/seedream-5.0-pro'
16
+ | 'meta/muse-image-1.0'
16
17
  | 'openai/gpt-image-1'
17
18
  | 'openai/gpt-image-1-mini'
18
19
  | 'openai/gpt-image-1.5'
@@ -1,5 +1,4 @@
1
1
  import {
2
- APICallError,
3
2
  type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
4
3
  type Experimental_BatchV4ItemResult as BatchV4ItemResult,
5
4
  type Experimental_BatchV4OperationOptions as BatchV4OperationOptions,
@@ -15,9 +14,10 @@ import {
15
14
  combineHeaders,
16
15
  convertAsyncIteratorToReadableStream,
17
16
  createJsonErrorResponseHandler,
17
+ createJsonLinesResponseHandler,
18
18
  createJsonResponseHandler,
19
19
  getErrorMessage,
20
- parseJSON,
20
+ normalizeBatchRequestCounts,
21
21
  postJsonToApi,
22
22
  resolve,
23
23
  WORKFLOW_SERIALIZE,
@@ -190,7 +190,7 @@ export class GatewayBatchLanguageModel
190
190
  : undefined;
191
191
 
192
192
  try {
193
- const { value: stream } = await postJsonToApi({
193
+ const { value: lines } = await postJsonToApi({
194
194
  url: this.getBatchUrl('results'),
195
195
  headers: combineHeaders(
196
196
  resolvedHeaders,
@@ -199,28 +199,9 @@ export class GatewayBatchLanguageModel
199
199
  await resolve(this.config.o11yHeaders),
200
200
  ),
201
201
  body: { batchId },
202
- successfulResponseHandler: async ({
203
- response,
204
- url,
205
- requestBodyValues,
206
- }: {
207
- url: string;
208
- requestBodyValues: unknown;
209
- response: Response;
210
- }) => {
211
- if (response.body == null) {
212
- throw new APICallError({
213
- message: 'Batch results response body is empty',
214
- url,
215
- requestBodyValues,
216
- statusCode: response.status,
217
- });
218
- }
219
- return {
220
- value: response.body,
221
- responseHeaders: Object.fromEntries([...response.headers]),
222
- };
223
- },
202
+ successfulResponseHandler: createJsonLinesResponseHandler(
203
+ gatewayBatchItemResultLineSchema,
204
+ ),
224
205
  failedResponseHandler: createJsonErrorResponseHandler({
225
206
  errorSchema: z.any(),
226
207
  errorToMessage: data => getErrorMessage(data) ?? 'unknown error',
@@ -230,7 +211,7 @@ export class GatewayBatchLanguageModel
230
211
  });
231
212
 
232
213
  return convertAsyncIteratorToReadableStream(
233
- parseGatewayBatchResultLines(stream),
214
+ convertGatewayBatchResultLines(lines),
234
215
  );
235
216
  } catch (error) {
236
217
  if (isAbortOrTimeoutError(error)) {
@@ -342,7 +323,12 @@ function convertGatewayBatchStatus(body: {
342
323
  expiresAt?: string | null;
343
324
  providerMetadata?: Record<string, Record<string, unknown>> | null;
344
325
  }): BatchV4Status {
345
- const requestCounts = convertGatewayBatchRequestCounts(body.requestCounts);
326
+ const requestCounts = normalizeBatchRequestCounts({
327
+ total: body.requestCounts?.total,
328
+ pending: body.requestCounts?.pending,
329
+ completed: body.requestCounts?.completed,
330
+ failed: body.requestCounts?.failed,
331
+ });
346
332
 
347
333
  return {
348
334
  status: body.status,
@@ -367,112 +353,27 @@ function convertGatewayBatchStatus(body: {
367
353
  }
368
354
 
369
355
  /**
370
- * The spec's `requestCounts` requires all four counters; the Gateway's
371
- * persisted descriptor allows partial counts. Only forward counts when the
372
- * full set is present rather than fabricating zeros.
373
- */
374
- function convertGatewayBatchRequestCounts(
375
- counts:
376
- | {
377
- total?: number | null;
378
- pending?: number | null;
379
- completed?: number | null;
380
- failed?: number | null;
381
- }
382
- | null
383
- | undefined,
384
- ): BatchV4Status['requestCounts'] | undefined {
385
- if (
386
- counts == null ||
387
- typeof counts.total !== 'number' ||
388
- typeof counts.pending !== 'number' ||
389
- typeof counts.completed !== 'number' ||
390
- typeof counts.failed !== 'number'
391
- ) {
392
- return undefined;
393
- }
394
-
395
- return {
396
- total: counts.total,
397
- pending: counts.pending,
398
- completed: counts.completed,
399
- failed: counts.failed,
400
- };
401
- }
402
-
403
- /**
404
- * Incremental NDJSON line splitter for the batch results stream: buffers
405
- * partial lines across chunks and flushes a trailing line without a final
406
- * newline. Each non-empty line is one `BatchV4ItemResult` JSON object.
356
+ * Converts the minimally validated Gateway batch result lines. The Gateway
357
+ * already sanitizes the complete result objects server-side.
407
358
  *
408
- * @param stream - The raw NDJSON byte stream from the batch results route.
409
- * @yields One minimally-validated `BatchV4ItemResult` per non-empty line.
359
+ * @yields Each Gateway batch item result.
410
360
  */
411
- async function* parseGatewayBatchResultLines(
412
- stream: ReadableStream<Uint8Array>,
361
+ async function* convertGatewayBatchResultLines(
362
+ lines: AsyncIterable<unknown>,
413
363
  ): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
414
- const reader = stream.getReader();
415
- const decoder = new TextDecoder();
416
- let buffer = '';
417
- let finished = false;
418
-
419
- try {
420
- while (true) {
421
- const { done, value } = await reader.read();
422
-
423
- if (done) {
424
- finished = true;
425
- buffer += decoder.decode();
426
- break;
427
- }
428
-
429
- buffer += decoder.decode(value, { stream: true });
430
-
431
- let lineEnd = buffer.indexOf('\n');
432
- while (lineEnd !== -1) {
433
- const line = buffer.slice(0, lineEnd).replace(/\r$/, '');
434
- buffer = buffer.slice(lineEnd + 1);
435
-
436
- if (line.trim().length > 0) {
437
- yield await parseGatewayBatchResultLine(line);
438
- }
439
-
440
- lineEnd = buffer.indexOf('\n');
364
+ for await (const line of lines) {
365
+ const item = line as BatchV4ItemResult<LanguageModelV4GenerateResult>;
366
+
367
+ // JSON carries `response.timestamp` as an ISO string; core expects a Date.
368
+ if (item.status === 'succeeded') {
369
+ const response = item.result?.response;
370
+ if (response !== undefined && typeof response.timestamp === 'string') {
371
+ response.timestamp = new Date(response.timestamp);
441
372
  }
442
373
  }
443
374
 
444
- const finalLine = buffer.replace(/\r$/, '');
445
- if (finalLine.trim().length > 0) {
446
- yield await parseGatewayBatchResultLine(finalLine);
447
- }
448
- } finally {
449
- if (!finished) {
450
- await reader.cancel().catch(() => {});
451
- }
452
- reader.releaseLock();
453
- }
454
- }
455
-
456
- async function parseGatewayBatchResultLine(
457
- line: string,
458
- ): Promise<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
459
- // Minimal validation (id + status); items pass through otherwise — the
460
- // Gateway already sanitizes them server-side.
461
- const parsed = await parseJSON({
462
- text: line,
463
- schema: gatewayBatchItemResultLineSchema,
464
- });
465
- const item =
466
- parsed as unknown as BatchV4ItemResult<LanguageModelV4GenerateResult>;
467
- // JSON carries `response.timestamp` as an ISO string; core expects a Date
468
- // (`GeneratedFile`-style consumers call `.toISOString()`).
469
- if (item.status === 'succeeded') {
470
- const response = item.result?.response;
471
- if (response !== undefined && typeof response.timestamp === 'string') {
472
- response.timestamp = new Date(response.timestamp);
473
- }
375
+ yield item;
474
376
  }
475
- return item;
476
377
  }
477
378
 
478
379
  const gatewayBatchItemResultLineSchema = z
@@ -26,6 +26,7 @@ export type GatewayModelId =
26
26
  | 'alibaba/qwen3.7-plus'
27
27
  | 'alibaba/qwen3.8-2.4t-a95b'
28
28
  | 'alibaba/qwen3.8-27b'
29
+ | 'alibaba/qwen3.8-flash'
29
30
  | 'alibaba/qwen3.8-max'
30
31
  | 'amazon/nova-2-lite'
31
32
  | 'amazon/nova-lite'
@@ -47,7 +48,6 @@ export type GatewayModelId =
47
48
  | 'anthropic/claude-sonnet-4.6'
48
49
  | 'anthropic/claude-sonnet-5'
49
50
  | 'arcee-ai/trinity-large-thinking'
50
- | 'arcee-ai/trinity-mini'
51
51
  | 'bytedance/seed-1.6'
52
52
  | 'bytedance/seed-1.8'
53
53
  | 'cohere/command-a'
@@ -83,6 +83,8 @@ export type GatewayModelId =
83
83
  | 'inception/mercury-2'
84
84
  | 'inception/mercury-coder-small'
85
85
  | 'inclusionai/ling-3.0-flash'
86
+ | 'inclusionai/ling-3.0-flash-fin'
87
+ | 'inclusionai/ling-3.0-flash-fin-free'
86
88
  | 'interfaze/interfaze-beta'
87
89
  | 'kwaipilot/kat-coder-air-v2.5'
88
90
  | 'kwaipilot/kat-coder-pro-v1'
@@ -103,13 +105,13 @@ export type GatewayModelId =
103
105
  | 'minimax/minimax-m2.5'
104
106
  | 'minimax/minimax-m2.5-highspeed'
105
107
  | 'minimax/minimax-m2.7'
108
+ | 'minimax/minimax-m2.7-free'
106
109
  | 'minimax/minimax-m2.7-highspeed'
107
110
  | 'minimax/minimax-m3'
111
+ | 'minimax/minimax-m3-free'
108
112
  | 'mistral/codestral'
109
113
  | 'mistral/devstral-2'
110
114
  | 'mistral/devstral-small-2'
111
- | 'mistral/magistral-medium'
112
- | 'mistral/magistral-small'
113
115
  | 'mistral/ministral-14b'
114
116
  | 'mistral/ministral-3b'
115
117
  | 'mistral/ministral-8b'
@@ -133,7 +135,6 @@ export type GatewayModelId =
133
135
  | 'nvidia/nemotron-3-super-120b-a12b'
134
136
  | 'nvidia/nemotron-3-ultra-550b-a55b'
135
137
  | 'nvidia/nemotron-3.5-lightning'
136
- | 'nvidia/nemotron-3.5-lightning-free'
137
138
  | 'nvidia/nemotron-nano-12b-v2-vl'
138
139
  | 'nvidia/nemotron-nano-9b-v2'
139
140
  | 'openai/gpt-3.5-turbo'
@@ -148,7 +149,6 @@ export type GatewayModelId =
148
149
  | 'openai/gpt-4o-fast'
149
150
  | 'openai/gpt-4o-mini'
150
151
  | 'openai/gpt-4o-mini-fast'
151
- | 'openai/gpt-4o-mini-search-preview'
152
152
  | 'openai/gpt-5'
153
153
  | 'openai/gpt-5-codex'
154
154
  | 'openai/gpt-5-fast'
@@ -184,10 +184,10 @@ export type GatewayModelId =
184
184
  | 'openai/gpt-5.6-terra-fast'
185
185
  | 'openai/gpt-oss-120b'
186
186
  | 'openai/gpt-oss-20b'
187
+ | 'openai/gpt-oss-safeguard-120b'
187
188
  | 'openai/gpt-oss-safeguard-20b'
188
189
  | 'openai/o1'
189
190
  | 'openai/o3'
190
- | 'openai/o3-deep-research'
191
191
  | 'openai/o3-fast'
192
192
  | 'openai/o3-mini'
193
193
  | 'openai/o3-pro'
@@ -235,5 +235,6 @@ export type GatewayModelId =
235
235
  | 'zai/glm-5.2'
236
236
  | 'zai/glm-5.2-fast'
237
237
  | 'zai/glm-5.3'
238
+ | 'zai/glm-5.3-flash'
238
239
  | 'zai/glm-5v-turbo'
239
240
  | (string & {});
@@ -1,6 +1,8 @@
1
1
  export type GatewayTranscriptionModelId =
2
2
  | 'fish-audio/transcribe-1'
3
3
  | 'fish-audio/transcribe-1-free'
4
+ | 'google/gemini-3.5-transcribe'
5
+ | 'google/gemini-3.5-transcribe-live'
4
6
  | 'openai/gpt-4o-mini-transcribe'
5
7
  | 'openai/gpt-4o-transcribe'
6
8
  | 'openai/gpt-realtime-whisper'
@@ -7,6 +7,7 @@ export type GatewayVideoModelId =
7
7
  | 'alibaba/wan-v2.6-t2v'
8
8
  | 'alibaba/wan-v2.7-r2v'
9
9
  | 'alibaba/wan-v2.7-t2v'
10
+ | 'alibaba/wan-v3.0-video'
10
11
  | 'bfl/flux-3-video'
11
12
  | 'bytedance/seedance-2.0'
12
13
  | 'bytedance/seedance-2.0-fast'
@@ -14,15 +14,28 @@ export type TakoContentFormat =
14
14
  | 'json_records';
15
15
 
16
16
  export interface TakoDataSourceConfig {
17
- /** Maximum number of data cards to return (1-20). */
17
+ /**
18
+ * Maximum number of data results to return (1-20). When includeContents is
19
+ * true, each additional result adds its own data surcharge.
20
+ */
18
21
  count?: number;
19
- /** Inline underlying card data in the response. This can add a data charge. */
22
+ /**
23
+ * Inline rows for each data result. This adds a data surcharge based on row
24
+ * count and dataset source. To estimate cost, search with includeContents
25
+ * disabled and inspect cards.content.export_pricing. This applies to every
26
+ * returned card; limit sources.data.count and sources.data.maxRows to control
27
+ * cost.
28
+ */
20
29
  includeContents?: boolean;
21
30
  /** Requested delivery mode for card data. Search cards are always inlined. */
22
31
  mode?: 'inline' | 'url';
23
32
  /** Serialization for inlined card data. */
24
33
  contentFormat?: TakoContentFormat;
25
- /** Maximum rows for inlined card data. The service clamps large values. */
34
+ /**
35
+ * Maximum rows to inline per result. Omit to use the allowance in
36
+ * cards.content.export_pricing. A data surcharge applies per 1,000 exported
37
+ * rows; lower values reduce cost.
38
+ */
26
39
  maxRows?: number;
27
40
  /** Data Graph node IDs to prioritize. */
28
41
  nodeIds?: string[];
@@ -190,7 +203,6 @@ export interface TakoCard {
190
203
  relevance?: 'High' | 'Low' | 'Medium' | null;
191
204
  content?: TakoResultContent | null;
192
205
  exportable?: boolean;
193
- relevance_score?: number | null;
194
206
  nodes?: Array<{
195
207
  id: string;
196
208
  type: 'entity' | 'metric';
@@ -215,7 +227,6 @@ export interface TakoWebResult {
215
227
  source_name?: string | null;
216
228
  publish_date?: string | null;
217
229
  content?: TakoResultContent | null;
218
- citation_number?: number | null;
219
230
  }
220
231
 
221
232
  export interface TakoSearchResponse {
@@ -254,11 +265,15 @@ const takoDataSourceInputSchema = z.object({
254
265
  count: z
255
266
  .number()
256
267
  .optional()
257
- .describe('Maximum number of data cards to return (1-20).'),
268
+ .describe(
269
+ 'Maximum number of data results to return (1-20). When include_contents is true, each additional result adds its own data surcharge.',
270
+ ),
258
271
  include_contents: z
259
272
  .boolean()
260
273
  .optional()
261
- .describe('Inline underlying card data. This can add a data charge.'),
274
+ .describe(
275
+ 'Inline rows for each data result. This adds a data surcharge based on row count and dataset source. To estimate cost, search with include_contents disabled and inspect cards.content.export_pricing. This applies to every returned card; limit sources.data.count and sources.data.max_rows to control cost.',
276
+ ),
262
277
  mode: z
263
278
  .enum(['inline', 'url'])
264
279
  .optional()
@@ -272,7 +287,9 @@ const takoDataSourceInputSchema = z.object({
272
287
  max_rows: z
273
288
  .number()
274
289
  .optional()
275
- .describe('Maximum rows for inlined card data.'),
290
+ .describe(
291
+ 'Maximum rows to inline per result. Omit to use the allowance in cards.content.export_pricing. A data surcharge applies per 1,000 exported rows; lower values reduce cost.',
292
+ ),
276
293
  node_ids: z
277
294
  .array(z.string())
278
295
  .optional()
@@ -493,7 +510,6 @@ const takoCardSchema = z
493
510
  relevance: z.enum(['High', 'Low', 'Medium']).nullish(),
494
511
  content: takoResultContentSchema.nullish(),
495
512
  exportable: z.boolean().optional(),
496
- relevance_score: z.number().nullish(),
497
513
  nodes: z
498
514
  .array(
499
515
  z.object({
@@ -525,7 +541,6 @@ const takoWebResultSchema = z
525
541
  source_name: z.string().nullish(),
526
542
  publish_date: z.string().nullish(),
527
543
  content: takoResultContentSchema.nullish(),
528
- citation_number: z.number().nullish(),
529
544
  })
530
545
  .passthrough();
531
546