Reference
Lossy protocol conversions
What is not preserved when inbound and outbound wires differ.
Any shipped inbound protocol can reach any shipped outbound protocol. That pivot is not always lossless. The cases below are known; claiming fidelity outside this list is how a bridge gets trusted more than it deserves.
Same-protocol requests keep provider field shapes. Cross-protocol requests translate through a shared request shape, request, response, and stream chunks, and only the fields both wires can express survive.
What it looks like
An OpenAI client can call acme/claude even when that alias resolves to Anthropic. The caller keeps speaking chat completions (or responses); the gateway translates. Worked examples are on Calling the gateway.
await client.chat.completions.create({
model: "acme/claude",
messages: [{ role: "user", content: "Say hello" }],
})await client.messages.create({
model: "acme/gpt",
max_tokens: 1024,
messages: [{ role: "user", content: "Say hello" }],
})await client.models.generateContent({
model: "acme/gpt",
contents: "Say hello",
})curl https://<app>.gw.ourseams.com/v1/chat/completions \
-H "Authorization: Bearer ak_acme_…" \
-H "Content-Type: application/json" \
-d '{
"model": "acme/claude",
"messages": [{ "role": "user", "content": "Say hello" }]
}'On the OpenAI wire, tools and images use /v1/responses. chat.completions inbound is text-only.
Known losses
| Direction | What changes | Why |
|---|---|---|
any → google.generate | Tool call ids become gc_* | Gemini has no call ids on the wire; we synthesise them |
google.generate → any | Tool results arrive name-only until pairing | Gemini answers by function name, not id |
any → google.generate | Tool result order may change | Gemini requires results in pending call order |
| any → any | Tool names longer than 64 chars are truncated with a hash suffix | Provider name limits; truncation is injective |
any → anthropic.messages | responseFormat is refused | Anthropic Messages has no response_format here |
any → openai.responses | stop is refused | Responses has no stop sequences |
openai.responses inbound | previous_response_id and conversation are refused | Pass input items explicitly; the gateway is stateless |
| any → non-Anthropic | thinking / redacted_thinking / cache_control dropped | Best-effort: kept when decoded; omitted on wires that cannot express them |
| reasoning dialect mismatch | Wrong dialect omitted (effort vs maxTokens) | Best-effort: outbound writes only the dialect it supports |