ai 7.0.105 → 7.0.107

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 (34) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/index.js +278 -88
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal/index.d.ts +15 -3
  5. package/dist/internal/index.js +153 -46
  6. package/dist/internal/index.js.map +1 -1
  7. package/docs/03-ai-sdk-core/19-tool-search.mdx +4 -2
  8. package/docs/07-reference/01-ai-sdk-core/23-tool-search.mdx +3 -2
  9. package/package.json +11 -11
  10. package/src/batch/batch.ts +1 -0
  11. package/src/generate-object/generate-object.ts +1 -0
  12. package/src/generate-object/stream-object.ts +1 -0
  13. package/src/generate-speech/generate-speech.ts +44 -4
  14. package/src/generate-text/generate-text.ts +28 -15
  15. package/src/generate-text/invoke-tool-callbacks-from-stream.ts +68 -16
  16. package/src/generate-text/prune-messages.ts +13 -0
  17. package/src/generate-text/stream-language-model-call.ts +1 -0
  18. package/src/generate-text/stream-text.ts +29 -12
  19. package/src/generate-video/generate-video.ts +61 -10
  20. package/src/middleware/extract-json-middleware.ts +35 -9
  21. package/src/middleware/simulate-streaming-middleware.ts +7 -1
  22. package/src/middleware/wrap-provider.ts +2 -0
  23. package/src/prompt/convert-to-language-model-prompt.ts +4 -2
  24. package/src/prompt/create-tool-model-output.ts +20 -11
  25. package/src/test/mock-server-response.ts +19 -3
  26. package/src/text-stream/pipe-text-stream-to-response.ts +3 -5
  27. package/src/ui/http-chat-transport.ts +12 -2
  28. package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +1 -1
  29. package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +2 -1
  30. package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +1 -3
  31. package/src/ui-message-stream/read-ui-message-stream.ts +13 -4
  32. package/src/util/data-url.ts +13 -1
  33. package/src/util/download/download-function.ts +5 -2
  34. package/src/util/write-to-server-response.ts +7 -3
@@ -10,8 +10,10 @@ definition into its initial context. Register tools with `deferLoading: true`;
10
10
  search matches their names and descriptions and makes them available on the
11
11
  **next model step**.
12
12
 
13
- Use it with `generateText`, `streamText`, or `ToolLoopAgent`. The factory takes no
14
- arguments; the model supplies a search query.
13
+ Use it with `generateText`, `streamText`, `ToolLoopAgent`, or `WorkflowAgent` from
14
+ `@ai-sdk/workflow`. The factory takes no arguments; the model supplies a search
15
+ query. `WorkflowAgent` supports direct tool calling; the other APIs also support
16
+ cache-preserving code mode.
15
17
 
16
18
  ## Direct Tool Calling
17
19
 
@@ -7,8 +7,9 @@ description: Search deferred tools and load their definitions on demand for dire
7
7
 
8
8
  Creates a tool that searches the surrounding generation's deferred tools by name
9
9
  and description. The factory takes no arguments. Use it with `generateText`,
10
- `streamText`, or `ToolLoopAgent`, either with direct tool calling or with code mode
11
- configured with `toolDiscovery: 'conversation'`.
10
+ `streamText`, `ToolLoopAgent`, or `WorkflowAgent` from `@ai-sdk/workflow`.
11
+ `WorkflowAgent` supports direct tool calling. The other APIs also support code
12
+ mode configured with `toolDiscovery: 'conversation'`.
12
13
 
