ai 6.0.218 → 6.0.220

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.
@@ -152,7 +152,7 @@ function detectMediaType({
152
152
  var import_provider_utils2 = require("@ai-sdk/provider-utils");
153
153
 
154
154
  // src/version.ts
155
- var VERSION = true ? "6.0.218" : "0.0.0-test";
155
+ var VERSION = true ? "6.0.220" : "0.0.0-test";
156
156
 
157
157
  // src/util/download/download.ts
158
158
  var download = async ({
@@ -132,7 +132,7 @@ import {
132
132
  } from "@ai-sdk/provider-utils";
133
133
 
134
134
  // src/version.ts
135
- var VERSION = true ? "6.0.218" : "0.0.0-test";
135
+ var VERSION = true ? "6.0.220" : "0.0.0-test";
136
136
 
137
137
  // src/util/download/download.ts
138
138
  var download = async ({
@@ -82,7 +82,7 @@ The open-source community has created the following providers:
82
82
  - [Browser AI Provider](/providers/community-providers/browser-ai) (`browser-ai`)
83
83
  - [Gemini CLI Provider](/providers/community-providers/gemini-cli) (`ai-sdk-provider-gemini-cli`)
84
84
  - [A2A Provider](/providers/community-providers/a2a) (`a2a-ai-provider`)
85
- - [SAP-AI Provider](/providers/community-providers/sap-ai) (`@mymediset/sap-ai-provider`)
85
+ - [SAP AI Core Provider](/providers/community-providers/sap-ai) (`@jerome-benoit/sap-ai-provider`)
86
86
  - [AI/ML API Provider](/providers/community-providers/aimlapi) (`@ai-ml.api/aimlapi-vercel-ai`)
87
87
  - [MCP Sampling Provider](/providers/community-providers/mcp-sampling) (`@mcpc-tech/mcp-sampling-ai-provider`)
88
88
  - [ACP Provider](/providers/community-providers/acp) (`@mcpc-tech/acp-ai-provider`)
@@ -207,6 +207,55 @@ The `bankId` identifies the memory store and is typically a user ID. In multi-us
207
207
 
208
208
  See the [Hindsight provider documentation](/providers/community-providers/hindsight) for full setup and configuration.
209
209
 
210
+ ### MongoDB
211
+
212
+ [`@mongodb-developer/vercel-ai-memory`](https://www.npmjs.com/package/@mongodb-developer/vercel-ai-memory) provides MongoDB Atlas-backed persistent memory with five structured tiers: **Session**, **Semantic**, **Procedural**, **Episodic**, and **Scratchpad**. Retrieval is powered by Atlas Vector Search using any AI SDK embedding model, with automatic index creation and per-type retention policies.
213
+
214
+ ```bash
215
+ pnpm add @mongodb-developer/vercel-ai-memory
216
+ ```
217
+
218
+ This integration targets AI SDK v6 because it relies on v6 APIs such as `ModelMessage`, `ToolLoopAgent`, and `isLoopFinished()`:
219
+
220
+ ```json
221
+ {
222
+ "peerDependencies": {
223
+ "ai": "^6.0.0",
224
+ "mongodb": "^6.0.0",
225
+ "zod": "^3.0.0"
226
+ }
227
+ }
228
+ ```
229
+
230
+ ```ts
231
+ import { createMongoDBMemory } from '@mongodb-developer/vercel-ai-memory';
232
+ import { openai } from '@ai-sdk/openai';
233
+ import { ToolLoopAgent, isLoopFinished } from 'ai';
234
+
235
+ // Create the memory instance once at module/server level
236
+ const mongodbMemory = createMongoDBMemory({
237
+ uri: process.env.MONGODB_URI!,
238
+ embedder: openai.embedding('text-embedding-3-small'),
239
+ });
240
+
241
+ // Scope to a user and session per request
242
+ const agent = new ToolLoopAgent({
243
+ model: openai('gpt-4.1'),
244
+ tools: mongodbMemory({ userId: 'alice', sessionId: 'sess-001' }),
245
+ stopWhen: isLoopFinished(),
246
+ });
247
+
248
+ const result = await agent.generate({
249
+ prompt: 'My name is Alice and I love hiking. Remember that.',
250
+ });
251
+ ```
252
+
253
+ `isLoopFinished()` lets the agent keep running until the tool loop naturally finishes, which is useful when memory tools need to read and write before the final response.
254
+
255
+ Session memory supports two modes: **tool-driven** (the LLM decides when to read/write — good for prototypes) and **hook-driven** (the runtime persists every turn via `prepareCall` + `onFinish` hooks — recommended for production). The other memory tiers (semantic, procedural, episodic, scratchpad) are always LLM-controlled and selective by design.
256
+
257
+ MongoDB memory works with any AI SDK model and embedding provider, with no vendor lock-in beyond MongoDB Atlas.
258
+
210
259
  **When to use memory providers**: these providers are a good fit when you want memory without building any storage infrastructure. The tradeoff is that the provider controls memory behavior, so you have less visibility into what gets stored and how it is retrieved. You also take on a dependency on an external service.
211
260
 
212
261
  ## Custom Tool
@@ -219,8 +219,8 @@ try {
219
219
  | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-2` (+ variants) |
220
220
  | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-3` (+ variants) |
221
221
  | [Gladia](/providers/ai-sdk-providers/gladia#transcription-models) | `default` |
222
- | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `best` |
223
- | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `nano` |
222
+ | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-5-pro` |
223
+ | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
224
224
  | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `whisper` |
225
225
  | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `wizper` |
226
226
  | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_2` |
@@ -307,9 +307,13 @@ export const yourLogMiddleware: LanguageModelV3Middleware = {
307
307
  console.log(`params: ${JSON.stringify(params, null, 2)}`);
308
308
 
309
309
  const result = await doGenerate();
310
+ const generatedText = result.content
311
+ .filter(part => part.type === 'text')
312
+ .map(part => part.text)
313
+ .join('');
310
314
 
311
315
  console.log('doGenerate finished');
312
- console.log(`generated text: ${result.text}`);
316
+ console.log(`generated text: ${generatedText}`);
313
317
 
314
318
  return result;
315
319
  },
@@ -437,12 +441,16 @@ import type { LanguageModelV3Middleware } from '@ai-sdk/provider';
437
441
 
438
442
  export const yourGuardrailMiddleware: LanguageModelV3Middleware = {
439
443
  wrapGenerate: async ({ doGenerate }) => {
440
- const { text, ...rest } = await doGenerate();
444
+ const result = await doGenerate();
441
445
 
442
446
  // filtering approach, e.g. for PII or other sensitive information:
443
- const cleanedText = text?.replace(/badword/g, '<REDACTED>');
447
+ const content = result.content.map(part =>
448
+ part.type === 'text'
449
+ ? { ...part, text: part.text.replace(/badword/g, '<REDACTED>') }
450
+ : part,
451
+ );
444
452
 
445
- return { text: cleanedText, ...rest };
453
+ return { ...result, content };
446
454
  },
447
455
 
448
456
  // here you would implement the guardrail logic for streaming
@@ -345,11 +345,20 @@ By default, message IDs are generated client-side:
345
345
  - AI response message IDs are generated by `streamText` on the server
346
346
 
347
347
  For applications without persistence, client-side ID generation works perfectly.
348
- However, **for persistence, you need server-side generated IDs** to ensure consistency across sessions and prevent ID conflicts when messages are stored and retrieved.
348
+ However, **for persistence, you should use IDs that are stable before messages
349
+ are stored** to ensure consistency across sessions and prevent ID conflicts when
350
+ messages are restored.
351
+
352
+ The server-side options below control IDs for generated assistant response
353
+ messages. User message IDs are created by `useChat` before the request is sent
354
+ to your API route, so keep those client-generated IDs when saving incoming
355
+ request messages, or generate and persist your own user message IDs before
356
+ sending/storing them.
349
357
 
350
358
  ### Setting Up Server-side ID Generation
351
359
 
352
- When implementing persistence, you have two options for generating server-side IDs:
360
+ When implementing persistence, you have two options for generating server-side
361
+ IDs for assistant response messages:
353
362
 
354
363
  1. **Using `generateMessageId` in `toUIMessageStreamResponse`**
355
364
  2. **Setting IDs in your start message part with `createUIMessageStream`**
@@ -40,7 +40,7 @@ You can create a GitHub repository from within your terminal, or on [github.com]
40
40
 
41
41
  To create your GitHub repository:
42
42
 
43
- 1. Navigate to [github.com](http://github.com/)
43
+ 1. Navigate to [github.com](https://github.com/)
44
44
  2. In the top right corner, click the "plus" icon and select "New repository"
45
45
  3. Pick a name for your repository (this can be anything)
46
46
  4. Click "Create repository"
@@ -139,7 +139,7 @@ export const weatherTool = tool({
139
139
  isOptional: true,
140
140
  type: '({toolCallId: string; input: INPUT; output: OUTPUT}) => ToolResultOutput | PromiseLike<ToolResultOutput>',
141
141
  description:
142
- 'Optional conversion function that maps the tool result to an output that can be used by the language model. If not provided, the tool result will be sent as a JSON object.',
142
+ 'Optional conversion function that maps the tool result to an output that can be used by the language model. If not provided, the tool result will be sent as a JSON object. This function is invoked on the server by "convertToModelMessages", so ensure that you pass the same "tools" (ToolSet) to both "convertToModelMessages" and "streamText" (or other generation APIs).',
143
143
  },
144
144
  {
145
145
  name: 'onInputStart',
@@ -80,6 +80,11 @@ It also contains the following helper functions:
80
80
  description: 'Creates a client for connecting to MCP servers.',
81
81
  href: '/docs/reference/ai-sdk-core/create-mcp-client',
82
82
  },
83
+ {
84
+ title: 'validateJSONRPCMessage()',
85
+ description: 'Validates unknown values as MCP JSON-RPC messages.',
86
+ href: '/docs/reference/ai-sdk-core/validate-json-rpc-message',
87
+ },
83
88
  {
84
89
  title: 'jsonSchema()',
85
90
  description: 'Creates AI SDK compatible JSON schema objects.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "6.0.218",
3
+ "version": "6.0.220",
4
4
  "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.",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -45,9 +45,9 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@opentelemetry/api": "^1.9.0",
48
- "@ai-sdk/gateway": "3.0.142",
48
+ "@ai-sdk/gateway": "3.0.144",
49
49
  "@ai-sdk/provider": "3.0.13",
50
- "@ai-sdk/provider-utils": "4.0.35"
50
+ "@ai-sdk/provider-utils": "4.0.36"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@edge-runtime/vm": "^5.0.0",
@@ -151,6 +151,16 @@ export type ToolLoopAgentSettings<
151
151
  * Prepare the parameters for the generateText or streamText call.
152
152
  *
153
153
  * You can use this to have templates based on call options.
154
+ *
155
+ * The design requires you to pass call parameters as follows to
156
+ * allow for the removal of parameters from the original settings
157
+ * by setting them to `undefined`:
158
+ *
159
+ * ```
160
+ * prepareCall: ({ options, ...rest }) => ({
161
+ * ...rest,
162
+ * }),
163
+ * ```
154
164
  */
155
165
  prepareCall?: (
156
166
  options: Omit<
@@ -19,6 +19,7 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
19
19
  tools: TOOLS | undefined;
20
20
  }): Promise<Array<AssistantModelMessage | ToolModelMessage>> {
21
21
  const responseMessages: Array<AssistantModelMessage | ToolModelMessage> = [];
22
+ const toolCallOrder = new Map<string, number>();
22
23
 
23
24
  const content: AssistantContent = [];
24
25
  for (const part of inputContent) {
@@ -64,6 +65,9 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
64
65
  });
65
66
  break;
66
67
  case 'tool-call':
68
+ if (!toolCallOrder.has(part.toolCallId)) {
69
+ toolCallOrder.set(part.toolCallId, toolCallOrder.size);
70
+ }
67
71
  content.push({
68
72
  type: 'tool-call',
69
73
  toolCallId: part.toolCallId,
@@ -157,9 +161,49 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
157
161
  if (toolResultContent.length > 0) {
158
162
  responseMessages.push({
159
163
  role: 'tool',
160
- content: toolResultContent,
164
+ content: sortToolResultContentByToolCallOrder({
165
+ toolResultContent,
166
+ toolCallOrder,
167
+ }),
161
168
  });
162
169
  }
163
170
 
164
171
  return responseMessages;
165
172
  }
173
+
174
+ function sortToolResultContentByToolCallOrder({
175
+ toolResultContent,
176
+ toolCallOrder,
177
+ }: {
178
+ toolResultContent: ToolContent;
179
+ toolCallOrder: Map<string, number>;
180
+ }): ToolContent {
181
+ const sortedToolResults = toolResultContent
182
+ .filter(part => part.type === 'tool-result')
183
+ .map((part, index) => ({ part, index }))
184
+ .sort((a, b) => {
185
+ const aOrder = toolCallOrder.get(a.part.toolCallId);
186
+ const bOrder = toolCallOrder.get(b.part.toolCallId);
187
+
188
+ if (aOrder == null && bOrder == null) {
189
+ return a.index - b.index;
190
+ }
191
+
192
+ if (aOrder == null) {
193
+ return 1;
194
+ }
195
+
196
+ if (bOrder == null) {
197
+ return -1;
198
+ }
199
+
200
+ return aOrder - bOrder || a.index - b.index;
201
+ })
202
+ .map(({ part }) => part);
203
+
204
+ let toolResultIndex = 0;
205
+
206
+ return toolResultContent.map(part =>
207
+ part.type === 'tool-result' ? sortedToolResults[toolResultIndex++] : part,
208
+ );
209
+ }
@@ -15,6 +15,10 @@ function defaultTransform(text: string): string {
15
15
  .trim();
16
16
  }
17
17
 
18
+ function stripMarkdownCodeFenceSuffix(text: string): string {
19
+ return text.replace(/\n?```\s*$/, '').trimEnd();
20
+ }
21
+
18
22
  /**
19
23
  * Middleware that extracts JSON from text content by stripping
20
24
  * markdown code fences and other formatting.
@@ -169,10 +173,15 @@ export function extractJsonMiddleware(options?: {
169
173
  remaining = transform(remaining);
170
174
  } else if (block.prefixStripped) {
171
175
  // strip suffix since prefix already handled
172
- remaining = remaining.replace(/\n?```\s*$/, '').trimEnd();
173
- } else {
174
- // Apply full transform (handles both prefix and suffix)
176
+ remaining = stripMarkdownCodeFenceSuffix(remaining);
177
+ } else if (block.phase === 'prefix') {
178
+ // No text has streamed yet, so the full transform is safe.
175
179
  remaining = transform(remaining);
180
+ } else {
181
+ // Only strip the suffix. Since earlier text may already have
182
+ // streamed, trimming the remaining suffix would remove valid
183
+ // leading whitespace at the stream boundary.
184
+ remaining = stripMarkdownCodeFenceSuffix(remaining);
176
185
  }
177
186
 
178
187
  if (remaining.length > 0) {