# How OmniRoute Translates Requests Between OpenAI, Claude, and Gemini Model Formats

> Discover how OmniRoute translates requests between OpenAI, Claude, and Gemini formats. Learn about its universal routing and registry-based translator system for seamless LLM integration.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: deep-dive
- Published: 2026-08-28

---

**OmniRoute acts as a universal routing layer that accepts requests written for one LLM provider and forwards them to another by converting the payload into the target provider's wire format using a registry-based translator system.**

The **OmniRoute** repository (`diegosouzapw/OmniRoute`) implements a sophisticated format translation layer that enables seamless interoperability between diverse LLM APIs. By treating provider-specific request shapes as interchangeable formats, OmniRoute allows developers to send an OpenAI-style request to a Gemini backend—or vice versa—without manual payload restructuring. This translation occurs through pure functions registered in a central map, ensuring type-safe conversions for both requests and streaming responses.

## The Two-Stage Translation Architecture

OmniRoute's translation system operates through two distinct but coordinated mechanisms: a lightweight registry for lookup and pure functions for payload transformation.

### Translator Registry

At the heart of the system lies the **Translator Registry**, defined in [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts). This module maintains two global maps—`requestRegistry` and `responseRegistry`—that bind a *source* format identifier to a *target* format identifier. The registry exposes three critical functions:

- `register(source, target, requestTranslator, responseTranslator)` – Associates conversion functions with format pairs at module load time.
- `getRequestTranslator(source, target)` – Retrieves the appropriate request conversion function.
- `getResponseTranslator(source, target)` – Retrieves the appropriate response conversion function.

This design decouples the routing logic from translation logic, allowing new providers to be added without modifying core request handlers.

### Request and Response Translators

**Request Translators** are pure functions that receive the original model name, JSON body, a streaming flag, and optional credential data, then emit a new object matching the target provider's API contract. For example, `openaiToGeminiRequest` in [`open-sse/translator/request/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) handles the **OpenAI → Gemini** conversion, while `claudeToOpenaiRequest` in [`open-sse/translator/request/claude-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/claude-to-openai.ts) handles the **Claude → OpenAI** path.

**Response Translators** perform the inverse operation for streaming replies. The file [`open-sse/translator/response/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-gemini.ts) registers `openaiToAntigravityResponse()`, which reshapes OpenAI-style streaming chunks into Gemini's `candidates` envelope format.

## How Request Translation Works

The translation pipeline follows a deterministic six-step process orchestrated by the chat handler in [`open-sse/handlers/chatCore.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/handlers/chatCore.ts):

1. **Detect Source Format** – The handler inspects incoming JSON. If the payload contains a `contents` array, OmniRoute identifies it as Gemini format; otherwise, it falls back to OpenAI or Claude based on the route URL.

2. **Lookup Translator** – The system calls `getRequestTranslator(FORMATS.OPENAI, FORMATS.GEMINI)` to fetch the conversion function from the registry.

