fix(clients): show model provider prefixes in model subtitles - #17
Conversation
Model slugs with nested slash or dot qualifiers (e.g. loem/azure.glm-5.3-Flash) showed no provider info or only subProvider. Add a shared getModelProviderLabel helper that derives the qualifier from the slug and use it in web model rows and mobile model options.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14f0405195
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🟡 Changes recommended
Provider-label inference misses valid qualified model slugs and separator differences.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates web and mobile model subtitles to display provider prefixes inferred from model slugs.
Changes:
- Added shared provider-label inference and tests.
- Applied provider labels to web and mobile model pickers.
- Added coverage for qualified and nested providers.
File summaries
| File | Description |
|---|---|
packages/shared/src/model.ts |
Adds provider-label inference. |
packages/shared/src/model.test.ts |
Tests provider-label behavior. |
apps/web/src/components/chat/ModelListRow.tsx |
Displays derived labels on web. |
apps/mobile/src/lib/modelOptions.ts |
Displays derived labels on mobile. |
apps/mobile/src/lib/modelOptions.test.ts |
Tests mobile subtitles. |
Review details
Suppressed comments (1)
packages/shared/src/model.ts:248
- This only recognizes a dot qualifier when the text after the dot exactly matches
nameorshortName, so valid qualified Codex entries such asopenai.gpt-5.6-lunawith the friendly nameLuna(seeapps/server/src/provider/Layers/CodexProvider.test.ts:133-138) return no provider label at all. The new web/mobile subtitles will therefore still be blank for this real model shape; infer the provider prefix from the qualified slug using an explicit provider rule/metadata, and add a regression test for theLunacase.
const inferDotQualifier = (value: string): string | undefined => {
const lowerValue = value.toLowerCase();
for (const name of names) {
const suffix = `.${name.toLowerCase()}`;
if (lowerValue.endsWith(suffix)) {
return value.slice(0, -suffix.length) || undefined;
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Match name suffixes by stripping separators and casing so variants like `minimax-m2` vs `Minimax M2` still strip the qualifier - Split at the first dot instead of requiring an exact string suffix
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06ce965647
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🔵 Needs a closer look
Duplicate provider detection should normalize punctuation and casing before approval.
Review details
Suppressed comments (1)
packages/shared/src/model.ts:270
- The duplicate check only uses
localeCompare, so it does not treat slug punctuation as equivalent to the friendly provider name. For a dot-qualified model such asgithub-copilot.claude-fable-5withsubProvider: "GitHub Copilot", this returnsGitHub Copilot · github-copiloteven though the qualifier is already represented. Use the existingnormalizeNamecomparison here so hyphens, spaces, and case are handled consistently.
qualifier && subProvider?.localeCompare(qualifier, undefined, { sensitivity: "accent" }) === 0
? undefined
: qualifier;
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
Three moderate provider-label inference issues remain unresolved.
Review details
Suppressed comments (3)
packages/shared/src/model.ts:263
- This unconditionally drops the first slash segment whenever
subProvideris present, even if that segment is not the provider represented by the display name. OpenCode builds the slug fromprovider.idandsubProviderfromprovider.nameindependently (apps/server/src/provider/Layers/OpenCodeProvider.ts:272-275), so a configured provider whose ID and name differ can lose part of its route (for example,loem/upstream/modelwithsubProvider: "Azure"becomesAzure · upstream, hidingloem). Only remove the segment when its normalized value matchessubProvider(while still allowinggithub-copilot/GitHub Copilot), and otherwise retain it.
if (subProvider) {
slashParts.shift();
}
packages/shared/src/model.ts:264
- This treats every slash component before the final component as a provider qualifier whenever
subProvideris present. OpenCode constructs slugs as${provider.id}/${model.id}, butmodel.iditself may contain slashes (for exampleopenrouter/qwen/qwen3-coder, as covered byapps/server/src/provider/opencodeRuntime.cliParsers.test.ts:139-155), so this helper will renderOpenRouter · qweneven thoughqwenis part of the model ID. Use the knownsubProviderboundary and avoid inferring additional slash qualifiers from the remainder (or carry explicit qualifier metadata), and add a regression case for slash-containing model IDs.
if (subProvider) {
slashParts.shift();
}
const qualifiers = slashParts.filter(Boolean);
packages/shared/src/model.ts:255
- This loop scans every dot, so a normal versioned slug such as
gpt-5.6-solwith nameSolmatches the final.soland returnsgpt-5.6as a provider. The added test expectsundefined, so the shared helper currently fails its own regression test and would show version text as a provider subtitle; only consider the first dot as the provider/model boundary (while retaining the explicit Codex case above).
for (let index = value.indexOf("."); index > 0; index = value.indexOf(".", index + 1)) {
const suffix = normalizeName(value.slice(index + 1));
if (
suffix &&
(suffix === normalizeName(model.name) ||
(model.shortName && suffix === normalizeName(model.shortName)))
) {
return value.slice(0, index);
}
}
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
[from Codex]: Addressed the remaining applicable Copilot duplicate-label finding in e07701d: provider-name deduplication now normalizes spaces, hyphens, underscores, and case. The new GitHub Copilot regression failed before the fix and passes afterward. The three suppressed findings on 3cb0c36 do not warrant changes:
Known qualified Codex IDs with shortened friendly names and searching the visible web provider qualifiers were addressed in 3cb0c36. |
There was a problem hiding this comment.
🔵 Needs a closer look
Preserve model IDs containing slash segments when deriving provider qualifiers.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
packages/shared/src/model.ts:264
- OpenCode constructs slugs as
${provider.id}/${model.id}, andmodel.idcan itself contain/(for exampleopenrouter/qwen/qwen3-coder). WhensubProvideris present, this removes only the first segment and then treats every remaining segment as a provider, so the model is shown asOpenRouter · qwenandqwenbecomes searchable as a provider qualifier. Preserve the provider/model-id boundary (or pass an explicit qualifier from the adapter) instead of treating every pre-final slash segment as a provider prefix.
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
What Changed
Model subtitles now include the provider qualifier from the model slug (slash prefixes like
anthropic/, nested prefixes likeloem/azure., and dot qualifiers likeopenai.), instead of only showing the optionalsubProviderfield.getModelProviderLabeltopackages/shared/src/model.ts, which derives a provider label from the slug while avoiding duplication whensubProvideralready covers the qualifier.ModelListRow.tsx) and mobile (modelOptions.ts) model subtitles now use this helper.packages/shared/src/model.test.tsandapps/mobile/src/lib/modelOptions.test.ts.Why
Models served through gateways or nested providers (e.g.
loem/azure.glm-5.3-Flash) previously rendered with an empty or misleading subtitle, sincesubProvideralone doesn't capture the slug's provider prefix. This surfaces the full provider path consistently across web and mobile without duplicating information already shown.UI Changes
Model picker subtitles in web and mobile now show provider prefixes such as
Loem · azurewhere they previously showed onlyLoemor nothing. Screenshots to be attached.Checklist