On this page
- Model the request as requirements
- Model capabilities separately
- Validate connection, model, and endpoint independently
- Build a validation pipeline
- Give errors a remediation
- Do not silently drop requirements
- Distinguish preferences from requirements
- Validate input modality from actual content
- Tool capability has sub-capabilities
- Structured output has levels
- Reasoning capability has modes
- Parameter compatibility can depend on mode
- Endpoint family can change the capability surface
- Model discovery can seed capabilities
- Capability probing should be narrow
- Unknown should not always block
- Context checks are estimates, not absolute truth
- Keep stale metadata visible
- Fallback must re-run validation
- UI controls should react to selection changes
- Preserve user intent when temporarily disabled
- Test the validator as a pure function
- Keep server errors as feedback
- A capability mismatch is a product-state problem
- Where BYOKchat fits
- Further reading
Many AI API errors are predictable before a request ever reaches the network.
Examples:
image attached to a text-only model
tools enabled on a model without tool calling
JSON Schema requested on an endpoint that supports only plain text
reasoning control sent to a non-reasoning model
background execution combined with a no-storage mode that forbids it
provider-specific parameter sent through the wrong adapter
A capability-aware client can catch these locally and explain the problem precisely.
Model the request as requirements
Before encoding provider JSON, derive what the request actually needs.
For example:
interface RequestRequirements {
inputModalities: Set<Modality>;
outputModality: Modality;
streaming: boolean;
tools: boolean;
structuredOutput: "none" | "json" | "jsonSchema";
reasoning: boolean;
background: boolean;
endpointFamily: EndpointFamily;
}
This requirement object is easier to validate than a half-built provider request.
Model capabilities separately
The selected target should have a profile:
interface CapabilityProfile {
textInput: Support;
imageInput: Support;
audioInput: Support;
tools: Support;
jsonSchema: Support;
reasoning: Support;
streaming: Support;
background: Support;
}
type Support = "supported" | "unsupported" | "unknown";
unknown is important.
Lack of metadata is not proof of lack of support.
Validate connection, model, and endpoint independently
A request target is more than a model ID.
Think of it as:
connection
+ endpoint family
+ model
+ request mode
A model may support tools through one provider/API but not another.
A custom compatible server may expose Chat Completions but not Responses.
A router may offer the same model through endpoints with different supported parameters.
Capability checks need the whole target context.
Build a validation pipeline
A useful order is:
1. connection exists
2. credentials available
3. endpoint family supported
4. model selected/available
5. input modalities supported
6. requested output mode supported
7. tools/reasoning supported
8. parameter combinations valid
9. context/output budgets plausible
10. transport/network policy valid
Stop at the first actionable failure or collect all safe-to-report issues.
Give errors a remediation
Bad:
Unsupported capability
Better:
Gemini model X does not support the attached audio input through this endpoint.
Choose a compatible model or remove the audio attachment.
A validation error should contain:
{
code,
message,
affectedSetting,
suggestedActions
}
That lets the UI offer one-tap recovery where appropriate.
Do not silently drop requirements
Suppose the user requested strict JSON Schema output.
This is dangerous:
selected model lacks schema output
→ client silently removes schema
→ model returns prose
The request technically succeeds but violates the user’s contract.
For hard requirements, fail locally.
Only degrade when the user explicitly accepts degradation or the feature is genuinely optional.
Distinguish preferences from requirements
Some options are preferences:
reasoning effort = high
preferred provider = A
Others are requirements:
must accept image
must call tools
must return schema-constrained JSON
must remain inside a privacy routing policy
A router can relax preferences during fallback but should not violate requirements.
Represent the distinction in code.
Validate input modality from actual content
Do not rely only on a saved toggle.
Derive requirements from the turn:
text + 2 images + PDF
→ requires text input + image/document support
An old model selection may have been valid before the user attached a file.
Run validation again when content changes.
Tool capability has sub-capabilities
tools: true can be too coarse.
A client may care about:
- client-executed function tools;
- parallel tool calls;
- streamed tool arguments;
- provider-hosted tools;
- specific schema features;
- tool choice controls.
If your workflow requires parallel tool calls, plain “supports tools” is insufficient.
Model the capability you actually need.
Structured output has levels
Similarly:
valid JSON
JSON object mode
JSON Schema constrained
strict schema subset
are different guarantees.
Validate against the exact requirement rather than one structuredOutput boolean.
Reasoning capability has modes
Reasoning can involve:
provider-internal reasoning only
visible reasoning summary
reasoning content stream
reasoning effort control
opaque continuation state
A UI asking to render reasoning needs a different capability than a UI merely requesting a higher reasoning effort.
See Reasoning Effort Explained.
Parameter compatibility can depend on mode
A model may support both:
temperature
reasoning
but not simultaneously in the selected reasoning mode.
Capabilities therefore need conditional rules:
if request.reasoning == .enabled {
forbid(.temperature)
forbid(.topP)
}
This is a constraint system, not just a list of feature flags.
Endpoint family can change the capability surface
The same provider may expose:
Chat Completions
Responses
Messages
Interactions
generateContent
A feature can exist in one API family before another.
Store endpoint-family metadata explicitly instead of assuming provider-wide support.
Model discovery can seed capabilities
Provider model APIs can supply:
- IDs;
- modality information;
- context limits;
- supported parameters;
- lifecycle metadata.
Use that data when available.
But do not assume the catalog is complete. Merge it with curated metadata and explicit user overrides using clear precedence.
See How AI Model Discovery APIs Work.
Capability probing should be narrow
For custom endpoints, documentation may be unavailable.
A probe can answer a focused question:
Does /models exist?
Can this model stream?
Does this endpoint reject tools?
Avoid a huge startup test suite that sends dozens of billable requests.
Cache probe results with source and timestamp.
Unknown should not always block
If capability is unknown, policy depends on risk.
For a low-risk optional setting:
unknown streaming support
→ try request, handle failure
For a hard business contract:
unknown strict schema support
→ require confirmation or known-compatible target
Make the risk policy explicit.
Context checks are estimates, not absolute truth
If model metadata includes context limits, estimate request size before sending.
But token counts can vary by tokenizer/provider formatting.
A local preflight check should say:
likely exceeds limit
rather than pretending its estimate is provider-authoritative unless you used the provider’s actual token-count method.
Keep stale metadata visible
Capabilities can change.
Store:
source
last updated
confidence
A model catalog fetched three months ago should not be treated as eternal truth.
For custom/local endpoints, capability overrides may be tied to the specific connection rather than global model name.
Fallback must re-run validation
This is critical.
Suppose model A fails and routing selects model B.
Before sending to B, validate the original requirements again.
Do not assume fallback targets share capabilities.
UI controls should react to selection changes
When the user changes model/provider, recompute settings state.
For example:
old model: tools + images + reasoning
new model: text + tools only
The UI should immediately flag the incompatible image and reasoning settings.
Do not wait until Send.
Preserve user intent when temporarily disabled
If a user set reasoning to High and switches to a model without reasoning, you have choices:
- clear the setting;
- retain it as inactive;
- ask the user.
Retaining inactive intent can improve UX when switching back, but the request encoder must never accidentally send it to an unsupported model.
Test the validator as a pure function
Capability validation is ideal for table-driven tests.
Example:
model tools=true, request tools=true → valid
model tools=false, request tools=true → error
model image=unknown, request image=true → policy-dependent warning
reasoning=true + temperature forbidden → error
schema=json only + request=jsonSchema → error
Pure validation logic is easier to test than UI-driven API failures.
Keep server errors as feedback
Preflight validation will never be perfect.
When the provider returns a capability-related error:
- show the real failure;
- classify it;
- optionally update cached capability metadata;
- avoid automatically converting one transient error into a permanent capability rule.
One 500 response is not proof that a feature is unsupported.
A capability mismatch is a product-state problem
The best UX treats incompatibility as a state the user can resolve:
Attached image requires a multimodal model.
[Choose compatible model] [Remove image]
not as an opaque network failure after 20 seconds.
That makes multi-provider complexity understandable.
Where BYOKchat fits
A multi-provider client can compute request requirements from the conversation, attachments, reasoning settings, tools, and output format, then compare them with connection/model capabilities before sending. The provider adapter only receives requests that are internally coherent.
This reduces avoidable API errors and makes fallback, model switching, and custom endpoints much more predictable.