# How OmniRoute Translates Requests Between Different AI Provider Formats

> OmniRoute translates AI provider request formats using a centralized registry. Connect OpenAI-compatible clients to Gemini, Claude, and more without changes.

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

---

**OmniRoute uses a centralized translator registry in `open-sse/translator/` to map request and response payloads between any pair of AI provider formats, enabling OpenAI-compatible clients to call Gemini, Claude, and dozens of other LLM APIs without format changes.**

OmniRoute acts as a universal front-end for LLM providers. The core translation capability lives in a dedicated layer that rewrites payloads on-the-fly, handling everything from generation parameters to tool calling conventions. This article examines how the system converts requests between formats like OpenAI, Gemini, and Claude based on the source code in the `diegosouzapw/OmniRoute` repository.

## Translator Registry Architecture

The translation system centers on a **registry** that maps `source:format → target:format` pairs to converter functions.

In [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts), two `Map` structures store the translators:

```ts
type RequestTranslator = (
  model: string,
  body: Record<string, unknown>,
  stream?: boolean,
  credentials?: Record<string, unknown> | null
) => unknown;

type ResponseTranslator = (
  chunk: Record<string, unknown>,
  state: Record<string, unknown>
) => unknown;

const requestRegistry = new Map<string, RequestTranslator>();
const responseRegistry = new Map<string, ResponseTranslator>();

```

The `register()` function populates these maps:

```ts
export function register(
  from: string,
  to: string,
  requestFn?: RequestTranslator,
  responseFn?: ResponseTranslator
) {
  const key = `${from}:${to}`;
  if (requestFn) requestRegistry.set(key, requestFn);
  if (responseFn) responseRegistry.set(key, responseFn);
}

```

Each provider pair implementation calls `register()` during initialization. The registry then serves `getRequestTranslator()` and `getResponseTranslator()` lookups at runtime.

## Request Translation: OpenAI to Gemini

The most complex translator in OmniRoute converts **OpenAI-formatted requests to Gemini's API schema**. Located in [`open-sse/translator/request/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts), this module handles numerous structural differences between the two formats.

### Generation Configuration Mapping

Lines 88-102 extract OpenAI parameters and map them to Gemini's `generationConfig`:

| OpenAI Field | Gemini Target |
|-------------|---------------|
| `temperature` | `temperature` |
| `top_p` | `topP` |
| `max_tokens` | `maxOutputTokens` |
| `stop` | `stopSequences` |

### Thinking and Reasoning Budget

Gemini supports a `thinkingConfig` budget for reasoning models. The translator derives this from either:

- **OpenAI's `reasoning_effort`** parameter (lines 124-140)
- **Claude-style `thinking`** objects with `type` and `budget_tokens` (lines 141-172)

This bridges capabilities across provider ecosystems.

### Tool Call Transformation

Tool handling spans lines 183-334 and addresses several incompatibilities:

- **Function signature conversion**: OpenAI's JSON Schema tools become Gemini `functionCall` parts
- **Name sanitization**: Removes characters illegal in Gemini tool names
- **Built-in tool stripping**: Optionally removes Gemini-native tool names that conflict with client-defined tools
- **Bidirectional mapping**: Maintains `Map<string,string>` to restore original names in responses (lines 16-23, 112-119)

### System Messages and Safety

Multiple system messages get merged into Gemini's single `systemInstruction` (lines 47-58). Safety settings propagate from the OpenAI payload or fall back to `DEFAULT_SAFETY_SETTINGS`.

### Response Format Conversion

Lines 90-106 translate OpenAI's `response_format` (JSON mode, JSON Schema) into Gemini's `responseMimeType` and `responseSchema` fields.

Finally, `deepCleanUndefined` (line 117) strips undefined values before the payload reaches Gemini's API.

## Response Translation

Response translation typically reuses shared implementations. For OpenAI ↔ Gemini, [`open-sse/translator/response/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-gemini.ts) registers the Antigravity response converter:

```ts
register(
  FORMATS.OPENAI,
  FORMATS.GEMINI,
  null, // no request translator in this file
  openaiToAntigravityResponse // shared response logic
);

```

The `openaiToAntigravityResponse` function in [`open-sse/translator/response/openai-to-antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-antigravity.ts) handles:

- Extracting candidates from Gemini's `candidates` array
- Converting `functionResponse` parts back to OpenAI-style `tool_calls`
- Restoring original tool names using the bidirectional map
- Streaming SSE chunks in the client's expected format

## End-to-End Translation Flow

Understanding how OmniRoute translates requests between AI provider formats requires seeing the full request lifecycle:

