Merge pull request #53335 from NousResearch/bb/desktop-custom-model-blank-selector

fix(desktop): show custom (non-curated) model in Settings model pickers
This commit is contained in:
brooklyn! 2026-06-26 18:59:43 -05:00 committed by GitHub
commit 7a38d64a85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 4 deletions

View file

@ -78,6 +78,12 @@ const AUX_TASKS: readonly AuxTaskMeta[] = [
const NO_PROVIDERS: readonly ModelOptionProvider[] = [{ name: '—', slug: '', models: [] }]
// Radix <Select> renders a blank trigger when `value` matches no <SelectItem>.
// A custom model (e.g. one added via config that isn't in the provider's
// curated list) would vanish — surface the active value so it stays selectable.
export const withActive = (models: readonly string[], active: string): readonly string[] =>
active && !models.includes(active) ? [active, ...models] : models
interface StaleAuxWarningProps {
applying: boolean
onReset: () => void
@ -555,7 +561,7 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
<SelectValue placeholder={m.model} />
</SelectTrigger>
<SelectContent>
{(selectedProviderModels.length ? selectedProviderModels : []).map(model => (
{withActive(selectedProviderModels, selectedModel).map(model => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
@ -708,7 +714,7 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
<SelectValue placeholder={m.model} />
</SelectTrigger>
<SelectContent>
{(auxDraftProviderModels.length ? auxDraftProviderModels : []).map(model => (
{withActive(auxDraftProviderModels, auxDraft.model).map(model => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
@ -880,7 +886,7 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
<SelectValue placeholder={m.model} />
</SelectTrigger>
<SelectContent>
{modelsForProvider(slot.provider).map(model => (
{withActive(modelsForProvider(slot.provider), slot.model).map(model => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
@ -957,7 +963,10 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
<SelectValue placeholder={m.model} />
</SelectTrigger>
<SelectContent>
{modelsForProvider(currentMoaPreset.aggregator.provider).map(model => (
{withActive(
modelsForProvider(currentMoaPreset.aggregator.provider),
currentMoaPreset.aggregator.model
).map(model => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>

View file

@ -0,0 +1,34 @@
import { describe, expect, it } from 'vitest'
import { withActive } from './model-settings'
// A Radix <Select> shows a blank trigger when its `value` matches no
// <SelectItem>. `withActive` guarantees the controlled value is always
// representable so a config-only / custom model never renders blank.
describe('withActive', () => {
const curated = ['hermes-4', 'hermes-4-mini']
it('prepends a custom model missing from the curated list', () => {
expect(withActive(curated, 'anthropic/claude-opus-4.7')).toEqual([
'anthropic/claude-opus-4.7',
...curated
])
})
it('leaves the list untouched when the active model is already curated', () => {
expect(withActive(curated, 'hermes-4')).toEqual(curated)
})
it('does not inject an empty active value', () => {
expect(withActive(curated, '')).toEqual(curated)
})
it('surfaces the active model even when the curated list is empty', () => {
expect(withActive([], 'anthropic/claude-opus-4.7')).toEqual(['anthropic/claude-opus-4.7'])
})
it('keeps the active model selectable as the invariant', () => {
const out = withActive(curated, 'custom/model')
expect(out).toContain('custom/model')
})
})