How OmniRoute Handles API Format Translation Between LLM Providers

TLDR: OmniRoute normalizes heterogeneous LLM provider schemas through a three-layer translator subsystem that converts OpenAI-compatible requests into provider-specific formats (and vice versa) using a bi-directional registry, lazy-loaded modules, and shared transformation helpers.

OmniRoute acts as a universal gateway for large language model APIs by implementing a robust API format translation layer that bridges the gap between OpenAI's ubiquitous schema and the diverse request/response formats used by providers like Claude, Gemini, and Antigravity. As implemented in the diegosouzapw/OmniRoute repository, this system enables a single client-facing endpoint to route requests across 268+ backends without requiring provider-specific client code. The translation pipeline is architecturally split into three discrete layers: a central format registry, request transformation logic, and response stream normalization.

The Three-Layer Translation Architecture

OmniRoute's translator subsystem organizes cross-provider communication into distinct responsibilities managed through specific source files:

Central Entry Points: translateRequest and translateResponse

All request handlers, including those in open-sse/handlers/chatCore.ts, interact with the translation layer through two primary functions exported from open-sse/translator/index.ts:

import { translateRequest, translateResponse } from "@/translator";

const translatedBody = await translateRequest(
  rawBody,        // Client JSON payload
  sourceFormat,   // e.g., "openai"
  targetFormat,   // e.g., "claude"
  requestOptions  // Optional flags: maxTokens, tool schemas, etc.
);

The translateRequest function executes a strict three-phase pipeline:

  1. Registry Lookup – Queries the registry in registry.ts to retrieve the appropriate translator factory for the sourceFormattargetFormat pair.
  2. Schema Validation – Applies Zod-based coercion via helpers/schemaCoercion.ts to reject malformed payloads before conversion.
  3. Provider Conversion – Delegates to concrete translator modules that handle field renaming, tool-call mapping, and media encoding.

After receiving the upstream response, handlers invoke translateResponse with the same signature pattern to convert the provider's output back to the client-expected format.

Bi-Directional Registry with Lazy Loading

The open-sse/translator/registry.ts file implements a nested dictionary structure supporting translation in both directions:

{
  openai: {
    claude: {
      request: () => import("./request/openai-to-claude"),
      response: () => import("./response/openai-to-claude")
    },
    gemini: { /* ... */ }
  },
  claude: {
    openai: { /* reverse translation */ }
  }
}

Lazy import() functions ensure modules load only when a specific API format translation occurs, minimizing memory footprint while maintaining support for bidirectional conversion between any supported format pair.

Provider-Specific Transformation Logic

Concrete translator modules handle mechanical schema differences through shared utilities in the helpers/ directory:

  • Field Remapping – Converting model to modelId and restructuring messages arrays into provider-specific prompt objects.
  • Tool-Call Normalization – Using helpers/toolCallShim.ts to map OpenAI's function_calls to Claude's tool_calls format and vice versa.
  • Image Encoding – Converting between base64 and multipart formats via image/sizeMapper.ts.
  • Token Budgeting – Adjusting max_tokens values through helpers/maxTokensHelper.ts to respect provider-specific limits.
  • System Prompt Handling – Guaranteeing system message compatibility through helpers/strictSystemHoist.ts.

Streaming Response Translation

For providers supporting Server-Sent Events (SSE), response translators implement Node.js TransformStream interfaces. The open-sse/translator/response/openai-to-gemini-sse.ts module, for example, consumes Gemini's native SSE chunks and yields OpenAI-compatible data: {"choices":[…]} frames. This allows chatCore.ts to treat all upstream streams identically, regardless of the provider's native streaming format.

Extending the Translator for New Providers

Adding support for a new LLM provider follows a predictable pattern:

  1. Create translator modules at src/translator/request/openai-to-myai.ts and src/translator/response/openai-to-myai.ts.

  2. Register bidirectional entries in open-sse/translator/registry.ts:

    Registry["openai"]["myai"] = {
      request: () => import("./request/openai-to-myai"),
      response: () => import("./response/openai-to-myai")
    };
    Registry["myai"]["openai"] = {
      request: () => import("./request/myai-to-openai"),
      response: () => import("./response/myai-to-openai")
    };
  3. Update open-sse/translator/formats.ts to include the new format identifier if required.

New translators automatically inherit utilities from helpers/jsonUtil.ts for safe deep-cloning and pruning, ensuring consistency across the ecosystem.

Summary

  • OmniRoute's API format translation architecture consists of a Format Registry, Request Translation layer, and Response Translation layer.
  • The translateRequest and translateResponse functions in open-sse/translator/index.ts serve as the unified entry points for all provider conversions.
  • A bi-directional registry in registry.ts uses lazy-loaded imports to minimize bundle size while supporting 268+ providers.
  • Provider-specific converters handle schema differences through shared helpers for tool calls, images, token limits, and system prompts.
  • Streaming responses are normalized via TransformStream implementations to provide uniform SSE output to clients.

Frequently Asked Questions

How does OmniRoute handle tool-call format differences between providers?

OmniRoute normalizes tool-call structures through helpers/toolCallShim.ts, which maps OpenAI's function_calls to Claude's tool_calls format during request translation, with reverse mapping occurring during response translation. This ensures compatibility regardless of which provider executes the function.

Can OmniRoute translate between any two provider formats, or only from OpenAI format?

While OpenAI-compatible format serves as the primary client-facing interface, the registry supports bi-directional translation between any supported format pairs. As defined in open-sse/translator/registry.ts, entries exist for both openai → claude and claude → openai, enabling translation in either direction, though OpenAI format typically acts as the canonical intermediate representation.

What happens to streaming responses during API format translation?

Response translators for streaming providers implement Node.js TransformStreams. For example, open-sse/translator/response/openai-to-gemini-sse.ts converts Gemini's SSE chunks into OpenAI-compatible data: frames, allowing open-sse/handlers/chatCore.ts to process all upstream streams uniformly without provider-specific branching logic.

How does the system validate requests before translation?

Before conversion, translateRequest executes Zod-based validation through helpers/schemaCoercion.ts. This rejects malformed payloads immediately, preventing invalid requests from reaching provider-specific translators and ensuring type safety across the 268 supported integrations.

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 →