ai 7.0.111 → 7.0.113

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/index.d.ts +27 -0
  3. package/dist/index.js +345 -222
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/index.d.ts +2 -1
  6. package/dist/internal/index.js +125 -31
  7. package/dist/internal/index.js.map +1 -1
  8. package/docs/03-agents/06-tool-approvals.mdx +16 -0
  9. package/docs/03-ai-sdk-core/37-speech.mdx +2 -0
  10. package/docs/03-ai-sdk-core/40-middleware.mdx +92 -7
  11. package/docs/06-advanced/02-stopping-streams.mdx +8 -0
  12. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +3 -3
  13. package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +1 -1
  14. package/docs/07-reference/01-ai-sdk-core/32-validate-ui-messages.mdx +18 -0
  15. package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +9 -3
  16. package/package.json +12 -12
  17. package/src/embed/embed-many.ts +18 -2
  18. package/src/generate-speech/generate-speech.ts +15 -4
  19. package/src/generate-speech/generated-audio-file.ts +0 -8
  20. package/src/generate-text/execute-tools-from-stream.ts +0 -2
  21. package/src/generate-text/generate-text.ts +1 -0
  22. package/src/generate-text/generated-file.ts +0 -8
  23. package/src/generate-text/invoke-tool-callbacks-from-stream.ts +9 -8
  24. package/src/generate-text/output.ts +0 -2
  25. package/src/generate-text/parse-tool-call.ts +38 -25
  26. package/src/generate-text/prune-messages.ts +3 -1
  27. package/src/generate-text/stream-text.ts +1 -0
  28. package/src/generate-text/to-response-messages.ts +7 -0
  29. package/src/generate-text/tool-call.ts +26 -0
  30. package/src/generate-text/validate-tool-approvals.ts +39 -3
  31. package/src/generate-video/generate-video.ts +0 -2
  32. package/src/middleware/extract-reasoning-middleware.ts +1 -1
  33. package/src/middleware/wrap-embedding-model.ts +9 -1
  34. package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
  35. package/src/prompt/content-part.ts +3 -0
  36. package/src/prompt/convert-to-language-model-prompt.ts +12 -2
  37. package/src/prompt/file-part-data.ts +11 -1
  38. package/src/registry/custom-provider.ts +12 -5
  39. package/src/ui/chat.ts +108 -17
  40. package/src/ui/convert-to-model-messages.ts +8 -0
  41. package/src/ui/direct-chat-transport.ts +2 -0
  42. package/src/ui/process-ui-message-stream.ts +6 -0
  43. package/src/ui/ui-messages.ts +10 -0
  44. package/src/ui/validate-ui-messages.ts +100 -134
  45. package/src/ui-message-stream/handle-ui-message-stream-finish.ts +5 -3
  46. package/src/ui-message-stream/to-ui-message-chunk.ts +7 -0
  47. package/src/ui-message-stream/ui-message-chunks.ts +2 -0
  48. package/src/ui-message-stream/ui-message-stream-on-end-callback.ts +8 -0
  49. package/src/ui-message-stream/ui-message-stream-outcome.ts +4 -0
  50. package/src/util/write-to-server-response.ts +0 -2
@@ -272,6 +272,22 @@ When a manual approval status includes a reason, it is available as
272
272
  `part.approval.requestReason`. A reason supplied with
273
273
  `addToolApprovalResponse` is stored separately as `part.approval.reason`.
274
274
 
275
+ ### Schema transforms and persisted approvals
276
+
277
+ Approval requests preserve the original schema input in `inputSchemaInput` when
278
+ it differs from the input presented for approval. Keep this field when persisting
279
+ `responseMessages` or UI messages; the UI message conversion functions preserve it
280
+ automatically.
281
+
282
+ On continuation, the SDK reconstructs the transformed input and checks that it
283
+ matches the approved input. It never replaces the approved input with a different
284
+ value. Older or projected histories that omit the original input are rejected as
285
+ invalid tool input if revalidation fails or would change the approved value.
286
+
287
+ The original input is included in persisted messages and UI approval streams,
288
+ including fields removed by schema transforms. A transform that removes a field
289
+ does not redact it from approval metadata.
290
+
275
291
  ## Security Considerations
