workflow 5.0.0-beta.23 → 5.0.0-beta.25
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/docs/ai/chat-session-modeling.mdx +7 -3
- package/docs/ai/defining-tools.mdx +2 -2
- package/docs/ai/message-queueing.mdx +10 -10
- package/docs/ai/resumable-streams.mdx +9 -1
- package/docs/api-reference/workflow-ai/durable-agent.mdx +1 -1
- package/docs/api-reference/workflow-ai/index.mdx +2 -2
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +44 -1
- package/docs/cookbook/advanced/serializable-steps.mdx +5 -1
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +5 -1
- package/docs/cookbook/agent-patterns/durable-agent.mdx +2 -2
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +5 -1
- package/docs/cookbook/integrations/ai-sdk.mdx +12 -12
- package/docs/deploying/world/postgres-world.mdx +33 -1
- package/package.json +10 -10
|
@@ -14,6 +14,10 @@ related:
|
|
|
14
14
|
- /docs/api-reference/workflow/define-hook
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
<Callout type="warn">
|
|
18
|
+
The examples below use the deprecated `DurableAgent` API. For new agents, use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) and follow the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent). The session-modeling patterns here (single- vs multi-turn, hooks, stream reconnection) apply to either API.
|
|
19
|
+
</Callout>
|
|
20
|
+
|
|
17
21
|
Chat sessions in AI agents can be modeled at different layers of your architecture. The choice affects state ownership and how you handle interruptions and reconnections.
|
|
18
22
|
|
|
19
23
|
While there are many ways to model chat sessions, the two most common categories are single-turn and multi-turn.
|
|
@@ -83,7 +87,7 @@ Chat messages need to be stored somewhere—typically a database. In this exampl
|
|
|
83
87
|
"use client";
|
|
84
88
|
|
|
85
89
|
import { useChat } from "@ai-sdk/react";
|
|
86
|
-
import { WorkflowChatTransport } from "@workflow
|
|
90
|
+
import { WorkflowChatTransport } from "@ai-sdk/workflow"; // [!code highlight]
|
|
87
91
|
import { useParams } from "next/navigation";
|
|
88
92
|
import { useMemo } from "react";
|
|
89
93
|
|
|
@@ -338,7 +342,7 @@ A custom hook wraps `useChat` to manage the multi-turn session. It handles:
|
|
|
338
342
|
|
|
339
343
|
import type { UIMessage, UIDataTypes, ChatStatus } from "ai";
|
|
340
344
|
import { useChat } from "@ai-sdk/react";
|
|
341
|
-
import { WorkflowChatTransport } from "@workflow
|
|
345
|
+
import { WorkflowChatTransport } from "@ai-sdk/workflow";
|
|
342
346
|
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
343
347
|
|
|
344
348
|
const STORAGE_KEY = "workflow-run-id";
|
|
@@ -592,4 +596,4 @@ export async function POST(
|
|
|
592
596
|
- [Building Durable AI Agents](/docs/ai) - Foundation guide for durable agents
|
|
593
597
|
- [Message Queueing](/docs/ai/message-queueing) - Queueing messages during tool execution
|
|
594
598
|
- [`defineHook()` API Reference](/docs/api-reference/workflow/define-hook) - Hook configuration options
|
|
595
|
-
- [`
|
|
599
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - AI SDK API for durable, resumable agents
|
|
@@ -14,11 +14,11 @@ related:
|
|
|
14
14
|
|
|
15
15
|
This page covers the details for some common patterns when defining tools for AI agents using Workflow SDK.
|
|
16
16
|
|
|
17
|
-
Using
|
|
17
|
+
Using WorkflowAgent, we model most tools as steps. These can be anything from a simple function call to a entire multi-day long workflow.
|
|
18
18
|
|
|
19
19
|
## Accessing message context in tools
|
|
20
20
|
|
|
21
|
-
Just like in regular AI SDK tool definitions, tool in
|
|
21
|
+
Just like in regular AI SDK tool definitions, tool in WorkflowAgent are called with a first argument of the tool's input parameters, and a second argument of the tool call context.
|
|
22
22
|
|
|
23
23
|
When you tool needs access to the full message history, you can access it via the `messages` property of the tool call context:
|
|
24
24
|
|
|
@@ -13,7 +13,7 @@ related:
|
|
|
13
13
|
|
|
14
14
|
When using [multi-turn workflows](/docs/ai/chat-session-modeling#multi-turn-workflows), messages typically arrive between agent turns. The workflow waits at a hook, receives a message, then starts a new turn. But sometimes you need to inject messages *during* an agent's turn, before tool calls complete or while the model is reasoning.
|
|
15
15
|
|
|
16
|
-
`
|
|
16
|
+
`WorkflowAgent`'s `prepareStep` callback enables this by running before each step in the agent loop, giving you a chance to inject queued messages into the conversation. `prepareStep` also allows you to modify the model choice and existing messages mid-turn, see AI SDK's [prepareStep callback](https://ai-sdk.dev/docs/agents/loop-control#prepare-step) for more details.
|
|
17
17
|
|
|
18
18
|
## When to Use This
|
|
19
19
|
|
|
@@ -52,20 +52,20 @@ interface PrepareStepResult {
|
|
|
52
52
|
Once you have a [multi-turn workflow](/docs/ai/chat-session-modeling#multi-turn-workflows), you can combine a message queue with `prepareStep` to inject messages that arrive during processing:
|
|
53
53
|
|
|
54
54
|
```typescript title="workflows/chat/index.ts" lineNumbers
|
|
55
|
-
import {
|
|
55
|
+
import { WorkflowAgent, type ModelCallStreamPart } from "@ai-sdk/workflow";
|
|
56
56
|
import { getWritable, getWorkflowMetadata } from "workflow";
|
|
57
57
|
import { chatMessageHook } from "./hooks/chat-message";
|
|
58
58
|
import { flightBookingTools, FLIGHT_ASSISTANT_PROMPT } from "./steps/tools";
|
|
59
|
-
import type {
|
|
59
|
+
import type { ModelMessage } from "ai";
|
|
60
60
|
|
|
61
61
|
export async function chat(initialMessages: ModelMessage[]) {
|
|
62
62
|
"use workflow";
|
|
63
63
|
|
|
64
64
|
const { workflowRunId: runId } = getWorkflowMetadata();
|
|
65
|
-
const writable = getWritable<
|
|
65
|
+
const writable = getWritable<ModelCallStreamPart>();
|
|
66
66
|
const messageQueue: Array<{ role: "user"; content: string }> = []; // [!code highlight]
|
|
67
67
|
|
|
68
|
-
const agent = new
|
|
68
|
+
const agent = new WorkflowAgent({
|
|
69
69
|
model: "bedrock/claude-haiku-4-5-20251001-v1",
|
|
70
70
|
instructions: FLIGHT_ASSISTANT_PROMPT,
|
|
71
71
|
tools: flightBookingTools,
|
|
@@ -111,20 +111,20 @@ The `prepareStep` callback receives messages in `ModelMessage[]` format (with co
|
|
|
111
111
|
You can also combine message queueing with the standard multi-turn pattern:
|
|
112
112
|
|
|
113
113
|
```typescript title="workflows/chat/index.ts" lineNumbers
|
|
114
|
-
import {
|
|
114
|
+
import { WorkflowAgent, type ModelCallStreamPart } from "@ai-sdk/workflow";
|
|
115
115
|
import { getWritable, getWorkflowMetadata } from "workflow";
|
|
116
116
|
import { chatMessageHook } from "./hooks/chat-message";
|
|
117
|
-
import type {
|
|
117
|
+
import type { ModelMessage } from "ai";
|
|
118
118
|
|
|
119
119
|
export async function chat(initialMessages: ModelMessage[]) {
|
|
120
120
|
"use workflow";
|
|
121
121
|
|
|
122
122
|
const { workflowRunId: runId } = getWorkflowMetadata();
|
|
123
|
-
const writable = getWritable<
|
|
123
|
+
const writable = getWritable<ModelCallStreamPart>();
|
|
124
124
|
const messages: ModelMessage[] = [...initialMessages];
|
|
125
125
|
const messageQueue: Array<{ role: "user"; content: string }> = [];
|
|
126
126
|
|
|
127
|
-
const agent = new
|
|
127
|
+
const agent = new WorkflowAgent({ /* ... */ });
|
|
128
128
|
const hook = chatMessageHook.create({ token: runId });
|
|
129
129
|
|
|
130
130
|
while (true) {
|
|
@@ -173,5 +173,5 @@ export async function chat(initialMessages: ModelMessage[]) {
|
|
|
173
173
|
|
|
174
174
|
- [Chat Session Modeling](/docs/ai/chat-session-modeling) - Single-turn vs multi-turn patterns
|
|
175
175
|
- [Building Durable AI Agents](/docs/ai) - Complete guide to creating durable agents
|
|
176
|
-
- [`
|
|
176
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - AI SDK API for durable, resumable agents
|
|
177
177
|
- [`defineHook()` API Reference](/docs/api-reference/workflow/define-hook) - Hook configuration options
|
|
@@ -12,6 +12,10 @@ related:
|
|
|
12
12
|
- /docs/api-reference/workflow-api/get-run
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
<Callout type="warn">
|
|
16
|
+
`WorkflowChatTransport` now ships in AI SDK as a 1:1 port — import it from `@ai-sdk/workflow` (the `@workflow/ai` export is deprecated). See [Resumable Streaming with `WorkflowChatTransport`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#resumable-streaming-with-workflowchattransport) for the full reference.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
15
19
|
When building chat interfaces, it's common to run into network interruptions, page refreshes, or serverless function timeouts, which can break the connection to an in-progress agent.
|
|
16
20
|
|
|
17
21
|
Where a standard chat implementation would require the user to resend their message and wait for the entire response again, workflow runs are durable, and so are the streams attached to them. This means a stream can be resumed at any point, optionally only syncing the data that was missed since the last connection.
|
|
@@ -109,7 +113,7 @@ Replace the default transport in AI-SDK's `useChat` with [`WorkflowChatTransport
|
|
|
109
113
|
"use client";
|
|
110
114
|
|
|
111
115
|
import { useChat } from "@ai-sdk/react";
|
|
112
|
-
import { WorkflowChatTransport } from "@workflow
|
|
116
|
+
import { WorkflowChatTransport } from "@ai-sdk/workflow"; // [!code highlight]
|
|
113
117
|
import { useMemo, useState } from "react";
|
|
114
118
|
|
|
115
119
|
export default function ChatPage() {
|
|
@@ -194,6 +198,10 @@ This avoids replaying potentially thousands of chunks and lets the UI render fas
|
|
|
194
198
|
When using a negative `initialStartIndex`, the reconnection endpoint **must** return the `x-workflow-stream-tail-index` header (as shown in [Step 2](#add-a-stream-reconnection-endpoint) above). The transport uses this header to compute absolute chunk positions so that retries after a disconnect resume from the correct position. If the header is missing, the transport falls back to `startIndex: 0` (replaying the entire stream) and logs a warning.
|
|
195
199
|
</Callout>
|
|
196
200
|
|
|
201
|
+
### Mid-part resumes
|
|
202
|
+
|
|
203
|
+
A workflow stream is a flat sequence of chunks, but the AI SDK's UI protocol groups chunks into logical parts (`text-*`, `reasoning-*`, `tool-input-*`) that must be opened with a `*-start` before any `*-delta` or `*-end`. A non-zero `startIndex` can land in the middle of an open part. See [`WorkflowChatTransport` → Mid-part resumes](/docs/api-reference/workflow-ai/workflow-chat-transport#mid-part-resumes) for how this is handled and an example of rewinding to a step boundary on the server.
|
|
204
|
+
|
|
197
205
|
## Related Documentation
|
|
198
206
|
|
|
199
207
|
- [`WorkflowChatTransport` API Reference](/docs/api-reference/workflow-ai/workflow-chat-transport) - Full configuration options
|
|
@@ -10,7 +10,7 @@ related:
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
<Callout type="warn">
|
|
13
|
-
`DurableAgent` is deprecated. Use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agents
|
|
13
|
+
`DurableAgent` is deprecated. Use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agents — see the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent).
|
|
14
14
|
</Callout>
|
|
15
15
|
|
|
16
16
|
This reference is kept for existing applications that still import `DurableAgent` from `@workflow/ai/agent`. Do not use `DurableAgent` for new code.
|
|
@@ -13,9 +13,9 @@ Helpers for integrating AI SDK for building AI-powered workflows.
|
|
|
13
13
|
|
|
14
14
|
<Cards>
|
|
15
15
|
<Card title="DurableAgent" href="/docs/api-reference/workflow-ai/durable-agent">
|
|
16
|
-
|
|
16
|
+
Deprecated — use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent). Reference kept for existing `@workflow/ai/agent` imports.
|
|
17
17
|
</Card>
|
|
18
18
|
<Card title="WorkflowChatTransport" href="/docs/api-reference/workflow-ai/workflow-chat-transport">
|
|
19
|
-
|
|
19
|
+
Deprecated — use AI SDK's [`WorkflowChatTransport`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#resumable-streaming-with-workflowchattransport) from `@ai-sdk/workflow`. Reference kept for existing `@workflow/ai` imports.
|
|
20
20
|
</Card>
|
|
21
21
|
</Cards>
|
|
@@ -9,6 +9,10 @@ related:
|
|
|
9
9
|
- /docs/ai/resumable-streams
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<Callout type="warn">
|
|
13
|
+
`WorkflowChatTransport` from `@workflow/ai` is deprecated. AI SDK ships a 1:1 port — use [`WorkflowChatTransport` from `@ai-sdk/workflow`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#resumable-streaming-with-workflowchattransport) instead. This reference is kept for existing applications that still import it from `@workflow/ai`.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
12
16
|
A chat transport implementation for the AI SDK that provides reliable message streaming with automatic reconnection to interrupted streams. This transport is a drop-in replacement for the default AI SDK transport, enabling seamless recovery from network issues, page refreshes, or Vercel Function timeouts.
|
|
13
17
|
|
|
14
18
|
<Callout>
|
|
@@ -250,9 +254,48 @@ export default function ChatWithCustomConfig() {
|
|
|
250
254
|
}
|
|
251
255
|
```
|
|
252
256
|
|
|
257
|
+
## Mid-part resumes
|
|
258
|
+
|
|
259
|
+
A workflow stream is a flat sequence of chunks, but the AI SDK's UI protocol groups chunks into logical parts: a `text-start` opens a text part that subsequent `text-delta`s extend and a `text-end` closes, and the same shape applies to `reasoning-*` and `tool-input-*`. The AI SDK client enforces that grammar — a `reasoning-delta` whose `reasoning-start` was never seen throws and breaks the chat.
|
|
260
|
+
|
|
261
|
+
A non-zero `startIndex` (in particular a negative `initialStartIndex`) resolves to a chunk offset with no awareness of those part boundaries, so it can land in the middle of an open part. When that happens, `WorkflowChatTransport` will **drop chunks that reference a part it didn't see a start for** and log a one-time warning. The chat keeps working, but any partial part overlapping the resume cursor is discarded. Tool calls are an exception: `tool-input-available` / `tool-input-error` chunks are self-contained (they carry the full input), so a tool call is recovered as soon as one of those chunks appears in the resumed window — only its streamed input deltas are lost.
|
|
262
|
+
|
|
263
|
+
To preserve those partial parts, rewind to a step boundary on the server before returning the readable. `start-step` / `finish-step` chunks are the natural seams — no UI part is ever open across them. Sketch:
|
|
264
|
+
|
|
265
|
+
{/*@skip-typecheck: incomplete code sample*/}
|
|
266
|
+
|
|
267
|
+
```typescript title="app/api/chat/[id]/stream/route.ts"
|
|
268
|
+
const run = getRun(id);
|
|
269
|
+
const tailIndex = await run.getReadable().getTailIndex();
|
|
270
|
+
|
|
271
|
+
let resolved = startIndex < 0
|
|
272
|
+
? Math.max(0, tailIndex + 1 + startIndex)
|
|
273
|
+
: startIndex;
|
|
274
|
+
|
|
275
|
+
if (startIndex !== 0) {
|
|
276
|
+
// Walk back from `resolved` to the most recent start-step (or chunk 0),
|
|
277
|
+
// capping the lookback so a single huge step can't trigger an unbounded scan.
|
|
278
|
+
const LOOKBACK = 200;
|
|
279
|
+
const probe = run.getReadable({ startIndex: Math.max(0, resolved - LOOKBACK) });
|
|
280
|
+
let i = Math.max(0, resolved - LOOKBACK);
|
|
281
|
+
let lastBoundary = i;
|
|
282
|
+
for await (const chunk of probe as unknown as AsyncIterable<{ type: string }>) {
|
|
283
|
+
if (i >= resolved) break;
|
|
284
|
+
if (chunk.type === "start-step") lastBoundary = i;
|
|
285
|
+
i++;
|
|
286
|
+
}
|
|
287
|
+
resolved = lastBoundary;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return createUIMessageStreamResponse({
|
|
291
|
+
stream: run.getReadable({ startIndex: resolved }),
|
|
292
|
+
headers: { "x-workflow-stream-tail-index": String(tailIndex) },
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
253
296
|
## See Also
|
|
254
297
|
|
|
255
|
-
- [
|
|
298
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - Building durable, resumable AI agents (replaces `DurableAgent`)
|
|
256
299
|
- [AI SDK `useChat` Documentation](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) - Using `useChat` with custom transports
|
|
257
300
|
- [Workflows and Steps](/docs/foundations/workflows-and-steps) - Understanding workflow fundamentals
|
|
258
301
|
- ["flight-booking-app" Example](https://github.com/vercel/workflow-examples/tree/main/flight-booking-app) - An example application which uses `WorkflowChatTransport`
|
|
@@ -63,6 +63,10 @@ The `DurableAgent` receives a function (`() => Promise<LanguageModel>`) instead
|
|
|
63
63
|
|
|
64
64
|
## How `@workflow/ai` Uses This
|
|
65
65
|
|
|
66
|
+
<Callout type="warn">
|
|
67
|
+
`@workflow/ai`'s pre-wrapped providers and `DurableAgent` are deprecated. AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) resolves models from AI Gateway model strings (e.g. `"openai/gpt-4o"`), which usually removes the need for a model factory — see the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent). The serialization pattern on this page still applies to any non-serializable dependency you own (for example, cloud SDK clients).
|
|
68
|
+
</Callout>
|
|
69
|
+
|
|
66
70
|
The `@workflow/ai` package ships pre-wrapped providers for all major AI SDK backends. Each one follows the same pattern:
|
|
67
71
|
|
|
68
72
|
```typescript lineNumbers
|
|
@@ -143,5 +147,5 @@ async function uploadFile(
|
|
|
143
147
|
|
|
144
148
|
- [`"use step"`](/docs/foundations/workflows-and-steps#step-functions) — marks a function for extraction and serialization
|
|
145
149
|
- [`"use workflow"`](/docs/foundations/workflows-and-steps#workflow-functions) — declares the orchestrator function
|
|
146
|
-
- [`
|
|
150
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) — AI SDK's durable agent (resolves models via AI Gateway strings; replaces `DurableAgent`)
|
|
147
151
|
- [Custom class serialization](/docs/foundations/serialization#custom-class-serialization) — the companion pattern for classes you own (`WORKFLOW_SERIALIZE` / `WORKFLOW_DESERIALIZE`)
|
|
@@ -7,6 +7,10 @@ summary: Cancel a running agent cooperatively with AbortController. A stop hook
|
|
|
7
7
|
|
|
8
8
|
Cancel a running agent from the outside — for example, a "Stop" button in a chat UI, an admin cancellation endpoint, or a timeout fallback.
|
|
9
9
|
|
|
10
|
+
<Callout type="warn">
|
|
11
|
+
This recipe uses the deprecated `DurableAgent` API. For new agents, use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) and follow the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent). The cancellation patterns here (`run.cancel()`, stop-signal hook + `Promise.race`, `AbortController`) apply to either API.
|
|
12
|
+
</Callout>
|
|
13
|
+
|
|
10
14
|
## Pattern
|
|
11
15
|
|
|
12
16
|
Create an `AbortController` in the workflow and race the agent (passing its signal) against a stop hook. When the hook fires, `controller.abort()` is called — the signal propagates into the agent step and cancels the underlying model stream. Before returning, a `data-stopped` part is written to the stream so any streaming clients can render a clean end state.
|
|
@@ -153,4 +157,4 @@ export function StopButton({ runId }: { runId: string }) {
|
|
|
153
157
|
* [`defineHook()`](/docs/api-reference/workflow/define-hook) — type-safe hook for the stop signal
|
|
154
158
|
* [`getWorkflowMetadata()`](/docs/api-reference/workflow/get-workflow-metadata) — access the run ID for deterministic hook tokens
|
|
155
159
|
* [`getWritable()`](/docs/api-reference/workflow/get-writable) — stream output and the stop notification to the client
|
|
156
|
-
* [`
|
|
160
|
+
* [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) — AI SDK's durable agent that respects the abort signal (replaces `DurableAgent`)
|
|
@@ -7,12 +7,12 @@ summary: Build durable, resumable AI agents with AI SDK v7's WorkflowAgent.
|
|
|
7
7
|
|
|
8
8
|
## WorkflowAgent from AI SDK v7
|
|
9
9
|
|
|
10
|
-
Use AI SDK v7's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agent work. It replaces `DurableAgent`
|
|
10
|
+
Use AI SDK v7's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agent work. It replaces `DurableAgent` and keeps the current agent pattern in the AI SDK package.
|
|
11
11
|
|
|
12
12
|
- Import `WorkflowAgent` from `@ai-sdk/workflow` and run it inside a `"use workflow"` function.
|
|
13
13
|
- Stream `ModelCallStreamPart` chunks with `getWritable()`, then convert the run stream to UI message chunks with `createModelCallToUIChunkTransform()` in your route.
|
|
14
14
|
- Mark tool `execute` functions with `"use step"` when they should run as durable workflow steps with retry and observability behavior.
|
|
15
15
|
|
|
16
16
|
<Callout type="warn">
|
|
17
|
-
`DurableAgent` is deprecated
|
|
17
|
+
`DurableAgent` is deprecated and remains documented for existing code only. See the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent), or the [`DurableAgent` API reference](/docs/api-reference/workflow-ai/durable-agent) while migrating.
|
|
18
18
|
</Callout>
|
|
@@ -5,6 +5,10 @@ type: guide
|
|
|
5
5
|
summary: Use defineHook with the tool call ID to suspend an agent for human approval, with an optional timeout.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<Callout type="warn">
|
|
9
|
+
This recipe uses the deprecated `DurableAgent` API. For new agents, use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) and follow the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent). The human-in-the-loop pattern here (hooks, `Promise.race`, approval gating) applies to either API.
|
|
10
|
+
</Callout>
|
|
11
|
+
|
|
8
12
|
Use this pattern when an AI agent needs human confirmation before performing a consequential action like booking, purchasing, or publishing. The workflow suspends without consuming resources until the human responds.
|
|
9
13
|
|
|
10
14
|
## When to use this
|
|
@@ -252,4 +256,4 @@ const approvalResult = messages
|
|
|
252
256
|
- [`defineHook()`](/docs/api-reference/workflow/define-hook) — type-safe hook with schema validation
|
|
253
257
|
- [`sleep()`](/docs/api-reference/workflow/sleep) — durable timeout for approval expiry
|
|
254
258
|
- [`getWritable()`](/docs/api-reference/workflow/get-writable) — stream custom data parts from steps
|
|
255
|
-
- [`
|
|
259
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) — AI SDK's durable agent (replaces `DurableAgent`)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: AI SDK
|
|
3
3
|
description: Use AI SDK's streamText directly inside durable workflows when you need the raw AI SDK API or a per-turn durability boundary.
|
|
4
4
|
type: guide
|
|
5
|
-
summary: Use streamText() inside a workflow when the durability boundary is an entire user turn, or when you need AI SDK APIs not exposed by
|
|
5
|
+
summary: Use streamText() inside a workflow when the durability boundary is an entire user turn, or when you need AI SDK APIs not exposed by WorkflowAgent. Individual tool calls and LLM calls inside a turn are not separately durable.
|
|
6
6
|
related:
|
|
7
7
|
- /docs/ai
|
|
8
8
|
- /docs/ai/chat-session-modeling
|
|
@@ -16,18 +16,18 @@ related:
|
|
|
16
16
|
For the full AI SDK reference (providers, `streamText`, `generateObject`, `useChat`, tool calling, etc.) see the [AI SDK docs](https://ai-sdk.dev/docs). This page covers the Workflow-specific integration points.
|
|
17
17
|
|
|
18
18
|
<Callout type="info">
|
|
19
|
-
For most agent use cases, prefer [`
|
|
19
|
+
For most agent use cases, prefer AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent), which implements the same agent loop as [`streamText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text), manages tool calling automatically, and runs tools at workflow scope — each tool can be marked `"use step"` for per-call durability and retries, or stay at workflow level to use primitives like `sleep()` and hooks. Use this page's raw `streamText()` pattern when you want the exact AI SDK API (for example `toUIMessageStream()`, `onChunk`, or `generateText`), or when the durability boundary should be an entire user turn in one step — accepting that tool calls inside that turn are not individually durable.
|
|
20
20
|
</Callout>
|
|
21
21
|
|
|
22
22
|
## When to use streamText directly
|
|
23
23
|
|
|
24
|
-
Use [`streamText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) instead of `
|
|
24
|
+
Use [`streamText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) instead of `WorkflowAgent` when you need:
|
|
25
25
|
|
|
26
|
-
* **The raw AI SDK API** — `streamText().toUIMessageStream()`, `onChunk`, `smoothStream`, or other options that map directly to the [`streamText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) return value rather than `
|
|
26
|
+
* **The raw AI SDK API** — `streamText().toUIMessageStream()`, `onChunk`, `smoothStream`, or other options that map directly to the [`streamText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) return value rather than `WorkflowAgent.stream()`
|
|
27
27
|
* **Per-turn durability** — wrap the entire agent response (model + tools) in a single `"use step"` function so one user turn is the atomic retry unit; useful when you want all tool calls inside a turn to re-execute together
|
|
28
|
-
* **Custom multi-turn orchestration** — manual hook loops, per-turn stream slicing (`sliceUntilFinish`), or other workflow patterns shown below that don't map cleanly to `
|
|
28
|
+
* **Custom multi-turn orchestration** — manual hook loops, per-turn stream slicing (`sliceUntilFinish`), or other workflow patterns shown below that don't map cleanly to `WorkflowAgent`
|
|
29
29
|
|
|
30
|
-
`
|
|
30
|
+
`WorkflowAgent` already supports `stopWhen`, `prepareStep`, lifecycle callbacks, structured output (`output`), per-step model switching, and [provider options](https://ai-sdk.dev/docs/ai-sdk-core/provider-options). See the [`WorkflowAgent` docs](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent).
|
|
31
31
|
|
|
32
32
|
## Multi-turn pattern
|
|
33
33
|
|
|
@@ -250,7 +250,7 @@ Store the `runId` in a ref and pass it in the body of every follow-up. `Workflow
|
|
|
250
250
|
"use client";
|
|
251
251
|
|
|
252
252
|
import { useChat } from "@ai-sdk/react";
|
|
253
|
-
import { WorkflowChatTransport } from "@workflow
|
|
253
|
+
import { WorkflowChatTransport } from "@ai-sdk/workflow";
|
|
254
254
|
import { useMemo, useRef, useState } from "react";
|
|
255
255
|
|
|
256
256
|
export function SupportChat() {
|
|
@@ -324,7 +324,7 @@ The consequences:
|
|
|
324
324
|
**Mitigations:**
|
|
325
325
|
|
|
326
326
|
- Make side-effectful tool implementations idempotent — dedupe server-side on a stable key (e.g. `orderId`, an `Idempotency-Key` header, etc.).
|
|
327
|
-
- Or use [`
|
|
327
|
+
- Or use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent), which runs tools at workflow scope — each tool can be marked `"use step"` to become its own durable, retryable step, or stay at workflow level to use primitives like `sleep()` and hooks.
|
|
328
328
|
|
|
329
329
|
### Snapshot `tailIndex` *before* resuming the hook
|
|
330
330
|
|
|
@@ -358,19 +358,19 @@ Clients can send a `runId` from a long-gone workflow (localStorage, back button,
|
|
|
358
358
|
|
|
359
359
|
This example stores the `runId` after the first response. For strict one-session-per-thread behavior, use a deterministic hook token derived from the thread ID or conversation ID and route retries through the active hook. See [Idempotency](/docs/foundations/idempotency).
|
|
360
360
|
|
|
361
|
-
## streamText vs
|
|
361
|
+
## streamText vs WorkflowAgent
|
|
362
362
|
|
|
363
|
-
| | `streamText()` (this pattern) | `
|
|
363
|
+
| | `streamText()` (this pattern) | `WorkflowAgent` |
|
|
364
364
|
|---|---|---|
|
|
365
365
|
| **Tool loop** | AI SDK handles via `stopWhen` | Handles internally (AI SDK–compatible options) |
|
|
366
366
|
| **LLM call durability** | Re-executes with the parent turn | Each LLM call is a durable step |
|
|
367
367
|
| **Tool call durability** | Not individually durable — re-executes with the parent turn | Per tool — mark `"use step"` for a durable, retryable step, or keep at workflow level for `sleep()` / hooks |
|
|
368
368
|
| **Stop conditions** | `stopWhen`, `prepareStep` | `stopWhen`, `prepareStep` |
|
|
369
|
-
| **Structured output** | `Output.object()`, `Output.array()` | `
|
|
369
|
+
| **Structured output** | `Output.object()`, `Output.array()` | `output` (`Output.object()`, `Output.text()`) |
|
|
370
370
|
| **Step callbacks** | `onStepFinish`, `onChunk`, etc. | `onStepFinish`, `onFinish`, `onError`, `onAbort` (`onChunk` not available) |
|
|
371
371
|
| **Setup** | Manual stream piping and turn slicing | Automatic |
|
|
372
372
|
|
|
373
|
-
Use `
|
|
373
|
+
Use `WorkflowAgent` for most agent use cases. Use `streamText` when you need the raw AI SDK surface or a per-turn durability boundary.
|
|
374
374
|
|
|
375
375
|
## Key APIs
|
|
376
376
|
|
|
@@ -31,10 +31,42 @@ WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"
|
|
|
31
31
|
|
|
32
32
|
Run the migration script to create the necessary tables in your database. Ensure `WORKFLOW_POSTGRES_URL` is set when running this command:
|
|
33
33
|
|
|
34
|
+
<Tabs items={["npm", "pnpm", "Yarn", "Bun"]}>
|
|
35
|
+
|
|
36
|
+
<Tab value="npm">
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx --package=@workflow/world-postgres bootstrap
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
</Tab>
|
|
43
|
+
|
|
44
|
+
<Tab value="pnpm">
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm dlx --package @workflow/world-postgres bootstrap
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
</Tab>
|
|
51
|
+
|
|
52
|
+
<Tab value="Yarn">
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
yarn dlx --package @workflow/world-postgres bootstrap
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
</Tab>
|
|
59
|
+
|
|
60
|
+
<Tab value="Bun">
|
|
61
|
+
|
|
34
62
|
```bash
|
|
35
|
-
|
|
63
|
+
bunx --package @workflow/world-postgres bootstrap
|
|
36
64
|
```
|
|
37
65
|
|
|
66
|
+
</Tab>
|
|
67
|
+
|
|
68
|
+
</Tabs>
|
|
69
|
+
|
|
38
70
|
<Callout type="info">
|
|
39
71
|
The migration is idempotent and can safely be run as a post-deployment lifecycle script.
|
|
40
72
|
</Callout>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workflow",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.25",
|
|
4
4
|
"description": "Workflow SDK - Build durable, resilient, and observable workflows",
|
|
5
5
|
"main": "dist/typescript-plugin.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -57,18 +57,18 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"ms": "2.1.3",
|
|
60
|
-
"@workflow/astro": "5.0.0-beta.
|
|
61
|
-
"@workflow/cli": "5.0.0-beta.
|
|
62
|
-
"@workflow/core": "5.0.0-beta.
|
|
60
|
+
"@workflow/astro": "5.0.0-beta.25",
|
|
61
|
+
"@workflow/cli": "5.0.0-beta.25",
|
|
62
|
+
"@workflow/core": "5.0.0-beta.25",
|
|
63
63
|
"@workflow/errors": "5.0.0-beta.8",
|
|
64
64
|
"@workflow/typescript-plugin": "5.0.0-beta.4",
|
|
65
65
|
"@workflow/utils": "5.0.0-beta.4",
|
|
66
|
-
"@workflow/next": "5.0.0-beta.
|
|
67
|
-
"@workflow/nest": "5.0.0-beta.
|
|
68
|
-
"@workflow/nitro": "5.0.0-beta.
|
|
69
|
-
"@workflow/nuxt": "5.0.0-beta.
|
|
70
|
-
"@workflow/sveltekit": "5.0.0-beta.
|
|
71
|
-
"@workflow/rollup": "5.0.0-beta.
|
|
66
|
+
"@workflow/next": "5.0.0-beta.25",
|
|
67
|
+
"@workflow/nest": "5.0.0-beta.25",
|
|
68
|
+
"@workflow/nitro": "5.0.0-beta.25",
|
|
69
|
+
"@workflow/nuxt": "5.0.0-beta.25",
|
|
70
|
+
"@workflow/sveltekit": "5.0.0-beta.25",
|
|
71
|
+
"@workflow/rollup": "5.0.0-beta.25"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@types/ms": "2.1.0",
|