OpenAI Responses

The OpenAI Responses API streams model output as typed events over HTTP. AI Transport's OpenAI codec carries that stream over an Ably channel, so the same server code reaches every subscribed client instead of a single HTTP response.

The OpenAI Responses API is OpenAI's interface for calling a model and getting back a stream of typed events: output text, reasoning, refusals, function-call arguments, and lifecycle events. AI Transport writes that stream to an Ably channel, so the channel gives you reconnects, multi-device delivery, and replay from your own store.

The OpenAI codec, openai from @ably/ai-transport/openai, carries the Responses API's own stream events, plus an input event for the input items a client sends. Pass it to createTransport, on the agent and on the client.

What the OpenAI Responses API brings

The openai npm package owns the model call and the typed event model:

FeatureDescription
Responses streamingclient.responses.create({ stream: true }) returns a stream of ResponseStreamEvents: output-text deltas, reasoning, refusals, function-call arguments, and item lifecycle events.
Function toolsYou advertise function tools on the request. The model emits function calls, you run them, and you send the outputs back on the next Responses API call.
Reasoning modelsReasoning items carry the model's summarised thinking, and encrypted_content for requests that do not store responses.
Round-trippable itemsEach output item is also valid Responses API input, so a stored conversation feeds the next turn without conversion.
Typed SDKThe official SDK owns auth, the HTTP call, streaming, and the Responses type definitions.

What AI Transport adds

Everything below comes from AI Transport rather than the framework.

FeatureDescription
Streams that outlive a connectionThe response flows through a channel rather than one HTTP response, so a client can reconnect and receive what it missed.
Multi-device deliveryEvery device subscribed to the conversation receives the same messages in realtime.
History and replayA client that loads the conversation from your store replays the channel from the stored serial, then receives live messages.
Token compactionA client that joins mid-response receives the text so far in one delivery, rather than a replay of every token.

Where they connect

On the server, AI Transport replaces the HTTP response with pipe(). To forward a Responses stream over HTTP yourself, you serialise each event into a Server-Sent Events response by hand:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

// Agent code, without AI Transport.
const encoder = new TextEncoder();
return new Response(
  new ReadableStream({
    async start(controller) {
      for await (const event of stream) {
        controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
      }
      controller.close();
    },
  }),
  { headers: { 'Content-Type': 'text/event-stream' } },
);

With AI Transport, a ResponseStreamEvent is already an event the codec knows, and pipe() takes the SDK's async iterable directly:

JavaScript

1

2

3

// Agent code.
const stream = await client.responses.create({ model: 'gpt-5.5', input, stream: true }, { signal: req.signal });
const { serial } = await transport.pipe(stream, { signal: req.signal });

Every subscribed client decodes the same events. A client sends the user's input as an input event, and merges the output text deltas into the response it renders:

JavaScript

1

2

3

4

5

6

7

8

9

// Client code.
const requestId = crypto.randomUUID();
await transport.send({ type: 'input', items: [{ role: 'user', content: 'What is the weather?' }] }, { headers: { requestId } });
// Wake the agent. It reads the prompt from the channel.
await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ channelName: 'conversations:42', requestId }) });

transport.subscribe(({ event }) => {
  if (event?.type === 'response.output_text.delta') appendText(event.item_id, event.delta);
});

The codec appends each streamed part of a response, such as output text or a function call's arguments, to one message, and the matching *.done event closes it. Text goes on the channel once: the codec sends the text fields that the Responses API repeats in its *.done events and finished items as empty strings, and the final response's output as an empty array, so a client builds the text from the deltas.

To store the conversation for the next turn, keep the output items from the stream on the agent before the codec encodes them. The quickstart passes each event to the pipe through a small generator that keeps the response.completed event's output.

Run tools

The agent runs its tool loop as it would without AI Transport: call the Responses API, run the function calls the model emits, add the model's output items and the tool outputs to the input, and call it again. Pipe each call's stream to the transport, so every subscribed client sees each model turn as it streams. For reasoning models, keep the reasoning items that came before a function call in the input with the call.

A client that runs a tool itself, in the browser, sends the result as a function_call_output item in an input event, so the agent and every other client receive it.

Scope and trade-offs

The codec carries the raw Responses events, so the channel uses OpenAI's own event model. Stored items stay round-trippable to the Responses API, and a conversation is tied to the Responses shape. To work with several model providers behind one interface, use the Vercel AI SDK.