Skip to main content

Translators

A translator is a service that converts a request from one AI API to another, so a deployment can serve an API it does not speak. For example, a model that serves only OpenAI Chat Completions can also serve Anthropic Messages, so Claude Code can use it. This reference is for platform engineers who expose models through more APIs; it assumes you know Models.

How a translated request flows​

  1. A client calls Core on the translated API, for example POST /anthropic/v1/messages.
  2. Core forwards the request to the translator, at the translator's baseUrl plus the exact path Core received.
  3. The translator converts the request and calls Core back on the API the deployment serves, for example Chat Completions.
  4. Core serves that callback pass-through from the deployment, and the translator converts the answer back for the client.

Where translators are defined​

SourceLocation
Config filetranslators.<name>
Configuration API/v1/translators/platform/<name>
Inlineinterfaces.<type>.translator on a model, for a translator no other deployment uses

DIAL Admin manages translators in Translators.

Fields​

FieldRequiredDescription
inYesThe interface the translator accepts: the API the client calls. Must differ from out. Not used inline, where the interface the definition sits under implies it.
outYesThe interface the translator converts to. The deployment must serve it itself, pass-through.
baseUrlYesThe root URL of the translator service. Each request goes to baseUrl plus the exact path Core received. A trailing slash is normalized.

in and out take interface names: openaiChatCompletions, openaiEmbeddings, openaiResponses, or anthropicMessages.

Use a translator​

A model references a translator by name from an interface in translator mode:

{
"translators": {
"anthropicMessagesToOpenaiChatCompletions": {
"in": "anthropicMessages",
"out": "openaiChatCompletions",
"baseUrl": "http://model-provider:5000/compat/bedrock/openaiChatCompletions"
}
},
"models": {
"openai-gpt-5.4-mini": {
"type": "chat",
"overrideName": "gpt-5.4-mini",
"baseUrl": "http://model-provider:5000/compat/openai",
"interfaces": {
"openaiChatCompletions": { "mode": "passthrough" },
"anthropicMessages": {
"mode": "translator",
"translator": "anthropicMessagesToOpenaiChatCompletions"
}
}
}
}
}

A request to POST /anthropic/v1/messages for this model goes to http://model-provider:5000/compat/bedrock/openaiChatCompletions/anthropic/v1/messages, the translator of the Bedrock adapter in DIAL Model Provider. The translator converts it and calls Core back for openai-gpt-5.4-mini, which serves it from http://model-provider:5000/compat/openai.

An inline translator takes out and baseUrl:

"anthropicMessages": {
"mode": "translator",
"translator": {
"out": "openaiChatCompletions",
"baseUrl": "http://custom-translator/to-chat-completions"
}
}

Rules​

Core checks these rules when it reads the configuration, and rejects a configuration that breaks them, keeping the previous one live:

  • A base URL or a translator, never both. mode: translator requires translator and rejects base_url on the same entry; passthrough is the other way round.
  • Complete definitions. A translator without out or baseUrl is rejected, and so is a registered translator without in.
  • Known interfaces. in and out must name interfaces this version of Core knows.
  • Two different interfaces. A translator whose out equals its in, or the interface it sits under, would hand its own output back to itself, and is rejected.
  • No chains. The deployment must serve out itself, pass-through. A translator that converts to another translated interface is refused, which also keeps cycles of translators out of the configuration.

A translator name that no translators entry defines is not a configuration error. That interface answers 503 and never falls back to endpoint or responsesEndpoint, while the rest of the deployment keeps working. The interface is still listed in /v1/deployments. Registering the translator serves it with no change to the deployment.

Editing a translator takes effect for every deployment that references it by name as soon as the change loads: Core resolves the name on each request.

Limits and usage​

A translated call reaches Core twice: from the client on the translated API, and from the translator on out. Only the callback counts against the caller's limits, so a client call is counted once:

Role limitThe client's translated callThe translator's callback
Token limits (minute, day, week, month)Not read, not added toChecked and added to
costLimitNot read, not added toChecked and added to
requestHour, requestDayNot read, not countedChecked and counted

A translated API is not a way around a quota. A caller who has used up a limit is rejected on the callback, and the 429 reaches the client through the translator.

  • Usage is attributed to the caller, not to the translator, as long as the translator calls back with the per-request key Core issued it. Interceptors follow the same contract.
  • Both hops are written to the request log, each with the usage it saw. Summing usage across log entries double counts a translated request.

Further reading​