On this page
- Why hard-coding by provider name fails
- Model identity must include connection context
- Use richer capability types than booleans
- Capability sources form a hierarchy
- Provider model metadata is useful but often incomplete
- Curated built-in knowledge can fill gaps
- Avoid guessing capabilities from model names alone
- Custom endpoints need an “unknown” state
- Runtime probing can help—but must be safe
- Prefer validation over speculative probing when possible
- Runtime errors can refine capability evidence
- User overrides are essential for custom servers
- Capabilities should drive controls, not only validation
- Disabled and hidden are different UX choices
- Capability dependencies matter
- Context window needs source and confidence
- Output limits should be separate from context limits
- Provider-native tools deserve explicit capabilities
- Capability caches need invalidation
- Stable built-in profiles and volatile discovery should be separate
- Precedence should be explicit
- Do not automatically trust compatible endpoints’ claims
- Model switching should trigger revalidation
- Test capability conflicts deliberately
- Capability UX checklist
- Where BYOKchat fits
- Further reading
A model selector tells you which model the user chose. It does not tell you what that model can do.
A multi-model AI client needs capability information for questions such as:
Can this model accept images?
Can it call tools?
Does it support reasoning controls?
Can it return structured output?
What context window should the app budget for?
Can it use provider-native search?
The difficult part is that there is rarely one perfect source of truth. Provider model-list APIs may expose only IDs. Documentation can lag model changes. Compatible endpoints can reuse familiar model names with different behavior. Custom local servers may provide no metadata at all.
A reliable client therefore treats capability detection as a layered evidence problem, not a single boolean lookup.
Why hard-coding by provider name fails
A weak UI often starts like this:
if provider == Anthropic:
show tools
if provider == Gemini:
show search
This breaks because capability usually belongs to the model, endpoint, or API version—not simply the provider brand.
Within one provider, different models can support different:
- input types;
- context sizes;
- output limits;
- reasoning modes;
- tool behavior;
- structured-output features;
- hosted tools.
A better question is:
What does this selected model on this selected connection support?
Model identity must include connection context
Do not assume a model ID is globally meaningful.
Two custom endpoints can both advertise:
model: llama-3.3
while one supports tools and the other does not.
Use an identity such as:
connection ID + model ID
and treat capability data as scoped to that identity.
For a provider-managed model, the provider connection may also imply an API family/version that affects supported behavior.
Use richer capability types than booleans
Some capabilities are not yes/no.
Instead of:
reasoning: Bool
use something closer to:
type ReasoningCapability =
| { kind: "none" }
| { kind: "fixed" }
| { kind: "effort", levels: string[] }
| { kind: "budget", min?: number, max?: number };
Similarly, attachment support can distinguish:
images
PDF/document files
audio
video
provider file IDs
inline bytes only
Structured output can distinguish:
none
JSON mode
JSON Schema constrained output
provider-specific subset
The UI becomes more accurate when the data model can express real differences.
Capability sources form a hierarchy
A practical client can combine several sources:
1. provider/API metadata
2. maintained built-in model knowledge
3. endpoint-specific discovery/probing
4. user overrides
5. runtime success/failure observations
Each source has strengths and weaknesses.
The important part is deciding which one wins when they disagree.
Provider model metadata is useful but often incomplete
A model-list endpoint may provide:
{
"id": "model-x",
"created": 123,
"owned_by": "provider"
}
That is enough for discovery but not for a rich UI.
Another provider may expose input/output modalities, context size, or feature flags.
Treat remote metadata as evidence. Do not assume absence means unsupported.
For example:
remote field missing
≠
capability false
It may simply mean the API does not publish that field.
Curated built-in knowledge can fill gaps
For well-known provider models, the client can ship maintained capability profiles.
Advantages:
- fast UI before network discovery;
- richer metadata than generic model-list APIs;
- known context/output limits;
- known reasoning/tool capabilities;
- controlled presentation names/categories.
The risk is staleness.
Do not present curated data as eternal truth. Version it, update it, and allow remote/provider evidence to supersede it where appropriate.
Avoid guessing capabilities from model names alone
A tempting heuristic is:
name contains "vision" → image support
name contains "reasoner" → reasoning
This is fragile.
Model names are marketing/identifier strings, not a formal capability protocol. Compatible endpoints can expose arbitrary names.
Name heuristics may be acceptable as a low-confidence hint in a developer tool, but should not silently enable sensitive or failure-prone features.
Custom endpoints need an “unknown” state
For a private OpenAI-compatible server, the app may know almost nothing initially.
Do not force every capability into:
true / false
Use:
supported
unsupported
unknown
or a confidence/source model.
Example:
type CapabilityValue<T> = {
value?: T;
state: "known" | "unknown";
source: CapabilitySource;
};
This allows the product to offer an explicit override rather than pretending uncertainty is “no.”
Runtime probing can help—but must be safe
Some capabilities can be tested by sending a small request.
For example:
Does this endpoint accept a tool definition?
But probing has costs:
- provider usage;
- latency;
- potential side effects if poorly designed;
- noisy errors;
- rate limits;
- model-specific behavior;
- changes over time.
Use probes only when they are cheap, non-mutating, and valuable.
Do not probe by executing real tools or sending user data.
Prefer validation over speculative probing when possible
If provider documentation or metadata clearly says a model does not support images, do not send an image just to see it fail.
A good hierarchy is:
known capability → validate locally
unknown capability → allow controlled test/override
runtime provider error → update diagnostics/evidence
Capability detection should reduce unnecessary errors, not generate them for every chat.
Runtime errors can refine capability evidence
Suppose a custom endpoint returns:
400: tools are not supported for this model
The client can record an endpoint/model-scoped observation:
connection X / model Y / tools = unsupported (runtime evidence)
Use caution:
- the error may be temporary;
- the request may be malformed for another reason;
- the server may upgrade later;
- one tool feature may not represent all tool behavior.
Runtime learning should be reversible and explainable.
User overrides are essential for custom servers
A power-user BYOK client may need a developer/advanced configuration such as:
Images: Auto / Supported / Unsupported
Tools: Auto / Supported / Unsupported
Reasoning: Auto / None / Effort / Budget
Context window: Auto / custom value
The user knows more about their private server than the client sometimes can.
Overrides should be scoped to the connection/model and easy to reset to automatic detection.
Capabilities should drive controls, not only validation
A capability system is most useful when it shapes the UI.
Examples:
no reasoning support → hide/disable reasoning control
no tools → do not expose tool selection for that model
no images → explain before user sends image
unknown context window → use conservative/explicit budgeting
no structured output → do not offer schema mode
This prevents “configuration that looks valid until the provider rejects it.”
Disabled and hidden are different UX choices
If a user switches from a tool-capable model to a model without tools, should the enabled tool configuration disappear?
Usually the underlying chat setting should remain, while the current generation marks it unavailable.
For example:
chat has tools enabled
selected model cannot use tools
→ show tools as unavailable for this model
→ preserve selection for later model switch
Do not destructively rewrite chat preferences merely because one model lacks a feature.
Capability dependencies matter
Some features depend on several capabilities at once.
For example, a workflow may require:
streaming + tools + image input
Validate the complete request against the selected model.
A model can support each concept in some contexts but not in the exact combination you are asking for.
This is why capability detection is not a replacement for provider-side request validation. It is an early guardrail.
Context window needs source and confidence
Context limits are especially important because incorrect values can create failed requests or overly aggressive truncation.
A capability record can store:
value: 128000
source: provider_docs
last_updated: ...
For unknown custom models, use conservative behavior or ask the user to configure a value rather than inventing one.
See What Is an AI Context Window?.
Output limits should be separate from context limits
Do not infer:
context window = maximum output
They are different constraints.
A model can have a large context but a much smaller maximum generated output.
Capability profiles should store them separately when known.
Provider-native tools deserve explicit capabilities
A model may support generic function/tool calling but not a provider-hosted search tool—or vice versa.
Represent these separately:
generic tool calling
web search
URL context
file search
code execution
computer use
Do not let one tools: true flag imply every hosted tool works.
Capability caches need invalidation
Remote model lists and runtime observations change.
A cache policy can include:
fetched_at
provider connection revision
model metadata version
manual refresh
TTL where appropriate
Do not fetch the full model catalog before every message.
But also do not cache forever after a provider adds a new feature.
Stable built-in profiles and volatile discovery should be separate
Useful architecture:
Each layer remains inspectable. A developer tool can answer:
Why does the app think this model supports tools?
That is much better than one mysterious boolean.
Precedence should be explicit
One possible policy is:
explicit user override
> authoritative provider metadata
> maintained built-in profile
> trusted runtime evidence
> unknown
Your exact precedence may differ.
Document it in code and tests. Otherwise capability bugs become difficult to reproduce.
Do not automatically trust compatible endpoints’ claims
A custom endpoint may advertise metadata that is wrong or incomplete.
Likewise, a provider can change behavior faster than a client update.
The final authority is still the actual API response.
Capability detection improves UX. It does not make runtime error handling unnecessary.
Model switching should trigger revalidation
When the selected model changes:
re-resolve capabilities
→ re-evaluate attachments/tools/reasoning/settings
→ recalculate context budget
→ update UI
Do not reuse the previous model’s capability snapshot.
This is especially important when switching across provider connections.
Test capability conflicts deliberately
Useful cases include:
- built-in says tools supported, remote says unsupported;
- remote omits the field;
- user override forces support;
- runtime error contradicts cached data;
- context window unknown;
- model ID identical on two different connections;
- provider changes model list after refresh;
- custom endpoint goes offline;
- capability registry has a stale entry;
- a model supports tools but not structured output.
The resolver should behave deterministically.
Capability UX checklist
Before exposing capability-aware controls, verify that:
- capabilities are scoped to connection + model;
- unknown is distinct from false;
- capability types can express more than booleans where needed;
- remote metadata absence does not automatically mean unsupported;
- built-in profiles are versioned/maintained;
- name heuristics are never treated as strong truth;
- custom endpoints can use explicit overrides;
- runtime evidence is reversible;
- model switching revalidates current chat settings;
- unsupported features do not destroy saved chat preferences;
- context/output limits are separate;
- provider-hosted tools have distinct capabilities;
- diagnostics can explain the source of resolved values.
Where BYOKchat fits
A multi-provider BYOK client needs capability resolution because it supports fixed provider catalogs, custom OpenAI-compatible endpoints, and private-network servers where metadata quality varies widely. The UI can remain clean for known models while advanced users can override incomplete information for custom connections.
The key design principle is simple: capability data should make uncertainty explicit instead of pretending every model with a familiar name behaves the same.