276
292
 
277
293
  ### Trust model
@@ -147,6 +147,8 @@ try {
147
147
  | [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-2.5-flash-preview-tts` |
148
148
  | [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-2.5-pro-preview-tts` |
149
149
  | [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.1-flash-tts-preview` |
150
+ | [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.8-flash-tts` |
151
+ | [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.8-flash-lite-tts` |
150
152
  | [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-flash-tts` |
151
153
  | [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-pro-tts` |
152
154
  | [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-flash-lite-preview-tts` |
@@ -310,6 +310,9 @@ You can implement any of the following three function to modify the behavior of
310
310
  3. `wrapStream`: Wraps the `doStream` method of the [language model](https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v4/language-model-v4.ts).
311
311
  You can modify the parameters, call the language model, and modify the result.
312
312
 
313
+ Every `LanguageModelV4Middleware` object must set
314
+ `specificationVersion: 'v4'`.
315
+
313
316
  Here are some examples of how to implement language model middleware:
314
317
 
315
318
  ## Examples
@@ -330,6 +333,8 @@ import type {
330
333
  } from '@ai-sdk/provider';
331
334
 
332
335
  export const yourLogMiddleware: LanguageModelV4Middleware = {
336
+ specificationVersion: 'v4',
337
+
333
338
  wrapGenerate: async ({ doGenerate, params }) => {
334
339
  console.log('doGenerate called');
335
340
  console.log(`params: ${JSON.stringify(params, null, 2)}`);
@@ -407,6 +412,8 @@ import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
407
412
  const cache = new Map<string, any>();
408
413
 
409
414
  export const yourCacheMiddleware: LanguageModelV4Middleware = {
415
+ specificationVersion: 'v4',
416
+
410
417
  wrapGenerate: async ({ doGenerate, params }) => {
411
418
  const cacheKey = JSON.stringify(params);
412
419
 
@@ -439,6 +446,8 @@ This example shows how to use RAG as middleware.
439
446
  import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
440
447
 
441
448
  export const yourRagMiddleware: LanguageModelV4Middleware = {
449
+ specificationVersion: 'v4',
450
+
442
451
  transformParams: async ({ params }) => {
443
452
  const lastUserMessageText = getLastUserMessageText({
444
453
  prompt: params.prompt,
@@ -465,28 +474,102 @@ Guard rails are a way to ensure that the generated text of a language model call
465
474
  is safe and appropriate. This example shows how to use guardrails as middleware.
466
475
 
467
476
  ```ts
468
- import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
477
+ import type {
478
+ LanguageModelV4Middleware,
479
+ LanguageModelV4StreamPart,
480
+ } from '@ai-sdk/provider';
481
+
482
+ const redactText = (text: string) => text.replace(/badword/g, '<REDACTED>');
469
483
 
470
484
  export const yourGuardrailMiddleware: LanguageModelV4Middleware = {
485
+ specificationVersion: 'v4',
486
+
471
487
  wrapGenerate: async ({ doGenerate }) => {
472
488
  const result = await doGenerate();
473
489
 
474
490
  // filtering approach, e.g. for PII or other sensitive information:
475
491
  const content = result.content.map(part =>
476
- part.type === 'text'
477
- ? { ...part, text: part.text.replace(/badword/g, '<REDACTED>') }
478
- : part,
492
+ part.type === 'text' ? { ...part, text: redactText(part.text) } : part,
479
493
  );
480
494
 
481
495
  return { ...result, content };
482
496
  },
483
497
 
484
- // here you would implement the guardrail logic for streaming
485
- // Note: streaming guardrails are difficult to implement, because
486
- // you do not know the full content of the stream until it's finished.
498
+ wrapStream: async ({ doStream }) => {
499
+ const { stream, ...rest } = await doStream();
500
+
501
+ // Keep a separate buffer for each text block in the stream.
502
+ const buffers = new Map<string, string>();
503
+
504
+ const transformStream = new TransformStream<
505
+ LanguageModelV4StreamPart,
506
+ LanguageModelV4StreamPart
507
+ >({
508
+ transform(chunk, controller) {
509
+ if (chunk.type === 'text-start') {
510
+ buffers.set(chunk.id, '');
511
+ controller.enqueue(chunk);
512
+ return;
513
+ }
514
+
515
+ if (chunk.type === 'text-delta') {
516
+ buffers.set(chunk.id, (buffers.get(chunk.id) ?? '') + chunk.delta);
517
+ return;
518
+ }
519
+
520
+ if (chunk.type === 'text-end') {
521
+ const bufferedText = buffers.get(chunk.id);
522
+
523
+ if (bufferedText != null) {
524
+ const redactedText = redactText(bufferedText);
525
+
526
+ if (redactedText) {
527
+ controller.enqueue({
528
+ type: 'text-delta',
529
+ id: chunk.id,
530
+ delta: redactedText,
531
+ });
532
+ }
533
+
534
+ buffers.delete(chunk.id);
535
+ }
536
+ }
537
+
538
+ controller.enqueue(chunk);
539
+ },
540
+
541
+ flush(controller) {
542
+ for (const [id, bufferedText] of buffers) {
543
+ const redactedText = redactText(bufferedText);
544
+
545
+ if (redactedText) {
546
+ controller.enqueue({
547
+ type: 'text-delta',
548
+ id,
549
+ delta: redactedText,
550
+ });
551
+ }
552
+ }
553
+ },
554
+ });
555
+
556
+ return {
557
+ stream: stream.pipeThrough(transformStream),
558
+ ...rest,
559
+ };
560
+ },
487
561
  };
488
562
  ```
489
563
 
564
+ <Note>
565
+ The streaming example buffers each text block until `text-end` so matches
566
+ split across `text-delta` chunks cannot leak through. This delays output and
567
+ uses memory proportional to the text block size. Do not redact each delta
568
+ independently. An incremental implementation must retain every possible
569
+ incomplete match, and a fixed-size buffer alone is not safe for unbounded
570
+ variable-length patterns.
571
+ </Note>
572
+
490
573
  ## Configuring Per Request Custom Metadata
491
574
 
492
575
  To send and access custom metadata in Middleware, you can use `providerOptions`. This is useful when building logging middleware where you want to pass additional context like user IDs, timestamps, or other contextual data that can help with tracking and debugging.
@@ -497,6 +580,8 @@ __PROVIDER_IMPORT__;
497
580
  import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
498
581
 
499
582
  export const yourLogMiddleware: LanguageModelV4Middleware = {
583
+ specificationVersion: 'v4',
584
+
500
585
  wrapGenerate: async ({ doGenerate, params }) => {
501
586
  console.log('METADATA', params?.providerMetadata?.yourLogMiddleware);
502
587
  const result = await doGenerate();
@@ -205,6 +205,14 @@ export async function POST(req: Request) {
205
205
 
206
206
  The `consumeStream` function is necessary for proper abort handling in UI message streams. It ensures that the stream is properly consumed even when aborted, preventing potential memory leaks or hanging connections.
207
207
 
208
+ The `onEnd` callback distinguishes consumer cancellation from an observed
209
+ abort. When the consumer cancels the UI message stream before an outcome is
210
+ declared, such as during a client disconnect, `isCancelled` is `true`,
211
+ `outcome.status` remains `'unknown'`, and `isAborted` remains `false`. When the
212
+ stream observes an `abort` part first, `outcome.status` is `'aborted'`,
213
+ `isAborted` is `true`, and `isCancelled` is absent. Check both flags when the
214
+ same cleanup should run for either case.
215
+
208
216
  ## AI SDK RSC
209
217
 
210
218
  <Note type="warning">
@@ -4082,13 +4082,13 @@ To see `streamText` in action, check out [these examples](#examples).
4082
4082
  },
4083
4083
  {
4084
4084
  name: 'onEnd',
4085
- type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
4085
+ type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
4086
4086
  isOptional: true,
4087
- description: 'Callback function called when the stream ends. Provides the updated messages, continuation and abort state, model finish reason, and operation-level outcome.',
4087
+ description: 'Callback function called when the stream ends. Provides the updated messages, continuation, abort and consumer-cancellation state, model finish reason, and operation-level outcome.',
4088
4088
  },
4089
4089
  {
4090
4090
  name: 'onFinish',
4091
- type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
4091
+ type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
4092
4092
  isOptional: true,
4093
4093
  description: 'Deprecated alias for `onEnd`.',
4094
4094
  },
@@ -79,7 +79,7 @@ const { audio } = await generateSpeech({
79
79
  type: 'string',
80
80
  isOptional: true,
81
81
  description:
82
- 'The output format to use for the speech e.g. "mp3", "wav", etc.',
82
+ 'The output format to use for the speech, such as "mp3", "wav", or headerless "audio/l16", "audio/mulaw", and "audio/alaw". Supported formats and defaults vary by provider and model.',
83
83
  },
84
84
  {
85
85
  name: 'instructions',
@@ -100,6 +100,24 @@ const validatedMessages = await validateUIMessages({
100
100
  });
101
101
  ```
102
102
 
103
+ When validating approval messages produced with
104
+ `experimental_refineToolInput`, pass the same refinement functions so
105
+ `validateUIMessages` can reconstruct and verify the approved input:
106
+
107
+ ```typescript
108
+ const experimental_refineToolInput = {
109
+ weather: (input: { location: string }) => ({
110
+ location: input.location.trim(),
111
+ }),
112
+ };
113
+
114
+ const validatedMessages = await validateUIMessages({
115
+ messages,
116
+ tools,
117
+ experimental_refineToolInput,
118
+ });
119
+ ```
120
+
103
121
  ## Deprecated `rawInput` field
104
122
 
105
123
  For backward compatibility, validation still accepts `rawInput` on tool parts
@@ -134,7 +134,7 @@ outcomes and call `setOutcome` once.
134
134
  },
135
135
  {
136
136
  name: 'onEnd',
137
- type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
137
+ type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
138
138
  description: 'A callback function that is called when the stream ends.',
139
139
  properties: [
140
140
  {
@@ -156,11 +156,17 @@ outcomes and call `setOutcome` once.
156
156
  type: 'boolean',
157
157
  description: 'Indicates whether the stream was aborted.',
158
158
  },
159
+ {
160
+ name: 'isCancelled',
161
+ type: 'true | undefined',
162
+ description:
163
+ 'Present and true when the consumer cancelled the stream before an outcome was declared, for example because the client disconnected.',
164
+ },
159
165
  {
160
166
  name: 'outcome',
161
167
  type: "UIMessageStreamOutcome = { status: 'completed' } | { status: 'failed'; error?: unknown } | { status: 'aborted' } | { status: 'unknown' }",
162
168
  description:
163
- 'The operation-level outcome of the stream. It reflects the stream owner declaration unless a fatal stream-processing failure occurs, and is separate from model finish reasons and individual error chunks.',
169
+ "The operation-level outcome of the stream. It reflects the stream owner declaration unless a fatal stream-processing failure occurs, and is separate from model finish reasons and individual error chunks. It remains 'unknown' when the consumer cancels before an outcome is declared; check isCancelled to distinguish that case from normal closure without a declared outcome.",
164
170
  },
165
171
  {
166
172
  name: 'responseMessage',
@@ -180,7 +186,7 @@ outcomes and call `setOutcome` once.
180
186
  },
181
187
  {
182
188
  name: 'onFinish',
183
- type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
189
+ type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
184
190
  description: 'Deprecated alias for `onEnd`.',
185
191
  },
186
192
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.111",
3
+ "version": "7.0.113",
4
4
  "type": "module",
5
5
  "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
6
6
  "license": "Apache-2.0",
@@ -42,20 +42,20 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.89",
46
- "@ai-sdk/provider": "4.0.17",
47
- "@ai-sdk/provider-utils": "5.0.45"
45
+ "@ai-sdk/gateway": "4.0.91",
46
+ "@ai-sdk/provider": "4.0.18",
47
+ "@ai-sdk/provider-utils": "5.0.47"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.91",
51
- "@ai-sdk/deepseek": "3.0.50",
52
- "@ai-sdk/google": "4.0.77",
53
- "@ai-sdk/groq": "4.0.46",
54
- "@ai-sdk/huggingface": "2.0.53",
55
- "@ai-sdk/moonshotai": "3.0.54",
56
- "@ai-sdk/openai": "4.0.72",
50
+ "@ai-sdk/amazon-bedrock": "5.0.93",
51
+ "@ai-sdk/deepseek": "3.0.52",
52
+ "@ai-sdk/google": "4.0.79",
53
+ "@ai-sdk/groq": "4.0.48",
54
+ "@ai-sdk/huggingface": "2.0.55",
55
+ "@ai-sdk/moonshotai": "3.0.56",
56
+ "@ai-sdk/openai": "4.0.74",
57
57
  "@ai-sdk/test-server": "2.0.1",
58
- "@ai-sdk/xai": "5.0.5",
58
+ "@ai-sdk/xai": "5.0.7",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -7,6 +7,7 @@ import {
7
7
  } from '@ai-sdk/provider-utils';
8
8
  import { logWarnings } from '../logger/log-warnings';
9
9
  import { getEmbeddingModelMaxInputBytesPerCall } from '../model/get-embedding-model-max-input-bytes-per-call';
10
+ import { getEmbeddingModelProviderOptionsTransformer } from '../model/get-embedding-model-provider-options-transformer';
10
11
  import { resolveEmbeddingModel } from '../model/resolve-model';
11
12
  import { createRestrictedTelemetryDispatcher } from './restricted-telemetry-dispatcher';
12
13
  import type { TelemetryOptions } from '../telemetry/telemetry-options';
@@ -321,6 +322,8 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
321
322
  ? maxInputBytesPerCall
322
323
  : Infinity,
323
324
  });
325
+ const providerOptionsTransformer =
326
+ getEmbeddingModelProviderOptionsTransformer(model);
324
327
 
325
328
  const embeddings: Array<Embedding> = [];
326
329
  const warnings: Array<Warning> = [];
@@ -339,9 +342,22 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
339
342
  supportsParallelCalls ? maxParallelCalls : 1,
340
343
  );
341
344
 
345
+ let nextChunkStartIndex = 0;
342
346
  for (const parallelChunk of parallelChunks) {
343
347
  const results = await Promise.all(
344
348
  parallelChunk.map(async chunk => {
349
+ // Capture the range before awaiting transformations or retrying.
350
+ const startIndex = nextChunkStartIndex;
351
+ nextChunkStartIndex += chunk.length;
352
+ const chunkProviderOptions = providerOptionsTransformer
353
+ ? await providerOptionsTransformer({
354
+ providerOptions,
355
+ values,
356
+ startIndex,
357
+ endIndex: startIndex + chunk.length,
358
+ })
359
+ : providerOptions;
360
+
345
361
  const result = await retry(async () => {
346
362
  const embedCallId = generateCallId();
347
363
 
@@ -361,7 +377,7 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
361
377
  values: chunk,
362
378
  abortSignal,
363
379
  headers: headersWithUserAgent,
364
- providerOptions,
380
+ providerOptions: chunkProviderOptions,
365
381
  });
366
382
 
367
383
  const chunkEmbeddings = modelResponse.embeddings;
@@ -412,7 +428,7 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
412
428
  result.providerMetadata,
413
429
  )) {
414
430
  providerMetadata[providerName] = {
415
- ...(providerMetadata[providerName] ?? {}),
431
+ ...providerMetadata[providerName],
416
432
  ...metadata,
417
433
  };
418
434
  }
@@ -204,10 +204,21 @@ function getOutputFormatMediaType(outputFormat: string | undefined) {
204
204
 
205
205
  const normalizedOutputFormat = outputFormat.trim().toLowerCase();
206
206
 
207
- return normalizedOutputFormat === 'pcm' ||
208
- normalizedOutputFormat === 'audio/pcm'
209
- ? 'audio/pcm'
210
- : undefined;
207
+ switch (normalizedOutputFormat) {
208
+ case 'pcm':
209
+ case 'audio/pcm':
210
+ return 'audio/pcm';
211
+ case 'audio/l16':
212
+ return 'audio/l16';
213
+ case 'mulaw':
214
+ case 'audio/mulaw':
215
+ return 'audio/mulaw';
216
+ case 'alaw':
217
+ case 'audio/alaw':
218
+ return 'audio/alaw';
219
+ default:
220
+ return undefined;
221
+ }
211
222
  }
212
223
 
213
224
  class DefaultSpeechResult implements SpeechResult {
@@ -53,12 +53,4 @@ export class DefaultGeneratedAudioFile
53
53
 
54
54
  export class DefaultGeneratedAudioFileWithType extends DefaultGeneratedAudioFile {
55
55
  readonly type = 'audio';
56
-
57
- constructor(options: {
58
- data: string | Uint8Array;
59
- mediaType: string;
60
- format: string;
61
- }) {
62
- super(options);
63
- }
64
56
  }
@@ -265,8 +265,6 @@ export function executeToolsFromStream<
265
265
  }
266
266
  }),
267
267
  );
268
-
269
- return;
270
268
  }
271
269
  }
272
270
  },
@@ -726,6 +726,7 @@ export async function generateText<
726
726
  toolsContext,
727
727
  runtimeContext,
728
728
  toolApprovalSecret: experimental_toolApprovalSecret,
729
+ refineToolInput,
729
730
  });
730
731
 
731
732
  const deniedToolApprovals = [
@@ -78,12 +78,4 @@ export class DefaultGeneratedFile implements GeneratedFile {
78
78
 
79
79
  export class DefaultGeneratedFileWithType extends DefaultGeneratedFile {
80
80
  readonly type = 'file';
81
-
82
- constructor(options: {
83
- data: string | Uint8Array;
84
- mediaType: string;
85
- providerMetadata?: Record<string, JSONObject>;
86
- }) {
87
- super(options);
88
- }
89
81
  }
@@ -35,7 +35,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
35
35
  string,
36
36
  {
37
37
  toolName: string;
38
- validatedContext: Promise<unknown> | undefined;
38
+ validatedContexts: Record<string, Promise<unknown> | undefined>;
39
39
  }
40
40
  > = createIdMap();
41
41
 
@@ -48,22 +48,23 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
48
48
  }): Promise<unknown> => {
49
49
  const ongoingToolCall = ongoingToolCalls[toolCallId];
50
50
 
51
- if (ongoingToolCall?.validatedContext != null) {
52
- return ongoingToolCall.validatedContext;
51
+ const validatedContext = ongoingToolCall?.validatedContexts[toolName];
52
+ if (validatedContext != null) {
53
+ return validatedContext;
53
54
  }
54
55
 
55
56
  const tool = getOwn(tools, toolName);
56
- const validatedContext = validateToolContext({
57
+ const newValidatedContext = validateToolContext({
57
58
  toolName,
58
59
  context: getOwn(toolsContext, toolName),
59
60
  contextSchema: tool?.contextSchema,
60
61
  });
61
62
 
62
63
  if (ongoingToolCall != null) {
63
- ongoingToolCall.validatedContext = validatedContext;
64
+ ongoingToolCall.validatedContexts[toolName] = newValidatedContext;
64
65
  }
65
66
 
66
- return validatedContext;
67
+ return newValidatedContext;
67
68
  };
68
69
 
69
70
  return stream.pipeThrough(
@@ -80,7 +81,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
80
81
  case 'tool-input-start': {
81
82
  ongoingToolCalls[chunk.id] = {
82
83
  toolName: chunk.toolName,
83
- validatedContext: undefined,
84
+ validatedContexts: createIdMap(),
84
85
  };
85
86
 
86
87
  const tool = getOwn(tools, chunk.toolName);
@@ -120,7 +121,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
120
121
  }
121
122
 
122
123
  case 'tool-call': {
123
- const toolName = ongoingToolCalls[chunk.toolCallId]?.toolName;
124
+ const toolName = chunk.toolName;
124
125
  const tool = getOwn(tools, toolName);
125
126
 
126
127
  if (!chunk.invalid && tool?.onInputAvailable != null) {
@@ -487,8 +487,6 @@ function getArrayLengthValidationError({
487
487
  cause: `elements array must contain at most ${maxItems} items`,
488
488
  });
489
489
  }
490
-
491
- return undefined;
492
490
  }
493
491
 
494
492
  /**
@@ -12,7 +12,12 @@ import { NoSuchToolError } from '../error/no-such-tool-error';
12
12
  import { ToolCallRepairError } from '../error/tool-call-repair-error';
13
13
  import type { Instructions } from '../prompt';
14
14
  import { getOwn } from '../util/get-own';
15
- import type { DynamicToolCall, TypedToolCall } from './tool-call';
15
+ import {
16
+ getToolCallInputSchemaInput,
17
+ setToolCallInputSchemaInput,
18
+ type DynamicToolCall,
19
+ type TypedToolCall,
20
+ } from './tool-call';
16
21
  import type { ToolCallRepairFunction } from './tool-call-repair-function';
17
22
  import type { ToolInputRefinement } from './tool-input-refinement';
18
23
 
@@ -167,7 +172,7 @@ async function waitForPromiseWithAbortSignal<T>({
167
172
  });
168
173
  }
169
174
 
170
- async function refineParsedToolCallInput<TOOLS extends ToolSet>({
175
+ export async function refineParsedToolCallInput<TOOLS extends ToolSet>({
171
176
  toolCall,
172
177
  refineToolInput,
173
178
  }: {
@@ -180,10 +185,15 @@ async function refineParsedToolCallInput<TOOLS extends ToolSet>({
180
185
  return toolCall;
181
186
  }
182
187
 
183
- return {
188
+ const refinedToolCall = {
184
189
  ...toolCall,
185
190
  input: await refine(toolCall.input as InferToolInput<TOOLS[keyof TOOLS]>),
186
191
  } as TypedToolCall<TOOLS>;
192
+
193
+ const inputSchemaInput = getToolCallInputSchemaInput(toolCall);
194
+ return inputSchemaInput == null
195
+ ? refinedToolCall
196
+ : setToolCallInputSchemaInput(refinedToolCall, inputSchemaInput.value);
187
197
  }
188
198
 
189
199
  async function parseProviderExecutedDynamicToolCall(
@@ -253,26 +263,29 @@ async function doParseToolCall<TOOLS extends ToolSet>({
253
263
  });
254
264
  }
255
265
 
256
- return tool.type === 'dynamic'
257
- ? {
258
- type: 'tool-call',
259
- toolCallId: toolCall.toolCallId,
260
- toolName: toolCall.toolName,
261
- input: parseResult.value,
262
- providerExecuted: toolCall.providerExecuted,
263
- providerMetadata: toolCall.providerMetadata,
264
- ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
265
- dynamic: true,
266
- title: tool.title,
267
- }
268
- : {
269
- type: 'tool-call',
270
- toolCallId: toolCall.toolCallId,
271
- toolName,
272
- input: parseResult.value,
273
- providerExecuted: toolCall.providerExecuted,
274
- providerMetadata: toolCall.providerMetadata,
275
- ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
276
- title: tool.title,
277
- };
266
+ return setToolCallInputSchemaInput(
267
+ tool.type === 'dynamic'
268
+ ? {
269
+ type: 'tool-call',
270
+ toolCallId: toolCall.toolCallId,
271
+ toolName: toolCall.toolName,
272
+ input: parseResult.value,
273
+ providerExecuted: toolCall.providerExecuted,
274
+ providerMetadata: toolCall.providerMetadata,
275
+ ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
276
+ dynamic: true,
277
+ title: tool.title,
278
+ }
279
+ : {
280
+ type: 'tool-call',
281
+ toolCallId: toolCall.toolCallId,
282
+ toolName,
283
+ input: parseResult.value,
284
+ providerExecuted: toolCall.providerExecuted,
285
+ providerMetadata: toolCall.providerMetadata,
286
+ ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
287
+ title: tool.title,
288
+ },
289
+ parseResult.rawValue,
290
+ );
278
291
  }
@@ -47,7 +47,9 @@ export function pruneMessages({
47
47
 
48
48
  return {
49
49
  ...message,
50
- content: message.content.filter(part => part.type !== 'reasoning'),
50
+ content: message.content.filter(
51
+ part => part.type !== 'reasoning' && part.type !== 'reasoning-file',
52
+ ),
51
53
  };
52
54
  });
53
55
  }
@@ -2046,6 +2046,7 @@ class DefaultStreamTextResult<
2046
2046
  toolsContext,
2047
2047
  runtimeContext,
2048
2048
  toolApprovalSecret: experimental_toolApprovalSecret,
2049
+ refineToolInput,
2049
2050
  });
2050
2051
 
2051
2052
  const localDeniedToolApprovals = [