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

The OmniRoute translation layer normalizes requests and responses through a registry-based dispatch system, converting provider-specific payloads to and from a canonical OpenAI-compatible schema using specialized translators for each format pair.

The OmniRoute project provides a unified routing layer for LLM providers. Its translation layer, located in the open-sse/translator package, handles the complex task of converting between divergent API specifications while maintaining a consistent internal representation.

Core Translation Architecture

Registry-Based Dispatch

The translation system uses a registry pattern to map conversion pairs. The [translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) file registers distinct translator functions for each supported source-to-target combination. This design allows OmniRoute to support bidirectional translation between any two formats without duplicating logic.

Entry Points and Normalization

The primary entry points are translateRequest and translateResponse exported from [open-sse/translator/index.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts). Before provider-specific conversion occurs, the layer applies a normalization pipeline that ensures downstream translators receive a predictable payload structure.

Request Normalization Pipeline

Thinking Budget Enforcement

The translation layer enforces token limits through applyThinkingBudget, implemented in [services/thinkingBudget.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts). This function inspects the request payload and truncates or filters content to respect provider-specific context window constraints before translation begins.

Role and Tool Call Normalization

The system normalizes non-standard role names (e.g., mapping developer to system) via [services/roleNormalizer.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts). Additionally, [helpers/toolCallHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) ensures tool-call IDs are consistent and inserts missing tool-result placeholders, preventing orphaned function calls during format conversion.

Provider-Specific Translation Implementations

OpenAI to Gemini Conversion

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 chat completions to Gemini’s content-based format. It rewrites the messages array into Gemini’s content blocks, maps image data to base64 parts, and extracts system messages into the system_instruction field. Helper utilities in [helpers/geminiHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/geminiHelper.ts) manage schema-specific details like safety settings and generation configuration.

OpenAI to Claude (Anthropic) Conversion

For Anthropic models, [translator/request/openai-to-claude.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) transforms OpenAI payloads into Claude’s expected structure. This includes merging multiple system role messages into a single top-level system parameter, rewriting tool_calls into Claude’s tool schema, and ensuring the assistant role is correctly mapped. The [helpers/claudeHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/claudeHelper.ts) file provides additional utilities for handling Claude’s tool-use blocks and thinking mode parameters.

Response Translation

After the upstream provider returns a response, translateResponse runs the inverse conversion. For Gemini responses, [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 reconstructs OpenAI-style choices arrays, preserving tool-call results and finish reasons. Streaming responses use SSE-specific translators to ensure chunked data maintains the OpenAI-compatible format.

Practical Code Examples

Converting OpenAI Requests to Gemini Format

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

// Original OpenAI-style payload
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,
};

// Translate to Gemini format
const geminiPayload = translateRequest(
  'openai',          // source format
  'gemini',          // target format
  'gemini-pro',      // model name for provider-specific tweaks
  openAIPayload,
  false,             // streaming disabled
);

The resulting geminiPayload contains system_instruction and contents fields compatible with the Gemini API.

Handling Anthropic 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,
);

The translator rewrites tool_calls into Claude’s tools array and adds the required tool_choice field.

Translating Gemini Streaming Responses

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

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

The returned stream emits OpenAI-compatible SSE chunks with data: {"choices":[...]} formatting.

Key Implementation Files

File Purpose
[open-sse/translator/index.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) Central entry points for translateRequest and translateResponse
[open-sse/translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) Translator pair registration and dispatch logic
[open-sse/translator/request/openai-to-gemini.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) OpenAI to Gemini request conversion
[open-sse/translator/request/openai-to-claude.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) OpenAI to Anthropic request conversion
[open-sse/translator/response/gemini-to-openai.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/gemini-to-openai.ts) Gemini response to OpenAI format conversion
[open-sse/translator/helpers/claudeHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/claudeHelper.ts) Claude-specific request shaping utilities
[open-sse/translator/helpers/geminiHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/geminiHelper.ts) Gemini-specific conversion utilities
[open-sse/translator/helpers/toolCallHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) Tool-call ID normalization and validation
[open-sse/services/roleNormalizer.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) Role name mapping across providers
[open-sse/services/thinkingBudget.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) Token budget enforcement pre-translation

Summary

  • Registry-driven dispatch: The translation layer uses [translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) to route requests between format pairs without tight coupling.
  • Pre-translation normalization: translateRequest applies thinking budgets, role normalization, and tool-call validation before provider-specific conversion.
  • Provider-specific modules: Dedicated translators in translator/request/ and translator/response/ handle the unique schema requirements of Gemini, Claude, and other providers.
  • Bidirectional compatibility: The layer converts incoming requests to provider formats and outgoing responses back to OpenAI-compatible schemas, ensuring client consistency.

Frequently Asked Questions

How does OmniRoute handle tool call format differences between providers?

The [helpers/toolCallHelper.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) module normalizes tool-call IDs and structures before translation. For Claude, tool_calls are rewritten into the tools array with specific tool_choice parameters, while Gemini uses a different schema for function declarations that the OpenAI-to-Gemini translator handles automatically.

What is the purpose of the registry in the OmniRoute translation layer?

The [translator/registry.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) file maintains a mapping of source-to-target format pairs to their respective translator functions. This allows the system to dynamically select the correct conversion logic at runtime based on the source API format and target provider, supporting extensibility for new providers without modifying core routing code.

How does the translation layer normalize role names across different APIs?

The [services/roleNormalizer.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) component maps non-standard roles (such as developer or model) to provider-compatible equivalents. For example, it ensures that system role messages are formatted correctly for Claude’s separate system parameter versus Gemini’s system_instruction field.

Which component handles token budget constraints before translation?

The [services/thinkingBudget.ts](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) module implements applyThinkingBudget, which inspects request payloads and enforces token limits before the translation layer processes the request. This prevents context window overflows when converting between formats that may have different token counting methodologies.

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 →