ai 7.0.32 → 7.0.34

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.
@@ -101,7 +101,22 @@ DevTools captures the following information from your AI SDK calls:
101
101
  - **Input parameters and prompts**: View the complete input sent to your LLM
102
102
  - **Output content and tool calls**: Inspect generated text and tool invocations
103
103
  - **Token usage and timing**: Monitor resource consumption and performance
104
- - **Raw provider data**: Access complete request and response payloads
104
+ - **Raw provider data**: Access provider request and response payloads when body retention is enabled
105
+
106
+ Telemetry is enabled automatically, but AI SDK 7 excludes raw request and response bodies from step results by default. To make them available to DevTools for `generateText`, enable body retention on the call:
107
+
108
+ ```ts highlight="4-7"
109
+ const result = await generateText({
110
+ model: openai('gpt-4o'),
111
+ prompt: 'Hello!',
112
+ include: {
113
+ requestBody: true,
114
+ responseBody: true,
115
+ },
116
+ });
117
+ ```
118
+
119
+ `ToolLoopAgent` accepts the same `include` setting in its constructor. For `streamText` and `ToolLoopAgent.stream()`, only the request body can be retained; use `include: { requestBody: true }`.
105
120
 
106
121
  ### Runs and steps
107
122
 
@@ -129,6 +144,6 @@ DevTools stores all AI interactions locally in plain text files, including:
129
144
  - User prompts and messages
130
145
  - LLM responses
131
146
  - Tool call arguments and results
132
- - API request and response data
147
+ - API request and response data when body retention is enabled
133
148
 
134
149
  **Only use DevTools in local development environments.** Do not enable DevTools in production or when handling sensitive data.
