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

> Learn how OmniRoute's translation layer converts OpenAI, Anthropic, and Gemini API formats. Normalize roles and more with open-sse translator functions for seamless integration.

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

---

**OmniRoute's translation layer converts between OpenAI, Anthropic, and Gemini formats using registry-based translators that normalize roles, tool calls, and reasoning budgets through the `translateRequest` and `translateResponse` functions exported from the `open-sse/translator` package.**

The OmniRoute translation layer serves as the interoperability core of the open-source routing proxy, enabling seamless communication between heterogeneous LLM providers. By implementing a bidirectional normalization pipeline in the `diegosouzapw/OmniRoute` repository, this layer ensures that clients can interact with Anthropic, Google Gemini, and OpenAI models using a single OpenAI-compatible schema while preserving provider-specific features like tool calling and reasoning budgets.

## Architecture of the Translation Layer

The translation layer operates on a **canonical OpenAI schema** principle. All incoming requests are treated as OpenAI-compatible, then translated to the target provider's native format, with responses converted back to OpenAI format before returning to the client.

### Central Dispatch Mechanism

The entry points [`translateRequest`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) and [`translateResponse`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) in [`open-sse/translator/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) serve as the universal gatekeepers. These functions accept a source format identifier, target format identifier, and payload, then delegate to the appropriate provider-specific implementation registered in the system.

### Registry-Based Translator Mapping

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 conversion pairs. Each supported route (e.g., `openai` → `gemini`, `anthropic` → `openai`) registers a **request translator** (source → target) and a **response translator** (target → source). This registry pattern enables OmniRoute to dynamically load conversion logic without hardcoding provider-specific logic into the core routing engine.

## Request Normalization Pipeline

Before provider-specific conversion occurs, OmniRoute applies a normalization pipeline to ensure consistency across disparate API structures.

### Role Normalization and System Instructions

The translation layer handles role name disparities through [[`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). For example, the `developer` role used in some OpenAI contexts is mapped to `system` when targeting providers that lack native developer role support. Similarly, system messages are extracted and reformatted—converted to Gemini's `system_instruction` field or Claude's top-level `system` parameter—depending on the target.

### Tool Call and ID Normalization

Tool calling semantics vary significantly 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, inserts missing tool-call structures, and strips orphaned results to prevent validation errors. This ensures that a tool call generated in OpenAI format (with `tool_calls` and `tool_call_id`) is correctly translated to Claude's `tool` block format or Gemini's function calling schema.

### Thinking Budget Enforcement

For models supporting reasoning tokens (such as DeepSeek or specific Claude configurations), 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 applies token limits before translation. This prevents expensive reasoning operations from exceeding user-defined budgets regardless of the target provider's native support for reasoning controls.

## Provider-Specific Conversion Strategies

Each LLM provider requires distinct structural transformations to map the OpenAI chat completions schema to native formats.

### OpenAI to Gemini Request Translation

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's `messages` array into Gemini's `content` format. Key transformations include:

- Mapping `system` role messages to Gemini's `system_instruction` field
- Converting text and image content into Gemini's `parts` array structure
- Translating OpenAI's `tool_calls` into Gemini's function declaration format using [[`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)

### OpenAI to Anthropic (Claude) Request Translation

Conversion to Claude format occurs in [[`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). This translator:

- Extracts system messages and places them in Claude's top-level `system` parameter
- Ensures the `messages` array contains only alternating `user` and `assistant` roles
- Reformats `tool_calls` into Claude's `tool_use` and `tool_result` blocks using [[`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)
- Handles the `tool_choice` parameter conversion to Claude's `tool_choice` object

### Gemini Response to OpenAI Format

For 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) performs the inverse operation, extracting Gemini's `content` blocks and reconstructing OpenAI-compatible `message` objects with `role: assistant` and `content` fields. The translator also handles streaming responses through dedicated SSE converters like [`openai-to-gemini-sse.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/openai-to-gemini-sse.ts).

## Practical Implementation Examples

The following examples demonstrate how to use the translation layer programmatically.

### Converting OpenAI Requests 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 quantum computing.' }
  ],
  temperature: 0.7
};

const geminiPayload = translateRequest(
  'openai',
  'gemini',
  'gemini-pro',
  openAIPayload,
  false
);

