ai 7.0.112 → 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 (40) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.d.ts +16 -0
  3. package/dist/index.js +315 -213
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/index.d.ts +2 -1
  6. package/dist/internal/index.js +105 -28
  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/07-reference/01-ai-sdk-core/12-generate-speech.mdx +1 -1
  11. package/docs/07-reference/01-ai-sdk-core/32-validate-ui-messages.mdx +18 -0
  12. package/package.json +11 -11
  13. package/src/embed/embed-many.ts +18 -2
  14. package/src/generate-speech/generate-speech.ts +15 -4
  15. package/src/generate-speech/generated-audio-file.ts +0 -8
  16. package/src/generate-text/execute-tools-from-stream.ts +0 -2
  17. package/src/generate-text/generate-text.ts +1 -0
  18. package/src/generate-text/generated-file.ts +0 -8
  19. package/src/generate-text/invoke-tool-callbacks-from-stream.ts +9 -8
  20. package/src/generate-text/output.ts +0 -2
  21. package/src/generate-text/parse-tool-call.ts +38 -25
  22. package/src/generate-text/stream-text.ts +1 -0
  23. package/src/generate-text/to-response-messages.ts +7 -0
  24. package/src/generate-text/tool-call.ts +26 -0
  25. package/src/generate-text/validate-tool-approvals.ts +39 -3
  26. package/src/generate-video/generate-video.ts +0 -2
  27. package/src/middleware/extract-reasoning-middleware.ts +1 -1
  28. package/src/middleware/wrap-embedding-model.ts +9 -1
  29. package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
  30. package/src/prompt/content-part.ts +3 -0
  31. package/src/registry/custom-provider.ts +12 -5
  32. package/src/ui/chat.ts +108 -17
  33. package/src/ui/convert-to-model-messages.ts +8 -0
  34. package/src/ui/direct-chat-transport.ts +2 -0
  35. package/src/ui/process-ui-message-stream.ts +6 -0
  36. package/src/ui/ui-messages.ts +10 -0
  37. package/src/ui/validate-ui-messages.ts +98 -132
  38. package/src/ui-message-stream/to-ui-message-chunk.ts +7 -0
  39. package/src/ui-message-stream/ui-message-chunks.ts +2 -0
  40. package/src/util/write-to-server-response.ts +0 -2
@@ -5,11 +5,13 @@ import {
5
5
  validateTypes,
6
6
  zodSchema,
7
7
  type FlexibleSchema,
8
+ type MaybePromiseLike,
8
9
  type Tool,
9
10
  } from '@ai-sdk/provider-utils';
10
11
  import { InvalidArgumentError } from '../error';
11
12
  import { jsonValueSchema } from '../types/json-value';
12
13
  import { getOwn } from '../util/get-own';
14
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
13
15
  import { providerMetadataSchema } from '../types/provider-metadata';
14
16
  import { z, type ZodType } from '../util/zod';
15
17
  import type {
@@ -48,8 +50,38 @@ function asDynamicToolPart(toolPart: ToolUIPart): DynamicToolUIPart {
48
50
  } as DynamicToolUIPart;
49
51
  }
50
52
 
