ai 7.0.30 → 7.0.31

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.
@@ -1043,7 +1043,7 @@ The following tool input lifecycle hooks are available:
1043
1043
  - **`onInputDelta`**: Called for each chunk of text as the input is streamed
1044
1044
  - **`onInputAvailable`**: Called when the complete input is available and validated
1045
1045
 
1046
- `onInputStart` and `onInputDelta` are only called in streaming contexts (when using `streamText`). They are not called when using `generateText`.
1046
+ `onInputStart` is always called before `onInputAvailable`, including when using `generateText`. `onInputDelta` is only called in streaming contexts (when using `streamText`).
1047
1047
 
1048
1048
  ### Example
1049
1049
 
@@ -171,7 +171,7 @@ export const weatherTool = tool({
171
171
  isOptional: true,
172
172
  type: '(options: ToolExecutionOptions<CONTEXT>) => void | PromiseLike<void>',
173
173
  description:
174
- 'Optional function that is called when the argument streaming starts. Only called when the tool is used in a streaming context.',
174
+ 'Optional function that is called when the model starts generating the tool input. In non-streaming contexts, it is called immediately before onInputAvailable.',
175
175
  },
176
176
  {
177
177
  name: 'onInputDelta',
@@ -130,7 +130,7 @@ export const customTool = dynamicTool({
130
130
  isOptional: true,
131
131
  type: '(options: ToolExecutionOptions<Context>) => void | PromiseLike<void>',
132
132
  description:
133
- 'Optional function that is called when the argument streaming starts. Only called when the tool is used in a streaming context.'
133
+ 'Optional function that is called when the model starts generating the tool input. In non-streaming contexts, it is called immediately before onInputAvailable.'
134
134
  },
135
135
  {
136
136
  name: 'onInputDelta',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.30",
3
+ "version": "7.0.31",
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,9 +42,9 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.22",
45
+ "@ai-sdk/gateway": "4.0.23",
46
46
  "@ai-sdk/provider": "4.0.3",
47
- "@ai-sdk/provider-utils": "5.0.10"
47
+ "@ai-sdk/provider-utils": "5.0.11"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@edge-runtime/vm": "^5.0.0",
@@ -2,17 +2,18 @@ import type {
2
2
  ModelMessage,
3
3
  ToolApprovalRequest,
4
4
  ToolApprovalResponse,
5
+ ToolResultPart,
5
6
  ToolSet,
6
7
  } from '@ai-sdk/provider-utils';
7
8
  import { InvalidToolApprovalError } from '../error/invalid-tool-approval-error';
8
9
  import { ToolCallNotFoundForApprovalError } from '../error/tool-call-not-found-for-approval-error';
9
10
  import type { TypedToolCall } from './tool-call';
10
- import type { TypedToolResult } from './tool-result';
11
11
 
12
12
  export type CollectedToolApprovals<TOOLS extends ToolSet> = {
13
13
  approvalRequest: ToolApprovalRequest;
14
14
  approvalResponse: ToolApprovalResponse;
15
15
  toolCall: TypedToolCall<TOOLS>;
16
+ existingToolResult?: ToolResultPart;
16
17
  };
17
18
 
18
19
  /**
@@ -75,12 +76,10 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
75
76
  }
76
77
 
77
78
  // gather tool results from the last tool message
78
- const toolResults: Record<string, TypedToolResult<TOOLS>> = Object.create(
79
- null,
80
- );
79
+ const toolResults: Record<string, ToolResultPart> = Object.create(null);
81
80
  for (const part of lastMessage.content) {
82
81
  if (part.type === 'tool-result') {
83
- toolResults[part.toolCallId] = part as TypedToolResult<TOOLS>;
82
+ toolResults[part.toolCallId] = part;
84
83
  }
85
84
  }
86
85
 
@@ -100,7 +99,12 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
100
99
  });
101
100
  }
102
101
 
103
- if (toolResults[approvalRequest.toolCallId] != null) {
102
+ const existingToolResult = toolResults[approvalRequest.toolCallId];
103
+ if (
104
+ existingToolResult != null &&
105
+ (approvalResponse.approved ||
106
+ existingToolResult.output.type !== 'execution-denied')
107
+ ) {
104
108
  continue;
105
109
  }
106
110
 
@@ -116,6 +120,7 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
116
120
  approvalRequest,
117
121
  approvalResponse,
118
122
  toolCall,
123
+ ...(existingToolResult != null ? { existingToolResult } : {}),
119
124
  };
120
125
 
121
126
  if (approvalResponse.approved) {
@@ -682,9 +682,12 @@ export async function generateText<
682
682
  ...collectedDeniedToolApprovals,
683
683
  ...revalidationDeniedToolApprovals,
684
684
  ];
685
+ const deniedToolApprovalsWithoutResults = deniedToolApprovals.filter(
686
+ toolApproval => toolApproval.existingToolResult == null,
687
+ );
685
688
 
686
689
  if (
687
- deniedToolApprovals.length > 0 ||
690
+ deniedToolApprovalsWithoutResults.length > 0 ||
688
691
  localApprovedToolApprovals.length > 0
689
692
  ) {
690
693
  const toolResults = await executeTools({
@@ -741,7 +744,7 @@ export async function generateText<
741
744
  }
742
745
 
743
746
  // add execution denied tool results for all denied tool approvals:
744
- for (const toolApproval of deniedToolApprovals) {
747
+ for (const toolApproval of deniedToolApprovalsWithoutResults) {
745
748
  toolContent.push({
746
749
  type: 'tool-result' as const,
747
750
  toolCallId: toolApproval.toolCall.toolCallId,
@@ -1057,6 +1060,15 @@ export async function generateText<
1057
1060
  continue;
1058
1061
  }
1059
1062
 
1063
+ if (tool.onInputStart != null) {
1064
+ await tool.onInputStart({
1065
+ toolCallId: toolCall.toolCallId,
1066
+ messages: stepMessages,
1067
+ abortSignal: mergedAbortSignal,
1068
+ context: runtimeContext,
1069
+ });
1070
+ }
1071
+
1060
1072
  if (tool?.onInputAvailable != null) {
1061
1073
  await tool.onInputAvailable({
1062
1074
  input: toolCall.input,
@@ -1637,6 +1637,10 @@ class DefaultStreamTextResult<
1637
1637
  ),
1638
1638
  ...revalidationDeniedToolApprovals,
1639
1639
  ];
1640
+ const localDeniedToolApprovalsWithoutResults =
1641
+ localDeniedToolApprovals.filter(
1642
+ toolApproval => toolApproval.existingToolResult == null,
1643
+ );
1640
1644
 
1641
1645
  const deniedProviderExecutedToolApprovals = deniedToolApprovals.filter(
1642
1646
  toolApproval => toolApproval.toolCall.providerExecuted,
@@ -1703,7 +1707,10 @@ class DefaultStreamTextResult<
1703
1707
  );
1704
1708
 
1705
1709
  // Local tool results (approved + denied) are sent as tool results:
1706
- if (toolOutputs.length > 0 || localDeniedToolApprovals.length > 0) {
1710
+ if (
1711
+ toolOutputs.length > 0 ||
1712
+ localDeniedToolApprovalsWithoutResults.length > 0
1713
+ ) {
1707
1714
  const localToolContent: ToolContent = [];
1708
1715
 
1709
1716
  // add regular tool results for approved tool calls:
@@ -1726,7 +1733,7 @@ class DefaultStreamTextResult<
1726
1733
  }
1727
1734
 
1728
1735
  // add execution denied tool results for denied local tool approvals:
1729
- for (const toolApproval of localDeniedToolApprovals) {
1736
+ for (const toolApproval of localDeniedToolApprovalsWithoutResults) {
1730
1737
  localToolContent.push({
1731
1738
  type: 'tool-result' as const,
1732
1739
  toolCallId: toolApproval.toolCall.toolCallId,