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.
- package/CHANGELOG.md +19 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +39 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +39 -4
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +1 -1
- package/docs/03-agents/06-memory.mdx +49 -0
- package/docs/03-ai-sdk-core/36-transcription.mdx +2 -2
- package/docs/03-ai-sdk-core/40-middleware.mdx +12 -4
- package/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +11 -2
- package/docs/06-advanced/10-vercel-deployment-guide.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/20-tool.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/index.mdx +5 -0
- package/package.json +3 -3
- package/src/agent/tool-loop-agent-settings.ts +10 -0
- package/src/generate-text/to-response-messages.ts +45 -1
- package/src/middleware/extract-json-middleware.ts +12 -3
package/dist/internal/index.js
CHANGED
|
@@ -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.
|
|
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 ({
|
package/dist/internal/index.mjs
CHANGED
|
@@ -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
|
|
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) | `
|
|
223
|
-
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `
|
|
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: ${
|
|
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
|
|
444
|
+
const result = await doGenerate();
|
|
441
445
|
|
|
442
446
|
// filtering approach, e.g. for PII or other sensitive information:
|
|
443
|
-
const
|
|
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 {
|
|
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
|
|
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
|
|
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](
|
|
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.
|
|
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.
|
|
48
|
+
"@ai-sdk/gateway": "3.0.144",
|
|
49
49
|
"@ai-sdk/provider": "3.0.13",
|
|
50
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
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:
|
|
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
|
|
173
|
-
} else {
|
|
174
|
-
//
|
|
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) {
|