ai 7.0.148 → 7.0.149

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 (30) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/index.d.ts +5 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +47 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +2 -1
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +4 -1
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +7 -0
  11. package/docs/03-ai-sdk-core/18-code-mode.mdx +3 -1
  12. package/docs/03-ai-sdk-core/36-transcription.mdx +40 -39
  13. package/docs/03-ai-sdk-core/37-speech.mdx +1 -0
  14. package/docs/03-ai-sdk-core/60-telemetry.mdx +25 -2
  15. package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +1 -1
  16. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +4 -4
  17. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +7 -7
  18. package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +1 -1
  19. package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +1 -1
  20. package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -1
  21. package/docs/07-reference/05-ai-sdk-errors/ai-tool-choice-violation-error.mdx +12 -7
  22. package/package.json +12 -12
  23. package/src/error/tool-choice-violation-error.ts +6 -4
  24. package/src/generate-text/collect-tool-approvals.ts +10 -0
  25. package/src/generate-text/generate-text.ts +24 -12
  26. package/src/generate-text/stream-language-model-call.ts +37 -12
  27. package/src/generate-text/stream-text.ts +4 -1
  28. package/src/types/language-model.ts +2 -0
  29. package/src/ui/chat.ts +40 -12
  30. package/src/ui-message-stream/ui-message-chunks.ts +1 -0
@@ -2465,7 +2465,7 @@ class DefaultStreamTextResult<
2465
2465
  activeTools: prepareStepResult?.activeTools ?? activeTools,
2466
2466
  });
2467
2467
  const {
2468
- executionTools: stepExecutionTools,
2468
+ executionTools,
2469
2469
  modelTools: stepModelTools,
2470
2470
  toolCallerMessages,
2471
2471
  } = prepareToolsForToolCallers({
@@ -2495,6 +2495,8 @@ class DefaultStreamTextResult<
2495
2495
  const stepToolChoice = prepareToolChoice({
2496
2496
  toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
2497
2497
  });
2498
+ const stepExecutionTools =
2499
+ stepToolChoice.type === 'none' ? undefined : executionTools;
2498
2500
 
2499
2501
  const stepMessages = appendToolCallerMessages({
2500
2502
  messages: prepareStepResult?.messages ?? stepInputMessages,
@@ -3215,6 +3217,7 @@ class DefaultStreamTextResult<
3215
3217
  cleanupStepTimeouts();
3216
3218
 
3217
3219
  if (
3220
+ stepFinishReason !== 'interrupted' &&
3218
3221
  // Continue only after all client tool calls have been executed or denied,
3219
3222
  // and if there are client results or pending deferred provider results.
3220
3223
  clientToolCalls.length ===
@@ -70,6 +70,7 @@ export type LanguageModel =
70
70
  * - `content-filter`: content filter violation stopped the model
71
71
  * - `tool-calls`: model triggered tool calls
72
72
  * - `error`: model stopped because of an error
73
+ * - `interrupted`: provider stream ended without a completion signal; output may be incomplete
73
74
  * - `other`: model stopped for other reasons
74
75
  */
75
76
  export type FinishReason =
@@ -78,6 +79,7 @@ export type FinishReason =
78
79
  | 'content-filter'
79
80
  | 'tool-calls'
80
81
  | 'error'
82
+ | 'interrupted'
81
83
  | 'other';
82
84
 
83
85
  /**
package/src/ui/chat.ts CHANGED
@@ -274,6 +274,9 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
274
274
 
275
275
  private pendingMessagePreparations = new Set<AbortController>();
276
276
  private pendingApprovalMessageId: string | undefined;
277
+ // Keep interruption state after activeResponse is cleared so late tool updates
278
+ // cannot automatically continue an interrupted message.
279
+ private interruptedMessageIds = new Set<string>();
277
280
  private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
278
281
  private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
279
282
  private resumableStreamState:
@@ -638,7 +641,9 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
638
641
  this.status !== 'submitted' &&
639
642
  this.sendAutomaticallyWhen
640
643
  ) {
641
- const shouldSend = await this.shouldSendAutomatically();
644
+ const shouldSend = await this.shouldSendAutomatically(
645
+ messages[messageIndex].id,
646
+ );
642
647
 
643
648
  if (shouldSend) {
644
649
  // no await to avoid deadlocking
@@ -735,7 +740,9 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
735
740
  this.status !== 'submitted' &&
736
741
  this.sendAutomaticallyWhen
737
742
  ) {
738
- const shouldSend = await this.shouldSendAutomatically();
743
+ const shouldSend = await this.shouldSendAutomatically(
744
+ messages[messageIndex].id,
745
+ );
739
746
 
740
747
  if (shouldSend) {
741
748
  // no await to avoid deadlocking
@@ -794,19 +801,29 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
794
801
  this.transport.close?.();
795
802
  };
796
803
 
797
- private async shouldSendAutomatically(): Promise<boolean> {
798
- if (!this.sendAutomaticallyWhen) return false;
804
+ private async shouldSendAutomatically(
805
+ messageId = this.lastMessage?.id,
806
+ ): Promise<boolean> {
807
+ const isInterrupted = (id: string | undefined) =>
808
+ id != null && this.interruptedMessageIds.has(id);
799
809
 
800
- const result = this.sendAutomaticallyWhen({
810
+ if (
811
+ !this.sendAutomaticallyWhen ||
812
+ isInterrupted(messageId) ||
813
+ isInterrupted(this.lastMessage?.id)
814
+ ) {
815
+ return false;
816
+ }
817
+
818
+ const result = await this.sendAutomaticallyWhen({
801
819
  messages: this.state.messages,
802
820
  });
803
821
 
804
- // Check if result is a promise
805
- if (result && typeof result === 'object' && 'then' in result) {
806
- return await result;
807
- }
808
-
809
- return result as boolean;
822
+ return (
823
+ result &&
824
+ !isInterrupted(messageId) &&
825
+ !isInterrupted(this.lastMessage?.id)
826
+ );
810
827
  }
811
828
 
812
829
  private async runAutomaticRequest(
@@ -1182,6 +1199,13 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
1182
1199
  } finally {
1183
1200
  try {
1184
1201
  if (activeResponse) {
1202
+ const { message, finishReason } = activeResponse.state;
1203
+ if (finishReason === 'interrupted') {
1204
+ this.interruptedMessageIds.add(message.id);
1205
+ } else if (!isAbort && !isError) {
1206
+ this.interruptedMessageIds.delete(message.id);
1207
+ }
1208
+
1185
1209
  this.onFinish?.({
1186
1210
  message: activeResponse.state.message,
1187
1211
  messages: this.state.messages,
@@ -1201,7 +1225,11 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
1201
1225
  }
1202
1226
 
1203
1227
  // automatically send the message if the sendAutomaticallyWhen function returns true
1204
- if (!isAbort && !isError) {
1228
+ if (
1229
+ !isAbort &&
1230
+ !isError &&
1231
+ activeResponse?.state.finishReason !== 'interrupted'
1232
+ ) {
1205
1233
  await this.runAutomaticRequest(() =>
1206
1234
  this.makeRequest({
1207
1235
  trigger: 'submit-message',
@@ -206,6 +206,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
206
206
  'content-filter',
207
207
  'tool-calls',
208
208
  'error',
209
+ 'interrupted',
209
210
  'other',
210
211
  ] as const satisfies readonly FinishReason[])
211
212
  .optional(),