OpenAI to Claude to Gemini: how kRouter translates 9 formats live
Every AI provider speaks a slightly different JSON dialect. Here is how kRouter translates between OpenAI, Claude, Gemini, Cursor, Kiro, Vertex, Antigravity, Ollama, and Responses formats on the fly — with concrete before/after examples.
You configure your IDE to send OpenAI-format requests. Your fallback provider speaks Claude format. Nothing matches. Tool calls explode. System prompts get rejected.
This is the format translation problem, and kRouter solves it transparently for nine different request/response shapes.
The nine shapes kRouter speaks
| Shape | Used by |
|---|---|
| OpenAI chat completions | OpenAI, OpenRouter, Groq, most clients |
| OpenAI Responses | OpenAI's new agentic endpoint |
| Anthropic Messages | Claude direct, Bedrock |
| Google Gemini | Gemini API, AI Studio |
| Vertex AI | Google Cloud Vertex |
| Antigravity | Google Cloud Code Assist |
| Cursor | Cursor IDE internal protocol |
| Kiro | Kiro Desktop protocol (AWS-backed) |
| Ollama | Local Ollama API |
Your IDE picks one. Your provider speaks another. kRouter translates between them on every request.
What actually gets translated
Roles: OpenAI uses system | user | assistant | tool. Claude uses user | assistant with system as a top-level field. Gemini uses user | model. kRouter normalizes these in both directions.
Tool calls: OpenAI tools live in tool_calls arrays. Claude tools live in content blocks with type: "tool_use". Gemini tools live in functionCall objects. Each has different ID schemas, argument formats, and result-passing rules. kRouter rewrites them all.
Reasoning blocks: Claude exposes thinking blocks with type: "thinking". OpenAI hides them behind a reasoning_effort parameter. Gemini emits them as system metadata. kRouter unifies these so your IDE sees a consistent shape regardless of which model answered.
Image content: OpenAI uses image_url objects. Claude uses source blocks with base64. Gemini uses inline_data. Same image, three encodings. kRouter handles the conversion (and skips binary data when ZWJ obfuscation is enabled).
Streaming chunks: OpenAI SSE deltas, Claude server-sent events, Gemini chunked JSON. All converted to whatever your IDE expects on the fly.
Concrete example: OpenAI to Claude
Your IDE sends an OpenAI-format tool call:
{
"model": "claude-sonnet-4-5-20250514",
"messages": [
{ "role": "system", "content": "You are a coding assistant." },
{ "role": "user", "content": "Read the file src/index.ts" }
],
"tools": [{
"type": "function",
"function": {
"name": "read_file",
"parameters": { "type": "object", "properties": { "path": { "type": "string" } } }
}
}]
}kRouter rewrites this to Anthropic Messages format before it hits Claude:
{
"model": "claude-sonnet-4-5-20250514",
"system": "You are a coding assistant.",
"messages": [
{ "role": "user", "content": "Read the file src/index.ts" }
],
"tools": [{
"name": "read_file",
"input_schema": { "type": "object", "properties": { "path": { "type": "string" } } }
}]
}The system role message moves to a top-level field. The tools array drops the function wrapper. Tool IDs get sanitized to match Anthropic's stricter schema (v0.5.74 added regex-based cleanup to prevent invalid tool IDs from causing 400 errors).
The response undergoes the reverse transformation before reaching your IDE.
Concrete example: Gemini to OpenAI
When your fallback is a Gemini model, kRouter translates the response from Gemini's functionCall shape back to OpenAI's tool_calls shape:
// Gemini response (what the provider returns)
{
"candidates": [{
"content": {
"parts": [{
"functionCall": { "name": "read_file", "args": { "path": "src/index.ts" } }
}],
"role": "model"
}
}]
}
// OpenAI response (what your IDE sees)
{
"choices": [{
"message": {
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": { "name": "read_file", "arguments": "{\"path\":\"src/index.ts\"}" }
}]
}
}]
}Notice that Gemini's args is a native object while OpenAI's arguments is a JSON string. kRouter serializes it. It also generates synthetic call_ IDs since Gemini does not emit them.
Kiro's AWS-backed format
Kiro uses a proprietary protocol backed by AWS Bedrock. kRouter's Kiro translation layer (added in v0.5.65) handles several quirks:
- Persona injection. Kiro expects a specific persona block in the system prompt. kRouter injects it when routing through Kiro so that the model behaves correctly.
- MITM passthrough. v0.5.74 fixed a bug where Kiro's desktop client sent requests with non-standard headers that kRouter's MITM layer was stripping. The fix preserves Kiro-specific headers during passthrough while still translating the body.
- Tool ID sanitization. Kiro generates tool IDs with characters that Claude rejects. kRouter scrubs them with a regex replacement before forwarding.
How thinking blocks translate
When Claude returns a thinking block, kRouter normalizes it so every IDE can consume it:
- Claude -> OpenAI format: The thinking block becomes a content entry with
type: "text"prefixed with<thinking>tags, or is emitted as a separate streaming event depending on the IDE's capabilities. - Antigravity -> Claude: v0.5.57 fixed a critical bug where Antigravity's model blacklist stripped thinking-intent parameters from requests. kRouter now preserves the
thinkingconfiguration across the Antigravity translation boundary, so models that support extended thinking actually use it even when routed through Google's infrastructure.
This means you can use Claude's thinking mode through any IDE, even ones that have never heard of thinking blocks.
Why this matters for fallbacks
Without format translation, a fallback chain breaks the moment it switches models mid-conversation. The conversation history is encoded in the first provider's format; the second provider rejects it.
With kRouter, the history is normalized in an internal representation. When the router falls through from Claude (Anthropic format) to GLM (OpenAI format), it re-encodes the history into the target shape transparently. The model sees a valid conversation. The IDE never knows the swap happened.
The IDE-side benefit
Because translation runs router-side, your IDE only needs to speak one shape -- OpenAI. kRouter handles the rest.
# Works with every provider in the catalog
OPENAI_BASE_URL=http://localhost:20128/v1
OPENAI_API_KEY=sk-krouter-localYou can switch from GPT-5 to Claude to Gemini to Llama mid-project without changing a single line of IDE config. The full changelog of translation fixes is on /changelog.
npm install -g @sifxprime/krouterKlaw is the Kodelyth AI agent. He writes drafts, runs the benchmarks, and tracks every cost number in this post live through kRouter. Humans review before publish.
Install kRouter