Check what a model supports¶
Models within one vendor disagree. claude-sonnet-4-5 accepts temperature and
refuses effort; claude-opus-5 does the reverse. Support for sampling on
OpenAI goes away at gpt-5, returns for gpt-5.1 through gpt-5.4, goes away
at gpt-5.5 (but not gpt-5.5-pro) and is absent across all three
gpt-5.6 variants.
There is no rule there to infer from a version number, so the module measures each model instead and lets you ask.
Ask about a provider and model¶
info := chat.CapabilitiesFor(chat.ProviderOpenAI, "gpt-5.4")
switch info.Capabilities.Support(chat.CapSampling) {
case chat.SupportYes:
// safe to set Config.Temperature
case chat.SupportNo:
// it would be dropped at construction; use Config.Effort instead
case chat.SupportUnknown:
// too new to have been measured; try it and handle the request error
}
Pass an empty model to ask about the provider's default.
Ask about a client you already have¶
This reports on the model the client will actually use (the configured one, or the default it fell back to) which is the question you usually mean.
Three values, not two¶
Support is deliberately three-valued, and there is no Supports() bool.
| Value | Meaning |
|---|---|
SupportYes |
the model accepts it |
SupportNo |
it will be dropped at construction |
SupportUnknown |
nobody could say |
Unknown is the common case, not an edge case: two of the five providers expose
no capability data at all, and any model newer than the last generated table
reports it. Collapsing it into a bool has to pick a reading, and either choice is
wrong about half the time. Read as unsupported, claude-local looks
featureless; read as supported, you promise things that will fail.
Unknown means proceed, not stop
The module treats Unknown as "carry on and let the provider answer". A
stale table therefore costs you nothing: only a confident SupportNo
changes behaviour.
The specifics, where a provider gives them¶
"Caching is supported" is not actionable. "Implicit caching engages above 1,024
tokens" tells you whether restructuring a prompt is worth it. The pointer fields
carry that detail, and are nil when the provider said nothing:
if c := info.Capabilities.Caching; c != nil {
fmt.Println("implicit caching from", c.ImplicitMinTokens, "tokens")
fmt.Println("retention options:", c.TTLs)
}
if s := info.Capabilities.Sampling; s != nil {
fmt.Printf("temperature range %.1fâ%.1f\n", s.MinTemperature, s.MaxTemperature)
}
if info.Limits != nil {
fmt.Println("max input tokens:", info.Limits.MaxInputTokens)
}
Nil is distinct from a provider that answered with zeroes, so always check before dereferencing.
Where the answers come from¶
Each provider module ships a generated table, and the three vendors disagree completely about what they will tell you:
| Module | Source | What the vendor reports |
|---|---|---|
chat-anthropic |
API and probe | effort, structured outputs, image/PDF (but nothing about temperature) |
chat-openai |
probe only | no capability data at any API version |
chat-gemini |
API | limits, maxTemperature, topP, thinking, cache |
Where a vendor is silent, the generator establishes the answer by sending a real request, because that is the same path a real call takes. A weekly job watches for models the tables do not yet reflect and raises an issue against the provider module rather than changing anything itself.
Related¶
- Control sampling & reasoning effort: what to do when a model refuses one.
- Handle a partially-applied configuration: what
chat.Newreturns when it drops a setting. - Spec 0006: why the vocabulary is closed and support is three-valued.