# How OmniRoute's Translation Layer Normalizes OpenAI, Claude, and Gemini API Formats

> OmniRoute's translation layer normalizes OpenAI Claude and Gemini API formats simplifying your LLM integrations. Connect with any provider using a single API.

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

---

**OmniRoute's translation layer converts inbound OpenAI-compatible requests into provider-specific formats for Claude and Gemini, then transforms responses back into the OpenAI shape clients expect.**

The OmniRoute proxy (available at `diegosouzapw/OmniRoute`) presents a unified **OpenAI-compatible façade** while internally routing to heterogeneous LLM providers. Because Anthropic's Claude and Google's Gemini expose fundamentally different request/response schemas, the codebase implements a bidirectional translation system that bridges these gaps without requiring client-side changes.

## Where the Translation Layer Lives

All translation logic resides under `open-sse/translator/` and is logically partitioned by direction and provider:

| Partition | Path | Responsibility |
|-----------|------|----------------|
| Request translators | `request/` | Transform OpenAI payloads into Claude/Gemini native formats |
| Response translators | `response/` | Convert provider responses back to OpenAI-compatible JSON |
| Registry | [`registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/registry.ts) | Maps provider names to their translator pairs |
| Bootstrap | [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts) | Initializes translators at application startup |
| Shared helpers | `helpers/` | Utilities for token limits, schema coercion, JSON parsing |

## Request Translation: OpenAI to Native Formats

Request translators handle the **inbound transformation** from the standard OpenAI chat completions schema to each provider's native structure.

### OpenAI to Claude Conversion

The [`request/openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/request/openai-to-claude.ts) module performs the heaviest lifting. Key transformations include:

- **Model selection** – Maps OpenAI model strings to Claude model identifiers
- **Temperature handling** – Strips `temperature` when Claude's *extended thinking* is active (lines 63-66)
- **Tool prefixing** – Prepends `CLAUDE_OAUTH_TOOL_PREFIX` to tool names to prevent namespace collisions
- **Thinking effort mapping** – Converts OpenAI's `reasoning_effort` to Claude's `thinking` payload using `ADAPTIVE_EFFORT_LEVELS` normalization
- **Message restructuring** – Flattens OpenAI's simple message format into Claude's content blocks with explicit type annotations
- **Tool result adjacency enforcement** – Calls `enforceToolResultAdjacency()` to ensure tool use and results appear consecutively

```typescript
// From request/openai-to-claude.ts
const claudeBody = {
  model: "claude-3-opus-2024-06-01",
  max_tokens: adjustMaxTokens(openAiPayload),
  messages: [
    { 
      role: "user", 
      content: [{ type: "text", text: "Write a haiku about clouds." }] 
    },
  ],
  tools: [
    {
      name: "proxy_save_note",  // prefixed to avoid clashes
      description: "Save a short note",
      input_schema: { type: "object", properties: { note: { type: "string" } } },
    },
  ],
};

```

### OpenAI to Gemini Conversion

