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 recordCompletedItem function tracks each finished item, while buildDenseOutput ensures proper ordering in the final response array.
  • Reasoning handling: Supports both native reasoning_content fields and textual `` tags, emitting them as response.reasoning_summary_* events before normal message content.
  • Tool-call streaming: Transforms Chat Completions tool_calls into separate response.output_item.added and response.function_call_arguments.delta events, stripping empty strings for clean JSON payloads.
  • Keep-alive mechanism: A periodic : keepalive SSE 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:

Summary

  • OmniRoute bridges Chat Completions and Responses API formats through a dedicated transformer pipeline.
  • The handleResponsesCore function orchestrates translation and streaming, while createResponsesApiTransformStream handles 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →