ai 6.0.288 → 6.0.290

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 +35 -0
  2. package/dist/index.d.mts +13 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +377 -281
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +314 -213
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal/index.js +20 -5
  9. package/dist/internal/index.js.map +1 -1
  10. package/dist/internal/index.mjs +20 -5
  11. package/dist/internal/index.mjs.map +1 -1
  12. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +57 -0
  13. package/docs/03-ai-sdk-core/40-middleware.mdx +92 -7
  14. package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +7 -0
  15. package/package.json +5 -4
  16. package/src/embed/embed-many.ts +19 -3
  17. package/src/generate-speech/generated-audio-file.ts +0 -8
  18. package/src/generate-text/generated-file.ts +0 -4
  19. package/src/generate-text/parse-tool-call.ts +30 -23
  20. package/src/generate-text/run-tools-transformation.ts +37 -3
  21. package/src/generate-text/stream-text.ts +9 -28
  22. package/src/generate-text/to-response-messages.ts +7 -0
  23. package/src/generate-text/tool-call.ts +26 -0
  24. package/src/generate-text/validate-tool-approvals.ts +20 -2
  25. package/src/middleware/extract-reasoning-middleware.ts +1 -1
  26. package/src/middleware/wrap-embedding-model.ts +12 -2
  27. package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
  28. package/src/prompt/content-part.ts +2 -0
  29. package/src/prompt/convert-to-language-model-prompt.ts +7 -2
  30. package/src/prompt/data-content.ts +11 -2
  31. package/src/ui/chat.ts +108 -17
  32. package/src/ui/convert-to-model-messages.ts +8 -0
  33. package/src/ui/http-chat-transport.ts +1 -1
  34. package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +1 -1
  35. package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +2 -1
  36. package/src/ui/process-ui-message-stream.ts +6 -0
  37. package/src/ui/ui-messages.ts +10 -0
  38. package/src/ui/validate-ui-messages.ts +79 -114
  39. package/src/ui-message-stream/ui-message-chunks.ts +2 -0
  40. package/src/util/write-to-server-response.ts +0 -2
@@ -201,6 +201,8 @@ export const toolApprovalRequestSchema: z.ZodType<ToolApprovalRequest> =
201
201
  type: z.literal('tool-approval-request'),
202
202
  approvalId: z.string(),
203
203
  toolCallId: z.string(),
204
+ signature: z.string().optional(),
205
+ inputSchemaInput: z.unknown().optional(),
204
206
  });
205
207
 
206
208
  /**
@@ -492,8 +492,11 @@ function convertPartToLanguageModelPart(
492
492
  throw new Error(`Unsupported part type: ${type}`);
493
493
  }
494
494
 
495
- const { data: convertedData, mediaType: convertedMediaType } =
496
- convertToLanguageModelV3DataContent(originalData);
495
+ const {
496
+ data: convertedData,
497
+ mediaType: convertedMediaType,
498
+ originalUrl,
499
+ } = convertToLanguageModelV3DataContent(originalData);
497
500
 
498
501
  let mediaType: string | undefined = convertedMediaType ?? part.mediaType;
499
502
  let data: Uint8Array | string | URL = convertedData; // binary | base64 | url
@@ -525,6 +528,7 @@ function convertPartToLanguageModelPart(
525
528
  mediaType: mediaType ?? 'image/*', // any image
526
529
  filename: undefined,
527
530
  data,
531
+ ...(data instanceof URL && originalUrl != null ? { originalUrl } : {}),
528
532
  providerOptions: part.providerOptions,
529
533
  };
530
534
  }
@@ -540,6 +544,7 @@ function convertPartToLanguageModelPart(
540
544
  mediaType,
541
545
  filename: part.filename,
542
546
  data,
547
+ ...(data instanceof URL && originalUrl != null ? { originalUrl } : {}),
543
548
  providerOptions: part.providerOptions,
544
549
  };
545
550
  }
@@ -28,7 +28,10 @@ export function convertToLanguageModelV3DataContent(
28
28
  ): {
29
29
  data: LanguageModelV3DataContent;
30
30
  mediaType: string | undefined;
31
+ originalUrl?: string;
31
32
  } {
33
+ let originalUrl: string | undefined;
34
+
32
35
  // Buffer & Uint8Array:
33
36
  if (content instanceof Uint8Array) {
34
37
  return { data: content, mediaType: undefined };
@@ -43,7 +46,9 @@ export function convertToLanguageModelV3DataContent(
43
46
  // is not a URL and likely some other sort of data.
44
47
  if (typeof content === 'string') {
45
48
  try {
46
- content = new URL(content);
49
+ const url = new URL(content);
50
+ originalUrl = url.toString() !== content ? content : undefined;
51
+ content = url;
47
52
  } catch (error) {
48
53
  // ignored
49
54
  }
@@ -65,7 +70,11 @@ export function convertToLanguageModelV3DataContent(
65
70
  return { data: base64Content, mediaType: dataUrlMediaType };
66
71
  }
67
72
 
68
- return { data: content, mediaType: undefined };
73
+ return {
74
+ data: content,
75
+ mediaType: undefined,
76
+ ...(originalUrl != null ? { originalUrl } : {}),
77
+ };
69
78
  }
70
79
 
71
80
  /**
package/src/ui/chat.ts CHANGED
@@ -259,6 +259,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
259
259
  private onData?: ChatInit<UI_MESSAGE>['onData'];
260
260
  private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
261
261
 
262
+ private pendingApprovalMessageId: string | undefined;
262
263
  private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
263
264
  private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
264
265
  private jobExecutor = new SerialJobExecutor();
@@ -361,9 +362,37 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
361
362
  options?: ChatRequestOptions,
362
363
  ): Promise<void> => {
363
364
  if (message == null) {
364
- await this.makeRequest({
365
- trigger: 'submit-message',
366
- messageId: this.lastMessage?.id,
365
+ let messageId = this.pendingApprovalMessageId;
366
+
367
+ if (messageId == null) {
368
+ messageId = this.lastMessage?.id;
369
+
370
+ // When hydrating a chat with an already-responded approval, continue
371
+ // the most recent matching assistant message so result chunks can
372
+ // resolve its tool invocation.
373
+ for (let i = this.state.messages.length - 1; i >= 0; i--) {
374
+ const candidate = this.state.messages[i];
375
+ if (
376
+ candidate.role === 'assistant' &&
377
+ candidate.parts.some(
378
+ part => isToolUIPart(part) && part.state === 'approval-responded',
379
+ )
380
+ ) {
381
+ messageId = candidate.id;
382
+ break;
383
+ }
384
+ }
385
+ }
386
+
387
+ const consumesPendingApproval =
388
+ messageId != null && messageId === this.pendingApprovalMessageId;
389
+ const pendingApprovalMessageIndex = consumesPendingApproval
390
+ ? this.state.messages.findIndex(message => message.id === messageId)
391
+ : -1;
392
+
393
+ await this.makeRequestForToolApproval({
394
+ messageId,
395
+ messageIndex: pendingApprovalMessageIndex,
367
396
  ...options,
368
397
  });
369
398
  return;
@@ -489,7 +518,6 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
489
518
  }) =>
490
519
  this.jobExecutor.run(async () => {
491
520
  const messages = this.state.messages;
492
- const lastMessage = messages[messages.length - 1];
493
521
 
494
522
  const updatePart = (
495
523
  part: UIMessagePart<UIDataTypes, UITools>,
@@ -504,11 +532,25 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
504
532
  }
505
533
  : part;
506
534
 
507
- // update the message to trigger an immediate UI update
508
- this.state.replaceMessage(messages.length - 1, {
509
- ...lastMessage,
510
- parts: lastMessage.parts.map(updatePart),
511
- });
535
+ const messageIndex = messages.findIndex(message =>
536
+ message.parts.some(
537
+ part =>
538
+ isToolUIPart(part) &&
539
+ part.state === 'approval-requested' &&
540
+ part.approval.id === id,
541
+ ),
542
+ );
543
+
544
+ if (messageIndex !== -1) {
545
+ const message = messages[messageIndex];
546
+
547
+ // update the message to trigger an immediate UI update
548
+ this.state.replaceMessage(messageIndex, {
549
+ ...message,
550
+ parts: message.parts.map(updatePart),
551
+ });
552
+ this.pendingApprovalMessageId = message.id;
553
+ }
512
554
 
513
555
  // update the active response if it exists
514
556
  if (this.activeResponse) {
@@ -525,9 +567,14 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
525
567
  this.shouldSendAutomatically().then(shouldSend => {
526
568
  if (shouldSend) {
527
569
  // no await to avoid deadlocking
528
- this.makeRequest({
529
- trigger: 'submit-message',
530
- messageId: this.lastMessage?.id,
570
+ const messageId =
571
+ messageIndex === -1
572
+ ? this.lastMessage?.id
573
+ : messages[messageIndex].id;
574
+
575
+ this.makeRequestForToolApproval({
576
+ messageId,
577
+ messageIndex,
531
578
  ...options,
532
579
  });
533
580
  }
@@ -610,6 +657,36 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
610
657
  return result as boolean;
611
658
  }
612
659
 
660
+ private async makeRequestForToolApproval({
661
+ messageId,
662
+ messageIndex,
663
+ ...options
664
+ }: {
665
+ messageId?: string;
666
+ messageIndex: number;
667
+ } & ChatRequestOptions) {
668
+ const consumesPendingApproval =
669
+ messageId != null && messageId === this.pendingApprovalMessageId;
670
+ if (consumesPendingApproval) {
671
+ this.pendingApprovalMessageId = undefined;
672
+ }
673
+
674
+ await this.makeRequest({
675
+ trigger: 'submit-message',
676
+ messageId,
677
+ ...options,
678
+ });
679
+
680
+ if (
681
+ consumesPendingApproval &&
682
+ this.status === 'error' &&
683
+ this.pendingApprovalMessageId == null
684
+ ) {
685
+ this.pendingApprovalMessageId =
686
+ this.state.messages[messageIndex]?.id ?? messageId;
687
+ }
688
+ }
689
+
613
690
  private async makeRequest({
614
691
  trigger,
615
692
  metadata,
@@ -697,6 +774,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
697
774
  this.setStatus({ status: 'submitted', error: undefined });
698
775
 
699
776
  const lastMessage = this.lastMessage;
777
+ const responseMessageIndex =
778
+ trigger === 'submit-message' && messageId != null
779
+ ? this.state.messages.findIndex(message => message.id === messageId)
780
+ : this.state.messages.length - 1;
781
+ const responseMessage =
782
+ responseMessageIndex === -1
783
+ ? lastMessage
784
+ : this.state.messages[responseMessageIndex];
785
+ const usesEarlierAssistantMessage =
786
+ responseMessageIndex !== -1 &&
787
+ responseMessageIndex < this.state.messages.length - 1 &&
788
+ responseMessage?.role === 'assistant';
700
789
 
701
790
  let isAbort = false;
702
791
  let isDisconnect = false;
@@ -709,7 +798,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
709
798
  lastMessage:
710
799
  trigger === 'resume-stream' || trigger === 'regenerate-message'
711
800
  ? undefined
712
- : this.state.snapshot(lastMessage),
801
+ : this.state.snapshot(responseMessage),
713
802
  messageId: this.generateId(),
714
803
  }),
715
804
  abortController,
@@ -763,10 +852,12 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
763
852
  this.setStatus({ status: 'streaming' });
764
853
  }
765
854
 
766
- const replaceLastMessage =
767
- response.state.message.id === this.lastMessage?.id;
768
-
769
- if (replaceLastMessage) {
855
+ if (usesEarlierAssistantMessage) {
856
+ this.state.replaceMessage(
857
+ responseMessageIndex,
858
+ response.state.message,
859
+ );
860
+ } else if (response.state.message.id === this.lastMessage?.id) {
770
861
  this.state.replaceMessage(
771
862
  this.state.messages.length - 1,
772
863
  response.state.message,
@@ -204,6 +204,14 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
204
204
  type: 'tool-approval-request' as const,
205
205
  approvalId: part.approval.id,
206
206
  toolCallId: part.toolCallId,
207
+ ...(Object.prototype.hasOwnProperty.call(
208
+ part.approval,
209
+ 'inputSchemaInput',
210
+ )
211
+ ? {
212
+ inputSchemaInput: part.approval.inputSchemaInput,
213
+ }
214
+ : {}),
207
215
  ...(part.approval.signature != null
208
216
  ? { signature: part.approval.signature }
209
217
  : {}),
@@ -191,7 +191,7 @@ export abstract class HttpChatTransport<
191
191
  const response = await fetch(api, {
192
192
  method: 'POST',
193
193
  headers: {
194
- 'Content-Type': 'application/json',
194
+ 'content-type': 'application/json',
195
195
  ...headers,
196
196
  },
197
197
  body: JSON.stringify(body),
@@ -35,7 +35,7 @@ export function lastAssistantMessageIsCompleteWithApprovalResponses({
35
35
  // all tool approvals must have a response
36
36
  lastStepToolInvocations.every(
37
37
  part =>
38
- part.state === 'output-available' ||
38
+ (part.state === 'output-available' && part.preliminary !== true) ||
39
39
  part.state === 'output-error' ||
40
40
  part.state === 'output-denied' ||
41
41
  part.state === 'approval-responded',
@@ -33,7 +33,8 @@ export function lastAssistantMessageIsCompleteWithToolCalls({
33
33
  lastStepToolInvocations.length > 0 &&
34
34
  lastStepToolInvocations.every(
35
35
  part =>
36
- part.state === 'output-available' || part.state === 'output-error',
36
+ (part.state === 'output-available' && part.preliminary !== true) ||
37
+ part.state === 'output-error',
37
38
  )
38
39
  );
39
40
  }
@@ -721,6 +721,12 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
721
721
  ...(chunk.approvalDescriptor != null
722
722
  ? { descriptor: chunk.approvalDescriptor }
723
723
  : {}),
724
+ ...(Object.prototype.hasOwnProperty.call(
725
+ chunk,
726
+ 'inputSchemaInput',
727
+ )
728
+ ? { inputSchemaInput: chunk.inputSchemaInput }
729
+ : {}),
724
730
  ...(chunk.signature != null
725
731
  ? { signature: chunk.signature }
726
732
  : {}),
@@ -263,6 +263,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
263
263
  descriptor?: unknown;
264
264
  reason?: never;
265
265
  signature?: string;
266
+ inputSchemaInput?: unknown;
266
267
  };
267
268
  }
268
269
  | {
@@ -277,6 +278,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
277
278
  descriptor?: unknown;
278
279
  reason?: string;
279
280
  signature?: string;
281
+ inputSchemaInput?: unknown;
280
282
  };
281
283
  }
282
284
  | {
@@ -293,6 +295,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
293
295
  descriptor?: unknown;
294
296
  reason?: string;
295
297
  signature?: string;
298
+ inputSchemaInput?: unknown;
296
299
  };
297
300
  }
298
301
  | {
@@ -309,6 +312,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
309
312
  descriptor?: unknown;
310
313
  reason?: string;
311
314
  signature?: string;
315
+ inputSchemaInput?: unknown;
312
316
  };
313
317
  }
314
318
  | {
@@ -323,6 +327,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
323
327
  descriptor?: unknown;
324
328
  reason?: string;
325
329
  signature?: string;
330
+ inputSchemaInput?: unknown;
326
331
  };
327
332
  }
328
333
  );
@@ -381,6 +386,7 @@ export type DynamicToolUIPart = {
381
386
  descriptor?: unknown;
382
387
  reason?: never;
383
388
  signature?: string;
389
+ inputSchemaInput?: unknown;
384
390
  };
385
391
  }
386
392
  | {
@@ -395,6 +401,7 @@ export type DynamicToolUIPart = {
395
401
  descriptor?: unknown;
396
402
  reason?: string;
397
403
  signature?: string;
404
+ inputSchemaInput?: unknown;
398
405
  };
399
406
  }
400
407
  | {
@@ -411,6 +418,7 @@ export type DynamicToolUIPart = {
411
418
  descriptor?: unknown;
412
419
  reason?: string;
413
420
  signature?: string;
421
+ inputSchemaInput?: unknown;
414
422
  };
415
423
  }
416
424
  | {
@@ -426,6 +434,7 @@ export type DynamicToolUIPart = {
426
434
  descriptor?: unknown;
427
435
  reason?: string;
428
436
  signature?: string;
437
+ inputSchemaInput?: unknown;
429
438
  };
430
439
  }
431
440
  | {
@@ -440,6 +449,7 @@ export type DynamicToolUIPart = {
440
449
  descriptor?: unknown;
441
450
  reason?: string;
442
451
  signature?: string;
452
+ inputSchemaInput?: unknown;
443
453
  };
444
454
  }
445
455
  );