UIMessageCodec

UIMessageCodec is the pre-built codec for the Vercel AI SDK. It implements Codec<VercelInput, VercelOutput, VercelProjection, UIMessage> so a session can encode UIMessageChunk events out and decode them back into UIMessage objects without a custom implementation.

You rarely import UIMessageCodec directly. The Vercel-pre-bound createClientSession and createAgentSession already supply it. Import it explicitly when you build a codec wrapper, run the encoder or decoder outside of a session, or compose UIMessageCodec into a different codec.

JavaScript

1

2

3

4

import { UIMessageCodec } from '@ably/ai-transport/vercel';

const decoder = UIMessageCodec.createDecoder();
const projection = UIMessageCodec.init();

Properties

UIMessageCodec is a value, not a type. It satisfies the full Codec interface for the Vercel TInput, TOutput, TProjection, and TMessage parameters listed below.

init() => VercelProjection
Build an empty VercelProjection.
fold(state, event, meta) => VercelProjection
Fold a VercelInput or VercelOutput into the projection.
createEncoder(channel, options?) => Encoder<VercelInput, VercelOutput>
Create a Vercel encoder bound to the supplied channel writer.
createDecoder() => Decoder<VercelInput, VercelOutput>
Create a Vercel decoder for the channel.
getMessages(projection) => CodecMessage<UIMessage>[]
Extract { codecMessageId, message } pairs from a VercelProjection. The domain UIMessage.id is preserved verbatim from the source stream; the SDK keys correlation off the paired codecMessageId.
createUserMessage(message: UIMessage) => VercelInput
Wrap a UIMessage as the UserMessage input variant.
createRegenerate(target, parent) => VercelInput
Build a Regenerate input targeting an assistant UIMessage.
createToolResult(codecMessageId, { toolCallId, output }) => VercelInput
Build a ToolResult input addressed at the assistant codec-message that contains the tool call.
createToolResultError(codecMessageId, { toolCallId, message }) => VercelInput
Build a ToolResultError input.
createToolApprovalResponse(codecMessageId, { toolCallId, approved, reason? }) => VercelInput
Build a ToolApprovalResponse input.

Type parameters

TInputUserMessage<UIMessage> | Regenerate | ToolResult<VercelToolResultPayload> | ToolResultError<VercelToolResultErrorPayload> | ToolApprovalResponse<VercelToolApprovalResponsePayload>
Discriminated union of every record-shape a client publishes on the ai-input wire. Composed from the SDK's well-known input variants, with the tool variants parameterised by the Vercel domain payload shapes (VercelToolResultPayload, VercelToolResultErrorPayload, VercelToolApprovalResponsePayload).
TOutputAI.UIMessageChunk
Every record-shape the agent publishes on the ai-output wire. The codec passes Vercel's UIMessageChunk through unchanged.
TProjection{ messages: CodecMessage<UIMessage>[], ... }
Per-Run projection. Carries the { codecMessageId, message } pair list plus internal stream-tracker state and a high-water-mark serial for idempotency. The SDK does not inspect this shape. Reach for getMessages instead.

The well-known input variants (UserMessage, Regenerate, ToolResult, ToolResultError, ToolApprovalResponse) are documented on the Codec reference page. The Vercel codec layers no extra input variants.

Example

Decode a single Ably message and fold the resulting events into a fresh projection. ReducerMeta.serial is required (the reducer uses it as the high-water-mark for idempotency); the messageId field is optional and only needed when the codec routes events to an existing message.

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import { UIMessageCodec } from '@ably/ai-transport/vercel';

const decoder = UIMessageCodec.createDecoder();
let projection = UIMessageCodec.init();

channel.subscribe((message) => {
  if (!message.serial) return; // live channel-subscribe messages always carry one
  const { inputs, outputs } = decoder.decode(message);
  for (const input of inputs) {
    projection = UIMessageCodec.fold(projection, input, { serial: message.serial });
  }
  for (const output of outputs) {
    projection = UIMessageCodec.fold(projection, output, { serial: message.serial });
  }
  render(UIMessageCodec.getMessages(projection).map((entry) => entry.message));
});