ai 7.0.70 → 7.0.73
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 +22 -0
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +1 -1
- package/docs/03-agents/07-workflow-agent.mdx +20 -7
- package/docs/03-ai-sdk-core/35-image-generation.mdx +6 -7
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +58 -0
- package/docs/04-ai-sdk-ui/21-transport.mdx +6 -2
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +15 -1
- package/docs/07-reference/04-ai-sdk-workflow/02-workflow-chat-transport.mdx +25 -11
- package/package.json +2 -2
- package/src/generate-object/stream-object.ts +4 -1
- package/src/generate-text/stream-text.ts +8 -2
package/dist/internal/index.js
CHANGED
|
@@ -207,7 +207,6 @@ export default function Chat() {
|
|
|
207
207
|
new WorkflowChatTransport({
|
|
208
208
|
api: '/api/chat',
|
|
209
209
|
maxConsecutiveErrors: 5,
|
|
210
|
-
initialStartIndex: -50, // On page refresh, fetch last 50 chunks
|
|
211
210
|
}),
|
|
212
211
|
[],
|
|
213
212
|
);
|
|
@@ -241,6 +240,7 @@ export async function POST(request: Request) {
|
|
|
241
240
|
|
|
242
241
|
```ts filename="app/api/chat/[runId]/stream/route.ts"
|
|
243
242
|
import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';
|
|
243
|
+
import { createUIMessageStreamResponse } from 'ai';
|
|
244
244
|
import type { NextRequest } from 'next/server';
|
|
245
245
|
import { getRun } from 'workflow/api';
|
|
246
246
|
|
|
@@ -252,23 +252,36 @@ export async function GET(
|
|
|
252
252
|
const startIndex = Number(
|
|
253
253
|
new URL(request.url).searchParams.get('startIndex') ?? '0',
|
|
254
254
|
);
|
|
255
|
+
if (!Number.isSafeInteger(startIndex) || startIndex < 0) {
|
|
256
|
+
return Response.json(
|
|
257
|
+
{ error: 'startIndex must be a non-negative safe integer' },
|
|
258
|
+
{ status: 400 },
|
|
259
|
+
);
|
|
260
|
+
}
|
|
255
261
|
|
|
256
262
|
const run = await getRun(runId);
|
|
257
263
|
const readable = run
|
|
258
|
-
.getReadable({ startIndex })
|
|
259
|
-
.pipeThrough(
|
|
264
|
+
.getReadable({ startIndex: 0 })
|
|
265
|
+
.pipeThrough(
|
|
266
|
+
createModelCallToUIChunkTransform({ uiStartIndex: startIndex }),
|
|
267
|
+
);
|
|
260
268
|
|
|
261
|
-
return
|
|
269
|
+
return createUIMessageStreamResponse({
|
|
270
|
+
stream: readable,
|
|
262
271
|
headers: {
|
|
263
|
-
'Content-Type': 'text/event-stream',
|
|
264
|
-
'Cache-Control': 'no-cache',
|
|
265
|
-
Connection: 'keep-alive',
|
|
266
272
|
'x-workflow-run-id': runId,
|
|
267
273
|
},
|
|
268
274
|
});
|
|
269
275
|
}
|
|
270
276
|
```
|
|
271
277
|
|
|
278
|
+
`WorkflowChatTransport` counts `UIMessageChunk` objects, while the durable
|
|
279
|
+
`WorkflowAgent` stream stores raw `ModelCallStreamPart` objects. Replay the raw
|
|
280
|
+
stream from index `0` and apply the non-negative UI cursor in
|
|
281
|
+
`createModelCallToUIChunkTransform()` as shown above. Negative start indexes
|
|
282
|
+
require a durable stream that already stores `UIMessageChunk` objects and
|
|
283
|
+
cannot be used with this raw-to-UI conversion.
|
|
284
|
+
|
|
272
285
|
For the full API reference, see [`WorkflowChatTransport`](/docs/reference/ai-sdk-workflow/workflow-chat-transport).
|
|
273
286
|
|
|
274
287
|
## Tools as Workflow Steps
|
|
@@ -309,13 +309,12 @@ for (const file of result.files) {
|
|
|
309
309
|
| [DeepInfra](/providers/ai-sdk-providers/deepinfra#image-models) | `stabilityai/sdxl-turbo` | 1:1, 16:9, 1:9, 3:2, 2:3, 4:5, 5:4, 9:16, 9:21 |
|
|
310
310
|
| [Replicate](/providers/ai-sdk-providers/replicate) | `black-forest-labs/flux-schnell` | 1:1, 2:3, 3:2, 4:5, 5:4, 16:9, 9:16, 9:21, 21:9 |
|
|
311
311
|
| [Replicate](/providers/ai-sdk-providers/replicate) | `recraft-ai/recraft-v3` | 1024x1024, 1365x1024, 1024x1365, 1536x1024, 1024x1536, 1820x1024, 1024x1820, 1024x2048, 2048x1024, 1434x1024, 1024x1434, 1024x1280, 1280x1024, 1024x1707, 1707x1024 |
|
|
312
|
-
| [Google](/providers/ai-sdk-providers/google#image-models) | `
|
|
313
|
-
| [Google](/providers/ai-sdk-providers/google#image-models) | `
|
|
314
|
-
| [Google](/providers/ai-sdk-providers/google#image-models) | `
|
|
315
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `
|
|
316
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `
|
|
317
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `
|
|
318
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `imagen-3.0-fast-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
|
|
312
|
+
| [Google](/providers/ai-sdk-providers/google#image-models) | `gemini-2.5-flash-image` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
313
|
+
| [Google](/providers/ai-sdk-providers/google#image-models) | `gemini-3-pro-image-preview` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
314
|
+
| [Google](/providers/ai-sdk-providers/google#image-models) | `gemini-3.1-flash-image-preview` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
315
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `gemini-2.5-flash-image` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
316
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `gemini-3-pro-image-preview` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
317
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#image-models) | `gemini-3.1-flash-image-preview` | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
|
|
319
318
|
| [Fireworks](/providers/ai-sdk-providers/fireworks#image-models) | `accounts/fireworks/models/flux-1-dev-fp8` | 1:1, 2:3, 3:2, 4:5, 5:4, 16:9, 9:16, 9:21, 21:9 |
|
|
320
319
|
| [Fireworks](/providers/ai-sdk-providers/fireworks#image-models) | `accounts/fireworks/models/flux-1-schnell-fp8` | 1:1, 2:3, 3:2, 4:5, 5:4, 16:9, 9:16, 9:21, 21:9 |
|
|
321
320
|
| [Fireworks](/providers/ai-sdk-providers/fireworks#image-models) | `accounts/fireworks/models/playground-v2-5-1024px-aesthetic` | 640x1536, 768x1344, 832x1216, 896x1152, 1024x1024, 1152x896, 1216x832, 1344x768, 1536x640 |
|
|
@@ -484,6 +484,64 @@ try {
|
|
|
484
484
|
}
|
|
485
485
|
```
|
|
486
486
|
|
|
487
|
+
### Basic Sandbox Sessions Without Network Control
|
|
488
|
+
|
|
489
|
+
The following example demonstrates how the basic-session API works. If your
|
|
490
|
+
project can expose a full network sandbox session, passing that session is
|
|
491
|
+
strongly recommended. Pass a restricted basic session only when your project
|
|
492
|
+
cannot expose the full network session.
|
|
493
|
+
|
|
494
|
+
A basic sandbox session only exposes filesystem and process APIs. The agent
|
|
495
|
+
still leaves the sandbox lifecycle to the caller. For a bridge-backed harness,
|
|
496
|
+
configure the bridge port and its externally reachable endpoint on the adapter
|
|
497
|
+
because the basic session cannot resolve them.
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
import { HarnessAgent, prepareSandboxForHarness } from '@ai-sdk/harness/agent';
|
|
501
|
+
import { createClaudeCode } from '@ai-sdk/harness-claude-code';
|
|
502
|
+
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
|
|
503
|
+
import { Sandbox } from '@vercel/sandbox';
|
|
504
|
+
|
|
505
|
+
const sandbox = await Sandbox.create({
|
|
506
|
+
runtime: 'node24',
|
|
507
|
+
ports: [4000],
|
|
508
|
+
});
|
|
509
|
+
const sandboxProvider = createVercelSandbox({ sandbox });
|
|
510
|
+
const sandboxSession = await sandboxProvider.createSession();
|
|
511
|
+
const portEndpoint = await sandboxSession.getPortEndpoint({
|
|
512
|
+
port: 4000,
|
|
513
|
+
protocol: 'ws',
|
|
514
|
+
});
|
|
515
|
+
const restrictedSandboxSession = sandboxSession.restricted();
|
|
516
|
+
const claudeCode = createClaudeCode({ port: 4000, portEndpoint });
|
|
517
|
+
|
|
518
|
+
await prepareSandboxForHarness({
|
|
519
|
+
session: restrictedSandboxSession,
|
|
520
|
+
harnesses: [claudeCode],
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
const agent = new HarnessAgent({ harness: claudeCode });
|
|
524
|
+
const session = await agent.createSession({
|
|
525
|
+
sandboxSession: restrictedSandboxSession,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const result = await agent.stream({
|
|
530
|
+
session,
|
|
531
|
+
prompt: 'Create a short TODO.md for this repository.',
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
for await (const part of result.stream) {
|
|
535
|
+
if (part.type === 'text-delta') {
|
|
536
|
+
process.stdout.write(part.text);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
} finally {
|
|
540
|
+
await session.destroy();
|
|
541
|
+
await sandbox.stop();
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
487
545
|
## Next Steps
|
|
488
546
|
|
|
489
547
|
- [Tools](/docs/ai-sdk-harnesses/tools) for built-in and host-executed tools.
|
|
@@ -171,7 +171,6 @@ export default function Chat() {
|
|
|
171
171
|
new WorkflowChatTransport({
|
|
172
172
|
api: '/api/chat',
|
|
173
173
|
maxConsecutiveErrors: 5,
|
|
174
|
-
initialStartIndex: -50, // On page refresh, fetch last 50 chunks
|
|
175
174
|
onChatEnd: ({ chatId, chunkIndex }) => {
|
|
176
175
|
console.log(`Chat complete: ${chunkIndex} chunks`);
|
|
177
176
|
},
|
|
@@ -188,10 +187,15 @@ export default function Chat() {
|
|
|
188
187
|
Key features:
|
|
189
188
|
|
|
190
189
|
- **Automatic reconnection**: Detects interrupted streams (no `finish` event) and reconnects via GET to `{api}/{runId}/stream`
|
|
191
|
-
- **Page refresh recovery**: `initialStartIndex`
|
|
190
|
+
- **Page refresh recovery**: `initialStartIndex` controls where the initial reconnection begins
|
|
192
191
|
- **Configurable retries**: `maxConsecutiveErrors` controls how many consecutive reconnection failures to tolerate
|
|
193
192
|
- **Lifecycle callbacks**: `onChatSendMessage` and `onChatEnd` for tracking chat state
|
|
194
193
|
|
|
194
|
+
Negative `initialStartIndex` values can fetch only the tail when the durable
|
|
195
|
+
server stream already stores `UIMessageChunk` objects. For raw `WorkflowAgent`
|
|
196
|
+
streams, use a non-negative cursor and follow the server-side conversion in the
|
|
197
|
+
WorkflowAgent guide.
|
|
198
|
+
|
|
195
199
|
For the full API reference, see [`WorkflowChatTransport`](/docs/reference/ai-sdk-workflow/workflow-chat-transport). For server-side endpoint setup, see the [WorkflowAgent guide](/docs/agents/workflow-agent#resumable-streaming-with-workflowchattransport).
|
|
196
200
|
|
|
197
201
|
## Building Custom Transports
|
|
@@ -696,7 +696,7 @@ Returns a `Promise<WorkflowAgentStreamResult>` with the following properties:
|
|
|
696
696
|
|
|
697
697
|
## Utilities
|
|
698
698
|
|
|
699
|
-
### `createModelCallToUIChunkTransform()`
|
|
699
|
+
### `createModelCallToUIChunkTransform(options?)`
|
|
700
700
|
|
|
701
701
|
Creates a `TransformStream` that converts raw `ModelCallStreamPart` chunks (written by the agent to the `writable` stream) into `UIMessageChunk` objects suitable for client consumption.
|
|
702
702
|
|
|
@@ -708,6 +708,20 @@ return createUIMessageStreamResponse({
|
|
|
708
708
|
});
|
|
709
709
|
```
|
|
710
710
|
|
|
711
|
+
When resuming with a `WorkflowChatTransport` cursor, replay the raw workflow
|
|
712
|
+
stream from index `0` and pass the non-negative UI chunk index to the transform:
|
|
713
|
+
|
|
714
|
+
```ts
|
|
715
|
+
const readable = run
|
|
716
|
+
.getReadable({ startIndex: 0 })
|
|
717
|
+
.pipeThrough(createModelCallToUIChunkTransform({ uiStartIndex: startIndex }));
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
`uiStartIndex` must be a non-negative safe integer. Raw model stream parts and
|
|
721
|
+
UI message chunks are not one-to-one, so do not pass a UI chunk index to
|
|
722
|
+
`getReadable`. Negative tail indexes require a durable stream that already
|
|
723
|
+
stores `UIMessageChunk` objects.
|
|
724
|
+
|
|
711
725
|
### `toUIMessageChunk()`
|
|
712
726
|
|
|
713
727
|
Converts a single `ModelCallStreamPart` to a `UIMessageChunk`. Returns `undefined` for parts that don't map to UI chunks.
|
|
@@ -20,7 +20,6 @@ export default function Chat() {
|
|
|
20
20
|
transport: new WorkflowChatTransport({
|
|
21
21
|
api: '/api/chat',
|
|
22
22
|
maxConsecutiveErrors: 5,
|
|
23
|
-
initialStartIndex: -50,
|
|
24
23
|
}),
|
|
25
24
|
});
|
|
26
25
|
|
|
@@ -67,7 +66,7 @@ export default function Chat() {
|
|
|
67
66
|
type: 'number',
|
|
68
67
|
isOptional: true,
|
|
69
68
|
description:
|
|
70
|
-
'Default chunk index to start from when reconnecting. Negative values read from the end of
|
|
69
|
+
'Default chunk index to start from when reconnecting. Negative values read from the end of a durable UIMessageChunk stream (e.g., -50 fetches the last 50 chunks), useful for resuming after a page refresh without replaying the full conversation. Raw ModelCallStreamPart streams do not support negative UI chunk indexes. Can be overridden per-call via reconnectToStream options. Default: 0.',
|
|
71
70
|
},
|
|
72
71
|
{
|
|
73
72
|
name: 'onChatSendMessage',
|
|
@@ -183,7 +182,7 @@ const stream = await transport.reconnectToStream({
|
|
|
183
182
|
type: 'number',
|
|
184
183
|
isOptional: true,
|
|
185
184
|
description:
|
|
186
|
-
"Override the start index for this reconnection. Negative values read from the end
|
|
185
|
+
"Override the start index for this reconnection. Negative values read from the end when the server's durable stream and tail-index header use the same UIMessageChunk index space. When omitted, falls back to the constructor's initialStartIndex.",
|
|
187
186
|
},
|
|
188
187
|
]}
|
|
189
188
|
/>
|
|
@@ -209,6 +208,10 @@ When `initialStartIndex` is negative (e.g., `-50`), the transport sends it as-is
|
|
|
209
208
|
|
|
210
209
|
If the header is missing or invalid, the transport falls back to replaying from the beginning (`startIndex=0`).
|
|
211
210
|
|
|
211
|
+
Negative indexes require a durable server stream whose stored objects are
|
|
212
|
+
already `UIMessageChunk` objects. The raw `WorkflowAgent` conversion shown
|
|
213
|
+
below supports non-negative indexes only.
|
|
214
|
+
|
|
212
215
|
## Server Requirements
|
|
213
216
|
|
|
214
217
|
For `WorkflowChatTransport` to work, your server must provide two endpoints:
|
|
@@ -262,7 +265,7 @@ export default function Chat() {
|
|
|
262
265
|
}
|
|
263
266
|
```
|
|
264
267
|
|
|
265
|
-
### With Callbacks
|
|
268
|
+
### With Callbacks
|
|
266
269
|
|
|
267
270
|
```tsx
|
|
268
271
|
'use client';
|
|
@@ -277,7 +280,6 @@ export default function Chat() {
|
|
|
277
280
|
new WorkflowChatTransport({
|
|
278
281
|
api: '/api/chat',
|
|
279
282
|
maxConsecutiveErrors: 5,
|
|
280
|
-
initialStartIndex: -50, // Resume from last 50 chunks on page refresh
|
|
281
283
|
onChatSendMessage: response => {
|
|
282
284
|
const runId = response.headers.get('x-workflow-run-id');
|
|
283
285
|
console.log('Workflow run started:', runId);
|
|
@@ -318,6 +320,7 @@ export async function POST(request: Request) {
|
|
|
318
320
|
|
|
319
321
|
```ts filename="app/api/chat/[runId]/stream/route.ts"
|
|
320
322
|
import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';
|
|
323
|
+
import { createUIMessageStreamResponse } from 'ai';
|
|
321
324
|
import type { NextRequest } from 'next/server';
|
|
322
325
|
import { getRun } from 'workflow/api';
|
|
323
326
|
|
|
@@ -329,19 +332,30 @@ export async function GET(
|
|
|
329
332
|
const startIndex = Number(
|
|
330
333
|
new URL(request.url).searchParams.get('startIndex') ?? '0',
|
|
331
334
|
);
|
|
335
|
+
if (!Number.isSafeInteger(startIndex) || startIndex < 0) {
|
|
336
|
+
return Response.json(
|
|
337
|
+
{ error: 'startIndex must be a non-negative safe integer' },
|
|
338
|
+
{ status: 400 },
|
|
339
|
+
);
|
|
340
|
+
}
|
|
332
341
|
|
|
333
342
|
const run = await getRun(runId);
|
|
334
343
|
const readable = run
|
|
335
|
-
.getReadable({ startIndex })
|
|
336
|
-
.pipeThrough(
|
|
344
|
+
.getReadable({ startIndex: 0 })
|
|
345
|
+
.pipeThrough(
|
|
346
|
+
createModelCallToUIChunkTransform({ uiStartIndex: startIndex }),
|
|
347
|
+
);
|
|
337
348
|
|
|
338
|
-
return
|
|
349
|
+
return createUIMessageStreamResponse({
|
|
350
|
+
stream: readable,
|
|
339
351
|
headers: {
|
|
340
|
-
'Content-Type': 'text/event-stream',
|
|
341
|
-
'Cache-Control': 'no-cache',
|
|
342
|
-
Connection: 'keep-alive',
|
|
343
352
|
'x-workflow-run-id': runId,
|
|
344
353
|
},
|
|
345
354
|
});
|
|
346
355
|
}
|
|
347
356
|
```
|
|
357
|
+
|
|
358
|
+
This `WorkflowAgent` endpoint replays raw `ModelCallStreamPart` objects from
|
|
359
|
+
index `0`, then applies the transport's non-negative cursor after converting
|
|
360
|
+
them to `UIMessageChunk` objects. Negative start indexes require a durable
|
|
361
|
+
stream whose stored objects are already `UIMessageChunk` objects.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.73",
|
|
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,7 +42,7 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.59",
|
|
46
46
|
"@ai-sdk/provider": "4.0.7",
|
|
47
47
|
"@ai-sdk/provider-utils": "5.0.28"
|
|
48
48
|
},
|
|
@@ -546,7 +546,10 @@ class DefaultStreamObjectResult<
|
|
|
546
546
|
controller.enqueue(chunk);
|
|
547
547
|
|
|
548
548
|
if (chunk.type === 'error') {
|
|
549
|
-
|
|
549
|
+
void notify({
|
|
550
|
+
event: { error: wrapGatewayError(chunk.error) },
|
|
551
|
+
callbacks: onError,
|
|
552
|
+
});
|
|
550
553
|
}
|
|
551
554
|
},
|
|
552
555
|
});
|
|
@@ -1193,7 +1193,10 @@ class DefaultStreamTextResult<
|
|
|
1193
1193
|
|
|
1194
1194
|
const { part } = chunk;
|
|
1195
1195
|
|
|
1196
|
-
await
|
|
1196
|
+
await notify({
|
|
1197
|
+
event: { chunk: part },
|
|
1198
|
+
callbacks: onChunk,
|
|
1199
|
+
});
|
|
1197
1200
|
|
|
1198
1201
|
if (part.type === 'error') {
|
|
1199
1202
|
const error = wrapGatewayError(part.error);
|
|
@@ -1202,7 +1205,10 @@ class DefaultStreamTextResult<
|
|
|
1202
1205
|
recordedNoOutputError = error;
|
|
1203
1206
|
}
|
|
1204
1207
|
|
|
1205
|
-
await
|
|
1208
|
+
await notify({
|
|
1209
|
+
event: { error },
|
|
1210
|
+
callbacks: onError,
|
|
1211
|
+
});
|
|
1206
1212
|
}
|
|
1207
1213
|
|
|
1208
1214
|
if (
|