The [`request/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/request/openai-to-gemini.ts) module handles Google's **Chat API** format:

- **Function declarations** – Transforms OpenAI `tools` into Gemini's `function_declarations` array
- **Safety settings** – Injects default safety filters compatible with OpenAI's unrestricted behavior
- **Generation config** – Maps `temperature`, `top_p`, `max_tokens`, and `stop` sequences to Gemini's `generationConfig` object

## Response Translation: Native Formats to OpenAI

Response translators perform the **outbound transformation**, ensuring clients receive the expected `choices`, `usage`, and `finish_reason` structure regardless of the upstream provider.

### Claude to OpenAI Response

The [`response/claude-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/claude-to-openai.ts) (and related streaming handler in [`response/openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/openai-to-claude.ts)) handles:

- **Content normalization** – Uses `normalizeContentToString()` to flatten Claude's content blocks into simple strings
- **Tool call reconstruction** – Rebuilds OpenAI-style `tool_calls` arrays from Claude's `tool_use` blocks
- **Streaming chunk sanitization** – Processes `sanitizeStreamingChunk()` for SSE stream compatibility
- **Usage extraction** – Derives `prompt_tokens` and `completion_tokens` from Claude's usage metadata

```typescript
// Produced by response transformers
const openAiResponse = {
  id: "chatcmpl-123",
  object: "chat.completion",
  created: Date.now() / 1000,
  model: "claude-3-opus-2024-06-01",
  choices: [
    {
      index: 0,
      message: { role: "assistant", content: "Clouds drift…", tool_calls: [] },
      finish_reason: "stop",
    },
  ],
  usage: { prompt_tokens: 12, completion_tokens: 15, total_tokens: 27 },
};

```

### Gemini to OpenAI Response

The [`response/gemini-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/gemini-to-openai.ts) module addresses Gemini's distinct response shape:

- **Candidate extraction** – Selects from Gemini's `candidates` array to populate `choices`
- **Finish reason mapping** – Translates Gemini's `finishReason` enum values to OpenAI equivalents
- **Safety rating filtering** – Optionally exposes Gemini's safety metadata as custom fields

### Chain Translation for Fusion Strategies

A specialized path exists at [`response/gemini-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/gemini-to-claude.ts), which enables **Gemini → Claude → OpenAI** chaining. This supports OmniRoute's "Fusion" strategies where responses from multiple providers are combined or compared.

## Registry and Bootstrap Architecture

Translators are **registered centrally** rather than hard-coded throughout the routing stack:

```typescript
// From translator/registry.ts
register(FORMATS.claude, openaiToClaudeRequest, openaiToClaudeResponse);
register(FORMATS.gemini, openaiToGeminiRequest, geminiToOpenaiResponse);

```

The `getTranslator("claude")` method returns both `requestTransformer` and `responseTransformer` functions, allowing the rest of the pipeline to operate agnostically.

Registration occurs during bootstrap in [`translator/bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/translator/bootstrap.ts), ensuring all translators are available before the first request arrives.

## Shared Translation Helpers

Common utilities in `helpers/` prevent code duplication:

- **[`maxTokensHelper.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/maxTokensHelper.ts)** – Normalizes max token limits across providers with different constraints
- **[`schemaCoercion.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/schemaCoercion.ts)** – Handles type coercion for parameters that differ in expected types
- **[`jsonUtil.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/jsonUtil.ts)** – Provides `safeParseJSON()` for resilience against malformed provider responses
- **Content utilities** – `stripEmptyTextBlocks()` removes artifacts from Claude's content block format

## Streaming Translation Support

Both request and response paths support **Server-Sent Events (SSE)** streaming:

- Request translators set appropriate streaming flags per provider
- Response translators apply `sanitizeStreamingChunk()` to each SSE chunk
- Claude's split tool-call chunks are reassembled correctly in [`openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/openai-to-claude.ts)

## Why This Architecture Matters

The translation layer enables **provider-agnostic features** upstream:

| Feature | Benefit from Translation Layer |
|---------|-------------------------------|
| Intelligent routing | Router sees uniform OpenAI-shaped requests |
| Circuit breakers | Failure detection uses consistent response formats |
| Caching | Cache keys derived from normalized request hashes |
| Fusion strategies | Multiple provider outputs combined in OpenAI format |

Without this boundary, every component would need provider-specific branches.

## Summary

- **OmniRoute's translation layer** lives in `open-sse/translator/` and consists of bidirectional request/response transformers for Claude and Gemini
- **Request translators** ([`request/openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/request/openai-to-claude.ts), [`request/openai-to-gemini.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/request/openai-to-gemini.ts)) convert OpenAI schema to native provider formats
- **Response translators** ([`response/claude-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/claude-to-openai.ts), [`response/gemini-to-openai.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/gemini-to-openai.ts)) reverse the transformation for client compatibility
- **Registry pattern** in [`registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/registry.ts) enables lookup by provider name, initialized via [`bootstrap.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/bootstrap.ts)
- **Shared helpers** handle cross-cutting concerns like token limits, JSON safety, and content normalization
- **Streaming support** maintains real-time compatibility through chunk-by-chunk transformation

## Frequently Asked Questions

### How does OmniRoute handle OpenAI's tool format with Claude's different tool structure?

OmniRoute's [`openai-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/openai-to-claude.ts) request transformer remaps OpenAI `function` tools to Claude's `tool` definitions with `input_schema` (JSON Schema) instead of `parameters`. It prefixes tool names with `CLAUDE_OAUTH_TOOL_PREFIX` to prevent collisions, sanitizes tool IDs via `sanitizeToolResultId()`, and enforces that tool results appear adjacent to their corresponding tool uses through `enforceToolResultAdjacency()`.

### What happens to temperature and reasoning settings when translating between APIs?

The translation layer maps OpenAI's `temperature` and `reasoning_effort` to provider-specific equivalents. For Claude, temperature is **stripped entirely** when extended thinking mode is active (detected via the `thinking` payload). The `ADAPTIVE_EFFORT_LEVELS` set normalizes effort levels across the `reasoning_effort` → `thinking` conversion, ensuring consistent behavior for low/medium/high reasoning configurations.

### Can OmniRoute chain translations for multi-provider strategies?

Yes. The [`response/gemini-to-claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/response/gemini-to-claude.ts) module implements a **Gemini → Claude → OpenAI** translation chain specifically for Fusion strategies. This allows OmniRoute to normalize responses from multiple providers into a single comparable format before presenting the final OpenAI-compatible result to clients.