Design system
Docs
Surfaces

Model picker

Choosing the model, reasoning level, and fast mode for a chat

HostImplementationNote
Primitivenone
Desktopconversation/model-picker.tsxModelPicker on Menu + Autocomplete
Terminalmodel-picker.tsModelPicker for the current session

The session's model is chosen per chat from the composer's model chip. Changes apply to the next message. Delegated tasks select their own exact model per call; they have no global model setting.

The composer chip: ModelPicker

File: packages/desktop/src/renderer/src/conversation/model-picker.tsx (memoized as ModelPicker), state helpers in model-picker-state.ts. From its header:

The composer's model chip. The menu holds the current model's parameters: Fast when the model offers it, Reasoning when it has more than one level, and the model itself in a searchable submenu grouped by provider. The host decides which models are listed (Settings › Models); the picker also keeps whatever the session already runs on.

Props

PropTypeDescription
catalogDesktopCatalog | undefinedProviders, models, defaults, subagentModel.
currentDesktopModelOption | undefinedThe session's model. Kept in the list even if unlisted.
thinkingLevelThinkingLevel | undefinedCoerced to a level the model supports via supportedThinkingLevel.
fastEnabledReadonlySet<string>"Setting ids whose current choice is on."
disabledboolean
onChange(change: ModelPickerChange) => void{ kind: "model", option, thinkingLevel }, { kind: "thinking", level }, or { kind: "fast", settingId, enabled }.

Anatomy

Menu label="Model" (controlled open, popupStyle=palette)
├─ trigger <button aria-label="Model: {name}[, {detail}]">  name · detail · chevron
├─ MenuSwitchItem  "Fast"            when current.fastMode.kind === "available"
├─ MenuSubmenu     "Reasoning" value={level}
│  └─ MenuRadioGroup › MenuRadioItem layout="plain" per level
├─ MenuSeparator                     when either parameter exists
├─ MenuSubmenu     "Model" value={name}
│  ├─ Autocomplete.Root
│  │  ├─ Autocomplete.Input aria-label="Search models"
│  │  ├─ MenuSeparator
│  │  └─ Autocomplete.List data-nyte-scrollport
│  │     └─ <div role="group" aria-label={provider}> › Autocomplete.Item (data-nyte-selected, check glyph)
│  ├─ MenuSeparator
│  └─ MenuItem "Manage models…" → /settings/models

Behavior that must be kept

  • The trigger's accessible name includes the current parameters ("Model: Claude Sonnet, High") so a screen reader hears the state without opening the menu.
  • A model is an Autocomplete.Item inside a submenu, "so its press must close the outer menu"; the picker controls open for that reason.
  • Search text clears on onOpenChangeComplete(false), after the exit transition, not on close, so the list does not reflow while fading.
  • The current model is rendered even when the host has unlisted it, and is scrolled into view on open (currentOptionRef).
  • Empty states name the fix: "Show some in Settings › Models." when nothing is listed, "Sign in or add an API key in Settings › Models." when no provider is connected.
  • Reasoning levels come from thinkingLevelsFor(current); the submenu is hidden when there is only one.

Where the change goes

ContextHandler
Follow-up composerSessionModelChip in composer.tsxuseConfigureSession / useApplyPluginSetting. "Shows the selected inputs for the next message, even while an older run is still executing."
New chatBlankConversation in screens/thread.tsx keeps a draft; "Freeze the displayed defaults with the first edit, not on every visit." On start: sessions.createsessions.configure → fast setting → outbox.submit.
Editing a messageUserMessageView footer picker (branchModel) → heads.move → configure → submit.

Subagent models

Subagent models are selected per task call, not in this picker. The parent receives the available models across authenticated providers and chooses an exact provider/model. Users can request a specific model in their prompt. There is no shared default or automatic substitution.

Keyboard

All from Base UI: the chip is a button (Enter/Space opens), arrows and typeahead in the menu, Arrow Right into the Model submenu where the search input has focus; typing filters; Arrow Down enters the list; Enter picks; Escape closes one level at a time. The settings select follows SettingsSelect.