3. **Convert Payload** – The translator executes provider-specific logic. In [`openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/openai-to-gemini.ts), the `openaiToGeminiBase()` function:
   - Copies generation-config fields (`temperature`, `top_p`, `stop`, etc.) into Gemini's `generationConfig`.
   - Transfers user-provided `cachedContent` if present.
   - Invokes `sanitizeGeminiToolName()` to rewrite tool names for Vertex AI compatibility.
   - Handles tool-calling configuration via `convertOpenAIToolChoiceToGemini()`.
   - Optionally wraps the request in a Cloud-Code envelope for Antigravity using `wrapInCloudCodeEnvelope`.

4. **Register Conversion** – During module initialization, [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts) invokes `register(FORMATS.OPENAI, FORMATS.GEMINI, openaiToGeminiRequest, null)`, making the conversion available for all subsequent requests.

5. **Stream Response Translation** – For streaming requests, the response translator reshapes chunks in real-time. The Gemini-compatible upstream streams through `openaiToAntigravityResponse()`, ensuring the downstream client receives its expected format.

6. **Edge-Case Handling** – The system manages provider-specific limitations:
   - **Tool name sanitization** removes built-in Gemini tool names that Antigravity rejects (defined in `GEMINI_BUILTIN_TOOL_NAMES`).
   - **Thought-signature support** adds signature fields for Vertex AI when `supportsSignatureBypass` is enabled.
   - **Safety settings** default to `DEFAULT_SAFETY_SETTINGS` unless the client overrides them.

## Code Examples: Converting Between Formats

Below are practical implementations showing how to trigger translations programmatically.

### Automatic Translation in API Routes

When handling chat completions, the router automatically selects the appropriate translator based on source and target formats:

```typescript
import { getRequestTranslator } from '@omniroute/open-sse/translator';
import { FORMATS } from '@omniroute/open-sse/translator/formats';

async function handleChat(req) {
  const source = FORMATS.OPENAI;       // Request arrived as OpenAI payload
  const target = FORMATS.GEMINI;       // Router decided to use Gemini
  const translate = getRequestTranslator(source, target);
  if (!translate) throw new Error('No translator for this pair');

  const { model, body, stream, credentials } = req;
  const geminiBody = translate(model, body, stream, credentials);
  return await callGeminiProvider(geminiBody);
}

```

### Direct Translator Invocation

For testing or utility scripts, you can import translators directly and bypass the registry:

```typescript
import { openaiToGeminiRequest } from '@omniroute/open-sse/translator/request/openai-to-gemini';

const openaiBody = {
  model: 'gpt-4o',
  temperature: 0.7,
  top_p: 0.9,
  messages: [{ role: 'user', content: 'Explain quantum entanglement.' }],
};

const geminiPayload = openaiToGeminiRequest(
  'gemini-1.5-pro',               // target Gemini model
  openaiBody,
  false,                          // not a streaming request
  { _provider: 'vertex', _preserveCacheControl: true }
);

console.log(JSON.stringify(geminiPayload, null, 2));

```

## Key Design Features

**Decoupled Registration** – Centralizing the mapping in [`registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/registry.ts) means adding a new provider requires only a single `register()` call in [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts), without touching the core request handling logic in [`chatCore.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/chatCore.ts).

**Pure Functions** – Translators receive only primitive data and return plain objects, containing no side effects. This architecture makes unit testing straightforward, as demonstrated by the test suite in [`tests/unit/translator-openai-to-gemini.test.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tests/unit/translator-openai-to-gemini.test.ts).

**Feature-Flag Awareness** – Helper functions like `capThinkingBudget` and `capMaxOutputTokens` respect model-level capabilities, ensuring translated requests never exceed provider-specific limits.

**Streaming Parity** – The response path mirrors the request path, guaranteeing that both request and response are consistently transformed for any combination of source and target formats.

## Summary

- OmniRoute translates requests between different model formats using a **registry-based architecture** defined in [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts).
- **Request translators** are pure functions that convert payloads between OpenAI, Claude, and Gemini formats, handling tool names, safety settings, and generation configs.
- **Response translators** ensure streaming chunks are reshaped to match the client's expected wire format.
- The system uses **feature flags** and provider-specific helpers to handle edge cases like Vertex AI requirements and Antigravity compatibility.
- Registration occurs at module load time via [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts), making new format pairs available globally without code changes to handlers.

## Frequently Asked Questions

### How does OmniRoute determine which translator to use for a request?

OmniRoute detects the source format by inspecting the incoming request structure in [`open-sse/handlers/chatCore.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/handlers/chatCore.ts). For example, if the JSON contains a `contents` array, it identifies the request as Gemini format. It then uses `getRequestTranslator(source, target)` from [`registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/registry.ts) to retrieve the appropriate conversion function based on the detected source and the intended target provider.

### Can I add support for a new LLM provider without modifying core files?

Yes. The decoupled registry design allows you to add support by creating a new translator file, implementing the pure function signature `(model, body, stream, credentials) => transformedBody`, and calling `register(FORMATS.NEW_PROVIDER, FORMATS.TARGET, requestFn, responseFn)` in [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts). The core routing logic in [`chatCore.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/chatCore.ts) does not require modification.

### How does OmniRoute handle tool calling when translating between formats?

The system sanitizes tool names and converts tool-choice configurations during translation. For **OpenAI → Gemini** conversions, `sanitizeGeminiToolName()` rewrites tool names to comply with Vertex AI limitations, while `convertOpenAIToolChoiceToGemini()` maps OpenAI's tool choice objects to Gemini's function-calling interface. These helpers are located in [`open-sse/translator/request/openai-to-gemini/helpers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini/helpers.ts).

### What happens to streaming responses during format translation?

Streaming responses undergo inverse transformation through registered response translators. When Gemini streams back a response intended for an OpenAI client, `openaiToAntigravityResponse()` (registered in [`open-sse/translator/response/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-gemini.ts)) reshapes the Gemini `candidates` envelope into OpenAI-style streaming chunks, ensuring the client receives data in the format it expects throughout the streaming session.