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
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:
translateRequestapplies thinking budgets, role normalization, and tool-call validation before provider-specific conversion. - Provider-specific modules: Dedicated translators in
translator/request/andtranslator/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →