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

> Learn how OmniRoute's translation layer converts OpenAI, Anthropic, and Gemini API formats. Discover its registry-based dispatch and canonical schema approach for seamless integration.

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

---

**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/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts#L49-L52) and `translateResponse` 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). 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/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/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/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/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/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/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/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) runs the inverse conversion. For Gemini responses, [[`translator/response/gemini-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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

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

```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,
);

```

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

### Translating Gemini Streaming Responses

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