Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/workflow-chat-transport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ai-sdk/workflow': patch
---

Export `WorkflowChatTransport` with `initialStartIndex` support for resumable stream reconnection, including negative start index resolution via `x-workflow-stream-tail-index` header.
82 changes: 82 additions & 0 deletions content/docs/03-agents/07-workflow-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,87 @@ return createUIMessageStreamResponse({
});
```

## Resumable Streaming with WorkflowChatTransport

Workflow functions can time out or be interrupted by network failures. `WorkflowChatTransport` is a [`ChatTransport`](/docs/ai-sdk-ui/transport) implementation that handles these interruptions automatically — it detects when a stream ends without a `finish` event and reconnects to resume from where it left off.

```tsx filename="app/page.tsx"
'use client';

import { useChat } from '@ai-sdk/react';
import { WorkflowChatTransport } from '@ai-sdk/workflow';
import { useMemo } from 'react';

export default function Chat() {
const transport = useMemo(
() =>
new WorkflowChatTransport({
api: '/api/chat',
maxConsecutiveErrors: 5,
initialStartIndex: -50, // On page refresh, fetch last 50 chunks
}),
[],
);

const { messages, sendMessage } = useChat({ transport });

// ... render chat UI
}
```

The transport requires your POST endpoint to return an `x-workflow-run-id` response header, and a GET endpoint at `{api}/{runId}/stream` for reconnection:

```ts filename="app/api/chat/route.ts"
import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';
import { createUIMessageStreamResponse, type UIMessage } from 'ai';
import { start } from 'workflow/api';
import { chat } from '@/workflow/agent-chat';

export async function POST(request: Request) {
const { messages }: { messages: UIMessage[] } = await request.json();
const run = await start(chat, [messages]);

return createUIMessageStreamResponse({
stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
headers: {
'x-workflow-run-id': run.runId,
},
});
}
```

```ts filename="app/api/chat/[runId]/stream/route.ts"
import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';
import type { NextRequest } from 'next/server';
import { getRun } from 'workflow/api';

export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ runId: string }> },
) {
const { runId } = await params;
const startIndex = Number(
new URL(request.url).searchParams.get('startIndex') ?? '0',
);

const run = await getRun(runId);
const readable = run
.getReadable({ startIndex })
.pipeThrough(createModelCallToUIChunkTransform());

return new Response(readable, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'x-workflow-run-id': runId,
},
});
}
```

For the full API reference, see [`WorkflowChatTransport`](/docs/reference/ai-sdk-workflow/workflow-chat-transport).

## Tools as Workflow Steps

Mark tool execute functions with `'use step'` to make them durable workflow steps. This gives each tool call:
Expand Down Expand Up @@ -385,5 +466,6 @@ export type MyAgentUIMessage = InferWorkflowAgentUIMessage<typeof myAgent>;
## Next Steps

- [WorkflowAgent API Reference](/docs/reference/ai-sdk-workflow/workflow-agent) for detailed parameter documentation
- [WorkflowChatTransport API Reference](/docs/reference/ai-sdk-workflow/workflow-chat-transport) for stream reconnection options
- [Building Agents](/docs/agents/building-agents) for the in-memory `ToolLoopAgent` alternative
- [Loop Control](/docs/agents/loop-control) for advanced stop conditions
38 changes: 38 additions & 0 deletions content/docs/04-ai-sdk-ui/21-transport.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,44 @@ const transport = new DirectChatTransport({

For complete API details, see the [DirectChatTransport reference](/docs/reference/ai-sdk-ui/direct-chat-transport).

## Workflow Transport

For chat apps built on [Vercel Workflows](/docs/agents/workflow-agent), `WorkflowChatTransport` from `@ai-sdk/workflow` provides automatic stream reconnection. It handles the common scenario where a workflow function times out mid-stream — the transport detects the missing `finish` event and reconnects to resume from where it left off.

```tsx
import { useChat } from '@ai-sdk/react';
import { WorkflowChatTransport } from '@ai-sdk/workflow';
import { useMemo } from 'react';

export default function Chat() {
const transport = useMemo(
() =>
new WorkflowChatTransport({
api: '/api/chat',
maxConsecutiveErrors: 5,
initialStartIndex: -50, // On page refresh, fetch last 50 chunks
onChatEnd: ({ chatId, chunkIndex }) => {
console.log(`Chat complete: ${chunkIndex} chunks`);
},
}),
[],
);

const { messages, sendMessage } = useChat({ transport });

// ... render chat UI
}
```

Key features:

- **Automatic reconnection**: Detects interrupted streams (no `finish` event) and reconnects via GET to `{api}/{runId}/stream`
- **Page refresh recovery**: `initialStartIndex` with negative values (e.g., `-50`) fetches only the tail of the stream instead of replaying everything
- **Configurable retries**: `maxConsecutiveErrors` controls how many consecutive reconnection failures to tolerate
- **Lifecycle callbacks**: `onChatSendMessage` and `onChatEnd` for tracking chat state

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).

## Building Custom Transports

To understand how to build your own transport, refer to the source code of the default implementation:
Expand Down
Loading