How OmniRoute Translates Request/Response Formats Between OpenAI, Claude, and Gemini
OmniRoute normalizes LLM provider differences through a bidirectional translation layer in open-sse/translator that converts OpenAI-compatible requests into native Claude and Gemini schemas, then transforms provider responses back into OpenAI-formatted payloads.
OmniRoute acts as a universal API gateway that bridges the gap between OpenAI’s ubiquitous Chat Completions format and the distinct request/response schemas used by Anthropic’s Claude and Google’s Gemini. The translation system resides in the open-sse/translator package, where provider-specific converters handle bidirectional transformations. Understanding how OmniRoute translates request/response formats between OpenAI, Claude, and Gemini reveals the architectural patterns that enable seamless multi-provider LLM integration.
The Translation Registry Architecture
OmniRoute’s translation system centers on a registry pattern defined in open-sse/translator/registry.ts. This file maintains a mapping between source and target format pairs—such as OpenAI → Gemini or Claude → OpenAI—and their corresponding translator functions.
The registry exposes two primary operations:
- Registration – Developers call
register(FORMATS.OPENAI, FORMATS.GEMINI, openaiToGeminiBase, null)to associate a converter function with a specific direction. - Lookup – The runtime invokes
getTranslator(sourceFormat, targetFormat)to retrieve the appropriate request or response converter.
When a request arrives at src/app/api/v1beta/models/[...path]/route.ts, the API route inspects the payload to detect the source format. If the body contains a Gemini-style contents array, the system marks the source as Gemini; otherwise it defaults to OpenAI. The route then retrieves the translator and executes the conversion pipeline.
Request Translation Workflow
The request translation phase transforms incoming OpenAI-formatted payloads into provider-native structures before forwarding them to upstream LLMs.
OpenAI to Gemini Conversion
The open-sse/translator/request/openai-to-gemini.ts module handles conversion to Google’s API format. This translator constructs a GeminiRequest object with several critical transformations:
- Generation Configuration – Maps OpenAI
max_tokensandtemperatureto Gemini’sgenerationConfig, applying caps viacapMaxOutputTokensandcapThinkingBudgetfrommodelCapabilities.ts. - Safety Settings – Injects
DEFAULT_SAFETY_SETTINGSwhile preserving caller-provided overrides via utilities inopen-sse/translator/helpers/geminiHelper.ts. - Tool Sanitization – Renames tools using
sanitizeGeminiToolName(with optionalGeminiToolNameOptions) to comply with Gemini’s naming constraints, storing the original mappings in_toolNameMapfor round-trip conversion. - System Instruction Handling – Collates OpenAI
systemmessages into Gemini’ssystemInstructionfield, merging with any existingbody.systemcontent. - Role Merging – Executes
mergeConsecutiveSameRoleContents()(lines 68-80) to collapse adjacent messages sharing the same role, as Gemini rejects consecutive identical roles.
OpenAI to Claude Conversion
For Anthropic targets, open-sse/translator/request/openai-to-claude.ts performs Anthropic-specific adaptations:
- Thinking Budget Injection – Adds Claude-specific
thinkingblocks when model capabilities indicate extended thinking mode. - Tool Prefixing – Rewrites tool call names with
CLAUDE_OAUTH_TOOL_PREFIXto satisfy Anthropic validation requirements. - Content Validation – Strips empty text blocks that would cause Claude API rejections.
- Token Management – Adjusts
max_tokensparameters to align with Anthropic’s limits versus OpenAI’s conventions.
Response Stream Translation
After the upstream provider returns data, OmniRoute reverses the transformation to emit OpenAI-compatible JSON or Server-Sent Events (SSE).
Gemini to OpenAI Response Translation
The open-sse/translator/response/gemini-to-openai.ts module processes Gemini’s candidates objects and streaming chunks:
- Parses Gemini streaming chunks to extract
candidatesandcontentparts. - Reconstructs OpenAI-style
tool_callsfrom Gemini function calls. - Restores thought signatures using
geminiThoughtSignatureStore.tsto preserve chain-of-thought metadata within OpenAI’s message schema. - Normalizes
finish_reasonmappings between Gemini’sSTOP/MAX_TOKENSand OpenAI’s equivalent states.
Claude to OpenAI Response Translation
For Anthropic responses, open-sse/translator/response/claude-to-openai.ts maps Claude’s rich content blocks:
- Transforms Claude’s
contentarray (containingtextandtool_useblocks) into OpenAI’smessagestructure withtool_calls. - Normalizes tool result IDs to ensure consistency between Claude’s UUID format and OpenAI’s string identifiers.
- Drops unsupported Anthropic-specific fields that lack OpenAI equivalents.
Provider-Specific Transformation Details
Tool Name Sanitization and Safety Settings
Gemini imposes strict constraints on tool names and content safety. The open-sse/translator/helpers/geminiToolsSanitizer.ts module provides sanitizeGeminiToolName to replace invalid characters, while open-sse/translator/helpers/geminiHelper.ts manages DEFAULT_SAFETY_SETTINGS for content filtering categories like HARM_CATEGORY_DANGEROUS_CONTENT.
Message Role Merging and System Instructions
Because Gemini requires alternating roles and collapses system prompts into a single systemInstruction field, the translator must preprocess message histories. The mergeConsecutiveSameRoleContents() function ensures compliance by concatenating adjacent user messages or assistant messages into single parts arrays within the contents envelope.
Practical Implementation Examples
The following examples demonstrate how clients interact with OmniRoute while the translation layer handles provider-specific formatting automatically.
Calling Gemini via OpenAI Format
import fetch from "node-fetch";
const resp = await fetch("http://localhost:20128/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "gemini-1.5-flash",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Explain quantum entanglement in plain language." },
],
tool_choice: "auto",
safetySettings: [{ category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_ONLY_HIGH" }],
}),
});
const data = await resp.json();
console.log(data);
OmniRoute detects the Gemini model identifier, invokes the OpenAI-to-Gemini translator, and returns an OpenAI-shaped response object.
Streaming Gemini Responses
const resp = await fetch("http://localhost:20128/v1/chat/completions?stream=true", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "gemini-1.5-pro",
messages: [{ role: "user", content: "Write a poem about sunrise." }],
stream: true,
}),
});
for await (const chunk of resp.body) {
process.stdout.write(chunk);
}
The streaming pipeline routes Gemini candidates through openai-to-gemini-sse.ts, emitting OpenAI-compatible delta events while preserving thought signatures.
Using Claude Tools via OmniRoute
const resp = await fetch("http://localhost:20128/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-3-opus-1.2",
messages: [{ role: "user", content: "What is the weather in Paris?" }],
tools: [{
type: "function",
function: {
name: "get_weather",
description: "Fetch weather",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"]
}
}
}],
tool_choice: "required",
}),
});
const data = await resp.json();
Behind the scenes, openai-to-claude.ts prefixes tool names and injects the required thinking block before forwarding to Anthropic’s API.
Summary
- OmniRoute uses a registry pattern in
open-sse/translator/registry.tsto map bidirectional conversions between OpenAI, Claude, and Gemini formats. - Request translators in
open-sse/translator/request/handle provider-specific constraints like Gemini’s role merging, safety settings, and Claude’s thinking blocks. - Response translators in
open-sse/translator/response/normalize streaming chunks and tool calls back into OpenAI-compatible schemas. - Helper utilities manage tool name sanitization, token capping, and thought signature preservation to ensure round-trip fidelity.
- The system operates transparently, allowing clients to use OpenAI’s API contract while communicating with any supported provider.
Frequently Asked Questions
How does OmniRoute detect which provider format to use?
OmniRoute inspects the incoming request body in src/app/api/v1beta/models/[...path]/route.ts. If the payload contains a Gemini-specific contents array, it marks the source format as Gemini; otherwise it defaults to OpenAI. The target format is determined by the model identifier specified in the request.
Can OmniRoute handle streaming responses from all three providers?
Yes. The response translation layer processes Server-Sent Events (SSE) through provider-specific handlers like openai-to-gemini-sse.ts and claude-to-openai.ts. These modules parse native streaming formats—such as Gemini’s candidates objects—and convert them into OpenAI-compatible delta chunks in real-time.
What happens to tool calls when translating between formats?
Tool calls undergo bidirectional mapping. When sending to Gemini, sanitizeGeminiToolName ensures compliance with naming constraints while storing original names in _toolNameMap. For Claude, tool names receive the CLAUDE_OAUTH_TOOL_PREFIX prefix. On the return path, translators restore original tool identifiers and map provider-specific tool result formats back to OpenAI’s tool_calls structure.
Does OmniRoute support Claude’s extended thinking mode?
Yes. The openai-to-claude.ts translator checks model capabilities and injects the thinking block into requests when appropriate. For Gemini thought signatures, the system uses geminiThoughtSignatureStore.ts to preserve reasoning metadata that can be re-injected into OpenAI-formatted responses.
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 →