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.

JavaScript

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

adapterTagString
vercel-ai-sdk-ui-message. The identifier the transport adds to its channel's params.agent.
encodeFunction
Encode one event, as the Codec contract describes.
decodeFunction
Decode one Ably message, as the Codec contract describes.

The codec writes each event type in one of three ways:

  • Text, reasoning and tool-input streams: text-delta, reasoning-delta and tool-input-delta each 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, or tool-input-available for a tool input, closes the stream.
  • Transient data parts: a data-* chunk marked transient is published as an ephemeral message, so it reaches subscribed clients and stays out of history.
  • Every other chunk, and the user-message event, 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.

JavaScript

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:

TMetadataoptionalType
The type of each message's metadata. Defaults to unknown.
TDataPartsoptionalType
The custom data part types. Defaults to UIDataTypes.
TToolsoptionalType
The tool set that types the tool parts of the user message. Defaults to UITools.

Returns

Codec<VercelEvent>. The codec, for events of the following type:

UIMessageChunkUIMessageChunk
Every chunk the AI SDK's toUIMessageStream produces, such as start, text-delta, tool-input-available and finish, unchanged.
user-messageVercelUserMessage
The message a client sends to start a turn.

Example

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.

JavaScript

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 });
JavaScript

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 }) });