1. **Request ingestion**: The API route (e.g., `/v1/chat/completions`) validates the body with Zod schemas
2. **Format detection**: The handler identifies the source format as `FORMATS.OPENAI`
3. **Target resolution**: Routing logic selects the downstream provider (e.g., Gemini)
4. **Request translation**: `translateRequest()` → `getRequestTranslator(FORMATS.OPENAI, FORMATS.GEMINI)` → `openaiToGeminiRequest()`
5. **Execution**: The translated payload passes to the provider-specific executor for the HTTP call
6. **Response streaming**: As chunks arrive, `translateResponse()` → `openaiToAntigravityResponse` rewrites each chunk back to OpenAI format before SSE delivery

This pipeline remains identical regardless of which provider pair is active—the registry abstraction makes the specific translator pluggable.

## Extending the Translator System

Adding support for a new AI provider requires only three steps:

1. **Implement request translator**: Map source format fields to the target provider's schema, handling tools, generation config, and provider-specific features
2. **Implement response translator** (optional): Convert the provider's response chunks back to the client's expected format
3. **Register the pair**: Call `register(from, to, requestFn, responseFn)` in the appropriate module

Because the registry uses simple `Map` lookups, no changes to routing, SSE handling, or streaming logic are necessary.

### Direct API Usage

OmniRoute's translators can be invoked directly for custom integrations:

```ts
import { getRequestTranslator, getResponseTranslator } from "@/open-sse/translator/registry";
import { FORMATS } from "@/open-sse/translator/formats";

// Translate OpenAI request to Gemini format
const requestBody = {
  model: "gpt-4",
  messages: [{ role: "user", content: "Hello" }],
  temperature: 0.7
};
const model = "gemini-1.5-flash";

const requestTranslator = getRequestTranslator(FORMATS.OPENAI, FORMATS.GEMINI);
const geminiPayload = requestTranslator?.(model, requestBody, false, null);

// Later: translate Gemini response chunk back to OpenAI format
const geminiChunk = { /* Gemini streaming response */ };
const responseTranslator = getResponseTranslator(FORMATS.GEMINI, FORMATS.OPENAI);
const openaiChunk = responseTranslator?.(geminiChunk, {});

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts) | Central translator registry with `register()`, `getRequestTranslator()`, `getResponseTranslator()` |
| [`open-sse/translator/formats.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/formats.ts) | Format enum definitions (`OPENAI`, `GEMINI`, `ANTIGRAVITY`, `CLAUDE`, etc.) |
| [`open-sse/translator/request/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-gemini.ts) | Full OpenAI → Gemini implementation with thinking budgets, tool mapping, safety settings |
| [`open-sse/translator/request/openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/openai-to-claude.ts) | OpenAI → Claude conversion with Anthropic-specific tool handling |
| [`open-sse/translator/request/claude-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/claude-to-openai.ts) | Reverse direction for Claude-native clients |
| [`open-sse/translator/request/gemini-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/request/gemini-to-openai.ts) | Gemini-native client to OpenAI backend |
| [`open-sse/translator/response/openai-to-antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/response/openai-to-antigravity.ts) | Shared response converter used by multiple provider pairs |

## Summary

- **OmniRoute's translator layer** decouples the public OpenAI-compatible API from downstream provider schemas using a lightweight registry pattern in [`open-sse/translator/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/registry.ts)
- **Pair-wise registration** maps `source:format → target:format` to optional request and response translator functions
- **OpenAI to Gemini translation** demonstrates complex field mapping: generation config, thinking budgets, tool calls with name sanitization, system message merging, and safety settings
- **Response reuse** allows multiple provider pairs to share converters like `openaiToAntigravityResponse`
- **Zero core changes** are needed to add new providers—just implement translators and register them

## Frequently Asked Questions

### How does OmniRoute handle streaming responses during translation?

The response translator receives each chunk as it arrives from the downstream provider, transforms it through the registered converter, and immediately streams the rewritten chunk to the client. For Gemini responses, `openaiToAntigravityResponse` processes the `candidates` array incrementally, ensuring low-latency SSE delivery without buffering the complete response.

### What happens if a provider format lacks equivalent fields?

OmniRoute's translators apply sensible defaults or omissions. For example, when OpenAI's `reasoning_effort` maps to Gemini's `thinkingConfig`, the translator falls back to a medium budget if no explicit value is provided. Unknown fields are stripped via `deepCleanUndefined` to prevent API errors, and provider-specific extensions are handled case-by-case in each translator implementation.

### Can I use OmniRoute's translators outside the main routing pipeline?

Yes. The registry exports `getRequestTranslator()` and `getResponseTranslator()` as public APIs. Import these from `@/open-sse/translator/registry` along with the `FORMATS` enum to translate payloads in custom scripts, testing utilities, or alternative server implementations without invoking the full OmniRoute request handler.

### Why does the OpenAI ↔ Gemini response translator reuse Antigravity logic?

Both Gemini and Antigravity share similar response envelope structures—nested `candidates` with `content` parts—so `openaiToAntigravityResponse` correctly handles both without duplication. This design pattern reduces maintenance burden: improvements to the shared converter automatically benefit both provider pairs, while provider-specific response handling remains in dedicated files when structural differences demand it.