51
- const uiMessagesSchema = lazySchema(() =>
52
- zodSchema(
53
+ function getToolPartInputSchemaInput(
54
+ toolPart: ToolUIPart,
55
+ ): { value: unknown } | undefined {
56
+ return toolPart.approval != null &&
57
+ Object.prototype.hasOwnProperty.call(toolPart.approval, 'inputSchemaInput')
58
+ ? { value: toolPart.approval.inputSchemaInput }
59
+ : undefined;
60
+ }
61
+
62
+ const uiMessagesSchema = lazySchema(() => {
63
+ const approvalRequestedSchema = z.object({
64
+ id: z.string(),
65
+ approved: z.never().optional(),
66
+ descriptor: z.unknown().optional(),
67
+ requestReason: z.string().optional(),
68
+ reason: z.never().optional(),
69
+ isAutomatic: z.boolean().optional(),
70
+ signature: z.string().optional(),
71
+ inputSchemaInput: z.unknown().optional(),
72
+ });
73
+ const approvalRespondedSchema = approvalRequestedSchema.extend({
74
+ approved: z.boolean(),
75
+ reason: z.string().optional(),
76
+ });
77
+ const approvalGrantedSchema = approvalRespondedSchema.extend({
78
+ approved: z.literal(true),
79
+ });
80
+ const approvalDeniedSchema = approvalRespondedSchema.extend({
81
+ approved: z.literal(false),
82
+ });
83
+
84
+ return zodSchema(
53
85
  z
54
86
  .array(
55
87
  z
@@ -154,15 +186,7 @@ const uiMessagesSchema = lazySchema(() =>
154
186
  output: z.never().optional(),
155
187
  errorText: z.never().optional(),
156
188
  callProviderMetadata: providerMetadataSchema.optional(),
157
- approval: z.object({
158
- id: z.string(),
159
- approved: z.never().optional(),
160
- descriptor: z.unknown().optional(),
161
- requestReason: z.string().optional(),
162
- reason: z.never().optional(),
163
- isAutomatic: z.boolean().optional(),
164
- signature: z.string().optional(),
165
- }),
189
+ approval: approvalRequestedSchema,
166
190
  }),
167
191
  z.object({
168
192
  type: z.literal('dynamic-tool'),
@@ -176,15 +200,7 @@ const uiMessagesSchema = lazySchema(() =>
176
200
  output: z.never().optional(),
177
201
  errorText: z.never().optional(),
178
202
  callProviderMetadata: providerMetadataSchema.optional(),
179
- approval: z.object({
180
- id: z.string(),
181
- approved: z.boolean(),
182
- descriptor: z.unknown().optional(),
183
- requestReason: z.string().optional(),
184
- reason: z.string().optional(),
185
- isAutomatic: z.boolean().optional(),
186
- signature: z.string().optional(),
187
- }),
203
+ approval: approvalRespondedSchema,
188
204
  }),
189
205
  z.object({
190
206
  type: z.literal('dynamic-tool'),
@@ -200,17 +216,7 @@ const uiMessagesSchema = lazySchema(() =>
200
216
  callProviderMetadata: providerMetadataSchema.optional(),
201
217
  resultProviderMetadata: providerMetadataSchema.optional(),
202
218
  preliminary: z.boolean().optional(),
203
- approval: z
204
- .object({
205
- id: z.string(),
206
- approved: z.literal(true),
207
- descriptor: z.unknown().optional(),
208
- requestReason: z.string().optional(),
209
- reason: z.string().optional(),
210
- isAutomatic: z.boolean().optional(),
211
- signature: z.string().optional(),
212
- })
213
- .optional(),
219
+ approval: approvalGrantedSchema.optional(),
214
220
  }),
215
221
  z.object({
216
222
  type: z.literal('dynamic-tool'),
@@ -226,17 +232,7 @@ const uiMessagesSchema = lazySchema(() =>
226
232
  errorText: z.string(),
227
233
  callProviderMetadata: providerMetadataSchema.optional(),
228
234
  resultProviderMetadata: providerMetadataSchema.optional(),
229
- approval: z
230
- .object({
231
- id: z.string(),
232
- approved: z.literal(true),
233
- descriptor: z.unknown().optional(),
234
- requestReason: z.string().optional(),
235
- reason: z.string().optional(),
236
- isAutomatic: z.boolean().optional(),
237
- signature: z.string().optional(),
238
- })
239
- .optional(),
235
+ approval: approvalGrantedSchema.optional(),
240
236
  }),
241
237
  z.object({
242
238
  type: z.literal('dynamic-tool'),
@@ -250,15 +246,7 @@ const uiMessagesSchema = lazySchema(() =>
250
246
  output: z.never().optional(),
251
247
  errorText: z.never().optional(),
252
248
  callProviderMetadata: providerMetadataSchema.optional(),
253
- approval: z.object({
254
- id: z.string(),
255
- approved: z.literal(false),
256
- descriptor: z.unknown().optional(),
257
- requestReason: z.string().optional(),
258
- reason: z.string().optional(),
259
- isAutomatic: z.boolean().optional(),
260
- signature: z.string().optional(),
261
- }),
249
+ approval: approvalDeniedSchema,
262
250
  }),
263
251
  z.object({
264
252
  type: z.string().startsWith('tool-'),
@@ -297,15 +285,7 @@ const uiMessagesSchema = lazySchema(() =>
297
285
  output: z.never().optional(),
298
286
  errorText: z.never().optional(),
299
287
  callProviderMetadata: providerMetadataSchema.optional(),
300
- approval: z.object({
301
- id: z.string(),
302
- approved: z.never().optional(),
303
- descriptor: z.unknown().optional(),
304
- requestReason: z.string().optional(),
305
- reason: z.never().optional(),
306
- isAutomatic: z.boolean().optional(),
307
- signature: z.string().optional(),
308
- }),
288
+ approval: approvalRequestedSchema,
309
289
  }),
310
290
  z.object({
311
291
  type: z.string().startsWith('tool-'),
@@ -318,15 +298,7 @@ const uiMessagesSchema = lazySchema(() =>
318
298
  output: z.never().optional(),
319
299
  errorText: z.never().optional(),
320
300
  callProviderMetadata: providerMetadataSchema.optional(),
321
- approval: z.object({
322
- id: z.string(),
323
- approved: z.boolean(),
324
- descriptor: z.unknown().optional(),
325
- requestReason: z.string().optional(),
326
- reason: z.string().optional(),
327
- isAutomatic: z.boolean().optional(),
328
- signature: z.string().optional(),
329
- }),
301
+ approval: approvalRespondedSchema,
330
302
  }),
331
303
  z.object({
332
304
  type: z.string().startsWith('tool-'),
@@ -341,17 +313,7 @@ const uiMessagesSchema = lazySchema(() =>
341
313
  callProviderMetadata: providerMetadataSchema.optional(),
342
314
  resultProviderMetadata: providerMetadataSchema.optional(),
343
315
  preliminary: z.boolean().optional(),
344
- approval: z
345
- .object({
346
- id: z.string(),
347
- approved: z.literal(true),
348
- descriptor: z.unknown().optional(),
349
- requestReason: z.string().optional(),
350
- reason: z.string().optional(),
351
- isAutomatic: z.boolean().optional(),
352
- signature: z.string().optional(),
353
- })
354
- .optional(),
316
+ approval: approvalGrantedSchema.optional(),
355
317
  }),
356
318
  z.object({
357
319
  type: z.string().startsWith('tool-'),
@@ -366,17 +328,7 @@ const uiMessagesSchema = lazySchema(() =>
366
328
  errorText: z.string(),
367
329
  callProviderMetadata: providerMetadataSchema.optional(),
368
330
  resultProviderMetadata: providerMetadataSchema.optional(),
369
- approval: z
370
- .object({
371
- id: z.string(),
372
- approved: z.literal(true),
373
- descriptor: z.unknown().optional(),
374
- requestReason: z.string().optional(),
375
- reason: z.string().optional(),
376
- isAutomatic: z.boolean().optional(),
377
- signature: z.string().optional(),
378
- })
379
- .optional(),
331
+ approval: approvalGrantedSchema.optional(),
380
332
  }),
381
333
  z.object({
382
334
  type: z.string().startsWith('tool-'),
@@ -389,15 +341,7 @@ const uiMessagesSchema = lazySchema(() =>
389
341
  output: z.never().optional(),
390
342
  errorText: z.never().optional(),
391
343
  callProviderMetadata: providerMetadataSchema.optional(),
392
- approval: z.object({
393
- id: z.string(),
394
- approved: z.literal(false),
395
- descriptor: z.unknown().optional(),
396
- requestReason: z.string().optional(),
397
- reason: z.string().optional(),
398
- isAutomatic: z.boolean().optional(),
399
- signature: z.string().optional(),
400
- }),
344
+ approval: approvalDeniedSchema,
401
345
  }),
402
346
  ]),
403
347
  ),
@@ -417,8 +361,8 @@ const uiMessagesSchema = lazySchema(() =>
417
361
  }),
418
362
  )
419
363
  .nonempty('Messages array must not be empty'),
420
- ),
421
- );
364
+ );
365
+ });
422
366
 
423
367
  export type SafeValidateUIMessagesResult<UI_MESSAGE extends UIMessage> =
424
368
  | {
@@ -453,6 +397,11 @@ type ValidateUIMessagesOptions<UI_MESSAGE extends UIMessage> = {
453
397
  >;
454
398
  };
455
399
  };
400
+ experimental_refineToolInput?: {
401
+ [NAME in keyof InferUIMessageTools<UI_MESSAGE> & string]?: (
402
+ input: InferUIMessageTools<UI_MESSAGE>[NAME]['input'],
403
+ ) => MaybePromiseLike<InferUIMessageTools<UI_MESSAGE>[NAME]['input']>;
404
+ };
456
405
  };
457
406
 
458
407
  async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
@@ -461,6 +410,7 @@ async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
461
410
  metadataSchema,
462
411
  dataSchemas,
463
412
  tools,
413
+ experimental_refineToolInput,
464
414
  }: ValidateUIMessagesOptions<UI_MESSAGE>,
465
415
  {
466
416
  convertMissingTerminalToolsToDynamic,
@@ -583,49 +533,65 @@ async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
583
533
  entityName: toolName,
584
534
  entityId: toolPart.toolCallId,
585
535
  };
536
+ const inputSchemaInput = getToolPartInputSchemaInput(toolPart);
537
+ const inputToValidate =
538
+ inputSchemaInput == null
539
+ ? toolPart.input
540
+ : inputSchemaInput.value;
586
541
  let convertToDynamic = false;
587
542
 
588
543
  // Tool input validation
589
- if (toolPart.state === 'output-error') {
590
- // Failed calls can retain invalid input. Keep them loadable, but
591
- // expose incompatible input as unknown instead of the current
592
- // static tool input type.
593
- if (toolPart.input !== undefined) {
594
- const result = await safeValidateTypes({
595
- value: toolPart.input,
596
- schema: tool.inputSchema,
597
- context: inputValidationContext,
598
- });
599
- convertToDynamic = !result.success;
600
- }
601
- } else if (toolPart.state === 'output-available') {
544
+ if (
545
+ toolPart.state !== 'input-streaming' &&
546
+ (toolPart.state !== 'output-error' ||
547
+ inputSchemaInput != null ||
548
+ toolPart.input !== undefined)
549
+ ) {
602
550
  const result = await safeValidateTypes({
603
- value: toolPart.input,
551
+ value: inputToValidate,
604
552
  schema: tool.inputSchema,
605
553
  context: inputValidationContext,
606
554
  });
607
555
 
556
+ let inputError: TypeValidationError | undefined;
608
557
  if (!result.success) {
609
- // Empty terminal input can represent aborted or incomplete
610
- // history whose input was never streamed. Preserve it without
611
- // claiming that it matches the current static input type.
612
- if (isEmptyObject(toolPart.input)) {
558
+ inputError = result.error;
559
+ } else if (inputSchemaInput != null) {
560
+ try {
561
+ const refine = getOwn(experimental_refineToolInput, toolName);
562
+ const reconstructedInput =
563
+ refine == null ? result.value : await refine(result.value);
564
+
565
+ if (!isDeepEqualData(reconstructedInput, toolPart.input)) {
566
+ inputError = new TypeValidationError({
567
+ value: toolPart.input,
568
+ cause:
569
+ 'Tool input does not match the output reconstructed from inputSchemaInput.',
570
+ context: inputValidationContext,
571
+ });
572
+ }
573
+ } catch (error) {
574
+ inputError = new TypeValidationError({
575
+ value: inputToValidate,
576
+ cause: error,
577
+ context: inputValidationContext,
578
+ });
579
+ }
580
+ }
581
+
582
+ if (inputError != null) {
583
+ // Failed calls and empty terminal inputs must remain loadable,
584
+ // but must not claim to match the current static input type.
585
+ if (
586
+ toolPart.state === 'output-error' ||
587
+ (toolPart.state === 'output-available' &&
588
+ isEmptyObject(toolPart.input))
589
+ ) {
613
590
  convertToDynamic = true;
614
591
  } else {
615
- throw result.error;
592
+ throw inputError;
616
593
  }
617
594
  }
618
- } else if (
619
- toolPart.state === 'input-available' ||
620
- toolPart.state === 'approval-requested' ||
621
- toolPart.state === 'approval-responded' ||
622
- toolPart.state === 'output-denied'
623
- ) {
624
- await validateTypes({
625
- value: toolPart.input,
626
- schema: tool.inputSchema,
627
- context: inputValidationContext,
628
- });
629
595
  }
630
596
 
631
597
  // Tool output validation
@@ -1,10 +1,12 @@
1
1
  import type { ToolSet } from '@ai-sdk/provider-utils';
2
2
  import type { TextStreamPart } from '../generate-text/stream-text-result';
3
+ import { getToolCallInputSchemaInput } from '../generate-text/tool-call';
3
4
  import type {
4
5
  InferUIMessageData,
5
6
  InferUIMessageMetadata,
6
7
  UIMessage,
7
8
  } from '../ui/ui-messages';
9
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
8
10
  import type { InferUIMessageChunk, UIMessageChunk } from './ui-message-chunks';
9
11
 
10
12
  export type ToUIMessageChunkOptions<
@@ -250,10 +252,15 @@ export function toUIMessageChunk<
250
252
  }
251
253
 
252
254
  case 'tool-approval-request': {
255
+ const inputSchemaInput = getToolCallInputSchemaInput(part.toolCall);
253
256
  return {
254
257
  type: 'tool-approval-request',
255
258
  approvalId: part.approvalId,
256
259
  toolCallId: part.toolCall.toolCallId,
260
+ ...(inputSchemaInput != null &&
261
+ !isDeepEqualData(inputSchemaInput.value, part.toolCall.input)
262
+ ? { inputSchemaInput: inputSchemaInput.value }
263
+ : {}),
257
264
  ...(part.reason != null ? { reason: part.reason } : {}),
258
265
  ...(part.isAutomatic != null ? { isAutomatic: part.isAutomatic } : {}),
259
266
  ...(part.signature != null ? { signature: part.signature } : {}),
@@ -86,6 +86,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
86
86
  approvalId: z.string(),
87
87
  toolCallId: z.string(),
88
88
  approvalDescriptor: z.unknown().optional(),
89
+ inputSchemaInput: z.unknown().optional(),
89
90
  reason: z.string().optional(),
90
91
  isAutomatic: z.boolean().optional(),
91
92
  signature: z.string().optional(),
@@ -301,6 +302,7 @@ export type UIMessageChunk<
301
302
  approvalId: string;
302
303
  toolCallId: string;
303
304
  approvalDescriptor?: unknown;
305
+ inputSchemaInput?: unknown;
304
306
  reason?: string;
305
307
  isAutomatic?: boolean;
306
308
  signature?: string;
@@ -51,8 +51,6 @@ export function writeToServerResponse({
51
51
  });
52
52
  }
53
53
  }
54
- } catch (error) {
55
- throw error;
56
54
  } finally {
57
55
  response.end();
58
56
  }