```

This produces a Gemini-compatible request where the system message becomes `system_instruction` and user content transforms into the `contents` array format.

### Translating OpenAI Tool Calls to Claude Format

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

const requestWithTools = {
  model: 'claude-3-5-sonnet-20240620',
  messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
  tools: [{
    type: 'function',
    function: { name: 'get_weather', parameters: { location: 'string' } }
  }],
  tool_choice: 'auto'
};

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

```

The translator extracts the system context, reformats tools into Claude's `tools` array, and ensures proper `tool_choice` serialization.

### Converting Gemini Streaming Responses to OpenAI SSE

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

const geminiStream = getGeminiStream(); // ReadableStream from Gemini API

const openAIStream = translateResponse(
  'gemini',
  'openai',
  geminiStream
);

```

The resulting stream emits OpenAI-compatible SSE chunks with `data: {"choices":[{"delta":{"content":"..."}}]}` formatting, allowing any OpenAI client to consume Gemini responses transparently.

## Core Source Files and Their Responsibilities

| File | Purpose | Source Link |
|------|---------|-------------|
| [`open-sse/translator/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) | Central `translateRequest` and `translateResponse` entry points, orchestrates normalization pipeline | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/index.ts) |
| [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) | Translator registration and lookup for format conversion pairs | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) |
| [`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 transformation logic | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) |
| [`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 Claude request conversion | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) |
| [`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 back to OpenAI format conversion | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/gemini-to-openai.ts) |
| [`open-sse/translator/helpers/geminiHelper.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/geminiHelper.ts) | Utility functions for Gemini-specific payload construction | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/geminiHelper.ts) |
| [`open-sse/translator/helpers/claudeHelper.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/claudeHelper.ts) | Claude-specific formatting helpers for system instructions and tools | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/claudeHelper.ts) |
| [`open-sse/translator/helpers/toolCallHelper.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) | Tool call ID normalization and structure validation | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/helpers/toolCallHelper.ts) |
| [`open-sse/services/roleNormalizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) | Role name mapping between provider conventions | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/roleNormalizer.ts) |
| [`open-sse/services/thinkingBudget.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) | Token budget enforcement for reasoning models | [view](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/thinkingBudget.ts) |

## Summary

- OmniRoute's translation layer provides **bidirectional conversion** between OpenAI-compatible schemas and native Anthropic, Gemini, and other provider formats.
- The **registry-based architecture** in [`translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/translator/registry.ts) allows dynamic dispatch to provider-specific converters without coupling the core router to implementation details.
- **Normalization pipelines** handle role mapping, tool call structure alignment, and reasoning budget enforcement before conversion occurs.
- **Provider-specific translators** in `translator/request/` and `translator/response/` handle the semantic differences between API formats, such as Gemini's `system_instruction` versus Claude's top-level `system` parameter.
- The layer supports **streaming (SSE)** conversion through dedicated translators, ensuring real-time compatibility across all supported providers.

## Frequently Asked Questions

### How does OmniRoute handle role mapping between different API formats?

The translation layer uses the `normalizeRoles` function in [[`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) to map non-standard roles like `developer` to provider-compatible equivalents such as `system`. For Claude specifically, it ensures that the `messages` array contains only alternating `user` and `assistant` roles by extracting system messages into Claude's dedicated `system` parameter.

### What happens to tool calls when converting from OpenAI to Claude format?

The [[`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) module reformats OpenAI's `tool_calls` and `tool_call_id` fields into Claude's native `tool_use` and `tool_result` blocks. 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 ensures that tool call IDs are normalized and that orphaned tool results are stripped to prevent validation errors in the Claude API.

### Does the translation layer support streaming responses?

Yes, OmniRoute provides dedicated SSE (Server-Sent Events) translators for streaming conversion. For example, [[`translator/response/openai-to-gemini-sse.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/translator/response/openai-to-gemini-sse.ts)](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-gemini-sse.ts) handles the conversion of Gemini's streaming chunks into OpenAI-compatible SSE format, allowing clients to consume real-time responses from any supported provider using a unified interface.

### How does OmniRoute enforce token budgets across different providers?

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 applies reasoning budget constraints before the translation phase begins. This ensures that expensive reasoning operations respect user-defined limits regardless of whether the target provider natively supports reasoning token controls, preventing cost overruns on models like DeepSeek or Claude 3.5 Sonnet with extended thinking.