@ai-sdk/gateway 4.0.63 → 4.0.67

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,200 @@ 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
+
216
+ ## Text Batches
217
+
218
+ <Note type="warning">
219
+ Text batch support is experimental and the API may change in patch releases.
220
+ </Note>
221
+
222
+ AI Gateway supports durable text batches through `experimental_startTextBatch`, `experimental_getBatchStatus`, and `experimental_getBatchResults`. See the [AI Gateway batch processing guide](https://vercel.com/docs/ai-gateway/models-and-providers/batch-processing) for supported models, limits, and the complete lifecycle.
223
+
224
+ Pass a publicly reachable HTTPS `webhookUrl` when starting a batch to receive a terminal notification instead of polling. AI Gateway sends `batch.completed`, `batch.failed`, or `batch.cancelled`. The event contains terminal status information but not batch results. Completion webhooks are an AI Gateway capability; direct Anthropic and OpenAI batch providers return an unsupported warning when this option is provided.
225
+
226
+ ```ts filename="start-batch.ts"
227
+ import { randomUUID } from 'node:crypto';
228
+ import {
229
+ experimental_startTextBatch as startTextBatch,
230
+ type Experimental_TextBatchReference as TextBatchReference,
231
+ type GatewayProviderMetadata,
232
+ } from 'ai';
233
+
234
+ // The receiver uses this token to look up the expected batch and signing
235
+ // secret before trusting anything in the webhook body.
236
+ const token = randomUUID();
237
+ const model = 'anthropic/claude-haiku-4.5' as const;
238
+
239
+ // Reserve the token before starting so an early delivery finds a retryable
240
+ // pending record rather than an unknown callback.
241
+ await reserveBatchWebhook(token);
242
+
243
+ const batch = await startTextBatch({
244
+ model,
245
+ requests: [
246
+ { id: 'france', prompt: 'What is the capital of France?' },
247
+ { id: 'germany', prompt: 'What is the capital of Germany?' },
248
+ ],
249
+ providerOptions: {
250
+ gateway: { idempotencyKey: token },
251
+ },
252
+ webhookUrl: `https://example.com/api/batch-webhook?token=${token}`,
253
+ });
254
+
255
+ const gatewayMetadata = batch.providerMetadata?.gateway as
256
+ | GatewayProviderMetadata
257
+ | undefined;
258
+ const signingSecret = gatewayMetadata?.asyncJob?.webhookSigningSecret;
259
+
260
+ if (typeof signingSecret !== 'string') {
261
+ throw new Error('AI Gateway did not return a webhook signing secret.');
262
+ }
263
+
264
+ // Keep sensitive provider metadata out of the receiver queue and logs.
265
+ const batchReference = {
266
+ version: batch.version,
267
+ type: batch.type,
268
+ id: batch.id,
269
+ provider: batch.provider,
270
+ modelId: batch.modelId,
271
+ } satisfies TextBatchReference;
272
+
273
+ await completeBatchWebhookReservation(token, {
274
+ batch: batchReference,
275
+ model,
276
+ signingSecret,
277
+ });
278
+ ```
279
+
280
+ The signing secret is returned only in the start response. Reserve the callback token before submitting the batch, then persist the secret, model, and minimal `batch` reference before the process exits. The stable `providerOptions.gateway.idempotencyKey` makes retrying an ambiguous start safe. Reuse the reserved token when retrying; do not generate a new one. Delete the reservation after a definitive start failure. While the reservation is still pending, the receiver should return a retryable response so an early delivery is sent again after setup completes.
281
+
282
+ The receiver must verify the `x-ai-gateway-signature` header against a bounded raw request body before trusting the event:
283
+
284
+ ```ts filename="app/api/batch-webhook/route.ts"
285
+ import { createHmac, timingSafeEqual } from 'node:crypto';
286
+
287
+ const MAX_AGE_SECONDS = 5 * 60;
288
+ const MAX_BODY_BYTES = 64 * 1024;
289
+
290
+ export async function POST(request: Request) {
291
+ const token = new URL(request.url).searchParams.get('token');
292
+ if (token == null) return new Response(null, { status: 401 });
293
+
294
+ const record = await loadBatchWebhook(token);
295
+ if (record == null) return new Response(null, { status: 401 });
296
+ if (record.state !== 'ready') return new Response(null, { status: 503 });
297
+
298
+ const declaredLength = request.headers.get('content-length');
299
+ if (declaredLength != null && Number(declaredLength) > MAX_BODY_BYTES) {
300
+ return new Response(null, { status: 413 });
301
+ }
302
+
303
+ const rawBody = await readBodyWithLimit(request, MAX_BODY_BYTES);
304
+ if (rawBody == null) return new Response(null, { status: 413 });
305
+
306
+ const signature = request.headers.get('x-ai-gateway-signature');
307
+ if (
308
+ signature == null ||
309
+ !verifySignature({
310
+ header: signature,
311
+ rawBody,
312
+ secret: record.signingSecret,
313
+ })
314
+ ) {
315
+ return new Response(null, { status: 401 });
316
+ }
317
+
318
+ let event: { data?: { jobId?: string; status?: string } };
319
+ try {
320
+ event = JSON.parse(rawBody);
321
+ } catch {
322
+ return new Response(null, { status: 400 });
323
+ }
324
+
325
+ const jobId = event.data?.jobId;
326
+ const status = event.data?.status;
327
+ if (
328
+ jobId !== record.batch.id ||
329
+ (status !== 'cancelled' && status !== 'completed' && status !== 'failed')
330
+ ) {
331
+ return new Response(null, { status: 401 });
332
+ }
333
+
334
+ // The header is not signed, so require it to match the signed event body.
335
+ const deliveryId = `${jobId}-${status}`;
336
+ if (request.headers.get('x-ai-gateway-idempotency-key') !== deliveryId) {
337
+ return new Response(null, { status: 401 });
338
+ }
339
+
340
+ await enqueueBatchResultFetch({
341
+ batch: record.batch,
342
+ deliveryId,
343
+ model: record.model,
344
+ });
345
+
346
+ return new Response(null, { status: 204 });
347
+ }
348
+
349
+ async function readBodyWithLimit(request: Request, maxBytes: number) {
350
+ if (request.body == null) return '';
351
+
352
+ const reader = request.body.getReader();
353
+ const decoder = new TextDecoder();
354
+ let body = '';
355
+ let size = 0;
356
+
357
+ try {
358
+ while (true) {
359
+ const { done, value } = await reader.read();
360
+ if (done) return body + decoder.decode();
361
+
362
+ size += value.byteLength;
363
+ if (size > maxBytes) {
364
+ await reader.cancel();
365
+ return undefined;
366
+ }
367
+
368
+ body += decoder.decode(value, { stream: true });
369
+ }
370
+ } finally {
371
+ reader.releaseLock();
372
+ }
373
+ }
374
+
375
+ function verifySignature({
376
+ header,
377
+ rawBody,
378
+ secret,
379
+ }: {
380
+ header: string;
381
+ rawBody: string;
382
+ secret: string;
383
+ }) {
384
+ const timestamp = header.match(/(?:^|,)t=(\d+)/)?.[1];
385
+ const digest = header.match(/(?:^|,)v1=([0-9a-f]{64})(?:,|$)/)?.[1];
386
+ if (timestamp == null || digest == null) return false;
387
+
388
+ const expected = createHmac('sha256', secret)
389
+ .update(`${timestamp}.${rawBody}`, 'utf8')
390
+ .digest('hex');
391
+ const provided = Buffer.from(digest, 'hex');
392
+ const computed = Buffer.from(expected, 'hex');
393
+ if (provided.length !== computed.length) return false;
394
+ if (!timingSafeEqual(provided, computed)) return false;
395
+
396
+ return Math.abs(Date.now() / 1000 - Number(timestamp)) <= MAX_AGE_SECONDS;
397
+ }
398
+ ```
399
+
400
+ The signature header has the form `t=<unix seconds>,v1=<hex digest>`, where `v1` is the HMAC-SHA256 digest of `"<t>.<raw body>"`. Verify a size-limited raw request body rather than a re-serialized object, use a timing-safe comparison, reject stale timestamps, and reject events whose `data.jobId` does not match the persisted `batch.id`.
401
+
402
+ AI Gateway expects a 2xx response within 10 seconds and retries failed deliveries. It does not follow redirects, so the callback endpoint itself must return the 2xx response. Retries carry the same `x-ai-gateway-idempotency-key` value (`<jobId>-<status>`), which receivers can use for deduplication. After verification, pass the persisted model and `batch` reference to `experimental_getBatchStatus` and `experimental_getBatchResults`. Delivery is best-effort, so periodically reconcile persisted batches by status as a fallback for a notification that exhausts its retries.
403
+
210
404
  ## Reranking Models
211
405
 
212
406
  You can create reranking models using the `rerankingModel` method on the provider instance:
@@ -829,9 +1023,9 @@ The Tako Search tool supports these optional configuration options:
829
1023
  - **includeContents** _boolean_ - Inline each page's extracted full text in `content`. **articleContentMaxChars** caps it, defaulting to 30,000 characters (maximum 1,000,000).
830
1024
  - **sources.data** _object_ - Configure data results:
831
1025
  - **count** _number_ - Maximum data results, 1-20. Defaults to 5.
832
- - **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.
833
1027
  - **contentFormat** _'json_compact' | 'json_records' | 'csv' | 'card_json'_ - Serialization for inlined card data. Defaults to `'json_compact'`.
834
- - **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.
835
1029
  - **nodeIds** _string[]_ - Data Graph node IDs to prioritize. Up to 20.
836
1030
  - **strict** _boolean_ - Only return cards matching `nodeIds`. Requires at least one `nodeIds` value.
837
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.63",
4
+ "version": "4.0.67",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "sideEffects": false,
@@ -30,18 +30,18 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@vercel/oidc": "3.2.0",
34
- "@ai-sdk/provider": "4.0.7",
35
- "@ai-sdk/provider-utils": "5.0.29"
33
+ "@ai-sdk/provider": "4.0.8",
34
+ "@ai-sdk/provider-utils": "5.0.32",
35
+ "@vercel/oidc": "3.2.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "@ai-sdk/test-server": "2.0.1",
38
39
  "@types/node": "22.19.19",
40
+ "@vercel/ai-tsconfig": "0.0.0",
39
41
  "tsup": "^8.5.1",
40
42
  "tsx": "4.23.12",
41
43
  "typescript": "5.8.3",
42
- "zod": "3.25.76",
43
- "@ai-sdk/test-server": "2.0.1",
44
- "@vercel/ai-tsconfig": "0.0.0"
44
+ "zod": "3.25.76"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "zod": "^3.25.76 || ^4.1.8"
@@ -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,
@@ -63,6 +63,7 @@ export class GatewayBatchLanguageModel
63
63
  providerOptions,
64
64
  headers,
65
65
  abortSignal,
66
+ webhookUrl,
66
67
  }: BatchV4StartOptions<LanguageModelV4BatchRequest>): Promise<BatchV4StartResult> {
67
68
  const resolvedHeaders = this.config.headers
68
69
  ? await resolve(this.config.headers)
@@ -84,6 +85,7 @@ export class GatewayBatchLanguageModel
84
85
  : undefined,
85
86
  ),
86
87
  body: {
88
+ ...(webhookUrl != null && { callbackUrl: webhookUrl }),
87
89
  modelId: this.modelId,
88
90
  requests: requests.map(request => ({
89
91
  id: request.id,
@@ -188,7 +190,7 @@ export class GatewayBatchLanguageModel
188
190
  : undefined;
189
191
 
190
192
  try {
191
- const { value: stream } = await postJsonToApi({
193
+ const { value: lines } = await postJsonToApi({
192
194
  url: this.getBatchUrl('results'),
193
195
  headers: combineHeaders(
194
196
  resolvedHeaders,
@@ -197,28 +199,9 @@ export class GatewayBatchLanguageModel
197
199
  await resolve(this.config.o11yHeaders),
198
200
  ),
199
201
  body: { batchId },
200
- successfulResponseHandler: async ({
201
- response,
202
- url,
203
- requestBodyValues,
204
- }: {
205
- url: string;
206
- requestBodyValues: unknown;
207
- response: Response;
208
- }) => {
209
- if (response.body == null) {
210
- throw new APICallError({
211
- message: 'Batch results response body is empty',
212
- url,
213
- requestBodyValues,
214
- statusCode: response.status,
215
- });
216
- }
217
- return {
218
- value: response.body,
219
- responseHeaders: Object.fromEntries([...response.headers]),
220
- };
221
- },
202
+ successfulResponseHandler: createJsonLinesResponseHandler(
203
+ gatewayBatchItemResultLineSchema,
204
+ ),
222
205
  failedResponseHandler: createJsonErrorResponseHandler({
223
206
  errorSchema: z.any(),
224
207
  errorToMessage: data => getErrorMessage(data) ?? 'unknown error',
@@ -228,7 +211,7 @@ export class GatewayBatchLanguageModel
228
211
  });
229
212
 
230
213
  return convertAsyncIteratorToReadableStream(
231
- parseGatewayBatchResultLines(stream),
214
+ convertGatewayBatchResultLines(lines),
232
215
  );
233
216
  } catch (error) {
234
217
  if (isAbortOrTimeoutError(error)) {
@@ -340,7 +323,12 @@ function convertGatewayBatchStatus(body: {
340
323
  expiresAt?: string | null;
341
324
  providerMetadata?: Record<string, Record<string, unknown>> | null;
342
325
  }): BatchV4Status {
343
- 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
+ });
344
332
 
345
333
  return {
346
334
  status: body.status,
@@ -365,112 +353,27 @@ function convertGatewayBatchStatus(body: {
365
353
  }
366
354
 
367
355
  /**
368
- * The spec's `requestCounts` requires all four counters; the Gateway's
369
- * persisted descriptor allows partial counts. Only forward counts when the
370
- * full set is present rather than fabricating zeros.
371
- */
372
- function convertGatewayBatchRequestCounts(
373
- counts:
374
- | {
375
- total?: number | null;
376
- pending?: number | null;
377
- completed?: number | null;
378
- failed?: number | null;
379
- }
380
- | null
381
- | undefined,
382
- ): BatchV4Status['requestCounts'] | undefined {
383
- if (
384
- counts == null ||
385
- typeof counts.total !== 'number' ||
386
- typeof counts.pending !== 'number' ||
387
- typeof counts.completed !== 'number' ||
388
- typeof counts.failed !== 'number'
389
- ) {
390
- return undefined;
391
- }
392
-
393
- return {
394
- total: counts.total,
395
- pending: counts.pending,
396
- completed: counts.completed,
397
- failed: counts.failed,
398
- };
399
- }
400
-
401
- /**
402
- * Incremental NDJSON line splitter for the batch results stream: buffers
403
- * partial lines across chunks and flushes a trailing line without a final
404
- * 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.
405
358
  *
406
- * @param stream - The raw NDJSON byte stream from the batch results route.
407
- * @yields One minimally-validated `BatchV4ItemResult` per non-empty line.
359
+ * @yields Each Gateway batch item result.
408
360
  */
409
- async function* parseGatewayBatchResultLines(
410
- stream: ReadableStream<Uint8Array>,
361
+ async function* convertGatewayBatchResultLines(
362
+ lines: AsyncIterable<unknown>,
411
363
  ): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
412
- const reader = stream.getReader();
413
- const decoder = new TextDecoder();
414
- let buffer = '';
415
- let finished = false;
416
-
417
- try {
418
- while (true) {
419
- const { done, value } = await reader.read();
420
-
421
- if (done) {
422
- finished = true;
423
- buffer += decoder.decode();
424
- break;
425
- }
426
-
427
- buffer += decoder.decode(value, { stream: true });
428
-
429
- let lineEnd = buffer.indexOf('\n');
430
- while (lineEnd !== -1) {
431
- const line = buffer.slice(0, lineEnd).replace(/\r$/, '');
432
- buffer = buffer.slice(lineEnd + 1);
433
-
434
- if (line.trim().length > 0) {
435
- yield await parseGatewayBatchResultLine(line);
436
- }
437
-
438
- 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);
439
372
  }
440
373
  }
441
374
 
442
- const finalLine = buffer.replace(/\r$/, '');
443
- if (finalLine.trim().length > 0) {
444
- yield await parseGatewayBatchResultLine(finalLine);
445
- }
446
- } finally {
447
- if (!finished) {
448
- await reader.cancel().catch(() => {});
449
- }
450
- reader.releaseLock();
451
- }
452
- }
453
-
454
- async function parseGatewayBatchResultLine(
455
- line: string,
456
- ): Promise<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
457
- // Minimal validation (id + status); items pass through otherwise — the
458
- // Gateway already sanitizes them server-side.
459
- const parsed = await parseJSON({
460
- text: line,
461
- schema: gatewayBatchItemResultLineSchema,
462
- });
463
- const item =
464
- parsed as unknown as BatchV4ItemResult<LanguageModelV4GenerateResult>;
465
- // JSON carries `response.timestamp` as an ISO string; core expects a Date
466
- // (`GeneratedFile`-style consumers call `.toISOString()`).
467
- if (item.status === 'succeeded') {
468
- const response = item.result?.response;
469
- if (response !== undefined && typeof response.timestamp === 'string') {
470
- response.timestamp = new Date(response.timestamp);
471
- }
375
+ yield item;
472
376
  }
473
- return item;
474
377
  }
475
378
 
476
379
  const gatewayBatchItemResultLineSchema = z
@@ -47,7 +47,6 @@ export type GatewayModelId =
47
47
  | 'anthropic/claude-sonnet-4.6'
48
48
  | 'anthropic/claude-sonnet-5'
49
49
  | 'arcee-ai/trinity-large-thinking'
50
- | 'arcee-ai/trinity-mini'
51
50
  | 'bytedance/seed-1.6'
52
51
  | 'bytedance/seed-1.8'
53
52
  | 'cohere/command-a'
@@ -103,13 +102,13 @@ export type GatewayModelId =
103
102
  | 'minimax/minimax-m2.5'
104
103
  | 'minimax/minimax-m2.5-highspeed'
105
104
  | 'minimax/minimax-m2.7'
105
+ | 'minimax/minimax-m2.7-free'
106
106
  | 'minimax/minimax-m2.7-highspeed'
107
107
  | 'minimax/minimax-m3'
108
+ | 'minimax/minimax-m3-free'
108
109
  | 'mistral/codestral'
109
110
  | 'mistral/devstral-2'
110
111
  | 'mistral/devstral-small-2'
111
- | 'mistral/magistral-medium'
112
- | 'mistral/magistral-small'
113
112
  | 'mistral/ministral-14b'
114
113
  | 'mistral/ministral-3b'
115
114
  | 'mistral/ministral-8b'
@@ -133,7 +132,6 @@ export type GatewayModelId =
133
132
  | 'nvidia/nemotron-3-super-120b-a12b'
134
133
  | 'nvidia/nemotron-3-ultra-550b-a55b'
135
134
  | 'nvidia/nemotron-3.5-lightning'
136
- | 'nvidia/nemotron-3.5-lightning-free'
137
135
  | 'nvidia/nemotron-nano-12b-v2-vl'
138
136
  | 'nvidia/nemotron-nano-9b-v2'
139
137
  | 'openai/gpt-3.5-turbo'
@@ -148,7 +146,6 @@ export type GatewayModelId =
148
146
  | 'openai/gpt-4o-fast'
149
147
  | 'openai/gpt-4o-mini'
150
148
  | 'openai/gpt-4o-mini-fast'
151
- | 'openai/gpt-4o-mini-search-preview'
152
149
  | 'openai/gpt-5'
153
150
  | 'openai/gpt-5-codex'
154
151
  | 'openai/gpt-5-fast'
@@ -184,10 +181,10 @@ export type GatewayModelId =
184
181
  | 'openai/gpt-5.6-terra-fast'
185
182
  | 'openai/gpt-oss-120b'
186
183
  | 'openai/gpt-oss-20b'
184
+ | 'openai/gpt-oss-safeguard-120b'
187
185
  | 'openai/gpt-oss-safeguard-20b'
188
186
  | 'openai/o1'
189
187
  | 'openai/o3'
190
- | 'openai/o3-deep-research'
191
188
  | 'openai/o3-fast'
192
189
  | 'openai/o3-mini'
193
190
  | 'openai/o3-pro'
@@ -235,5 +232,6 @@ export type GatewayModelId =
235
232
  | 'zai/glm-5.2'
236
233
  | 'zai/glm-5.2-fast'
237
234
  | 'zai/glm-5.3'
235
+ | 'zai/glm-5.3-flash'
238
236
  | 'zai/glm-5v-turbo'
239
237
  | (string & {});
@@ -0,0 +1,27 @@
1
+ import type { JSONValue } from '@ai-sdk/provider';
2
+
3
+ /**
4
+ * Metadata for an asynchronous AI Gateway job.
5
+ */
6
+ export type GatewayAsyncJobMetadata = {
7
+ readonly jobId: string;
8
+ readonly status: string;
9
+
10
+ /**
11
+ * Secret for verifying customer webhook deliveries. This is sensitive,
12
+ * start-response-only metadata and must not be logged or forwarded.
13
+ */
14
+ readonly webhookSigningSecret?: string;
15
+ readonly [key: string]: JSONValue | undefined;
16
+ };
17
+
18
+ /**
19
+ * Shape of the inner `providerMetadata.gateway` object returned by AI Gateway
20
+ * operations.
21
+ *
22
+ * Additional fields may be added without a package update.
23
+ */
24
+ export type GatewayProviderMetadata = {
25
+ readonly asyncJob?: GatewayAsyncJobMetadata;
26
+ readonly [key: string]: JSONValue | undefined;
27
+ };
@@ -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'
package/src/index.ts CHANGED
@@ -41,6 +41,10 @@ export type {
41
41
  GatewayProvider,
42
42
  GatewayProviderSettings,
43
43
  } from './gateway-provider';
44
+ export type {
45
+ GatewayAsyncJobMetadata,
46
+ GatewayProviderMetadata,
47
+ } from './gateway-provider-metadata';
44
48
  export type {
45
49
  GatewayProviderOptions,
46
50
  /** @deprecated Use `GatewayProviderOptions` instead. */
@@ -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