# Wire dialects

Which providers cost code to support, which cost a line of config, and what a wire driver owns that a descriptor cannot change.

Source: https://tokentriage.com/docs/build/providers-dialects

---
import Callout from '../../../components/Callout.astro';
import NextSteps from '../../../components/NextSteps.astro';

Your application speaks one request shape. Your providers between them speak several. A gateway
either translates every pair, which multiplies, or it treats the shape as one axis of a provider's
identity and keeps the number of shapes small. This one does the second.

A **dialect** is a wire format: what a request body looks like, where the model id lives, how a
streamed response is framed, and where usage counts appear in the reply. It is not the same thing
as a provider. Twelve providers can speak one dialect and still bill under twelve different
namespaces.

## What the proxy accepts on the way in

The proxy recognises each shipped dialect on its own native paths. A client speaking any of them is
matched, given an endpoint kind, governed and ledgered against that dialect's default upstream:
Anthropic's `/v1/messages`, Bedrock's `/model/{id}/converse` and Deepgram's `/v1/listen` are each
answered as themselves. The suite drives that last one as a client request, at Deepgram's own path,
and reads back a priced transcription row.

A path matching no registered dialect and endpoint is forwarded as a ledgered passthrough rather
than rejected, so an unusual call still reaches your provider and still lands on a row.

**Translation is narrower than recognition**, and the difference is where the number two belongs.
Two client formats can be translated onto an upstream that speaks something else:

| Client format | Paths | Your client |
|---|---|---|
| `openai` | `/v1/chat/completions`, `/v1/responses`, `/v1/embeddings`, and the rest of the `/v1` surface | any OpenAI SDK, or anything OpenAI-shaped |
| `anthropic` | `/v1/messages` | the Anthropic SDK, Claude Code |

Those two reach four upstream dialects between them, `openai`, `anthropic`, `gemini` and
`bedrock`, streaming and not. Gemini- and Bedrock-format
requests are recognised and served on their own dialects, but they are translation *targets* rather
than translation *sources*, which is a product decision to defer rather than a gap in the wire
code.

## What the drivers speak on the way out

The generated September 6 catalog snapshot counts 15 wire-dialect ids across 95 built-in provider descriptors.
The generator resolves the dialect id declared by the compiled Go package; directory spelling is not the identity.

| Driver | Descriptors |
|---|---:|
| `anthropic` | 1 |
| `anthropic_vertex` | 1 |
| `assemblyai` | 1 |
| `bedrock` | 2 |
| `bfl` | 1 |
| `deepgram` | 1 |
| `elevenlabs` | 1 |
| `fal` | 1 |
| `gemini` | 3 |
| `openai` | 78 |
| `replicate` | 1 |
| `runway` | 1 |
| `soniox` | 1 |
| `stability` | 1 |
| `vertex_predict_embed` | 1 |

78 descriptors use the `openai` driver. Mock verification remains per endpoint cell;
the [provider reference](/docs/build/providers-reference) names the ten descriptors with no gated cell.

## What the driver owns and the descriptor cannot change

A descriptor refines a driver; it does not replace it. The split matters when you are deciding
whether a provider you want is a config change or a feature request.

**The driver owns** the body shape it parses and emits, how a stream is framed and flushed, where
the model id is spliced when a routing decision rewrites it, and where usage counts are read from
in the response.

**The descriptor overrides** the path templates, any required query parameters, the mandatory
headers, the auth scheme and the pricing namespace.

So a provider that speaks OpenAI's body shape at an unusual URL is data. A provider that speaks a
body shape nothing ships is code, and it is the single most common reason a provider appears in
[the uncovered list](/docs/build/providers-coverage).

## Where the model id lives

A request can carry its model in four places, and which one it uses decides what a routing rewrite
is allowed to touch.

| Where the model comes from | Drivers | What a routing rewrite does |
|---|---|---|
| the request body | `openai` and `anthropic` (`model`), `elevenlabs` (`model_id`), `assemblyai` (`speech_model`), `soniox` (`model`) | splices the one field |
| the URL path | `bedrock`, `gemini`, `anthropic_vertex`, and `stability` for all but one of its operations | rewrites the path and leaves the body byte-identical |
| the URL query | `deepgram` (`?model=`) | nothing to splice; the driver reads the model there for pricing |
| a multipart form field | `stability`, for the one operation that carries its model in the form instead | nothing to splice; the field is read for pricing |

The multipart form field is the newest of the four, and it arrived with image generation. Two of
the four carriers are worth knowing about for a different reason: a body with no `model` field
would be corrupted by a splice, so the proxy checks which kind of driver it is holding and takes
the path route when that is the truthful one. `replicate` is the case that resists the rule, and it
says so rather than guessing: a submit carrying a `version` field is rewritable, and a submit whose
model *is* the URL is relayed unchanged with the enforcement fields cleared.

Only the routable endpoint kinds are rewritten at all. Speech, audio and image requests are priced
from wherever their model is carried and are not re-pointed at another model.

## Two dialects on one request

A request that arrives in one client format and is routed to an upstream speaking another is a
**cross-dialect** route. It is off out of the box, and streaming across a pair needs a second
opt-in on top of the first. Both accepted client formats reach all four of those upstream
dialects, and
every parameter a translation could not carry across is named on the row it produced.

Because that is a property of the pair of dialects rather than of routing policy, it is documented
here rather than under Routing.
[Cross-dialect translation](/docs/build/providers-cross-dialect) has the config shape,
[which client formats reach which upstreams](/docs/build/providers-dialect-matrix) has the grid
pair by pair, and [what a translation drops](/docs/build/providers-translation-loss) has the
inventory of what did not survive the crossing.

## The `dialect:` field, and when you still write it

An upstream configured with `provider:` derives its dialect from the descriptor, so you leave
`dialect:` out. Setting it anyway is legal as long as it agrees; a mismatch is a validation error
that names both values.

There is one situation today where writing it is not merely legal but necessary. Cross-dialect
translation reads the dialect from the upstream config rather than from the compiled descriptor,
and the descriptor path leaves that field empty. If you are enabling cross-dialect translation,
name both `provider:` and `dialect:` on the target upstream. The
[cross-dialect page](/docs/build/providers-cross-dialect) shows what happens when you do not, with
both captures.

<NextSteps items={[
  {title: "Which client formats reach which upstreams", href: "/docs/build/providers-dialect-matrix", description: "Which of these drivers the format your client already speaks can actually reach, pair by pair, streaming and not."},
  {title: "Cross-dialect translation", href: "/docs/build/providers-cross-dialect", description: "The opt-in that lets one dialect's request land on another's driver, and the config shape it needs today."},
  {title: "What a translation drops, and how you find out", href: "/docs/build/providers-translation-loss", description: "The parameters a driver cannot carry across from another dialect, and the field where the list of them is written down."},
  {title: "Provider reference", href: "/docs/build/providers-reference", description: "Which driver each descriptor rides, if you want the whole assignment rather than the shape of it."}
]} />
