Vercel codec
The Vercel codec carries the Vercel AI SDK's UIMessageChunk events over an Ably channel, unchanged, plus a user-message event for the message a client sends. The agent pipes the stream that toUIMessageStream returns, and the client merges the chunks it receives with the AI SDK's own readUIMessageStream.
Import vercel from @ably/ai-transport/vercel and pass it to createTransport. The codec works with the AI SDK's ai package at version 6 or 7.
1
2
3
4
5
import { createTransport } from '@ably/ai-transport';
import { vercel } from '@ably/ai-transport/vercel';
// ably is your Ably.Realtime client.
const transport = createTransport({ client: ably, channelName: 'conversations:42', codec: vercel });Properties
The codec writes each event type in one of three ways:
- Text, reasoning and tool-input streams:
text-delta,reasoning-deltaandtool-input-deltaeach append their text to one message for the stream, keyed by the stream id or the tool call id. The first delta publishes the message. The chunk that starts the stream and the chunk that ends it are plain publishes of their own, and the end chunk, ortool-input-availablefor a tool input, closes the stream. - Transient data parts: a
data-*chunk markedtransientis published as an ephemeral message, so it reaches subscribed clients and stays out of history. - Every other chunk, and the
user-messageevent, is a plain publish that carries the whole event as the message data.
Create a Vercel codec
createVercelCodec<TMetadata, TDataParts, TTools>(): Codec<VercelEvent<TMetadata, TDataParts, TTools>>Build a Vercel codec typed for your own UIMessage type parameters. vercel is a codec built with the defaults. Each call builds a new codec with its own decoder state.
1
2
3
import { createVercelCodec } from '@ably/ai-transport/vercel';
const codec = createVercelCodec();Parameters
createVercelCodec takes no arguments. In TypeScript, pass the type parameters of your UIMessage:
TMetadataoptionalTypeunknown.TDataPartsoptionalTypeUIDataTypes.TToolsoptionalTypeUITools.Returns
Codec<VercelEvent>. The codec, for events of the following type:
UIMessageChunkUIMessageChunktoUIMessageStream produces, such as start, text-delta, tool-input-available and finish, unchanged.user-messageVercelUserMessageExample
The agent pipes the AI SDK's chunk stream. The client sends the user's message, and merges each response from its start chunk to its finish chunk with readUIMessageStream.
1
2
3
4
5
// Agent code.
import { streamText, toUIMessageStream } from 'ai';
const result = streamText({ model, messages });
await transport.pipe(toUIMessageStream({ stream: result.fullStream }), { signal: req.signal });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Client code.
import { readUIMessageStream } from 'ai';
let response;
transport.subscribe(({ event }) => {
if (event === undefined || event.type === 'user-message') return;
if (event.type === 'start') {
const stream = new ReadableStream({ start: (controller) => (response = controller) });
(async () => {
for await (const message of readUIMessageStream({ stream })) render(message);
})();
}
response?.enqueue(event);
if (event.type === 'finish') response?.close();
});
const message = { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: 'What is the weather?' }] };
await transport.send({ type: 'user-message', message }, { headers: { requestId: message.id } });
// Wake the agent. It reads the prompt from the channel.
await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ channelName: 'conversations:42', requestId: message.id }) });