Skip to content
Open
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
23 changes: 15 additions & 8 deletions src/pages/docs/ai-transport/frameworks/vercel-ai-sdk-core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ On the server, AI Transport replaces `createUIMessageStreamResponse()` with `Run
return createUIMessageStreamResponse({ stream: result.toUIMessageStream() });

// After: Ably transport
const { reason } = await run.pipe(result.toUIMessageStream());
await run.end({ reason });
const pipeResult = await run.pipe(result.toUIMessageStream());
const outcome = await vercelRunOutcome(pipeResult, result.finishReason);
await run.end(outcome);

return Response.json({ invocationId: run.invocationId });
```
</Code>
Expand All @@ -59,9 +61,9 @@ A full agent route:
import { after } from 'next/server';
import { streamText, convertToModelMessages } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import Ably from 'ably';
import * as Ably from 'ably';
import { Invocation } from '@ably/ai-transport';
import { createAgentSession } from '@ably/ai-transport/vercel';
import { createAgentSession, vercelRunOutcome } from '@ably/ai-transport/vercel';

const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });

Expand All @@ -81,12 +83,17 @@ export async function POST(req) {
abortSignal: run.abortSignal,
});

const { reason } = await run.pipe(result.toUIMessageStream());
await run.end({ reason });
const pipeResult = await run.pipe(result.toUIMessageStream());
const outcome = await vercelRunOutcome(pipeResult, result.finishReason);
if (outcome.reason === 'suspend') {
await run.suspend();
} else {
await run.end(outcome);
}
session.close();
});

return Response.json({ invocationId: run.invocationId });
return Response.json({ runId: run.runId, invocationId: run.invocationId });
}
```
</Code>
Expand All @@ -95,7 +102,7 @@ The integration has three pieces:

1. The `UIMessageCodec` encodes Vercel's `UIMessageChunk` events as Ably messages. Every chunk type maps to an Ably operation with headers that track the metadata. The codec encodes on the agent and decodes on the client.
2. `createAgentSession({ client, channelName })` from `@ably/ai-transport/vercel` constructs the agent session bound to the channel from the invocation. The default codec is `UIMessageCodec`.
3. `run.pipe()` reads the model's `UIMessageChunk` stream, encodes each chunk, and publishes the resulting Ably messages. `run.abortSignal` wires cancellation through from the client.
3. `run.pipe()` reads the model's `UIMessageChunk` stream, encodes each chunk, and publishes the resulting Ably messages. `run.abortSignal` wires cancellation through from the client. [`vercelRunOutcome()`](/docs/ai-transport/api/javascript/vercel/run-outcome) then maps the pipe result and Vercel's `finishReason` to the right lifecycle action: suspend the Run when the model requested tools the SDK did not auto-execute, otherwise end it with the matching reason.

## Scope and trade-offs <a id="scope"/>

Expand Down