# Model picker (/cloud/surfaces/model-picker)



<HostMatrix
  primitive="none"
  desktop="{
  label: &#x22;conversation/model-picker.tsx&#x22;,
  href: &#x22;/cloud/desktop/menu&#x22;,
  note: &#x22;ModelPicker on Menu + Autocomplete&#x22;,
}"
  terminal="{
  label: &#x22;model-picker.ts&#x22;,
  note: &#x22;ModelPicker for the current session&#x22;,
}"
/>

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` [#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 [#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 [#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 [#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 [#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]

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 [#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](/cloud/desktop/settings-controls#settingsselectt-extends-string).
