How OmniRoute Handles Structured Output Conversion Across LLM Provider Formats
OmniRoute uses a pluggable translator layer with a central Format Registry to convert requests and responses between incompatible LLM provider schemas while preserving structured outputs like tool calls, thinking signatures, and JSON schemas.
OmniRoute is an open-source routing layer that normalizes interactions between diverse LLM providers. The project's structured output conversion system ensures that complex payloads—including tool definitions, reasoning budgets, and response formats—remain intact when translating between OpenAI, Gemini, Claude, and other providers.
The Format Registry: Mapping Source to Target
At the heart of OmniRoute's conversion system is a central registry located in open-sse/translator/registry.ts. This module maintains a bidirectional map that pairs source formats with target formats, registering both request and optional response translators for each combination.
The registry exposes two key lookup functions: getRequestTranslator and getResponseTranslator (lines 35‑42). When registering a new translation path, developers provide the source format, target format, request translator function, and an optional response translator function. For example, mapping OpenAI to Gemini uses openaiToGeminiRequest for the request side while passing null for the response side when no custom conversion is needed.
This design creates a systematic pipeline where any provider format can be supported by implementing the translation interface and registering the pair, enabling OmniRoute to handle heterogeneous API schemas through a unified abstraction.
Request Translation Pipeline
Request translators act as transformers that receive the original model name, raw body payload, stream flag, and credential object, then produce a provider-specific payload. The implementation in open-sse/translator/request/openai-to-gemini.ts demonstrates how complex structured data is preserved during conversion.
Generation Config and Reasoning Budgets
The OpenAI-to-Gemini translator maps standard generation parameters between schemas. Lines 188‑207 handle the conversion of temperature, top-p, top-k, and stop tokens into Gemini's generationConfig structure.
For advanced reasoning features, the translator maps OpenAI's reasoning_effort parameter to Gemini's thinkingConfig (lines 224‑252). This ensures that structured thinking budgets and reasoning controls survive the translation process, allowing users to specify reasoning intensity regardless of the target provider's native terminology.
Tool Call Conversion
Tool definitions require significant schema reshaping. Lines 368‑467 in open-sse/translator/request/openai-to-gemini.ts convert OpenAI's tools array into Gemini's functionCall parts, handling the nesting structure differences between the providers. The translator manages the tool_choice parameter, converting OpenAI's "auto" or specific tool selections into Gemini's equivalent enabling modes.
Response Format Handling
When clients specify structured output requirements using OpenAI's response_format parameter, the translator maps these to Gemini's responseMimeType and responseSchema fields (lines 992‑1012). This preserves JSON schema constraints and MIME type specifications, ensuring that providers enforce the same structured output contracts even when the request originated from a different API format.
Response Translation and Streaming
After the upstream provider processes a request, OmniRoute may need to reshape the response back into the client's expected format. This occurs through registered response translators that handle both complete payloads and streaming Server-Sent Events (SSE).
Reusing Projectors Across Providers
Not every source-target pair requires unique response translation logic. The OpenAI-to-Gemini path reuses the generic Antigravity projector defined in open-sse/translator/response/openai-to-antigravity.ts because Gemini and Antigravity share compatible response envelopes. This registration occurs in open-sse/translator/response/openai-to-gemini.ts (lines 14‑15), demonstrating how the registry supports composition and code reuse across similar provider formats.
Claude-Specific Response Mapping
When converting OpenAI streams to Claude format, OmniRoute employs a dedicated response mapper in open-sse/translator/response/openai-to-claude.ts. This translator handles the conversion of OpenAI's delta-based streaming chunks into Claude's content_block_* structures, manages XML-based tool invocations, and maps finish reasons between the two schemas. The implementation accounts for Claude's specific event types and tool use representations that differ significantly from OpenAI's streaming protocol.
Tool Name and Signature Preservation
Translators maintain a toolNameMap to reconcile provider-specific naming conventions, particularly when tool identifiers contain characters allowed by one provider but restricted by another. The open-sse/translator/helpers/geminiToolsSanitizer.ts module sanitizes tool names and maintains bidirectional rename mappings to ensure round-trip consistency.
For advanced Gemini features, the system supports signaturelessToolCallMode and supportsSignatureBypass flags, allowing thought signatures to be embedded or bypassed depending on the target model's capabilities. These utilities in open-sse/translator/helpers/geminiHelper.ts handle content conversion, safety settings, and JSON schema cleaning to ensure that structured tool outputs remain valid across provider boundaries.
Execution Flow
When an API route receives a request, OmniRoute determines the source format (e.g., FORMATS.OPENAI) through request inspection. It then looks up the appropriate target format based on the selected provider and invokes the registered request translator from open-sse/translator/registry.ts. After the upstream request completes, the response or SSE stream passes through the matching response translator before returning to the client.
This execution model ensures that structured output conversion happens transparently at the edge, allowing client applications to use a single API format while routing to multiple backend providers without losing fidelity in tool definitions, reasoning parameters, or response schemas.
// Resolve a request translator for OpenAI → Gemini
import { getRequestTranslator } from '@/open-sse/translator/registry';
import { FORMATS } from '@/open-sse/translator/formats';
const translate = getRequestTranslator(FORMATS.OPENAI, FORMATS.GEMINI);
if (!translate) throw new Error('No translator registered');
// Example OpenAI payload
const openaiBody = {
model: 'gpt-4o',
messages: [{ role: 'user', content: 'What is the weather?' }],
temperature: 0.7,
tool_choice: 'auto',
tools: [{ type: 'function', function: { name: 'get_weather', parameters: {} } }],
};
// Convert to Gemini format (ready for provider dispatch)
const geminiRequest = translate('gpt-4o', openaiBody, false, null);
console.log(geminiRequest);
// Convert an OpenAI streaming chunk to Claude format
import { getResponseTranslator } from '@/open-sse/translator/registry';
const toClaude = getResponseTranslator(FORMATS.OPENAI, FORMATS.CLAUDE);
if (!toClaude) throw new Error('Missing response translator');
// Simulated OpenAI chunk (normally received from SSE)
const openaiChunk = {
id: 'chatcmpl-123',
model: 'gpt-4o',
choices: [{ delta: { content: 'Hello' }, finish_reason: 'stop' }],
};
const claudeMessages = toClaude(openaiChunk, {}); // state object managed across chunks
console.log(claudeMessages);
Summary
- OmniRoute implements structured output conversion through a centralized Format Registry in
open-sse/translator/registry.tsthat maps source and target provider pairs to specific translator functions. - Request translators in
open-sse/translator/request/openai-to-gemini.tshandle complex mappings including generation configs (lines 188‑207), reasoning budgets (lines 224‑252), tool calls (lines 368‑467), and response formats (lines 992‑1012). - Response translators normalize streaming chunks and completion payloads back to the client's expected schema, with some paths like OpenAI → Gemini reusing Antigravity projectors while others like OpenAI → Claude require dedicated mappers in
open-sse/translator/response/openai-to-claude.ts. - Tool name sanitization and signature handling occur through helper modules including
geminiToolsSanitizer.tsandgeminiHelper.ts, maintaining mapping integrity across provider-specific constraints. - The pluggable architecture allows new provider formats to be supported by registering translation functions without modifying core routing logic.
Frequently Asked Questions
How does OmniRoute register new provider format translators?
Developers register translators by calling the register function in open-sse/translator/registry.ts with the source format, target format, request translator function, and optional response translator. This creates a lookup entry that the routing layer queries at runtime via getRequestTranslator and getResponseTranslator to resolve the appropriate conversion logic for any provider pair.
What happens to OpenAI's response_format when converting to Gemini?
The translator maps OpenAI's response_format parameter to Gemini's responseMimeType and responseSchema fields (lines 992‑1012 in open-sse/translator/request/openai-to-gemini.ts). This ensures JSON schema constraints and MIME type specifications are preserved, allowing Gemini to enforce the same structured output contracts that were requested in the OpenAI format.
Why does the OpenAI to Gemini conversion reuse the Antigravity response translator?
The OpenAI-to-Gemini path registers null for custom response translation and instead uses the Antigravity projector because Gemini and Antigravity share compatible response envelope structures. This design pattern, visible in open-sse/translator/response/openai-to-gemini.ts (lines 14‑15), reduces code duplication by allowing similar provider formats to share translation logic through the registry's composition model.
How does OmniRoute handle tool name incompatibilities between providers?
Translators maintain a toolNameMap to track bidirectional rename mappings between provider-specific naming conventions. The geminiToolsSanitizer.ts helper sanitizes tool names to satisfy Gemini's character restrictions while preserving the original identifiers for round-trip consistency, ensuring that tool calls invoked in one format can be correctly matched and executed in the target provider's ecosystem.
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 →