How to Use the Responses API Transformer in OmniRoute to Convert Between API Formats
To convert between API formats, pipe the Chat Completions SSE stream through createResponsesApiTransformStream in open-sse/transformer/responsesTransformer.ts, or use handleResponsesCore to automatically translate Requests API payloads and transform the response.
The OmniRoute proxy supports both standard OpenAI Chat Completions and the newer Responses API streaming protocols. When you need to serve Responses API clients while connecting to providers that only support Chat Completions, the Responses API transformer bridges this gap by translating requests and transforming the streaming response format through a stateful pipeline.
How the Responses API Transformer Works
The conversion process follows a specific pipeline that ensures streaming compatibility and accurate stateful event emission.
Request Translation Phase
When a client sends a request in the Responses API shape, the handleResponsesCore function in open-sse/handlers/responsesHandler.ts receives the payload. This function translates the request body using convertResponsesApiFormat from open-sse/translator/helpers/responsesApiHelper.ts to match the native Chat Completions structure. The translation process automatically sets stream = true to ensure the upstream provider returns a Server-Sent Events (SSE) stream compatible with the transformation pipeline.
Stream Transformation Phase
After dispatching the translated request via handleChatCore, the upstream Chat Completions SSE stream flows through createResponsesApiTransformStream located in open-sse/transformer/responsesTransformer.ts. This TransformStream parses each incoming chunk and emits corresponding Responses API events such as response.output_item.added, response.output_text.delta, and response.function_call_arguments.delta. A keep-alive heartbeat is then added to prevent client timeouts during long-running operations.
State Management and Output Ordering
The transformer maintains a state object that buffers message text, function-call arguments, reasoning snippets, and an ordered list of emitted items called completedOutputItems. When the stream completes, the buildDenseOutput function sorts these items by output_index and emission sequence. This ensures the final response.completed event contains a stable, deterministic array ready for client consumption.
Key Implementation Details
Understanding the internal mechanics helps troubleshoot edge cases and optimize performance:
- State tracking: The transformer holds buffers for partial content across multiple SSE chunks, including reasoning text and tool call arguments.
- Deterministic output: The
recordCompletedItemfunction tracks each finished item, whilebuildDenseOutputensures proper ordering in the final response array. - Reasoning handling: Supports both native
reasoning_contentfields and textual `` tags, emitting them asresponse.reasoning_summary_*events before normal message content. - Tool-call streaming: Transforms Chat Completions
tool_callsinto separateresponse.output_item.addedandresponse.function_call_arguments.deltaevents, stripping empty strings for clean JSON payloads. - Keep-alive mechanism: A periodic
: keepaliveSSE line prevents client timeouts, with automatic cleanup on stream cancellation.
Code Examples
Handling Responses API Requests End-to-End
Use the handler function to process full Requests API conversations:
import { handleResponsesCore } from "@omniroute/open-sse/handlers/responsesHandler.ts";
await handleResponsesCore({
body: {
model: "gpt-4o",
input: "Explain quantum computing",
tools: [{ type: "code_interpreter" }]
},
modelInfo: { provider: "openai", model: "gpt-4o" },
credentials: { apiKey: process.env.OPENAI_KEY },
log: null,
onCredentialsRefreshed: () => {},
onRequestSuccess: () => console.log("Stream started"),
onDisconnect: () => console.log("Client disconnected"),
connectionId: "conn-123",
signal: abortController.signal,
});
Manual Stream Transformation
For custom pipelines or testing, apply the transformer directly to a Chat Completions SSE stream:
import { createResponsesApiTransformStream } from "@omniroute/open-sse/transformer/responsesTransformer.ts";
import { createSseHeartbeatTransform, HEARTBEAT_SHAPES } from "@omniroute/open-sse/utils/heartbeat.ts";
const chatSseStream: ReadableStream = fetchChatCompletionsStream(); // Your upstream source
const transformed = chatSseStream
.pipeThrough(createResponsesApiTransformStream())
.pipeThrough(
createSseHeartbeatTransform({
signal: abortSignal,
intervalMs: 3000,
shape: HEARTBEAT_SHAPES.OPENAI_RESPONSES_IN_PROGRESS,
})
);
for await (const chunk of transformed) {
console.log(new TextDecoder().decode(chunk));
}
Understanding the Output Structure
When the stream completes, the transformer constructs a final response object:
// Internal structure from responsesTransformer.ts
const response = {
id: state.responseId,
object: "response",
created_at: state.created,
status: "completed",
output: buildDenseOutput(), // Sorted array of all items
usage: state.usage, // Optional token statistics
};
Source File Reference
Implementations reside in specific modules within the OmniRoute repository:
open-sse/transformer/responsesTransformer.ts— CoreTransformStreamimplementation that maps Chat Completions SSE events to Responses API events.open-sse/handlers/responsesHandler.ts— Endpoint handler coordinating request translation, chat core invocation, and transformation.open-sse/translator/helpers/responsesApiHelper.ts— Helper utilities converting Responses API request bodies to Chat Completions format.
Summary
- OmniRoute bridges Chat Completions and Responses API formats through a dedicated transformer pipeline.
- The
handleResponsesCorefunction orchestrates translation and streaming, whilecreateResponsesApiTransformStreamhandles the SSE conversion. - State tracking ensures accurate representation of tool calls, reasoning, and message ordering in the final output.
- Keep-alive heartbeats prevent timeouts during long-running streaming operations.
Frequently Asked Questions
What is the difference between Chat Completions and Responses API formats?
The Chat Completions API streams delta updates to conversation content, while the Responses API emits discrete events like response.output_item.added and response.output_text.delta. OmniRoute's transformer converts the former into the latter by parsing deltas and reconstructing them as semantic output items with proper indexing.
How does OmniRoute handle tool calls when converting API formats?
The transformer detects tool_calls from the Chat Completions stream and emits them as response.output_item.added events followed by response.function_call_arguments.delta chunks. Empty strings and arrays are automatically stripped to ensure valid JSON payloads in the transformed stream.
What happens to reasoning content during the transformation?
Reasoning content is extracted from either the reasoning_content field or parsed from `` tags in the source stream. The transformer emits response.reasoning_summary_* events for reasoning items before processing normal message content, ensuring proper ordering in the final output array.
How do I add keep-alive heartbeats to the transformed stream?
Pipe the transformed stream through createSseHeartbeatTransform imported from the OmniRoute utilities, specifying an interval (typically 3000ms) and the OPENAI_RESPONSES_IN_PROGRESS shape. This enqueues periodic : keepalive SSE lines that prevent client timeouts without interfering with the actual response data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →