How OmniRoute's Translation Layer Converts Between OpenAI, Anthropic, and Gemini API Formats

OmniRoute's translation layer converts between OpenAI, Anthropic, and Gemini formats using registry-based translators that normalize roles, tool calls, and reasoning budgets through the translateRequest and translateResponse functions exported from the open-sse/translator package.

The OmniRoute translation layer serves as the interoperability core of the open-source routing proxy, enabling seamless communication between heterogeneous LLM providers. By implementing a bidirectional normalization pipeline in the diegosouzapw/OmniRoute repository, this layer ensures that clients can interact with Anthropic, Google Gemini, and OpenAI models using a single OpenAI-compatible schema while preserving provider-specific features like tool calling and reasoning budgets.

Architecture of the Translation Layer

The translation layer operates on a canonical OpenAI schema principle. All incoming requests are treated as OpenAI-compatible, then translated to the target provider's native format, with responses converted back to OpenAI format before returning to the client.

Central Dispatch Mechanism

The entry points translateRequest and translateResponse in open-sse/translator/index.ts serve as the universal gatekeepers. These functions accept a source format identifier, target format identifier, and payload, then delegate to the appropriate provider-specific implementation registered in the system.

Registry-Based Translator Mapping

The [translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) file maintains a mapping of conversion pairs. Each supported route (e.g., openai → gemini, anthropic → openai) registers a request translator (source → target) and a response translator (target → source). This registry pattern enables OmniRoute to dynamically load conversion logic without hardcoding provider-specific logic into the core routing engine.

Request Normalization Pipeline

Before provider-specific conversion occurs, OmniRoute applies a normalization pipeline to ensure consistency across disparate API structures.

Role Normalization and System Instructions

The translation layer handles role name disparities through [services/roleNormalizer.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts). For example, the developer role used in some OpenAI contexts is mapped to system when targeting providers that lack native developer role support. Similarly, system messages are extracted and reformatted—converted to Gemini's system_instruction field or Claude's top-level system parameter—depending on the target.

Tool Call and ID Normalization

Tool calling semantics vary significantly between providers. The [helpers/toolCallHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) module normalizes tool call IDs, inserts missing tool-call structures, and strips orphaned results to prevent validation errors. This ensures that a tool call generated in OpenAI format (with tool_calls and tool_call_id) is correctly translated to Claude's tool block format or Gemini's function calling schema.

Thinking Budget Enforcement

For models supporting reasoning tokens (such as DeepSeek or specific Claude configurations), the [services/thinkingBudget.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) module applies token limits before translation. This prevents expensive reasoning operations from exceeding user-defined budgets regardless of the target provider's native support for reasoning controls.

Provider-Specific Conversion Strategies

Each LLM provider requires distinct structural transformations to map the OpenAI chat completions schema to native formats.

OpenAI to Gemini Request Translation

The [translator/request/openai-to-gemini.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) module handles the conversion of OpenAI's messages array into Gemini's content format. Key transformations include:

OpenAI to Anthropic (Claude) Request Translation

Conversion to Claude format occurs in [translator/request/openai-to-claude.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts). This translator:

Gemini Response to OpenAI Format

For responses, [translator/response/gemini-to-openai.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/gemini-to-openai.ts) performs the inverse operation, extracting Gemini's content blocks and reconstructing OpenAI-compatible message objects with role: assistant and content fields. The translator also handles streaming responses through dedicated SSE converters like openai-to-gemini-sse.ts.

Practical Implementation Examples

The following examples demonstrate how to use the translation layer programmatically.

Converting OpenAI Requests to Gemini Format

import { translateRequest } from '@/open-sse/translator';

const openAIPayload = {
  model: 'gpt-4o-mini',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain quantum computing.' }
  ],
  temperature: 0.7
};

const geminiPayload = translateRequest(
  'openai',
  'gemini',
  'gemini-pro',
  openAIPayload,
  false
);

This produces a Gemini-compatible request where the system message becomes system_instruction and user content transforms into the contents array format.

Translating OpenAI Tool Calls to Claude Format

import { translateRequest } from '@/open-sse/translator';

const requestWithTools = {
  model: 'claude-3-5-sonnet-20240620',
  messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
  tools: [{
    type: 'function',
    function: { name: 'get_weather', parameters: { location: 'string' } }
  }],
  tool_choice: 'auto'
};

const claudePayload = translateRequest(
  'openai',
  'anthropic',
  'claude-3-5-sonnet-20240620',
  requestWithTools,
  false
);

The translator extracts the system context, reformats tools into Claude's tools array, and ensures proper tool_choice serialization.

Converting Gemini Streaming Responses to OpenAI SSE

import { translateResponse } from '@/open-sse/translator';

const geminiStream = getGeminiStream(); // ReadableStream from Gemini API

const openAIStream = translateResponse(
  'gemini',
  'openai',
  geminiStream
);

The resulting stream emits OpenAI-compatible SSE chunks with data: {"choices":[{"delta":{"content":"..."}}]} formatting, allowing any OpenAI client to consume Gemini responses transparently.

Core Source Files and Their Responsibilities

File Purpose Source Link
open-sse/translator/index.ts Central translateRequest and translateResponse entry points, orchestrates normalization pipeline view
open-sse/translator/registry.ts Translator registration and lookup for format conversion pairs view
open-sse/translator/request/openai-to-gemini.ts OpenAI to Gemini request transformation logic view
open-sse/translator/request/openai-to-claude.ts OpenAI to Anthropic Claude request conversion view
open-sse/translator/response/gemini-to-openai.ts Gemini response back to OpenAI format conversion view
open-sse/translator/helpers/geminiHelper.ts Utility functions for Gemini-specific payload construction view
open-sse/translator/helpers/claudeHelper.ts Claude-specific formatting helpers for system instructions and tools view
open-sse/translator/helpers/toolCallHelper.ts Tool call ID normalization and structure validation view
open-sse/services/roleNormalizer.ts Role name mapping between provider conventions view
open-sse/services/thinkingBudget.ts Token budget enforcement for reasoning models view

Summary

  • OmniRoute's translation layer provides bidirectional conversion between OpenAI-compatible schemas and native Anthropic, Gemini, and other provider formats.
  • The registry-based architecture in translator/registry.ts allows dynamic dispatch to provider-specific converters without coupling the core router to implementation details.
  • Normalization pipelines handle role mapping, tool call structure alignment, and reasoning budget enforcement before conversion occurs.
  • Provider-specific translators in translator/request/ and translator/response/ handle the semantic differences between API formats, such as Gemini's system_instruction versus Claude's top-level system parameter.
  • The layer supports streaming (SSE) conversion through dedicated translators, ensuring real-time compatibility across all supported providers.

Frequently Asked Questions

How does OmniRoute handle role mapping between different API formats?

The translation layer uses the normalizeRoles function in [services/roleNormalizer.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) to map non-standard roles like developer to provider-compatible equivalents such as system. For Claude specifically, it ensures that the messages array contains only alternating user and assistant roles by extracting system messages into Claude's dedicated system parameter.

What happens to tool calls when converting from OpenAI to Claude format?

The [translator/request/openai-to-claude.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) module reformats OpenAI's tool_calls and tool_call_id fields into Claude's native tool_use and tool_result blocks. The [helpers/toolCallHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) module ensures that tool call IDs are normalized and that orphaned tool results are stripped to prevent validation errors in the Claude API.

Does the translation layer support streaming responses?

Yes, OmniRoute provides dedicated SSE (Server-Sent Events) translators for streaming conversion. For example, [translator/response/openai-to-gemini-sse.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-gemini-sse.ts) handles the conversion of Gemini's streaming chunks into OpenAI-compatible SSE format, allowing clients to consume real-time responses from any supported provider using a unified interface.

How does OmniRoute enforce token budgets across different providers?

The [services/thinkingBudget.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) module applies reasoning budget constraints before the translation phase begins. This ensures that expensive reasoning operations respect user-defined limits regardless of whether the target provider natively supports reasoning token controls, preventing cost overruns on models like DeepSeek or Claude 3.5 Sonnet with extended thinking.

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 →