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

OmniRoute's translation layer converts between OpenAI, Anthropic, and Gemini API formats using a registry-based architecture that normalizes requests through centralized translateRequest and translateResponse functions, applying role normalization, thinking budgets, and provider-specific transformation logic.

OmniRoute acts as a unified routing gateway for Large Language Model (LLM) APIs, abstracting away provider-specific payload structures through a robust translation system. The translation layer, located in the open-sse/translator package, ensures that requests conform to each provider's unique schema—whether OpenAI-compatible, Anthropic Claude, or Google Gemini—while maintaining a consistent OpenAI-style interface for clients.

Core Translation Architecture

Registry-Based Dispatch

The translation system relies on a registry pattern defined in [open-sse/translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) that maps source-to-target format pairs. Each registry entry contains a request translator (converting source format to target) and a response translator (converting target response back to OpenAI format).

The generic entry points translateRequest and translateResponse exported from [open-sse/translator/index.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) dispatch to these registered handlers based on the source and target format identifiers.

Request Normalization Pipeline

Before provider-specific conversion, OmniRoute normalizes payloads through several preprocessing steps implemented in the core translator index:

Provider-Specific Conversion Logic

OpenAI to Gemini Translation

The [open-sse/translator/request/openai-to-gemini.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) module handles conversion from OpenAI's chat completion format to Google's Gemini API structure. This translator rewrites the messages array into Gemini's content format, embedding system instructions into the system_instruction field and handling multimodal content through helper functions in [helpers/geminiHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/geminiHelper.ts).

The translator also respects provider-specific cache-control headers via the providerHonorsOpenAIFormatCacheControl flag, ensuring efficient token caching when supported.

OpenAI to Anthropic (Claude) Translation

For Anthropic's Claude API, [open-sse/translator/request/openai-to-claude.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) performs the conversion. Claude expects a top-level system parameter (separate from the messages array) and specific tool schema formatting.

The translator merges multiple system messages and restructures tool calls using utilities from [helpers/claudeHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/claudeHelper.ts), ensuring the assistant role and tool_choice fields conform to Claude's requirements.

Response Conversion and Streaming

After the upstream provider returns data, translateResponse executes the inverse conversion (target format → OpenAI). For Gemini responses, [open-sse/translator/response/gemini-to-openai.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/gemini-to-openai.ts) extracts content blocks and reformats them into OpenAI-style message objects.

The layer also supports Server-Sent Events (SSE) streaming through dedicated SSE translators that process chunked responses while maintaining OpenAI-compatible event formatting, allowing real-time consumption regardless of the upstream provider.

Code Examples

Converting an OpenAI chat request 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 the difference between AI and ML.' },
  ],
  temperature: 0.7,
};

const geminiPayload = translateRequest(
  'openai',          // source format
  'gemini',          // target format
  'gemini-pro',      // model name
  openAIPayload,
  false,             // streaming disabled
);

Handling Claude-style tool calls:

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

const request = {
  model: 'claude-3-5-sonnet-20240620',
  messages: [
    { role: 'user', content: 'List the current weather in Paris.' },
  ],
  tool_calls: [{ 
    id: 'tc_1', 
    type: 'function', 
    function: { name: 'weather', arguments: '{}' } 
  }],
};

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

Converting a Gemini streaming response back to OpenAI SSE:

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

// Assume geminiStream is a ReadableStream from Gemini's HTTP/2 SSE endpoint
const openAiStream = translateResponse(
  'gemini',
  'openai',
  geminiStream,
);

Summary

  • OmniRoute's translation layer uses a registry-based dispatch system in open-sse/translator/registry.ts to route between format converters.
  • Normalization occurs before translation via thinkingBudget.ts and roleNormalizer.ts to ensure provider compatibility.
  • Bidirectional translators exist for each provider pair (e.g., openai-to-gemini.ts, openai-to-claude.ts) handling both requests and responses.
  • The system preserves OpenAI-compatible client interfaces while managing provider-specific idiosyncrasies like Claude's separate system parameter and Gemini's content structure.
  • Streaming support is implemented through SSE-specific translators that maintain real-time OpenAI-format event streams.

Frequently Asked Questions

How does OmniRoute handle tool calls across different providers?

OmniRoute normalizes tool calls using toolCallHelper.ts to standardize IDs and structure before conversion. For Claude, it restructures OpenAI's tool_calls into Anthropic's tool schema, while for Gemini, it maps them to function calling formats supported by the Gemini API, ensuring consistent behavior regardless of the upstream provider.

What is the role normalization process in OmniRoute?

The normalizeRoles function in services/roleNormalizer.ts maps non-standard roles like developer to provider-compatible equivalents such as system. This ensures that conversations containing experimental or extended role types don't fail when sent to providers with strict role enums like Anthropic or Gemini.

How does OmniRoute manage streaming responses?

OmniRoute processes streaming responses through translateResponse with SSE-specific translators that convert provider-specific chunked formats (like Gemini's stream events) into OpenAI-compatible SSE chunks. This allows clients to consume streaming data using a single parsing implementation regardless of the upstream provider.

Where is the translation logic centralized?

The central entry points are translateRequest and translateResponse in open-sse/translator/index.ts (lines 49-52), which coordinate the normalization pipeline and dispatch to provider-specific modules. This centralization allows the rest of the OmniRoute application to remain agnostic to LLM provider API differences.

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 →