Skip to content
This repository was archived by the owner on Aug 10, 2026. It is now read-only.

Commit 3fcb6c5

Browse files
committed
Document OpenAI codec client-side tool support
The ResponsesCodec now supports the full client-side tool surface (client-executed tools, tool failures, and human approvals), so the docs no longer match the SDK. - Add a "Run client-side tools and approvals" section to the OpenAI framework page covering the createToolResult, createToolResultError, and createToolApprovalResponse factories and the tool-approval-request output, and remove the stale run-outcome Aside. - Rewrite "Scope and trade-offs" so only hosted tools remain listed as unsupported. - Add an "OpenAI codec" section to the tool-calling feature page.
1 parent 41f11e8 commit 3fcb6c5

2 files changed

Lines changed: 49 additions & 5 deletions

File tree

src/pages/docs/ai-transport/features/tool-calling.mdx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,29 @@ if (pending) {
122122
123123
The result is addressed to the suspended assistant message by `codecMessageId`. Reusing the original `runId` keeps the resume on the same run instead of starting a fresh one.
124124
125+
## OpenAI codec <a id="openai"/>
126+
127+
The examples above use the Vercel codec, but the OpenAI Responses codec supports the same tool surface: server-executed function calls, client-executed tools, tool failures, and human approvals. The suspend and resume mechanics live in the transport, so they are identical for both codecs. The wire types and the factory payload field names differ.
128+
129+
The OpenAI codec expresses tool state against the Responses types, so a tool call is a `function_call` item and its result is a `function_call_output` item. The client factories take snake_case payloads keyed by `call_id`, and you address each to the assistant message that holds the call:
130+
131+
<Code>
132+
```javascript
133+
import { ResponsesCodec } from '@ably/ai-transport/openai';
134+
135+
// A client-run tool succeeded.
136+
await view.send(ResponsesCodec.createToolResult(codecMessageId, { call_id, output }), { runId });
137+
138+
// A client-run tool failed. The message becomes the output the model sees next turn.
139+
await view.send(ResponsesCodec.createToolResultError(codecMessageId, { call_id, message }), { runId });
140+
141+
// A user approved or denied a gated tool. A denial resolves entirely on the client.
142+
await view.send(ResponsesCodec.createToolApprovalResponse(codecMessageId, { call_id, approved, reason }), { runId });
143+
```
144+
</Code>
145+
146+
The Responses `function_call_output` item has no field for an approval decision or an error, so the codec holds that render-only state on `OpenAIMessage.toolCallStates`, a map keyed by `call_id`. `toResponsesInput` never reads it, so it cannot reach the model. See [OpenAI Responses](/docs/ai-transport/frameworks/openai) for the agentic loop and the approval-request output.
147+
125148
## History persistence <a id="history"/>
126149
127150
Tool invocations and results are part of the channel's message history. When a client reconnects or a late joiner loads the conversation, tool activity is replayed along with text messages. The view reconstructs tool state so the UI shows the correct status: pending, complete, or failed.

src/pages/docs/ai-transport/frameworks/openai.mdx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "OpenAI Responses"
33
meta_description: "How Ably AI Transport integrates with the OpenAI Responses API. The ResponsesCodec encodes the Responses event stream onto an Ably channel, and toResponsesInput feeds the conversation back to the model."
4-
meta_keywords: "AI Transport, OpenAI, Responses API, ResponsesCodec, toResponsesInput, function calling, reasoning, durable sessions, server"
4+
meta_keywords: "AI Transport, OpenAI, Responses API, ResponsesCodec, toResponsesInput, function calling, client-side tools, tool approval, reasoning, durable sessions, server"
55
intro: "The OpenAI Responses API streams model output as typed events over HTTP. AI Transport's ResponsesCodec encodes that stream onto an Ably channel, so the same server code feeds durable sessions instead of an ephemeral HTTP response."
66
---
77

@@ -96,13 +96,34 @@ Server-executed tools do not suspend the Run. The agent runs an agentic loop: ca
9696

9797
For reasoning models, the loop must re-append the whole turn's output items, including the reasoning items that preceded a function call, since reasoning models expect that reasoning to travel with the call on the next request. The [runnable demo](https://github.com/ably/ably-ai-transport-js) implements the full loop.
9898

99-
<Aside data-type='note'>
100-
The Run outcome for the OpenAI codec is text-only today: `complete`, `cancelled`, or `error`. A run-outcome mapper that forwards an in-band model failure lands with the client-side tool and approval increments.
101-
</Aside>
99+
## Run client-side tools and approvals <a id="client-tools"/>
100+
101+
The codec also carries the client-driven half of tool calling: a client executes a tool in the browser and publishes the result, reports a tool failure, or answers a human approval prompt. The suspend and resume mechanics belong to the transport, so they work the same way they do for the Vercel codec. The agent calls `run.suspend()` to wait for a client, the client publishes its input on the same `runId`, and a continuation resumes the Run.
102+
103+
`ResponsesCodec` exposes the full well-known factory set. You address each client input to the assistant message that holds the `function_call`, and key each payload by the OpenAI snake_case `call_id`:
104+
105+
<Code>
106+
```javascript
107+
import { ResponsesCodec } from '@ably/ai-transport/openai';
108+
109+
// A client-run tool succeeded.
110+
await view.send(ResponsesCodec.createToolResult(codecMessageId, { call_id, output }), { runId });
111+
112+
// A client-run tool failed. The message becomes the output the model sees next turn.
113+
await view.send(ResponsesCodec.createToolResultError(codecMessageId, { call_id, message }), { runId });
114+
115+
// A user approved or denied a gated tool. A denial resolves entirely on the client.
116+
await view.send(ResponsesCodec.createToolApprovalResponse(codecMessageId, { call_id, approved, reason }), { runId });
117+
```
118+
</Code>
119+
120+
To gate a tool on a human decision, the agent publishes the codec's own `tool-approval-request` output, carrying the `call_id`, the tool name, and the arguments so a client can render the prompt without the streamed `function_call`.
121+
122+
The Responses `function_call_output` item has no field for an approval decision or an error, so the codec holds that render-only state out of band on `OpenAIMessage.toolCallStates`, a map keyed by `call_id`. `toResponsesInput` never reads that state, so it cannot reach the model.
102123

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

105-
The `ResponsesCodec` is scoped to the streamed shapes the Responses API produces: assistant text, refusals, reasoning (summary and raw), and server-executed function calls. Client-side tools, hosted tools, and approval-gated tools are not yet supported; the codec exposes the user-message and regenerate input variants, with the tool variants to follow.
126+
The `ResponsesCodec` covers the shapes the Responses API streams: assistant text, refusals, reasoning (summary and raw), and server-executed function calls. It also covers the client-driven tool surface: client-executed tools, tool failures, and human approvals. Hosted tools (web and file search, code interpreter, image generation, MCP, custom tools) are not yet supported.
106127

107128
The codec transmits the raw Responses events rather than a normalised abstraction. The wire therefore tracks OpenAI's own event model, which keeps stored items round-trippable to the Responses API but ties a conversation to the Responses shape. To integrate a different model provider behind one abstraction, use [Vercel AI SDK Core](/docs/ai-transport/frameworks/vercel-ai-sdk-core) instead.
108129

0 commit comments

Comments
 (0)