On this page
- Start with the user’s actual decision
- Separate model selection from generation settings
- A capability map should drive the UI
- Unsupported and default are different states
- Inherit is another distinct state
- Reasoning effort is not universal
- Avoid a fake cross-provider reasoning percentage
- “High” does not guarantee a better answer
- Model presets can simplify ordinary use
- Keep model identity visible when presets change it
- Sampling controls are not always applicable
- Temperature labels need modest claims
- Avoid exposing temperature and top-p as competing beginner controls
- Output limit needs correct language
- Context window should usually not be a user slider
- Show current context pressure instead
- Tool controls belong to both model capability and app policy
- Tool choice controls can be provider-specific
- Parallel tool control should not imply unsafe concurrency
- Structured output is more than “JSON mode”
- Provider-native settings should be namespaced
- Saved settings need capability revalidation
- Effective settings should be inspectable
- Configuration precedence should be deterministic
- Model switching should preview incompatible settings
- Do not silently migrate settings between semantically different controls
- Conversation history should preserve effective settings
- Regenerate should default to the same effective settings
- Per-message model switching needs clear scope
- Pricing and latency can inform controls without taking over
- Advanced controls should not expose invalid combinations
- Validation should happen before network send
- Custom OpenAI-compatible endpoints need uncertainty states
- Capability probes should be bounded
- Local models need runtime-aware controls
- Settings UI should remain stable while streaming
- Cancellation should not mutate settings history
- Reasoning display and reasoning control are separate
- Reasoning privacy deserves clear labels
- Presets should be versioned
- Use sensible progressive disclosure
- Explain disabled states locally
- Avoid dynamic UI jumping while capability metadata loads
- Analytics can reveal confusing controls without recording prompts
- A request configuration pipeline
- Useful invariants
- Test the control layer
- Where BYOKchat fits
- Further reading
AI clients often start with one model picker.
Then the settings panel grows:
model
reasoning effort
temperature
top-p
max output tokens
structured output
tool choice
parallel tools
thinking budget
response style
The obvious implementation is to show every control for every provider.
That produces a confusing UI full of:
disabled sliders
ignored values
provider-specific jargon
settings that silently do nothing
The better design is capability-driven: show controls only when they are meaningful for the selected model, keep provider-native semantics intact, and make inheritance/defaults explicit.
Start with the user’s actual decision
Most users are trying to answer one of these questions:
Which model should answer?
How much reasoning should it use?
Should it be more creative or deterministic?
How long may it answer?
May it use tools?
Design around those decisions rather than around raw JSON request fields.
Separate model selection from generation settings
Model selection chooses the execution target:
connection + model
Generation settings influence one request or conversation:
reasoning effort
sampling
output limit
structured output
tool behavior
Keep them separate internally even if they share one UI sheet.
A capability map should drive the UI
Conceptually:
interface GenerationCapabilities {
reasoningEffort: "none" | "levels" | "budget" | "provider_specific"
temperature: boolean
topP: boolean
maxOutput: boolean
tools: boolean
structuredOutput: boolean
parallelTools: boolean
}
The exact fields can evolve.
The important rule is:
UI state derives from selected target capability
not:
show everything and hope provider ignores unsupported fields
See Capability Detection in Multi-Model AI Apps.
Unsupported and default are different states
These are not equivalent:
Reasoning: Default
and:
Reasoning: Not supported
“Default” means the provider/model accepts the concept but the app is not overriding it.
“Not supported” means the control should not be sent at all.
Inherit is another distinct state
Inside projects/conversations, a setting may be:
inherit project default
which differs from:
provider default
A clean model can represent:
inherit
provider_default
explicit(value)
unsupported
without overloading null for every meaning.
Reasoning effort is not universal
Different providers/models may expose:
low / medium / high
multiple named effort levels
numeric token budget
automatic reasoning
no user-adjustable reasoning control
Do not pretend all of these are the same knob.
A provider-neutral UI can offer a small semantic layer where mapping is honest, but retain native detail when needed.
See Reasoning Effort Explained.
Avoid a fake cross-provider reasoning percentage
A slider:
Reasoning: 0% ---------------- 100%
suggests comparable effort across providers.
That is rarely justified.
Prefer native labels or clearly documented semantic presets.
“High” does not guarantee a better answer
Reasoning effort can trade off:
latency
cost
output behavior
and may not improve every simple task.
UI copy should avoid promises such as:
High = best quality
A better label is:
More reasoning for harder tasks; may take longer and cost more.
Model presets can simplify ordinary use
Instead of exposing ten advanced controls by default, the app can offer:
Fast
Balanced
Deep reasoning
only if those presets map transparently to supported settings.
Advanced users can expand details.
Do not make presets silently switch providers unless that behavior is explicit.
Keep model identity visible when presets change it
If “Fast” chooses a different model, display that target.
For example:
Fast — Model X
Deep — Model Y
This is better than one opaque mode that changes billing and data destination invisibly.
Sampling controls are not always applicable
Some reasoning models/providers restrict or ignore traditional sampling parameters.
If temperature is unsupported:
hide it or show Not supported
Do not send a stale saved value and hope it is ignored.
See How to Detect API Capability Mismatches Before Sending.
Temperature labels need modest claims
Common explanations like:
0 = accurate
1 = creative
are oversimplified.
Temperature changes sampling randomness, not truthfulness directly.
A safer UI explanation:
Lower values generally make token selection less varied; higher values can increase variation when the model supports this control.
Avoid exposing temperature and top-p as competing beginner controls
For many users, presenting both creates confusion.
A product can:
show temperature normally
put top-p in Advanced
or expose only provider-recommended controls.
If both are shown, explain that adjusting both simultaneously can make behavior harder to reason about.
Output limit needs correct language
A setting such as:
Max output tokens: 4096
is a limit, not a target length.
Do not label it:
Answer length: 4096 tokens
The model may finish much earlier.
See Context Window vs Output Limit vs Reasoning Tokens.
Context window should usually not be a user slider
The model’s maximum context is a capability.
The app’s context manager decides how much history/files/tool schema fit.
Advanced users may configure retention/budget strategy, but a raw slider labeled:
Context: 0-200k
can imply the app can magically create a larger model context.
Show current context pressure instead
Useful diagnostics:
Estimated context: 61k / 128k
Output reserve: 8k
Project files retrieved: 6 chunks
This helps users understand truncation/summarization decisions.
See How to Design Context Management for Long AI Conversations.
Tool controls belong to both model capability and app policy
A model may support tools, but the app decides which tools are exposed.
The settings layers are:
model supports tool calling
app/tool server provides tools
project/chat enables selected tools
permission policy governs execution
Do not collapse them into one “Tools: On” switch internally.
Tool choice controls can be provider-specific
Some APIs may allow concepts such as:
auto tool selection
no tools
force a specific tool
require some tool
Normalize only the semantics your client can represent safely.
Provider-specific modes can live in an advanced/native extension layer.
Parallel tool control should not imply unsafe concurrency
Even if a model can emit multiple tool calls in one turn, the app still determines whether calls are independent and safe to execute concurrently.
A raw parallel_tool_calls setting does not replace application concurrency policy.
See How to Execute Parallel AI Tool Calls Safely.
Structured output is more than “JSON mode”
A user may need:
valid JSON
schema-constrained JSON
application-validated semantic object
These are different guarantees.
If a model supports strict schema output, the UI can expose it in task-specific workflows rather than a mysterious global toggle.
See Structured AI Output Explained.
Provider-native settings should be namespaced
A provider adapter may support fields that have no cross-provider equivalent.
Keep them as:
provider-specific advanced settings
rather than polluting the global model with dozens of optional fields.
See How to Handle Provider-Specific Request Parameters.
Saved settings need capability revalidation
A user can save:
reasoning = high
then switch to a model that does not support reasoning control.
The app should not send the old value.
Possible UX:
Reasoning setting is unavailable for this model.
Keep the saved preference for when the user switches back if that is useful, but separate stored preference from effective request settings.
Effective settings should be inspectable
Before send, the application can derive:
selected connection
selected model
project defaults
conversation overrides
capability filter
provider adapter mapping
into one effective request configuration.
Developer diagnostics can show that result without exposing secrets.
Configuration precedence should be deterministic
For ordinary preference fields:
per-request override
> conversation override
> project default
> app default
> provider default
For hard security rules, use constraints rather than override precedence.
For example:
project cannot enable cloud fallback if app-wide privacy policy forbids it
See How to Build Reusable AI Projects and Workspaces.
Model switching should preview incompatible settings
When switching from Model A to Model B, the UI can summarize:
Reasoning effort: unavailable
Image input: supported
Tools: supported
Structured output: unavailable
This helps users understand why some controls disappear.
Do not silently migrate settings between semantically different controls
Suppose Provider A has:
reasoning effort = high
and Provider B has:
reasoning budget = 32k
Mapping high -> 32k may be a product heuristic, not a protocol truth.
If you implement it, document it as a client preset mapping and allow the provider-native value to remain inspectable.
Conversation history should preserve effective settings
An old assistant response should retain metadata such as:
model
reasoning setting actually sent
sampling settings actually sent
output limit
Changing current settings must not rewrite historical explanation.
See How AI Chat Branching and Regeneration Should Work.
Regenerate should default to the same effective settings
When a user taps Regenerate, the least surprising behavior is generally:
same parent context
same provider/model
same generation settings
new generation
If the user changed settings first, show that the regenerated branch uses the new configuration.
Per-message model switching needs clear scope
A model selector can apply to:
this next request only
this conversation from now on
project default
app default
Do not make users guess.
Use labels such as:
Use for this chat
Set as project default
Pricing and latency can inform controls without taking over
The model picker can show:
estimated pricing
recent TTFT
context size
capabilities
but should not silently change reasoning/model based on tiny metric differences.
See How to Compare AI Models Inside a Client App.
Advanced controls should not expose invalid combinations
If the provider rejects:
reasoning mode + temperature
or another combination, encode that constraint in capability validation.
Do not rely on trial-and-error API errors as the main UX.
Validation should happen before network send
The effective request builder should check:
model exists
required capability supported
setting values in valid range
forbidden setting combination absent
attachment/tool requirements satisfied
Then return an actionable local error.
Custom OpenAI-compatible endpoints need uncertainty states
For an unknown compatible server, capability metadata may be incomplete.
Use states such as:
supported
unsupported
unknown
For unknown, the UI can allow cautious attempts or user override rather than pretending certainty.
See OpenAI-Compatible Does Not Mean OpenAI-Identical.
Capability probes should be bounded
Do not fire expensive synthetic generations every time the model picker opens.
Prefer:
provider metadata
maintained catalog
cached runtime observations
explicit user test
Probing should not consume BYOK quota unexpectedly.
Local models need runtime-aware controls
A local model may support an option in principle but the current server/runtime does not expose it.
Capability belongs to the execution target:
connection + model + endpoint/runtime behavior
not merely to the model family name.
Settings UI should remain stable while streaming
If the user changes the model selector during an active stream, define whether that affects:
current generation
next generation only
Usually current generation settings should remain immutable; changes apply to the next request.
Cancellation should not mutate settings history
A cancelled generation still has an effective configuration record.
Preserve it for diagnostics/analytics even if the output is partial.
Reasoning display and reasoning control are separate
A provider may:
support reasoning effort but expose no reasoning text
expose reasoning summaries but no effort control
expose opaque reasoning state
Do not infer one from the other.
The UI should model:
reasoning configuration capability
reasoning output/display capability
separately.
Reasoning privacy deserves clear labels
If reasoning summaries or provider-exposed reasoning content are stored locally, treat them as conversation data.
If the provider does not expose hidden reasoning, the app cannot export or display it.
Avoid suggesting otherwise.
Presets should be versioned
If Balanced maps to:
model A + medium effort
today and changes next month, historical generations should still record the actual target/settings.
A preset name alone is not enough for reproducibility.
Use sensible progressive disclosure
A strong settings hierarchy can be:
Primary:
- Model
- Reasoning (when supported)
Advanced:
- Output limit
- Temperature/top-p when supported
- Provider-native options
Tool and project settings may live in separate areas to avoid one giant sheet.
Explain disabled states locally
If a control is visible but disabled, explain why:
Temperature is unavailable for this reasoning model.
not simply a grey slider.
Where possible, hiding unsupported controls is even cleaner.
Avoid dynamic UI jumping while capability metadata loads
When model metadata is still loading, use a stable loading/unknown state rather than rapidly showing then removing controls.
Cache capability profiles and update deliberately.
Analytics can reveal confusing controls without recording prompts
Privacy-safe events can measure:
model changed
reasoning setting changed
unsupported setting encountered
advanced controls opened
request rejected by local capability validation
No prompt/response content is required.
A request configuration pipeline
Hard privacy/authorization constraints should wrap this pipeline rather than being ordinary preferences.
Useful invariants
unsupported settings are never sent
provider default and unsupported are distinct states
historical generations retain actual effective settings
reasoning labels do not claim cross-provider equivalence
security/tool authorization is not controlled by prompt/generation settings
model switching revalidates attachments/tools/settings before send
Test the control layer
Important cases:
switch reasoning model -> non-reasoning model
switch cloud -> local compatible endpoint
saved temperature unsupported by new model
project default + conversation override
per-request reasoning override
regenerate with unchanged settings
regenerate after model switch
model capability metadata unknown
provider-native setting round trip
structured output incompatible
active stream while selector changes
model removed from provider catalog
Where BYOKchat fits
A multi-provider client needs a capability-aware control layer more than a single-provider app because the same visual setting can be valid for one model, invalid for another, or mean something different across APIs. The app can keep a small semantic settings model, let provider adapters map supported fields, and expose native options only when they add real value.
That keeps the chat UI powerful without turning it into a dump of every provider request parameter.