@@ -114,7 +114,7 @@ import { dataPartsSchema, metadataSchema } from '@util/schemas';
114
114
  const tools = {
115
115
  weather: tool({
116
116
  description: 'Get weather information',
117
- parameters: z.object({
117
+ inputSchema: z.object({
118
118
  location: z.string(),
119
119
  units: z.enum(['celsius', 'fahrenheit']),
120
120
  }),
@@ -238,7 +238,7 @@ export async function POST(request) {
238
238
  tools: {
239
239
  displayWeather: {
240
240
  description: 'Display the weather for a location',
241
- parameters: z.object({
241
+ inputSchema: z.object({
242
242
  latitude: z.number(),
243
243
  longitude: z.number(),
244
244
  }),
@@ -55,7 +55,7 @@ const dataSchemas = {
55
55
  const tools = {
56
56
  weather: tool({
57
57
  description: 'Get weather info',
58
- parameters: z.object({
58
+ inputSchema: z.object({
59
59
  location: z.string(),
60
60
  }),
61
61
  execute: async ({ location }) => `Weather in ${location}: sunny`,
@@ -61,7 +61,7 @@ const dataSchemas = {
61
61
  const tools = {
62
62
  weather: tool({
63
63
  description: 'Get weather info',
64
- parameters: z.object({
64
+ inputSchema: z.object({
65
65
  location: z.string(),
66
66
  }),
67
67
  execute: async ({ location }) => `Weather in ${location}: sunny`,
@@ -121,7 +121,7 @@ const result = await generateText({
121
121
  calculate: calculateTool,
122
122
  finalAnswer: {
123
123
  description: 'Provide the final answer to the user',
124
- parameters: z.object({
124
+ inputSchema: z.object({
125
125
  answer: z.string(),
126
126
  }),
127
127
  execute: async ({ answer }) => answer,
@@ -237,7 +237,7 @@ import { z } from 'zod';
237
237
 
238
238
  const weatherTool = tool({
239
239
  description: 'Get the current weather',
240
- parameters: z.object({
240
+ inputSchema: z.object({
241
241
  location: z.string().describe('The city and state'),
242
242
  }),
243
243
  execute: async ({ location }) => {
@@ -25,7 +25,7 @@ You have two options for handling tool results:
25
25
  const tools = {
26
26
  weather: tool({
27
27
  description: 'Get the weather in a location',
28
- parameters: z.object({
28
+ inputSchema: z.object({
29
29
  location: z
30
30
  .string()
31
31
  .describe('The city and state, e.g. "San Francisco, CA"'),
@@ -20,7 +20,7 @@ export async function POST(req: Request) {
20
20
  tools: {
21
21
  weather: {
22
22
  description: 'Get the weather for a location',
23
- parameters: z.object({
23
+ inputSchema: z.object({
24
24
  location: z.string(),
25
25
  }),
26
26
  execute: async ({ location }) => {
@@ -54,7 +54,7 @@ export async function POST(req: Request) {
54
54
  tools: {
55
55
  weather: {
56
56
  description: 'Get the weather for a location',
57
- parameters: z.object({
57
+ inputSchema: z.object({
58
58
  location: z.string(),
59
59
  }),
60
60
  execute: async ({ location }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.32",
3
+ "version": "7.0.34",
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.24",
45
+ "@ai-sdk/gateway": "4.0.26",
46
46
  "@ai-sdk/provider": "4.0.3",
47
- "@ai-sdk/provider-utils": "5.0.11"
47
+ "@ai-sdk/provider-utils": "5.0.12"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@edge-runtime/vm": "^5.0.0",
@@ -25,6 +25,7 @@ import {
25
25
  createDefaultDownloadFunction,
26
26
  type DownloadFunction,
27
27
  } from '../util/download/download-function';
28
+ import { mergeObjects } from '../util/merge-objects';
28
29
  import { convertToLanguageModelV4FilePart } from './file-part-data';
29
30
  import { logWarnings } from '../logger/log-warnings';
30
31
  import type { Warning } from '../types/warning';
@@ -108,7 +109,19 @@ export async function convertToLanguageModelPrompt({
108
109
 
109
110
  const lastCombinedMessage = combinedMessages.at(-1);
110
111
  if (lastCombinedMessage?.role === 'tool') {
112
+ const lastContentPart = lastCombinedMessage.content.at(-1);
113
+ if (
114
+ lastContentPart != null &&
115
+ lastCombinedMessage.providerOptions != null
116
+ ) {
117
+ lastContentPart.providerOptions = mergeObjects(
118
+ lastCombinedMessage.providerOptions,
119
+ lastContentPart.providerOptions,
120
+ );
121
+ }
122
+
111
123
  lastCombinedMessage.content.push(...message.content);
124
+ lastCombinedMessage.providerOptions = message.providerOptions;
112
125
  } else {
113
126
  combinedMessages.push(message);
114
127
  }
@@ -104,13 +104,43 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
104
104
  new TransformStream<UIMessageChunk, InferUIMessageChunk<UI_MESSAGE>>({
105
105
  async transform(chunk, controller) {
106
106
  await runUpdateMessageJob(async ({ state, write }) => {
107
+ function getCurrentStepParts() {
108
+ const parts = state.message.parts;
109
+ let currentStepStartIndex = parts.length - 1;
110
+
111
+ while (
112
+ currentStepStartIndex >= 0 &&
113
+ parts[currentStepStartIndex].type !== 'step-start'
114
+ ) {
115
+ currentStepStartIndex--;
116
+ }
117
+
118
+ return parts.slice(currentStepStartIndex + 1);
119
+ }
120
+
121
+ function getCurrentStepToolInvocations() {
122
+ return getCurrentStepParts().filter(isToolUIPart);
123
+ }
124
+
107
125
  function getToolInvocation(toolCallId: string) {
108
- const toolInvocations = state.message.parts.filter(isToolUIPart);
126
+ const toolInvocations = getCurrentStepToolInvocations();
109
127
 
110
- const toolInvocation = toolInvocations.find(
128
+ let toolInvocation = toolInvocations.find(
111
129
  invocation => invocation.toolCallId === toolCallId,
112
130
  );
113
131
 
132
+ if (toolInvocation == null) {
133
+ const parts = state.message.parts;
134
+
135
+ for (let i = parts.length - 1; i >= 0; i--) {
136
+ const part = parts[i];
137
+ if (isToolUIPart(part) && part.toolCallId === toolCallId) {
138
+ toolInvocation = part;
139
+ break;
140
+ }
141
+ }
142
+ }
143
+
114
144
  if (toolInvocation == null) {
115
145
  throw new UIMessageStreamError({
116
146
  chunkType: 'tool-invocation',
@@ -177,12 +207,15 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
177
207
  providerMetadata?: ProviderMetadata;
178
208
  }
179
209
  ),
210
+ existingPart?: ToolUIPart<InferUIMessageTools<UI_MESSAGE>>,
180
211
  ) {
181
- const part = state.message.parts.find(
182
- part =>
183
- isStaticToolUIPart(part) &&
184
- part.toolCallId === options.toolCallId,
185
- ) as ToolUIPart<InferUIMessageTools<UI_MESSAGE>> | undefined;
212
+ const part =
213
+ existingPart ??
214
+ (getCurrentStepParts().find(
215
+ part =>
216
+ isStaticToolUIPart(part) &&
217
+ part.toolCallId === options.toolCallId,
218
+ ) as ToolUIPart<InferUIMessageTools<UI_MESSAGE>> | undefined);
186
219
 
187
220
  const anyOptions = options as any;
188
221
  const anyPart = part as any;
@@ -284,12 +317,15 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
284
317
  providerMetadata?: ProviderMetadata;
285
318
  }
286
319
  ),
320
+ existingPart?: DynamicToolUIPart,
287
321
  ) {
288
- const part = state.message.parts.find(
289
- part =>
290
- part.type === 'dynamic-tool' &&
291
- part.toolCallId === options.toolCallId,
292
- ) as DynamicToolUIPart | undefined;
322
+ const part =
323
+ existingPart ??
324
+ (getCurrentStepParts().find(
325
+ part =>
326
+ part.type === 'dynamic-tool' &&
327
+ part.toolCallId === options.toolCallId,
328
+ ) as DynamicToolUIPart | undefined);
293
329
 
294
330
  const anyOptions = options as any;
295
331
  const anyPart = part as any;
@@ -540,7 +576,7 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
540
576
 
541
577
  case 'tool-input-start': {
542
578
  const toolInvocations =
543
- state.message.parts.filter(isStaticToolUIPart);
579
+ getCurrentStepParts().filter(isStaticToolUIPart);
544
580
 
545
581
  // add the partial tool call to the map
546
582
  state.partialToolCalls[chunk.toolCallId] = {
@@ -665,7 +701,7 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
665
701
  // When a part already exists for this toolCallId (e.g. from
666
702
  // tool-input-start), honour its type so we update in place
667
703
  // instead of creating a duplicate with a mismatched type.
668
- const existingPart = state.message.parts
704
+ const existingPart = getCurrentStepParts()
669
705
  .filter(isToolUIPart)
670
706
  .find(p => p.toolCallId === chunk.toolCallId);
671
707
  const isDynamic =
@@ -756,31 +792,37 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
756
792
  const toolInvocation = getToolInvocation(chunk.toolCallId);
757
793
 
758
794
  if (toolInvocation.type === 'dynamic-tool') {
759
- updateDynamicToolPart({
760
- toolCallId: chunk.toolCallId,
761
- toolName: toolInvocation.toolName,
762
- state: 'output-available',
763
- input: (toolInvocation as any).input,
764
- output: chunk.output,
765
- preliminary: chunk.preliminary,
766
- providerExecuted: chunk.providerExecuted,
767
- providerMetadata: chunk.providerMetadata,
768
- title: toolInvocation.title,
769
- toolMetadata: toolInvocation.toolMetadata,
770
- });
795
+ updateDynamicToolPart(
796
+ {
797
+ toolCallId: chunk.toolCallId,
798
+ toolName: toolInvocation.toolName,
799
+ state: 'output-available',
800
+ input: (toolInvocation as any).input,
801
+ output: chunk.output,
802
+ preliminary: chunk.preliminary,
803
+ providerExecuted: chunk.providerExecuted,
804
+ providerMetadata: chunk.providerMetadata,
805
+ title: toolInvocation.title,
806
+ toolMetadata: toolInvocation.toolMetadata,
807
+ },
808
+ toolInvocation,
809
+ );
771
810
  } else {
772
- updateToolPart({
773
- toolCallId: chunk.toolCallId,
774
- toolName: getStaticToolName(toolInvocation),
775
- state: 'output-available',
776
- input: (toolInvocation as any).input,
777
- output: chunk.output,
778
- providerExecuted: chunk.providerExecuted,
779
- preliminary: chunk.preliminary,
780
- providerMetadata: chunk.providerMetadata,
781
- title: toolInvocation.title,
782
- toolMetadata: toolInvocation.toolMetadata,
783
- });
811
+ updateToolPart(
812
+ {
813
+ toolCallId: chunk.toolCallId,
814
+ toolName: getStaticToolName(toolInvocation),
815
+ state: 'output-available',
816
+ input: (toolInvocation as any).input,
817
+ output: chunk.output,
818
+ providerExecuted: chunk.providerExecuted,
819
+ preliminary: chunk.preliminary,
820
+ providerMetadata: chunk.providerMetadata,
821
+ title: toolInvocation.title,
822
+ toolMetadata: toolInvocation.toolMetadata,
823
+ },
824
+ toolInvocation as ToolUIPart<InferUIMessageTools<UI_MESSAGE>>,
825
+ );
784
826
  }
785
827
 
786
828
  write();
@@ -791,30 +833,36 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
791
833
  const toolInvocation = getToolInvocation(chunk.toolCallId);
792
834
 
793
835
  if (toolInvocation.type === 'dynamic-tool') {
794
- updateDynamicToolPart({
795
- toolCallId: chunk.toolCallId,
796
- toolName: toolInvocation.toolName,
797
- state: 'output-error',
798
- input: (toolInvocation as any).input,
799
- errorText: chunk.errorText,
800
- providerExecuted: chunk.providerExecuted,
801
- providerMetadata: chunk.providerMetadata,
802
- title: toolInvocation.title,
803
- toolMetadata: toolInvocation.toolMetadata,
804
- });
836
+ updateDynamicToolPart(
837
+ {
838
+ toolCallId: chunk.toolCallId,
839
+ toolName: toolInvocation.toolName,
840
+ state: 'output-error',
841
+ input: (toolInvocation as any).input,
842
+ errorText: chunk.errorText,
843
+ providerExecuted: chunk.providerExecuted,
844
+ providerMetadata: chunk.providerMetadata,
845
+ title: toolInvocation.title,
846
+ toolMetadata: toolInvocation.toolMetadata,
847
+ },
848
+ toolInvocation,
849
+ );
805
850
  } else {
806
- updateToolPart({
807
- toolCallId: chunk.toolCallId,
808
- toolName: getStaticToolName(toolInvocation),
809
- state: 'output-error',
810
- input: (toolInvocation as any).input,
811
- rawInput: (toolInvocation as any).rawInput,
812
- errorText: chunk.errorText,
813
- providerExecuted: chunk.providerExecuted,
814
- providerMetadata: chunk.providerMetadata,
815
- title: toolInvocation.title,
816
- toolMetadata: toolInvocation.toolMetadata,
817
- });
851
+ updateToolPart(
852
+ {
853
+ toolCallId: chunk.toolCallId,
854
+ toolName: getStaticToolName(toolInvocation),
855
+ state: 'output-error',
856
+ input: (toolInvocation as any).input,
857
+ rawInput: (toolInvocation as any).rawInput,
858
+ errorText: chunk.errorText,
859
+ providerExecuted: chunk.providerExecuted,
860
+ providerMetadata: chunk.providerMetadata,
861
+ title: toolInvocation.title,
862
+ toolMetadata: toolInvocation.toolMetadata,
863
+ },
864
+ toolInvocation as ToolUIPart<InferUIMessageTools<UI_MESSAGE>>,
865
+ );
818
866
  }
819
867
 
820
868
  write();