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

> Discover how OmniRoute's translation layer seamlessly converts data between OpenAI, Anthropic, and Gemini APIs. Learn about its registry-based architecture and normalization functions.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: internals
- Published: 2026-07-14

---

**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)](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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts#L49-L52) and [`translateResponse`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts#L49-L52) exported from [[`open-sse/translator/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

- **Thinking Budget Enforcement**: The `applyThinkingBudget` function from [[`services/thinkingBudget.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/services/thinkingBudget.ts)](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) enforces token limits on reasoning models like DeepSeek V4 and Xiaomi MiMo.
- **Role Normalization**: [[`services/roleNormalizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/services/roleNormalizer.ts)](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) maps non-standard roles (e.g., `developer`) to provider-compatible equivalents (e.g., `system`).
- **Tool Call Sanitization**: [[`helpers/toolCallHelper.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/helpers/toolCallHelper.ts)](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) normalizes tool call IDs, inserts missing tool-result placeholders, and strips orphaned results.

## 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)](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/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)](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/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) 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)](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:**

```typescript
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:**

```typescript
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:**

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) to route between format converters.
- **Normalization** occurs before translation via [`thinkingBudget.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/thinkingBudget.ts) and [`roleNormalizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/roleNormalizer.ts) to ensure provider compatibility.
- **Bidirectional translators** exist for each provider pair (e.g., [`openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/openai-to-gemini.ts), [`openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.