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
- A client calls Core on the translated API, for example
POST /anthropic/v1/messages. - Core forwards the request to the translator, at the translator's
baseUrlplus the exact path Core received. - The translator converts the request and calls Core back on the API the deployment serves, for example Chat Completions.
- Core serves that callback pass-through from the deployment, and the translator converts the answer back for the client.
Where translators are defined
| Source | Location |
|---|---|
| Config file | translators.<name> |
| Configuration API | /v1/translators/platform/<name> |
| Inline | interfaces.<type>.translator on a model, for a translator no other deployment uses |
DIAL Admin manages translators in Translators.
Fields
| Field | Required | Description |
|---|---|---|
in | Yes | The 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. |
out | Yes | The interface the translator converts to. The deployment must serve it itself, pass-through. |
baseUrl | Yes | The 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: translatorrequirestranslatorand rejectsbase_urlon the same entry;passthroughis the other way round. - Complete definitions. A translator without
outorbaseUrlis rejected, and so is a registered translator withoutin. - Known interfaces.
inandoutmust name interfaces this version of Core knows. - Two different interfaces. A translator whose
outequals itsin, or the interface it sits under, would hand its own output back to itself, and is rejected. - No chains. The deployment must serve
outitself, 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 limit | The client's translated call | The translator's callback |
|---|---|---|
Token limits (minute, day, week, month) | Not read, not added to | Checked and added to |
costLimit | Not read, not added to | Checked and added to |
requestHour, requestDay | Not read, not counted | Checked 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
- DIAL Core configuration: Models — declare the interfaces a model serves
- AI API overview — passthrough and translation across adapters
- Coding agents and harnesses — run Claude Code and Codex through DIAL
- DIAL Admin: Translators — add API translators in DIAL Admin
- DIAL Bedrock Adapter capabilities — Claude integrations, caching, and load balancing in the adapter