On this page
- Start with hard capability filters
- Capability is not quality
- Build a model profile from facts first
- Model metadata can be incomplete
- Latency has several dimensions
- Compare distributions, not one lucky request
- Keep sample count visible
- Do not compare unlike request shapes blindly
- Cost belongs next to capability
- Context capacity needs practical interpretation
- Reliability should describe observed failures
- Tool reliability deserves its own evaluation
- Structured-output reliability is measurable
- User preference is a valid signal
- Pairwise comparison is more useful than global rankings
- Preserve identical parent context for fair comparison
- Provider-hosted state can complicate A/B comparisons
- Reasoning effort must be normalized carefully
- Compare model + provider target, not model name alone
- Local model comparisons need machine context
- Do not mix local API charge with compute cost
- Privacy can be a comparison dimension
- Routing policy can use requirements without claiming best-model truth
- Benchmark suites should be task-specific
- Use deterministic evaluators where possible
- Human preference still matters for open-ended output
- Model-as-judge evaluation needs caveats
- Keep benchmark prompts separate from user chats
- Version evaluation suites
- Model aliases can change underneath you
- Pricing can change independently of model quality
- Availability is part of practical usefulness
- A comparison table can stay honest
- If you do create a recommendation, explain it
- Do not automatically switch mid-chat for a minor metric difference
- Model comparison should integrate with branch history
- Cost comparison should include context shape
- Tool schema size can affect comparisons
- Observability can power personal recommendations
- A practical decision pipeline
- Useful invariants
- Test comparison UX
- Where BYOKchat fits
- Further reading
Once an AI client supports many models, users immediately ask:
Which one should I use?
A model picker that shows only names is not enough.
A model picker that invents one number such as:
Model A: 94/100
Model B: 89/100
Model C: 86/100
is usually worse.
There is no universal model ranking that remains correct across:
coding
writing
reasoning
image understanding
tool calling
long context
structured output
latency
cost
privacy
local execution
A useful AI client compares models across explicit dimensions and lets the user’s task determine which dimensions matter.
Start with hard capability filters
Before ranking anything, eliminate models that cannot perform the requested workflow.
Examples:
image input required
PDF/document input required
tool calling required
strict structured output required
very large context required
reasoning control required
provider-hosted search required
offline/local execution required
A model that fails a hard requirement should not appear as “slightly worse.”
It is incompatible with that request.
See Capability Detection in Multi-Model AI Apps.
Capability is not quality
Two models may both support tool calling.
That does not mean they call tools with equal reliability.
Capability means:
feature available
Quality means something more task-dependent:
how well the model performs when using it
Keep those concepts separate.
Build a model profile from facts first
A provider/model profile can include:
interface ModelProfile {
provider: string
modelId: string
contextWindow?: number
maxOutput?: number
modalities: Set<string>
supportsTools?: boolean
supportsStructuredOutput?: boolean
supportsReasoningControl?: boolean
local?: boolean
}
These are capability/metadata fields, not quality judgments.
Model metadata can be incomplete
Provider model-list endpoints often do not expose every capability a client needs.
A robust client may combine:
provider discovery
maintained capability catalog
runtime observations
user overrides for custom endpoints
Do not assume /models is a complete truth source.
See How AI Model Discovery APIs Work.
Latency has several dimensions
A single “speed” number hides important behavior.
Measure separately:
time to first event
time to first text token
generation throughput
total duration
queue wait
tool round duration
A model can have fast TTFT but slow generation.
Another can start slowly and then stream quickly.
See Measuring Time to First Token Correctly and AI Generation Speed Explained.
Compare distributions, not one lucky request
If one request takes 0.8 seconds and another takes 1.1 seconds, that is weak evidence.
Latency varies with:
network
provider load
prompt size
output length
connection reuse
region
account
model routing
Use recent distributions such as:
median TTFT
p90 TTFT
median tokens/sec
sample count
where you have enough samples.
Keep sample count visible
A UI can say:
Median TTFT: 0.9 s (42 requests)
instead of:
Speed: 91/100
The first is interpretable.
The second hides the evidence.
Do not compare unlike request shapes blindly
A coding request with 20k input tokens is not a fair latency comparison against a short greeting.
For observational analytics, segment by rough workload features such as:
input token bucket
output token bucket
tools enabled
attachments present
reasoning enabled
Do not collect prompt content just to create perfect benchmarks.
Cost belongs next to capability
A model may be excellent for one task but unnecessary for another.
Useful display fields include:
estimated input rate
estimated cached-input rate when relevant
estimated output rate
recent average cost per request for this user
Mark public pricing as estimate metadata, not exact billing.
See How to Estimate Per-Conversation AI Cost.
Context capacity needs practical interpretation
A large advertised context window does not mean every request should fill it.
The UI can distinguish:
maximum supported context
current conversation estimated context
remaining practical headroom
A model with smaller context may still be preferable for a short task.
See Context Window vs Output Limit vs Reasoning Tokens.
Reliability should describe observed failures
Useful metrics include:
completed request rate
transient failure rate
stream interruption rate
rate-limit events
recent sample count
Do not mix:
bad API key
unsupported feature
user cancellation
into provider reliability.
See How to Classify AI API Errors.
Tool reliability deserves its own evaluation
For tool-enabled workflows, evaluate:
selected correct tool
valid arguments
did not invent unavailable tool
stopped after sufficient result
handled tool error
respected application denial
A model that writes great prose but repeatedly produces invalid tool arguments may be a poor choice for an agentic workflow.
Structured-output reliability is measurable
For a schema-driven task, run a fixed test set and record:
schema-valid on first attempt
repair required
semantic validation passed
refusal/error
Do not collapse this into general “intelligence.”
See Structured AI Output Explained.
User preference is a valid signal
The user may simply prefer one model’s style.
A client can support:
favorite
preferred for coding
preferred for writing
preferred local model
without pretending that preference is a scientific benchmark.
Pairwise comparison is more useful than global rankings
A practical UX:
Generate with Model A
Generate same parent turn with Model B
View responses side by side
Choose preferred response
This compares models on the user’s real prompt and context.
Conversation branching makes this natural.
See How AI Chat Branching and Regeneration Should Work.
Preserve identical parent context for fair comparison
For a pairwise test, both branches should start from the same semantic parent context.
Do not compare:
Model A after 10-turn history
against:
Model B after a summarized 5-turn history
unless context strategy is itself part of the experiment.
Provider-hosted state can complicate A/B comparisons
If one model receives provider-native hidden state while another is reconstructed from portable messages, the inputs are not perfectly equivalent.
Record that distinction.
For fair cross-provider comparison, build both requests from the same provider-neutral conversation representation where possible.
See Stateful vs Stateless AI Conversations.
Reasoning effort must be normalized carefully
Providers can expose different controls:
low/medium/high
budget tokens
auto reasoning
no user control
Do not assume “high” means the same amount of work across providers.
Display native semantics accurately while keeping the UI understandable.
See Reasoning Effort Explained.
Compare model + provider target, not model name alone
A routed model can behave differently depending on:
provider
region
backend
quantization
host configuration
A local model ID and a cloud-hosted version of a similarly named model are not necessarily identical execution targets.
Represent the target as:
connection + model
for operational metrics.
Local model comparisons need machine context
Local performance depends on:
Mac/iPhone hardware
available memory
model quantization
context length
concurrency
thermal state
runtime
A local benchmark without machine metadata can be misleading.
For user-local analytics, showing results for “this device” is often enough.
See Local AI on Apple Silicon.
Do not mix local API charge with compute cost
A local model can show:
Provider API charge: none
while still using significant device resources.
Do not assign a fake dollar amount unless the product has a defensible compute-cost model.
Privacy can be a comparison dimension
For a task containing sensitive data, the most important distinction may be:
on-device/local
private network
cloud provider A
cloud provider B
That is not a quality score.
It is a data-boundary choice.
Routing policy can use requirements without claiming best-model truth
A router can say:
Need image + tools + 100k context
Candidates = A, B
User prefers lower cost
Select B
This is explainable.
A rule such as:
smart score says B = 92.7
is much harder to audit.
See Cloud Model vs Local Model Routing.
Benchmark suites should be task-specific
If you add built-in evaluation, define test groups such as:
JSON extraction
Swift code generation
long-document QA
tool selection
math reasoning
instruction following
Each group has its own scoring method.
Do not average them blindly into one number unless the weighting has a clear product purpose.
Use deterministic evaluators where possible
Good evaluation targets include:
JSON schema validity
exact field extraction
unit tests pass
expected tool selected
forbidden tool not selected
answer contains required citations
These reduce dependence on subjective model-as-judge scoring.
Human preference still matters for open-ended output
For writing quality or usefulness, user pairwise choices can be more meaningful than an automated score.
Store only what the user wants to store locally:
preferred branch A/B
optional task tag
Do not upload private prompts and responses to a central benchmark service by default.
Model-as-judge evaluation needs caveats
Using another model to score answers can be useful, but introduces:
judge bias
position bias
provider cost
prompt sensitivity
self-preference
non-determinism
Treat judge scores as one signal, not objective truth.
Keep benchmark prompts separate from user chats
A built-in evaluation suite should use explicit fixture data rather than silently harvesting private user conversations.
This makes results reproducible and avoids privacy surprises.
Version evaluation suites
If you change:
prompts
expected answers
test data
judge rubric
then scores from before and after are not directly comparable.
Store:
evaluationSuiteVersion
with results.
Model aliases can change underneath you
Provider aliases such as a “latest” name can point to newer versions over time.
For serious comparisons, store the exact model identifier returned/used when possible.
If the provider does not expose a pinned revision, note that limitation.
Pricing can change independently of model quality
A model comparison view should load current pricing separately from historical benchmark results.
Do not invalidate old quality measurements merely because price changed.
Likewise, do not rewrite historical cost estimates silently.
Availability is part of practical usefulness
A model that is excellent but frequently unavailable for the user’s connection may be a poor default.
Use recent client-observed reliability with sample counts.
Do not claim a global outage rate from one user’s local data.
A comparison table can stay honest
Example:
| Dimension | Model A | Model B |
|---|---|---|
| Image input | Yes | No |
| Tools | Yes | Yes |
| Context | 128k | 64k |
| Median TTFT | 0.9 s / 42 samples | 0.6 s / 37 samples |
| Recent completion | 98% / 50 requests | 96% / 50 requests |
| Est. API cost | Higher | Lower |
| Local | No | Yes |
This is far more useful than one composite rating.
If you do create a recommendation, explain it
For example:
Recommended for this chat because:
- image input is required
- current context is 74k tokens
- tools are enabled
- user prefers lower estimated cost among compatible models
That gives the user control.
Do not automatically switch mid-chat for a minor metric difference
A routing engine that changes models every turn because one had 100 ms better recent TTFT creates inconsistent behavior and provider-state complications.
Prefer stable defaults and switch only for meaningful policy/capability reasons.
Model comparison should integrate with branch history
If the user compares two responses, preserve both branches and generation metadata.
The chosen answer can become the selected path while the alternative remains available.
This turns model comparison into normal conversation history rather than a separate benchmark artifact.
Cost comparison should include context shape
A cheap-output model may become expensive for a huge repeated context.
For the current chat, estimate:
current input context
cached portions if provider-reported/predictable
expected output range
rather than comparing price cards in isolation.
Tool schema size can affect comparisons
A chat with many tools includes additional request context.
Models may differ in how reliably they handle large tool sets.
Test the actual enabled tool configuration when agent workflows matter.
Observability can power personal recommendations
A local client can learn:
user chooses Model A for coding
Model B has lower TTFT on this connection
Model C often hits context limits for this project
without sending prompts to a central analytics service.
Personalization can remain local.
See Privacy-Preserving Analytics for AI Apps.
A practical decision pipeline
Useful invariants
hard capability mismatch eliminates a model before ranking
metrics identify connection + model, not only a display name
sample counts accompany observational latency/reliability
pricing is estimate metadata, not quality
user preferences are not presented as global benchmarks
private conversation content is not required for telemetry
model comparison branches share the same parent context
Test comparison UX
Important cases:
model lacks required image capability
same model across two provider connections
local and cloud candidates
one candidate has no pricing metadata
one candidate has only 2 latency samples
model alias changes
reasoning controls differ
pairwise branch comparison
provider state cannot be ported exactly
selected model becomes unavailable
privacy policy eliminates cloud options
Where BYOKchat fits
A multi-provider client is in a strong position to make model comparison useful because it already sees capabilities, provider/model identity, context size, tokens, estimated cost, timing, reliability, and tool usage for the user’s own requests.
The best product behavior is to expose those dimensions and preserve user choice, not to hide them behind one opaque “best model” score.