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:
- Format Registry – Located in
open-sse/translator/registry.ts, this maintains a bi-directional map ofsource → targettranslator factories for all supported providers. - Request Translation – Modules like
open-sse/translator/request/openai-to-claude.tsconvert incoming OpenAI-style payloads into provider-specific request bodies, handling field remapping and schema adjustments. - Response Translation – Files such as
open-sse/translator/response/openai-to-gemini.tsrewrite upstream responses back into OpenAI-compatible shapes while preserving streaming semantics.
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:
- Registry Lookup – Queries the registry in
registry.tsto retrieve the appropriate translator factory for thesourceFormat→targetFormatpair. - Schema Validation – Applies Zod-based coercion via
helpers/schemaCoercion.tsto reject malformed payloads before conversion. - 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
modeltomodelIdand restructuringmessagesarrays into provider-specificpromptobjects. - Tool-Call Normalization – Using
helpers/toolCallShim.tsto map OpenAI'sfunction_callsto Claude'stool_callsformat and vice versa. - Image Encoding – Converting between base64 and multipart formats via
image/sizeMapper.ts. - Token Budgeting – Adjusting
max_tokensvalues throughhelpers/maxTokensHelper.tsto 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:
-
Create translator modules at
src/translator/request/openai-to-myai.tsandsrc/translator/response/openai-to-myai.ts. -
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") }; -
Update
open-sse/translator/formats.tsto 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
translateRequestandtranslateResponsefunctions inopen-sse/translator/index.tsserve as the unified entry points for all provider conversions. - A bi-directional registry in
registry.tsuses 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →