Skip to content
Tier

Model picker

Pro

Which model the next message uses, as a small menu for Composer's tools slot. It takes the same ModelOption list as ModelSettings: models are grouped by vendor with their relative cost, and one whose zero-retention terms haven't been attested stays in the list, says so, and can't be picked. The button shows the model the app says is in use, or "Choose a model" when there is none; nothing is chosen for the person. When they can't change it, locked says why.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0 and 2.8.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/model-picker

Brings in: bidi, dropdown-menu, know, locale, locked, tokens, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import * as React from "react"
import { Composer } from "@/components/candor/composer"
import { ModelPicker } from "@/components/candor/model-picker"
import type { ModelOption } from "@/lib/candor/know"

/** The same list ModelSettings takes: every model the app offers, attested or not. */
const models: ModelOption[] = [
  { id: "claude-haiku", vendor: "Anthropic", name: "Claude Haiku", cost: 1, zdr: "attested", note: "Quick lookups" },
  { id: "claude-sonnet", vendor: "Anthropic", name: "Claude Sonnet", cost: 2, zdr: "attested", note: "Most work" },
  { id: "claude-opus", vendor: "Anthropic", name: "Claude Opus", cost: 4, zdr: "attested", note: "Design and review" },
  { id: "gemini-pro", vendor: "Google", name: "Gemini Pro", cost: 3, zdr: "not-attested" },
]

export default function Example() {
  const [value, setValue] = React.useState("")
  const [model, setModel] = React.useState("claude-sonnet")
  return (
    <div className="flex max-w-2xl flex-col gap-6">
      <Composer value={value} onValueChange={setValue} onSubmit={() => setValue("")} tools={<ModelPicker models={models} value={model} onValueChange={setModel} />} />
      {/* When the person can't change it, the picker stays and says why. */}
      <Composer
        value=""
        onValueChange={() => {}}
        onSubmit={() => {}}
        tools={<ModelPicker models={models} value="claude-sonnet" onValueChange={() => {}} locked="Your workspace admin sets the model for this project." />}
      />
    </div>
  )
}

<ModelPicker>

PropTypeDefaultDescription
modelsrequiredModelOption[]—Every model the app offers, from any vendor: the same list ModelSettings takes.
valuestring | undefined—The id of the model in use, from your app. Without one, the button says "Choose a model".
onValueChangerequired(id: string) => void—Called with the id of the model the person picks. Never called for a model that isn't attested.
lockedstring | null | undefined—Why the person can't change the model, e.g. "Your workspace admin sets the model." The button stays, locked, and says it. `null` or left out: unlocked.
classNamestring | undefined—Extra classes for the outer element.

Strings

The fixed words, in English by default. Change any of them under modelPicker in <LocaleProvider strings={{ … }}>; the table is exported as modelPickerStrings.

KeyEnglish
labelModel: {name}
chooseChoose a model
notAttestedZero-retention terms not attested
notAvailableNot available until {vendor}’s zero-retention terms are attested.
cost{ one: "cost {count} of 4", other: "cost {count} of 4" }

Motion

MomentFamilyWhat it does
tinttintThe button's background changes when the pointer is over it; a locked one doesn't change.

Set with --cd-motion-model-picker-<moment>-* in your brand file; the families are in the design reference.

Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

ModelOption from @/lib/candor/know
{ id: string; vendor: ModelVendor; name: string; /** One line on what it's good for. */ note?: string | undefined; /** Relative cost, 1 (cheapest) to 4. */ cost: 1 | 2 | 3 | 4; /** The vendor's zero-retention contract has been attested by an operator. */ zdr: "attested" | "not-attested"; }
ModelVendor from @/lib/candor/know
The four vendors Candor lists first, in this order. Any other vendor ("Meta", "Mistral", "Local") is listed after them, by the name you give.
"Anthropic" | "Google" | "OpenAI" | "xAI" | (string & {})