13
14
  ```ts
14
15
  import { toolSearch } from 'ai';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.105",
3
+ "version": "7.0.107",
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.85",
45
+ "@ai-sdk/gateway": "4.0.87",
46
46
  "@ai-sdk/provider": "4.0.17",
47
- "@ai-sdk/provider-utils": "5.0.43"
47
+ "@ai-sdk/provider-utils": "5.0.45"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.86",
51
- "@ai-sdk/deepseek": "3.0.47",
52
- "@ai-sdk/google": "4.0.74",
53
- "@ai-sdk/groq": "4.0.44",
54
- "@ai-sdk/huggingface": "2.0.51",
55
- "@ai-sdk/moonshotai": "3.0.52",
56
- "@ai-sdk/openai": "4.0.69",
50
+ "@ai-sdk/amazon-bedrock": "5.0.88",
51
+ "@ai-sdk/deepseek": "3.0.49",
52
+ "@ai-sdk/google": "4.0.76",
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.71",
57
57
  "@ai-sdk/test-server": "2.0.1",
58
- "@ai-sdk/xai": "5.0.2",
58
+ "@ai-sdk/xai": "5.0.4",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -202,6 +202,7 @@ export async function startBatch<
202
202
  prompt: standardizedPrompt,
203
203
  supportedUrls,
204
204
  download: undefined,
205
+ abortSignal: operationAbortSignal,
205
206
  provider: batchApi.provider.split('.')[0],
206
207
  }),
207
208
  tools: preparedTools,
@@ -379,6 +379,7 @@ export async function generateObject<
379
379
  prompt: standardizedPrompt,
380
380
  supportedUrls: await model.supportedUrls,
381
381
  download,
382
+ abortSignal,
382
383
  provider: model.provider.split('.')[0],
383
384
  });
384
385
 
@@ -612,6 +612,7 @@ class DefaultStreamObjectResult<
612
612
  prompt: standardizedPrompt,
613
613
  supportedUrls: await model.supportedUrls,
614
614
  download,
615
+ abortSignal,
615
616
  provider: model.provider.split('.')[0],
616
617
  }),
617
618
  providerOptions,
@@ -155,14 +155,19 @@ export async function generateSpeech({
155
155
  model: resolvedModel.modelId,
156
156
  });
157
157
 
158
+ const detectedMediaType = detectMediaType({
159
+ data: result.audio,
160
+ topLevelType: 'audio',
161
+ });
162
+
158
163
  return new DefaultSpeechResult({
159
164
  audio: new DefaultGeneratedAudioFile({
160
165
  data: result.audio,
161
166
  mediaType:
162
- detectMediaType({
163
- data: result.audio,
164
- topLevelType: 'audio',
165
- }) ?? 'audio/mp3',
167
+ detectedMediaType ??
168
+ getResponseAudioMediaType(result.response.headers) ??
169
+ getOutputFormatMediaType(outputFormat) ??
170
+ 'audio/mp3',
166
171
  }),
167
172
  warnings: result.warnings,
168
173
  responses: [result.response],
@@ -170,6 +175,41 @@ export async function generateSpeech({
170
175
  });
171
176
  }
172
177
 
178
+ function getResponseAudioMediaType(
179
+ headers: Record<string, string> | undefined,
180
+ ): string | undefined {
181
+ const mediaType = Object.entries(headers ?? {}).find(
182
+ ([name]) => name.toLowerCase() === 'content-type',
183
+ )?.[1];
184
+
185
+ if (mediaType == null) {
186
+ return undefined;
187
+ }
188
+
189
+ const normalizedMediaType = mediaType.split(';', 1)[0].trim().toLowerCase();
190
+
191
+ if (normalizedMediaType.length === 0) {
192
+ return undefined;
193
+ }
194
+
195
+ return normalizedMediaType.startsWith('audio/')
196
+ ? normalizedMediaType
197
+ : undefined;
198
+ }
199
+
200
+ function getOutputFormatMediaType(outputFormat: string | undefined) {
201
+ if (outputFormat == null) {
202
+ return undefined;
203
+ }
204
+
205
+ const normalizedOutputFormat = outputFormat.trim().toLowerCase();
206
+
207
+ return normalizedOutputFormat === 'pcm' ||
208
+ normalizedOutputFormat === 'audio/pcm'
209
+ ? 'audio/pcm'
210
+ : undefined;
211
+ }
212
+
173
213
  class DefaultSpeechResult implements SpeechResult {
174
214
  readonly audio: GeneratedAudioFile;
175
215
  readonly warnings: Array<Warning>;
@@ -118,6 +118,7 @@ import type {
118
118
  OnToolExecutionEndCallback,
119
119
  OnToolExecutionStartCallback,
120
120
  } from './tool-execution-events';
121
+ import { validateToolContext } from './validate-tool-context';
121
122
  import type { ToolInputRefinement } from './tool-input-refinement';
122
123
  import type { ToolOrder } from './tool-order';
123
124
  import type { ToolOutput } from './tool-output';
@@ -964,6 +965,7 @@ export async function generateText<
964
965
  },
965
966
  supportedUrls: await stepModel.supportedUrls,
966
967
  download,
968
+ abortSignal: mergedAbortSignal,
967
969
  provider: stepModel.provider.split('.')[0],
968
970
  });
969
971
 
@@ -1182,23 +1184,34 @@ export async function generateText<
1182
1184
  continue;
1183
1185
  }
1184
1186
 
1185
- if (tool.onInputStart != null) {
1186
- await tool.onInputStart({
1187
- toolCallId: toolCall.toolCallId,
1188
- messages: stepMessages,
1189
- abortSignal: mergedAbortSignal,
1190
- context: runtimeContext,
1187
+ if (
1188
+ tool.onInputStart != null ||
1189
+ tool.onInputAvailable != null
1190
+ ) {
1191
+ const context = await validateToolContext({
1192
+ toolName: toolCall.toolName,
1193
+ context: getOwn(toolsContext, toolCall.toolName),
1194
+ contextSchema: tool.contextSchema,
1191
1195
  });
1192
- }
1193
1196
 
1194
- if (tool?.onInputAvailable != null) {
1195
- await tool.onInputAvailable({
1196
- input: toolCall.input,
1197
- toolCallId: toolCall.toolCallId,
1198
- messages: stepMessages,
1199
- abortSignal: mergedAbortSignal,
1200
- context: runtimeContext,
1201
- });
1197
+ if (tool.onInputStart != null) {
1198
+ await tool.onInputStart({
1199
+ toolCallId: toolCall.toolCallId,
1200
+ messages: stepMessages,
1201
+ abortSignal: mergedAbortSignal,
1202
+ context,
1203
+ });
1204
+ }
1205
+
1206
+ if (tool.onInputAvailable != null) {
1207
+ await tool.onInputAvailable({
1208
+ input: toolCall.input,
1209
+ toolCallId: toolCall.toolCallId,
1210
+ messages: stepMessages,
1211
+ abortSignal: mergedAbortSignal,
1212
+ context,
1213
+ });
1214
+ }
1202
1215
  }
1203
1216
 
1204
1217
  const toolApprovalStatus = await resolveToolApproval({
@@ -1,4 +1,8 @@
1
- import type { Context, ModelMessage, ToolSet } from '@ai-sdk/provider-utils';
1
+ import type {
2
+ InferToolSetContext,
3
+ ModelMessage,
4
+ ToolSet,
5
+ } from '@ai-sdk/provider-utils';
2
6
  import { createIdMap } from '../util/create-id-map';
3
7
  import { getOwn } from '../util/get-own';
4
8
  import type { LanguageModelStreamPart } from './stream-language-model-call';
@@ -6,30 +10,61 @@ import {
6
10
  isStreamRetryAttemptBoundaryPart,
7
11
  type StreamRetryAttemptBoundaryPart,
8
12
  } from './stream-retry-attempt-boundary';
13
+ import { validateToolContext } from './validate-tool-context';
9
14
 
10
15
  type ToolCallbackStreamPart<TOOLS extends ToolSet> =
11
16
  | LanguageModelStreamPart<TOOLS>
12
17
  | StreamRetryAttemptBoundaryPart;
13
18
 
14
- export function invokeToolCallbacksFromStream<
15
- TOOLS extends ToolSet,
16
- RUNTIME_CONTEXT extends Context,
17
- >({
19
+ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
18
20
  stream,
19
21
  tools,
20
22
  stepInputMessages,
21
23
  abortSignal,
22
- runtimeContext,
24
+ toolsContext,
23
25
  }: {
24
26
  stream: ReadableStream<ToolCallbackStreamPart<TOOLS>>;
25
27
  tools: TOOLS | undefined;
26
28
  stepInputMessages: Array<ModelMessage>;
27
29
  abortSignal: AbortSignal | undefined;
28
- runtimeContext: RUNTIME_CONTEXT;
30
+ toolsContext: InferToolSetContext<TOOLS>;
29
31
  }): ReadableStream<ToolCallbackStreamPart<TOOLS>> {
30
32
  if (tools == null) return stream;
31
33
 
32
- const ongoingToolCallToolNames: Record<string, string> = createIdMap();
34
+ let ongoingToolCalls: Record<
35
+ string,
36
+ {
37
+ toolName: string;
38
+ validatedContext: Promise<unknown> | undefined;
39
+ }
40
+ > = createIdMap();
41
+
42
+ const getValidatedContext = ({
43
+ toolCallId,
44
+ toolName,
45
+ }: {
46
+ toolCallId: string;
47
+ toolName: string;
48
+ }): Promise<unknown> => {
49
+ const ongoingToolCall = ongoingToolCalls[toolCallId];
50
+
51
+ if (ongoingToolCall?.validatedContext != null) {
52
+ return ongoingToolCall.validatedContext;
53
+ }
54
+
55
+ const tool = getOwn(tools, toolName);
56
+ const validatedContext = validateToolContext({
57
+ toolName,
58
+ context: getOwn(toolsContext, toolName),
59
+ contextSchema: tool?.contextSchema,
60
+ });
61
+
62
+ if (ongoingToolCall != null) {
63
+ ongoingToolCall.validatedContext = validatedContext;
64
+ }
65
+
66
+ return validatedContext;
67
+ };
33
68
 
34
69
  return stream.pipeThrough(
35
70
  new TransformStream({
@@ -37,12 +72,16 @@ export function invokeToolCallbacksFromStream<
37
72
  controller.enqueue(chunk);
38
73
 
39
74
  if (isStreamRetryAttemptBoundaryPart(chunk)) {
75
+ ongoingToolCalls = createIdMap();
40
76
  return;
41
77
  }
42
78
 
43
79
  switch (chunk.type) {
44
80
  case 'tool-input-start': {
45
- ongoingToolCallToolNames[chunk.id] = chunk.toolName;
81
+ ongoingToolCalls[chunk.id] = {
82
+ toolName: chunk.toolName,
83
+ validatedContext: undefined,
84
+ };
46
85
 
47
86
  const tool = getOwn(tools, chunk.toolName);
48
87
  if (tool?.onInputStart != null) {
@@ -50,7 +89,10 @@ export function invokeToolCallbacksFromStream<
50
89
  toolCallId: chunk.id,
51
90
  messages: stepInputMessages,
52
91
  abortSignal,
53
- context: runtimeContext,
92
+ context: await getValidatedContext({
93
+ toolCallId: chunk.id,
94
+ toolName: chunk.toolName,
95
+ }),
54
96
  });
55
97
  }
56
98
 
@@ -58,7 +100,7 @@ export function invokeToolCallbacksFromStream<
58
100
  }
59
101
 
60
102
  case 'tool-input-delta': {
61
- const toolName = ongoingToolCallToolNames[chunk.id];
103
+ const toolName = ongoingToolCalls[chunk.id]?.toolName;
62
104
  const tool = getOwn(tools, toolName);
63
105
 
64
106
  if (tool?.onInputDelta != null) {
@@ -67,7 +109,10 @@ export function invokeToolCallbacksFromStream<
67
109
  toolCallId: chunk.id,
68
110
  messages: stepInputMessages,
69
111
  abortSignal,
70
- context: runtimeContext,
112
+ context: await getValidatedContext({
113
+ toolCallId: chunk.id,
114
+ toolName,
115
+ }),
71
116
  });
72
117
  }
73
118
 
@@ -75,19 +120,26 @@ export function invokeToolCallbacksFromStream<
75
120
  }
76
121
 
77
122
  case 'tool-call': {
78
- const toolName = ongoingToolCallToolNames[chunk.toolCallId];
123
+ const toolName = ongoingToolCalls[chunk.toolCallId]?.toolName;
79
124
  const tool = getOwn(tools, toolName);
80
125
 
81
- delete ongoingToolCallToolNames[chunk.toolCallId];
126
+ if (!chunk.invalid && tool?.onInputAvailable != null) {
127
+ const validatedContext = getValidatedContext({
128
+ toolCallId: chunk.toolCallId,
129
+ toolName,
130
+ });
131
+
132
+ delete ongoingToolCalls[chunk.toolCallId];
82
133
 
83
- if (tool?.onInputAvailable != null) {
84
134
  await tool.onInputAvailable({
85
135
  input: chunk.input,
86
136
  toolCallId: chunk.toolCallId,
87
137
  messages: stepInputMessages,
88
138
  abortSignal,
89
- context: runtimeContext,
139
+ context: await validatedContext,
90
140
  });
141
+ } else {
142
+ delete ongoingToolCalls[chunk.toolCallId];
91
143
  }
92
144
  }
93
145
  }
@@ -121,6 +121,7 @@ export function pruneMessages({
121
121
  }
122
122
  }
123
123
 
124
+ const approvalIdToToolCallId = new Map<string, string>();
124
125
  const approvalIdToToolName = new Map<string, string>();
125
126
  for (const message of messages) {
126
127
  if (
@@ -129,6 +130,8 @@ export function pruneMessages({
129
130
  ) {
130
131
  for (const part of message.content) {
131
132
  if (part.type === 'tool-approval-request') {
133
+ approvalIdToToolCallId.set(part.approvalId, part.toolCallId);
134
+
132
135
  const toolName = toolCallIdToToolName.get(part.toolCallId);
133
136
  if (toolName != null) {
134
137
  approvalIdToToolName.set(part.approvalId, toolName);
@@ -138,6 +141,16 @@ export function pruneMessages({
138
141
  }
139
142
  }
140
143
 
144
+ // Approval requests depend on their originating tool call. When an
145
+ // approval response is in the retained messages, trace through its request
146
+ // so pruning does not leave a pending approval without the call to execute.
147
+ for (const approvalId of keptApprovalIds) {
148
+ const toolCallId = approvalIdToToolCallId.get(approvalId);
149
+ if (toolCallId != null) {
150
+ keptToolCallIds.add(toolCallId);
151
+ }
152
+ }
153
+
141
154
  messages = messages.map((message, messageIndex) => {
142
155
  if (
143
156
  (message.role !== 'assistant' && message.role !== 'tool') ||
@@ -306,6 +306,7 @@ export async function streamLanguageModelCall<
306
306
  },
307
307
  supportedUrls: await resolvedModel.supportedUrls,
308
308
  download,
309
+ abortSignal,
309
310
  provider: resolvedModel.provider.split('.')[0],
310
311
  });
311
312
 
@@ -968,14 +968,16 @@ function createOutputTransformStream<
968
968
  let text = '';
969
969
  let textChunk = '';
970
970
  let textProviderMetadata: ProviderMetadata | undefined = undefined;
971
- let lastPublishedValue = '';
971
+ let lastPublishedValue: string | undefined = undefined;
972
+ let hasPublishedValue = false;
972
973
 
973
- function resetAttemptState() {
974
+ function resetOutputState() {
974
975
  firstTextChunkId = undefined;
975
976
  text = '';
976
977
  textChunk = '';
977
978
  textProviderMetadata = undefined;
978
- lastPublishedValue = '';
979
+ lastPublishedValue = undefined;
980
+ hasPublishedValue = false;
979
981
  }
980
982
 
981
983
  function enqueueChunk({
@@ -1020,11 +1022,15 @@ function createOutputTransformStream<
1020
1022
  >({
1021
1023
  async transform(chunk, controller) {
1022
1024
  if (isStreamRetryBoundaryPart(chunk)) {
1023
- resetAttemptState();
1025
+ resetOutputState();
1024
1026
  controller.enqueue(chunk);
1025
1027
  return;
1026
1028
  }
1027
1029
 
1030
+ if (chunk.type === 'start-step') {
1031
+ resetOutputState();
1032
+ }
1033
+
1028
1034
  // ensure that we publish the last text chunk before the step finish:
1029
1035
  if (chunk.type === 'finish-step' && textChunk.length > 0) {
1030
1036
  publishTextChunk({ controller });
@@ -1096,9 +1102,10 @@ function createOutputTransformStream<
1096
1102
  typeof result.partial === 'string'
1097
1103
  ? result.partial
1098
1104
  : JSON.stringify(result.partial);
1099
- if (currentValue !== lastPublishedValue) {
1105
+ if (!hasPublishedValue || currentValue !== lastPublishedValue) {
1100
1106
  publishTextChunk({ controller, partialOutput: result.partial });
1101
1107
  lastPublishedValue = currentValue;
1108
+ hasPublishedValue = true;
1102
1109
  }
1103
1110
  }
1104
1111
  },
@@ -1414,6 +1421,8 @@ class DefaultStreamTextResult<
1414
1421
  const initialResponseMessages: Array<ResponseMessage> = [];
1415
1422
  let stepMessagesForNextStep: Array<ModelMessage> | undefined;
1416
1423
  let currentStepMessages: Array<ModelMessage> = [];
1424
+ let isAborted = false;
1425
+ let currentStepModel = model;
1417
1426
 
1418
1427
  // provider-assigned text/reasoning part IDs are only unique within a
1419
1428
  // single model call (e.g. Anthropic uses the content block index, which
@@ -1661,8 +1670,8 @@ class DefaultStreamTextResult<
1661
1670
  new DefaultStepResult({
1662
1671
  callId,
1663
1672
  stepNumber: recordedSteps.length,
1664
- provider: model.provider,
1665
- modelId: model.modelId,
1673
+ provider: currentStepModel.provider,
1674
+ modelId: currentStepModel.modelId,
1666
1675
  runtimeContext,
1667
1676
  toolsContext,
1668
1677
  content: recordedContent,
@@ -1691,8 +1700,8 @@ class DefaultStreamTextResult<
1691
1700
 
1692
1701
  logWarnings({
1693
1702
  warnings: recordedWarnings,
1694
- provider: model.provider,
1695
- model: model.modelId,
1703
+ provider: currentStepModel.provider,
1704
+ model: currentStepModel.modelId,
1696
1705
  });
1697
1706
 
1698
1707
  recordedSteps.push(currentStepResult);
@@ -1743,6 +1752,10 @@ class DefaultStreamTextResult<
1743
1752
  // aggregate results:
1744
1753
  self._steps.resolve(recordedSteps);
1745
1754
 
1755
+ if (isAborted) {
1756
+ return;
1757
+ }
1758
+
1746
1759
  // call onEnd callback:
1747
1760
  const finalStep = recordedSteps[recordedSteps.length - 1];
1748
1761
  const content = recordedSteps.flatMap(step => step.content);
@@ -1845,6 +1858,8 @@ class DefaultStreamTextResult<
1845
1858
  async pull(controller) {
1846
1859
  // abort handling:
1847
1860
  async function abort() {
1861
+ isAborted = true;
1862
+
1848
1863
  await notify({
1849
1864
  event: {
1850
1865
  callId,
@@ -1885,6 +1900,7 @@ class DefaultStreamTextResult<
1885
1900
  if (isAbortError(error) && abortSignal?.aborted) {
1886
1901
  await abort();
1887
1902
  } else {
1903
+ await telemetryDispatcher.onError?.({ callId, error });
1888
1904
  controller.error(error);
1889
1905
  }
1890
1906
  }
@@ -2328,6 +2344,7 @@ class DefaultStreamTextResult<
2328
2344
  const stepModel = resolveLanguageModel(
2329
2345
  prepareStepResult?.model ?? model,
2330
2346
  );
2347
+ currentStepModel = stepModel;
2331
2348
 
2332
2349
  const stepActiveTools = filterActiveTools({
2333
2350
  tools,
@@ -2658,7 +2675,7 @@ class DefaultStreamTextResult<
2658
2675
  tools: stepExecutionTools as TOOLS,
2659
2676
  stepInputMessages: stepMessages,
2660
2677
  abortSignal,
2661
- runtimeContext,
2678
+ toolsContext,
2662
2679
  });
2663
2680
 
2664
2681
  // Create child spans under the current step context.
@@ -2752,7 +2769,7 @@ class DefaultStreamTextResult<
2752
2769
  const createStepResponse = () => ({
2753
2770
  id: generateId(),
2754
2771
  timestamp: new Date(),
2755
- modelId: model.modelId,
2772
+ modelId: stepModel.modelId,
2756
2773
  });
2757
2774
  let stepResponse: {
2758
2775
  id: string;
@@ -3375,7 +3392,7 @@ class DefaultStreamTextResult<
3375
3392
  InferPartialOutput<OUTPUT>
3376
3393
  >({
3377
3394
  transform({ partialOutput }, controller) {
3378
- if (partialOutput != null) {
3395
+ if (partialOutput !== undefined) {
3379
3396
  controller.enqueue(partialOutput);
3380
3397
  }
3381
3398
  },