Model picker
Choosing the model, reasoning level, and fast mode for a chat
| Host | Implementation | Note |
|---|---|---|
| Primitive | none | |
| Desktop | conversation/model-picker.tsx | ModelPicker on Menu + Autocomplete |
| Terminal | model-picker.ts | ModelPicker 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
| Prop | Type | Description |
|---|---|---|
catalog | DesktopCatalog | undefined | Providers, models, defaults, subagentModel. |
current | DesktopModelOption | undefined | The session's model. Kept in the list even if unlisted. |
thinkingLevel | ThinkingLevel | undefined | Coerced to a level the model supports via supportedThinkingLevel. |
fastEnabled | ReadonlySet<string> | "Setting ids whose current choice is on." |
disabled | boolean | |
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/modelsBehavior 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.Iteminside a submenu, "so its press must close the outer menu"; the picker controlsopenfor 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
| Context | Handler |
|---|---|
| Follow-up composer | SessionModelChip in composer.tsx → useConfigureSession / useApplyPluginSetting. "Shows the selected inputs for the next message, even while an older run is still executing." |
| New chat | BlankConversation in screens/thread.tsx keeps a draft; "Freeze the displayed defaults with the first edit, not on every visit." On start: sessions.create → sessions.configure → fast setting → outbox.submit. |
| Editing a message | UserMessageView 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.