Skip to content

Fidelity

A request is passed through byte for byte when its provider speaks the dialect it arrived in, and translated when the provider does not. This page is about how the provider is chosen, when translation happens, and what the fidelity guard does about a translation that would drop something.

A provider entry is a name, a dialect and a URL. dialect is openai or anthropic, and it says what the endpoint accepts: a server that serves /v1/chat/completions is openai, one that serves /v1/messages is anthropic. There is no third value. A dialect ships with a translator, a probe and a coverage fixture.

Vendors host each other’s dialects. Anthropic serves its Messages API and an OpenAI-compatible endpoint. Bedrock serves both, split by model family. Gemini serves only an OpenAI-compatible endpoint. Each of those surfaces is one provider entry in its own dialect, and a vendor with two surfaces is two entries claiming the same models:

providers:
- name: anthropic
dialect: anthropic
base_url: https://api.anthropic.com
api_key: ${secret:ANTHROPIC_API_KEY}
models: ["claude-*"]
- name: anthropic-openai
dialect: openai
base_url: https://api.anthropic.com
api_key: ${secret:ANTHROPIC_API_KEY}
models: ["claude-*"]

Two entries may claim one model only when their dialects differ. Two claiming it in the same dialect are refused at load, because file order would decide between them and nobody wrote that on purpose.

The route a request arrives on fixes its dialect: /v1/messages is an Anthropic-dialect client, /v1/chat/completions and /v1/responses are OpenAI-dialect clients. Among the entries claiming the requested model, the first that speaks the request’s dialect is chosen, and the request is passed through. When none does, the first declared is chosen and the request is translated to it.

Two routes are translated: /v1/chat/completions to an anthropic entry, and /v1/messages to an openai entry. /v1/responses, /v1/completions, /v1/embeddings and /v1/realtime exist only in the OpenAI dialect, and no translator carries them. A request on one of them whose chosen entry speaks anthropic is refused with 422 route_not_translatable. The refusal comes before the budget hold and before anything is sent. A failover candidate in that position is skipped. /v1/models is served in both dialects, and a path the gateway has no label for, such as /v1/messages/count_tokens, cannot be placed in either. Both are passed through to the chosen entry.

With the two entries above, a Claude Code session on /v1/messages reaches anthropic untouched, and an OpenAI SDK on /v1/chat/completions reaches anthropic-openai untouched. Anthropic does the translation in the second case, on its own infrastructure, and what its endpoint drops is Anthropic’s to document. Declare only anthropic and the OpenAI SDK’s request is translated by the gateway instead, under the guard below.

The choice is per request. A model matching one entry’s exact name and another’s * prefix goes to the exact match; among prefixes the longest wins; an alias ranks below every pattern; the default provider catches the rest. The dialect preference applies within whichever of those groups matched.

Translation drops things. Translation lists which capabilities each of the two pairs does not preserve, and the list is checked against the vendored translator on every build. Before a translated request is forwarded, the gateway reads which of those capabilities the body uses. That read is a bytes.Contains prefilter, then a gjson structural confirm, memoized, and never run on a same-dialect request because there is nothing to decide.

A request that uses a capability its translation would drop is decided by on_fidelity_loss, on the deployment or on the provider:

  • reject (default) refuses with a structured error naming every such capability, so the request is fixed in one pass instead of one retry per loss.
  • allow forwards anyway and counts the downgrade in kirto_gate_fidelity_downgrade_total{provider,cap,action}.

usage_details is the one capability no probe can read, because it is a property of the response: whether the usage block keeps the cached and reasoning token splits. Translated usage carries no splits in either direction, so a budget typed on a split is charged by total tokens on a translated path, counted under the same metric with the action metered_by_total.

A translated request can also leave out a field the provider’s dialect requires. max_tokens is optional in the OpenAI dialect and required by the Anthropic one, and the translator sends it as 0 when it is absent. The gateway refuses such a request with 400 required_field_missing and names the field and both of its OpenAI spellings, max_tokens and max_completion_tokens. The refusal comes after the guard, before the budget hold, and a failover candidate in that position is skipped. The gateway does not fill in a default. An absent max_tokens means no cap below the model’s own, and any number the gateway chose would cut long answers short with only finish_reason: "length" to say so.

POST /v1/preflight answers the whole question without sending anything: which provider, whether the request would be translated, what the body uses and what the translation would lose.

What the gateway claims, and what it does not

Section titled “What the gateway claims, and what it does not”

Every fidelity claim here is about code this process runs. A translator’s losses are a table in internal/fidelity, and a fixture per capability runs through the real translator to hold the table to what the code does.

The gateway makes no claim about another party’s server. A vendor’s compatibility endpoint is a provider entry in its own dialect, passed through like any other, and what that endpoint does with the bytes is in the vendor’s documentation, not in a table here. The recipe for such a surface says so in its note.

  • Translation: the two pairs, what each drops, and why an available translator is not a reason to use one.
  • Passthrough: what byte for byte means when the dialects match.
  • Provider recipes: both surfaces written out for the vendors that offer both.