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

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 Maps provider names to their translator pairs
Bootstrap 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 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
// 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 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 (and related streaming handler in 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
// 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 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, 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:

// 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, ensuring all translators are available before the first request arrives.

Shared Translation Helpers

Common utilities in helpers/ prevent code duplication:

  • maxTokensHelper.ts – Normalizes max token limits across providers with different constraints
  • schemaCoercion.ts – Handles type coercion for parameters that differ in expected types
  • jsonUtil.ts – Provides safeParseJSON() for resilience against malformed provider responses
  • Content utilitiesstripEmptyTextBlocks() 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

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, request/openai-to-gemini.ts) convert OpenAI schema to native provider formats
  • Response translators (response/claude-to-openai.ts, response/gemini-to-openai.ts) reverse the transformation for client compatibility
  • Registry pattern in registry.ts enables lookup by provider name, initialized via 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 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_effortthinking 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →