@assistant-ui/mcp-docs-server 0.1.33 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.docs/organized/code-examples/waterfall.md +7 -7
  2. package/.docs/organized/code-examples/with-a2a.md +8 -8
  3. package/.docs/organized/code-examples/with-ag-ui.md +12 -12
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
  5. package/.docs/organized/code-examples/with-artifacts.md +40 -34
  6. package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
  7. package/.docs/organized/code-examples/with-browser-extension.md +9 -9
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
  10. package/.docs/organized/code-examples/with-cloud.md +10 -10
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
  14. package/.docs/organized/code-examples/with-expo.md +66 -31
  15. package/.docs/organized/code-examples/with-external-store.md +8 -8
  16. package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
  17. package/.docs/organized/code-examples/with-generative-ui.md +98 -368
  18. package/.docs/organized/code-examples/with-google-adk.md +9 -9
  19. package/.docs/organized/code-examples/with-heat-graph.md +7 -7
  20. package/.docs/organized/code-examples/with-image-generation.md +10 -10
  21. package/.docs/organized/code-examples/with-interactables.md +10 -10
  22. package/.docs/organized/code-examples/with-langchain.md +10 -10
  23. package/.docs/organized/code-examples/with-langgraph.md +33 -29
  24. package/.docs/organized/code-examples/with-livekit.md +12 -12
  25. package/.docs/organized/code-examples/with-mcp.md +11 -11
  26. package/.docs/organized/code-examples/with-opencode.md +109 -583
  27. package/.docs/organized/code-examples/with-pi.md +2044 -0
  28. package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
  29. package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
  30. package/.docs/organized/code-examples/with-react-ink.md +309 -102
  31. package/.docs/organized/code-examples/with-react-router.md +14 -14
  32. package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
  33. package/.docs/organized/code-examples/with-store.md +70 -66
  34. package/.docs/organized/code-examples/with-tanstack.md +25 -11
  35. package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
  36. package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
  37. package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
  38. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
  39. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
  40. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
  41. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
  42. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
  43. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
  44. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
  45. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
  46. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
  47. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  48. package/.docs/raw/docs/guides/index.mdx +3 -0
  49. package/.docs/raw/docs/guides/input-history.mdx +55 -0
  50. package/.docs/raw/docs/guides/virtualization.mdx +63 -0
  51. package/.docs/raw/docs/ink/adapters.mdx +23 -1
  52. package/.docs/raw/docs/ink/hooks.mdx +22 -19
  53. package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
  54. package/.docs/raw/docs/react-native/hooks.mdx +26 -18
  55. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
  56. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
  57. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
  58. package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
  59. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
  60. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
  61. package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
  62. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
  63. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
  64. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
  65. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
  66. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
  67. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
  68. package/.docs/raw/docs/tools/backend.mdx +19 -11
  69. package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
  70. package/.docs/raw/docs/tools/index.mdx +7 -12
  71. package/.docs/raw/docs/tools/mcp.mdx +83 -15
  72. package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
  73. package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
  74. package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
  75. package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
  76. package/.docs/raw/docs/ui/mermaid.mdx +16 -9
  77. package/.docs/raw/docs/ui/model-selector.mdx +219 -52
  78. package/.docs/raw/docs/ui/number-roll.mdx +154 -0
  79. package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
  80. package/.docs/raw/docs/ui/reasoning.mdx +3 -3
  81. package/.docs/raw/docs/ui/streamdown.mdx +2 -0
  82. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
  83. package/.docs/raw/docs/ui/thread.mdx +52 -0
  84. package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
  85. package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
  86. package/dist/constants.js.map +1 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/prepare-docs/code-examples.js.map +1 -1
  89. package/dist/prepare-docs/copy-raw.js.map +1 -1
  90. package/dist/prepare-docs/prepare.js.map +1 -1
  91. package/dist/stdio.js.map +1 -1
  92. package/dist/tools/docs.js.map +1 -1
  93. package/dist/tools/examples.js.map +1 -1
  94. package/dist/tools/tests/test-setup.js.map +1 -1
  95. package/dist/utils/mdx.js.map +1 -1
  96. package/dist/utils/paths.js.map +1 -1
  97. package/package.json +4 -4
  98. /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
  99. /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
  100. /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
  101. /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
  102. /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
@@ -1,12 +1,12 @@
1
1
  ---
2
- title: ModelSelector
3
- description: Model picker with unified overlay positioning and runtime integration.
2
+ title: Model Selector
3
+ description: Composable model picker with reasoning effort levels, search, and runtime integration.
4
4
  platforms: ["react"]
5
5
  ---
6
6
 
7
7
  import { ModelSelectorSample } from "@/components/docs/samples/model-selector";
8
8
 
9
- A select component that lets users switch between AI models. Uses item-aligned positioning so the selected model overlays the trigger for a unified look. Integrates with assistant-ui's `ModelContext` system to automatically propagate the selected model to your backend.
9
+ A picker that lets users switch between AI models and choose a reasoning effort (thinking) level. It is built on Popover + Command, so search, provider grouping, and filtering compose in without being built in. The default export integrates with assistant-ui's `ModelContext` system, so the selection reaches your backend on every request with no extra wiring.
10
10
 
11
11
  <ModelSelectorSample />
12
12
 
@@ -24,9 +24,9 @@ A select component that lets users switch between AI models. Uses item-aligned p
24
24
 
25
25
  ### Use in your application
26
26
 
27
- Place the `ModelSelector` inside your thread component, typically in the composer area:
27
+ Place the `ModelSelector` inside your thread component, typically in the composer area. Each model needs an `id` and a display `name`; everything else is optional:
28
28
 
29
- ```tsx title="/components/assistant-ui/thread.tsx" {1,6-14}
29
+ ```tsx title="/components/assistant-ui/thread.tsx" {1,6-15}
30
30
  import { ModelSelector } from "@/components/assistant-ui/model-selector";
31
31
 
32
32
  const ComposerAction: FC = () => {
@@ -36,9 +36,10 @@ const ComposerAction: FC = () => {
36
36
  models={[
37
37
  { id: "gpt-5.4-nano", name: "GPT-5.4 Nano", description: "Fast and efficient" },
38
38
  { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", description: "Balanced performance" },
39
- { id: "gpt-5.5", name: "GPT-5.5", description: "Most capable" },
39
+ { id: "gpt-5.5", name: "GPT-5.5", description: "Most capable", efforts: true },
40
40
  ]}
41
41
  defaultValue="gpt-5.4-nano"
42
+ defaultEffort="medium"
42
43
  size="sm"
43
44
  />
44
45
  </div>
@@ -49,16 +50,22 @@ const ComposerAction: FC = () => {
49
50
  </Step>
50
51
  <Step>
51
52
 
52
- ### Read the model in your API route
53
+ ### Read the selection in your API route
53
54
 
54
- The selected model's `id` is sent as `config.modelName` in the request body:
55
+ The selected model's `id` arrives as `config.modelName`, and the effort level as `config.reasoningEffort`:
55
56
 
56
- ```tsx title="app/api/chat/route.ts" {2,5}
57
+ ```tsx title="app/api/chat/route.ts" {2,5-11}
57
58
  export async function POST(req: Request) {
58
59
  const { messages, config } = await req.json();
59
60
 
60
61
  const result = streamText({
61
62
  model: openai(config?.modelName ?? "gpt-5.4-nano"),
63
+ providerOptions: {
64
+ openai:
65
+ config?.reasoningEffort !== undefined
66
+ ? { reasoningEffort: config.reasoningEffort }
67
+ : {},
68
+ },
62
69
  messages: await convertToModelMessages(messages),
63
70
  });
64
71
 
@@ -66,77 +73,126 @@ export async function POST(req: Request) {
66
73
  }
67
74
  ```
68
75
 
76
+ `config.reasoningEffort` is only present when the selected model supports the chosen level, so the route only forwards it when it exists. See [How It Works](#how-it-works).
77
+
69
78
  </Step>
70
79
  </Steps>
71
80
 
72
- ## Variants
81
+ ## Reasoning Efforts
73
82
 
74
- Use the `variant` prop to change the trigger's visual style.
83
+ A model that declares `efforts` shows a "Thinking" row at the bottom of the popover. `efforts: true` enables the default Low / Medium / High levels; pass a list of `{ id, name }` objects to define your own:
75
84
 
76
85
  ```tsx
77
- <ModelSelector variant="outline" /> // Border (default)
78
- <ModelSelector variant="ghost" /> // No background
79
- <ModelSelector variant="muted" /> // Solid background
86
+ {
87
+ id: "gpt-5.5",
88
+ name: "GPT-5.5",
89
+ efforts: [
90
+ { id: "minimal", name: "Minimal" },
91
+ { id: "high", name: "High" },
92
+ ],
93
+ }
80
94
  ```
81
95
 
82
- | Variant | Description |
83
- | --------- | ---------------------------------------------- |
84
- | `outline` | Border with transparent background (default) |
85
- | `ghost` | No background, subtle hover |
86
- | `muted` | Solid secondary background |
96
+ Omit `efforts` for models without configurable reasoning. The row is hidden while such a model is selected.
87
97
 
88
- ## Sizes
98
+ ### Sticky Selection
89
99
 
90
- Use the `size` prop to control the trigger dimensions.
100
+ The effort selection survives model switches. Switching to a model that doesn't support the current level omits `reasoningEffort` from the request instead of resetting the user's choice, and the level applies again when the user switches back. The exported `resolveModelEffort` helper applies the same rule if you build your own runtime integration around `ModelSelector.Root`; see [`resolveModelEffort`](#resolvemodeleffort).
91
101
 
92
- ```tsx
93
- <ModelSelector size="sm" /> // Compact (h-8, text-xs)
94
- <ModelSelector size="default" /> // Standard (h-9)
95
- <ModelSelector size="lg" /> // Large (h-10)
96
- ```
97
-
98
- ## Model Options
102
+ ### Custom Effort UI
99
103
 
100
- Each model in the `models` array supports:
104
+ `ModelSelector.Effort` lays the levels out as horizontal segments, which overflows the popover width once a model has more than a few. For those cases, or for a different layout such as a slider or a sub-dropdown, build your own control with the `useModelSelectorEfforts` hook. It exposes the selected model's levels and the active selection:
101
105
 
102
106
  ```tsx
103
- const models = [
104
- {
105
- id: "gpt-5.4-nano", // Sent to backend as config.modelName
106
- name: "GPT-5.4 Nano", // Display name in trigger and items
107
- description: "Fast and efficient", // Optional subtitle in items only
108
- icon: <SparklesIcon />, // Optional icon (any ReactNode)
109
- },
110
- ];
107
+ import { useModelSelectorEfforts } from "@/components/assistant-ui/model-selector";
108
+ import {
109
+ DropdownMenu,
110
+ DropdownMenuContent,
111
+ DropdownMenuRadioGroup,
112
+ DropdownMenuRadioItem,
113
+ DropdownMenuTrigger,
114
+ } from "@/components/ui/dropdown-menu";
115
+
116
+ function EffortDropdown() {
117
+ const { efforts, effort, setEffort } = useModelSelectorEfforts();
118
+ if (!efforts?.length) return null;
119
+
120
+ return (
121
+ <div className="flex items-center justify-between gap-3 border-t px-3 py-2">
122
+ <span className="text-muted-foreground text-xs">Thinking</span>
123
+ <DropdownMenu>
124
+ <DropdownMenuTrigger className="text-xs">
125
+ {efforts.find((e) => e.id === effort)?.name ?? "Select"}
126
+ </DropdownMenuTrigger>
127
+ <DropdownMenuContent align="end">
128
+ <DropdownMenuRadioGroup value={effort} onValueChange={setEffort}>
129
+ {efforts.map((option) => (
130
+ <DropdownMenuRadioItem key={option.id} value={option.id}>
131
+ {option.name}
132
+ </DropdownMenuRadioItem>
133
+ ))}
134
+ </DropdownMenuRadioGroup>
135
+ </DropdownMenuContent>
136
+ </DropdownMenu>
137
+ </div>
138
+ );
139
+ }
111
140
  ```
112
141
 
113
- ## Runtime Integration
142
+ Render it inside `ModelSelector.Content` in place of `ModelSelector.Effort`. The same hook supports any shape that reads the levels and writes the selection.
114
143
 
115
- The default `ModelSelector` export automatically registers the selected model with assistant-ui's `ModelContext` system. When a user selects a model:
144
+ ## Search
116
145
 
117
- 1. The component calls `aui.modelContext().register()` with `config.modelName`
118
- 2. The `AssistantChatTransport` includes `config` in the request body
119
- 3. Your API route reads `config.modelName` to determine which model to use
146
+ Search is opt-in. Pass `searchable` to the default component:
120
147
 
121
- This works out of the box with `@assistant-ui/react-ai-sdk`.
148
+ ```tsx
149
+ <ModelSelector models={models} searchable />
150
+ ```
122
151
 
123
- ## API Reference
152
+ Or compose `ModelSelector.Search` into a custom layout. Matching runs against each model's `id`, `name`, and `keywords`; add the provider name to `keywords` so typing "openai" finds its models.
124
153
 
125
- ### Composable API
154
+ ## Composition
126
155
 
127
- For custom layouts, use the sub-components directly with `ModelSelector.Root`:
156
+ All parts are exported individually. The default popover content is `List` + `Effort`; replace it to add search, provider groups, or anything else:
128
157
 
129
158
  ```tsx
130
159
  import {
131
160
  ModelSelectorRoot,
132
161
  ModelSelectorTrigger,
133
162
  ModelSelectorContent,
163
+ ModelSelectorSearch,
164
+ ModelSelectorList,
165
+ ModelSelectorEmpty,
166
+ ModelSelectorGroup,
134
167
  ModelSelectorItem,
168
+ ModelSelectorEffort,
135
169
  } from "@/components/assistant-ui/model-selector";
136
170
 
137
- <ModelSelectorRoot models={models} value={modelId} onValueChange={setModelId}>
171
+ <ModelSelectorRoot
172
+ models={models}
173
+ value={modelId}
174
+ onValueChange={setModelId}
175
+ effort={effort}
176
+ onEffortChange={setEffort}
177
+ >
138
178
  <ModelSelectorTrigger variant="outline" />
139
- <ModelSelectorContent />
179
+ <ModelSelectorContent>
180
+ <ModelSelectorSearch placeholder="Search models..." />
181
+ <ModelSelectorList>
182
+ <ModelSelectorEmpty />
183
+ <ModelSelectorGroup heading="OpenAI">
184
+ {openaiModels.map((model) => (
185
+ <ModelSelectorItem key={model.id} model={model} />
186
+ ))}
187
+ </ModelSelectorGroup>
188
+ <ModelSelectorGroup heading="Anthropic">
189
+ {anthropicModels.map((model) => (
190
+ <ModelSelectorItem key={model.id} model={model} />
191
+ ))}
192
+ </ModelSelectorGroup>
193
+ </ModelSelectorList>
194
+ <ModelSelectorEffort label="Thinking" />
195
+ </ModelSelectorContent>
140
196
  </ModelSelectorRoot>
141
197
  ```
142
198
 
@@ -144,9 +200,64 @@ import {
144
200
  |-----------|-------------|
145
201
  | `ModelSelector` | Default export with runtime integration |
146
202
  | `ModelSelector.Root` | Presentational root (no runtime, controlled state) |
147
- | `ModelSelector.Trigger` | CVA-styled trigger showing current model |
148
- | `ModelSelector.Content` | Select content with model items |
149
- | `ModelSelector.Item` | Individual model option with icon, name, description |
203
+ | `ModelSelector.Trigger` | CVA-styled trigger showing the current selection |
204
+ | `ModelSelector.Value` | Selected model name, icon, and active effort |
205
+ | `ModelSelector.Content` | Popover content wrapping a Command |
206
+ | `ModelSelector.Search` | Search input that filters the list |
207
+ | `ModelSelector.List` | List of model items (renders all models by default) |
208
+ | `ModelSelector.Empty` | Empty state shown when search has no matches |
209
+ | `ModelSelector.Group` | Labeled group of items (e.g. by provider) |
210
+ | `ModelSelector.Separator` | Divider between groups or items |
211
+ | `ModelSelector.Item` | Individual model option |
212
+ | `ModelSelector.Effort` | Thinking level row for the selected model |
213
+
214
+ `ModelSelector.List` is a Command list, so filtering and keyboard navigation work across groups automatically. Custom sorting is plain code: order the models before rendering items.
215
+
216
+ <Callout type="warn">
217
+ `ModelSelector.Content` wraps a Command whose root keydown handler claims
218
+ <kbd>Enter</kbd> to select the highlighted model. Interactive elements
219
+ composed inside it (filter chips, custom effort controls) should stop
220
+ propagation for Enter in their own `onKeyDown` so the focused control
221
+ activates instead. `ModelSelector.Effort` already does this.
222
+ </Callout>
223
+
224
+ ## Variants
225
+
226
+ Use the `variant` prop to change the trigger's visual style.
227
+
228
+ ```tsx
229
+ <ModelSelector variant="outline" /> // Border (default)
230
+ <ModelSelector variant="ghost" /> // No background
231
+ <ModelSelector variant="muted" /> // Solid background
232
+ ```
233
+
234
+ | Variant | Description |
235
+ | --------- | ---------------------------------------------- |
236
+ | `outline` | Border with transparent background (default) |
237
+ | `ghost` | No background, subtle hover |
238
+ | `muted` | Solid secondary background |
239
+
240
+ ## Sizes
241
+
242
+ Use the `size` prop to control the trigger dimensions.
243
+
244
+ ```tsx
245
+ <ModelSelector size="sm" /> // Compact (h-8, text-xs)
246
+ <ModelSelector size="default" /> // Standard (h-9)
247
+ <ModelSelector size="lg" /> // Large (h-10)
248
+ ```
249
+
250
+ ## How It Works
251
+
252
+ The default `ModelSelector` export registers the selection with assistant-ui's `ModelContext` system:
253
+
254
+ 1. The component calls `aui.modelContext().register()` with `config.modelName`, plus `config.reasoningEffort` when the selected model supports the chosen level
255
+ 2. The `AssistantChatTransport` includes `config` in the request body of every chat request
256
+ 3. Your API route reads `config.modelName` and `config.reasoningEffort`
257
+
258
+ This works out of the box with `@assistant-ui/react-ai-sdk`. `ModelSelector.Root` performs no registration; it is purely presentational, with controlled and uncontrolled props for the value, effort, and open state.
259
+
260
+ ## API Reference
150
261
 
151
262
  ### ModelSelector
152
263
 
@@ -162,7 +273,7 @@ import {
162
273
  {
163
274
  name: "defaultValue",
164
275
  type: "string",
165
- description: "Initial model ID for uncontrolled usage.",
276
+ description: "Initial model ID for uncontrolled usage. Defaults to the first model, captured on first render; if models loads asynchronously, control the value instead.",
166
277
  },
167
278
  {
168
279
  name: "value",
@@ -174,6 +285,27 @@ import {
174
285
  type: "(value: string) => void",
175
286
  description: "Callback when selected model changes.",
176
287
  },
288
+ {
289
+ name: "defaultEffort",
290
+ type: "string",
291
+ description: "Initial effort level ID for uncontrolled usage.",
292
+ },
293
+ {
294
+ name: "effort",
295
+ type: "string",
296
+ description: "Controlled effort level ID.",
297
+ },
298
+ {
299
+ name: "onEffortChange",
300
+ type: "(effort: string) => void",
301
+ description: "Callback when effort level changes.",
302
+ },
303
+ {
304
+ name: "searchable",
305
+ type: "boolean",
306
+ default: "false",
307
+ description: "Render a search input above the model list.",
308
+ },
177
309
  {
178
310
  name: "variant",
179
311
  type: '"outline" | "ghost" | "muted"',
@@ -221,5 +353,40 @@ import {
221
353
  type: "React.ReactNode",
222
354
  description: "Optional icon displayed before the model name.",
223
355
  },
356
+ {
357
+ name: "disabled",
358
+ type: "boolean",
359
+ description: "Disable selection of this model.",
360
+ },
361
+ {
362
+ name: "keywords",
363
+ type: "string[]",
364
+ description: "Extra search terms matched by ModelSelector.Search (e.g. the provider name).",
365
+ },
366
+ {
367
+ name: "efforts",
368
+ type: "boolean | ModelSelectorEffortOption[]",
369
+ description: "Reasoning effort levels. true enables the default Low/Medium/High; pass a custom { id, name } list to override. Omit for models without configurable reasoning.",
370
+ },
224
371
  ]}
225
372
  />
373
+
374
+ ### `useModelSelectorEfforts`
375
+
376
+ ```tsx
377
+ const { efforts, effort, setEffort } = useModelSelectorEfforts();
378
+ ```
379
+
380
+ The selected model's effort levels and the active selection, for building a [custom effort UI](#custom-effort-ui) inside `ModelSelector.Content`. `efforts` is `undefined` for models without configurable reasoning.
381
+
382
+ ### `resolveModelEffort`
383
+
384
+ ```tsx
385
+ resolveModelEffort(models, modelId, effort); // => string | undefined
386
+ ```
387
+
388
+ Returns the effort ID when the given model supports it, otherwise `undefined`. This is the [sticky selection](#sticky-selection) rule the default component applies before registering the selection.
389
+
390
+ ## Related
391
+
392
+ - [Model Context](/docs/copilots/model-context): How registered context (instructions, tools, config) reaches your backend
@@ -0,0 +1,154 @@
1
+ ---
2
+ title: Number Roll
3
+ description: Animated number that rolls digits odometer-style when the value changes.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ import { PreviewCode } from "@/components/docs/preview-code.server";
8
+ import {
9
+ NumberRollSample,
10
+ NumberRollCompactSample,
11
+ NumberRollFormatSample,
12
+ NumberRollLiveSample,
13
+ } from "@/components/docs/samples/number-roll";
14
+
15
+ <Callout>
16
+ This is a **standalone component** that does not depend on the assistant-ui runtime. Use it anywhere in your application.
17
+ </Callout>
18
+
19
+ <NumberRollSample />
20
+
21
+ ## Installation
22
+
23
+ <InstallCommand shadcn={["number-roll"]} />
24
+
25
+ This adds a `/components/assistant-ui/number-roll.tsx` file to your project, which you can adjust as needed. The component has no dependencies beyond React.
26
+
27
+ ## Usage
28
+
29
+ ```tsx
30
+ import { NumberRoll } from "@/components/assistant-ui/number-roll";
31
+
32
+ export function TokenCounter({ count }: { count: number }) {
33
+ return <NumberRoll value={count} format={{ notation: "compact" }} />;
34
+ }
35
+ ```
36
+
37
+ When `value` changes, each digit rolls in place to its new value. Formatting is handled by `Intl.NumberFormat`, so structural changes animate too: when `700` becomes `1.1K` with compact notation, the surviving ones digit rolls from 0 to 1 while the leading `70` slides out and the decimal point, fraction digit, and `K` suffix slide in.
38
+
39
+ ## Examples
40
+
41
+ ### Compact Notation
42
+
43
+ Pass any `Intl.NumberFormatOptions` via the `format` prop. Crossing a compact-notation threshold animates the formatting change instead of remounting the whole string.
44
+
45
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollCompactSample">
46
+ <NumberRollCompactSample />
47
+ </PreviewCode>
48
+
49
+ ### Formats and Locales
50
+
51
+ Currencies, percentages, suffixes, and non-English locales all work through the same `Intl.NumberFormat` pipeline. Compact thresholds and suffixes are locale-dependent (`1.2万` in `zh-CN`), so never hardcode them.
52
+
53
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollFormatSample">
54
+ <NumberRollFormatSample />
55
+ </PreviewCode>
56
+
57
+ ### Live Counter
58
+
59
+ Rapid successive updates retarget the in-flight roll smoothly, which makes the component suitable for streaming token counts and other live metrics.
60
+
61
+ <PreviewCode file="components/docs/samples/number-roll" name="NumberRollLiveSample">
62
+ <NumberRollLiveSample />
63
+ </PreviewCode>
64
+
65
+ ## How It Works
66
+
67
+ The value is formatted with `Intl.NumberFormat.formatToParts` and split into keyed parts. Integer digits are keyed by place value counted from the right, so when `999` becomes `1,000` the existing columns keep their identity and roll in place while only the new leading digit and separator enter. Symbols (decimal point, group separators, currency signs, compact suffixes) cross-fade and collapse via animated `grid-template-columns`.
68
+
69
+ Each digit renders a strip of 0 to 9 and animates a registered CSS custom property; CSS `mod()` math wraps the strip into an endless ribbon, so rolling from 9 to 0 continues in the trend direction instead of spinning backwards. There is no JavaScript animation loop and no animation library dependency.
70
+
71
+ In browsers without CSS `mod()` support, and during server rendering, the component renders the plain formatted string. With `prefers-reduced-motion`, values swap instantly without rolling. When server rendering, pass an explicit `locales`: the server's default locale can differ from the visitor's browser locale, and a mismatched formatted string causes a React hydration error. Locales whose default numbering system is non-Latin (such as `ar-EG`) cross-fade their digits as symbols instead of rolling.
72
+
73
+ The formatted value is exposed to screen readers as plain text while the animated digits are `aria-hidden`. Value changes are not announced automatically; wrap the component in an `aria-live` region if you need announcements.
74
+
75
+ ## API Reference
76
+
77
+ ### NumberRoll
78
+
79
+ <ParametersTable
80
+ type="NumberRollProps"
81
+ parameters={[
82
+ {
83
+ name: "value",
84
+ type: "number",
85
+ required: true,
86
+ description: "The number to display.",
87
+ },
88
+ {
89
+ name: "format",
90
+ type: "Intl.NumberFormatOptions",
91
+ description:
92
+ "Number formatting options, e.g. `{ notation: \"compact\" }` or `{ style: \"currency\", currency: \"USD\" }`.",
93
+ },
94
+ {
95
+ name: "locales",
96
+ type: "Intl.LocalesArgument",
97
+ description:
98
+ "Locale(s) passed to `Intl.NumberFormat`. Pass an explicit value in server-rendered apps so the server and client format identically.",
99
+ },
100
+ {
101
+ name: "prefix",
102
+ type: "string",
103
+ description: "Static text rendered before the number.",
104
+ },
105
+ {
106
+ name: "suffix",
107
+ type: "string",
108
+ description: "Static text rendered after the number.",
109
+ },
110
+ {
111
+ name: "trend",
112
+ type: '"auto" | "up" | "down"',
113
+ default: '"auto"',
114
+ description:
115
+ "Roll direction. `auto` follows the sign of the value change; `up` and `down` force a direction, wrapping digits through 9/0 as needed.",
116
+ },
117
+ {
118
+ name: "duration",
119
+ type: "number",
120
+ default: "500",
121
+ description:
122
+ "Digit roll duration in milliseconds. Enter/exit fades run at 60% of this value.",
123
+ },
124
+ {
125
+ name: "className",
126
+ type: "string",
127
+ description: "Additional CSS classes.",
128
+ },
129
+ ]}
130
+ />
131
+
132
+ ### Styling
133
+
134
+ The root applies `tabular-nums` so digits keep a constant width while rolling; the font you use must provide tabular figures for the layout to stay stable. Size and color are inherited from the surrounding text, so style it like any span:
135
+
136
+ ```tsx
137
+ <NumberRoll value={value} className="text-4xl font-semibold" />
138
+ ```
139
+
140
+ Change the animation speed via the `duration` prop; it drives both the CSS timing and the cleanup that unmounts exited characters, so overriding the duration variable in CSS alone would remove them mid-fade. The fade and easing variables are safe to override via the `style` prop (the defaults are set as inline styles, so plain `className` overrides do not apply):
141
+
142
+ | Variable | Default | Description |
143
+ |----------|---------|-------------|
144
+ | `--aui-number-roll-duration` | `500ms` (from the `duration` prop) | Digit roll duration. |
145
+ | `--aui-number-roll-fade` | 60% of the duration | Enter/exit fade and width-collapse duration. |
146
+ | `--aui-number-roll-ease` | `cubic-bezier(0.23, 1, 0.32, 1)` | Digit roll easing. |
147
+
148
+ Parts are targetable via data attributes: `[data-slot="number-roll"]`, `[data-slot="number-roll-part"]`, `[data-slot="number-roll-digit"]`, and `[data-slot="number-roll-symbol"]`.
149
+
150
+ ## Related Components
151
+
152
+ - [Context Display](/docs/ui/context-display) - Live token usage ring and bar
153
+ - [Message Timing](/docs/ui/message-timing) - Streaming stats badge
154
+ - [Badge](/docs/ui/badge) - Small status and metadata labels
@@ -213,7 +213,7 @@ Tool UIs fall into three buckets: prompting the user (human-in-the-loop), inform
213
213
  Mark a tool with `display: "standalone"` to keep its UI out of the grouped trace. `human` tools and MCP apps are standalone automatically; every other tool defaults to `"inline"` and opts in explicitly:
214
214
 
215
215
  ```tsx
216
- const toolkit = {
216
+ const toolkit = defineToolkit({
217
217
  ask_user: { type: "human", render: AskUI }, // standalone (forced)
218
218
  search_web: { type: "frontend", render: SearchUI }, // inline trace (default)
219
219
  checkout: {
@@ -221,7 +221,7 @@ const toolkit = {
221
221
  render: CheckoutUI,
222
222
  display: "standalone", // opt in
223
223
  },
224
- } satisfies Toolkit;
224
+ });
225
225
  ```
226
226
 
227
227
  The synthetic `"standalone-tool-call"` key on `groupPartByType` matches all of these. `MessagePrimitive.GroupedParts` passes the live tool-UI registry to `groupBy` as a second `context` argument, and the helper reads it to resolve the registry-driven cases — MCP-app calls are detected from the part alone, so nothing is threaded in:
@@ -28,7 +28,7 @@ Previously, reasoning parts were rendered via `components.Reasoning` and grouped
28
28
 
29
29
  Render reasoning parts through `MessagePrimitive.GroupedParts`. Group consecutive reasoning parts with `"group-reasoning"`, then compose `ReasoningRoot`, `ReasoningTrigger`, `ReasoningContent`, and `ReasoningText` around the grouped children.
30
30
 
31
- While reasoning is streaming, `part.status.type === "running"`. Pass that to `defaultOpen` so the accordion auto-opens during streaming and lets the user collapse it once the model moves on.
31
+ While reasoning is streaming, `part.status.type === "running"`. Pass that to `streaming` so the accordion auto-opens during streaming with a bottom-pinned live preview of the newest tokens, auto-collapses when the model moves on, and permanently defers to the first manual toggle. When `streaming` is provided it supersedes `defaultOpen`.
32
32
 
33
33
  ```tsx title="/app/components/assistant-ui/thread.tsx"
34
34
  import { MessagePrimitive, groupPartByType } from "@assistant-ui/react";
@@ -56,7 +56,7 @@ const AssistantMessage: FC = () => {
56
56
  case "group-reasoning": { // [!code ++]
57
57
  const running = part.status.type === "running"; // [!code ++]
58
58
  return ( // [!code ++]
59
- <ReasoningRoot defaultOpen={running}> // [!code ++]
59
+ <ReasoningRoot streaming={running}> // [!code ++]
60
60
  <ReasoningTrigger active={running} /> // [!code ++]
61
61
  <ReasoningContent aria-busy={running}> // [!code ++]
62
62
  <ReasoningText>{children}</ReasoningText> // [!code ++]
@@ -139,7 +139,7 @@ const ReasoningGroupImpl: ReasoningGroupComponent = ({
139
139
  });
140
140
 
141
141
  return (
142
- <ReasoningRoot defaultOpen={isReasoningStreaming}>
142
+ <ReasoningRoot streaming={isReasoningStreaming}>
143
143
  <ReasoningTrigger active={isReasoningStreaming} />
144
144
  <ReasoningContent aria-busy={isReasoningStreaming}>
145
145
  <ReasoningText>{children}</ReasoningText>
@@ -119,6 +119,8 @@ const StreamdownText = () => (
119
119
  | `components` | `object` | - | Custom components including `SyntaxHighlighter` and `CodeHeader` |
120
120
  | `componentsByLanguage` | `object` | - | Language-specific component overrides |
121
121
  | `preprocess` | `(text: string) => string` | - | Text preprocessing function |
122
+ | `defer` | `boolean` | `false` | Defer markdown parsing to a lower priority via `useDeferredValue` so typing and scrolling stay responsive during streaming |
123
+ | `smooth` | `boolean \| SmoothOptions` | `false` | Typewriter-style reveal via `useSmooth`; the caret and controls stay active until the reveal catches up. Prefer the native `animated` prop for entrance animations |
122
124
  | `controls` | `boolean \| object` | `true` | Enable/disable UI controls for code blocks and tables |
123
125
  | `caret` | `"block" \| "circle"` | - | Streaming caret style |
124
126
  | `mermaid` | `MermaidOptions` | - | Mermaid diagram configuration |
@@ -29,7 +29,7 @@ import { SyntaxHighlightingSample } from "@/components/docs/samples/syntax-highl
29
29
 
30
30
  This adds a `/components/assistant-ui/shiki-highlighter.tsx` file to your project and
31
31
  installs the `react-shiki` dependency. The highlighter can be customized by editing
32
- the config in the `shiki-highlighter.tsx` file.
32
+ the config in the `shiki-highlighter.tsx` file. While a message part is still streaming, the component renders the plain code without tokenization and highlights once the part settles, so streaming stays cheap.
33
33
 
34
34
  </Step>
35
35
  <Step>
@@ -133,6 +133,58 @@ const SuggestionItem = () => (
133
133
  );
134
134
  ```
135
135
 
136
+ ## Component Overrides
137
+
138
+ `Thread` accepts an optional `components` prop that swaps parts of the rendering without editing the copied file. All slots are optional; omitted slots keep the built-in rendering.
139
+
140
+ ```tsx
141
+ import { Thread, type ThreadComponents } from "@/components/assistant-ui/thread";
142
+
143
+ const THREAD_COMPONENTS: ThreadComponents = {
144
+ ToolFallback: MyToolFallback,
145
+ ToolGroup: MyToolGroup,
146
+ };
147
+
148
+ export default function Chat() {
149
+ return <Thread components={THREAD_COMPONENTS} />;
150
+ }
151
+ ```
152
+
153
+ <ParametersTable
154
+ type="ThreadComponents"
155
+ parameters={[
156
+ {
157
+ name: "AssistantMessage",
158
+ type: "ComponentType",
159
+ description: "Replaces the entire assistant message, including the action bar and branch picker.",
160
+ },
161
+ {
162
+ name: "Welcome",
163
+ type: "ComponentType",
164
+ description: "Replaces the welcome screen shown for a new chat.",
165
+ },
166
+ {
167
+ name: "ToolFallback",
168
+ type: "ToolCallMessagePartComponent",
169
+ description: "Renders tool calls that have no tool UI registered by name. Registered tool UIs take precedence over this slot.",
170
+ },
171
+ {
172
+ name: "ToolGroup",
173
+ type: "ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>",
174
+ description: "Wraps runs of consecutive tool calls. Receives the group part (`indices`, `status`) and the rendered children.",
175
+ },
176
+ {
177
+ name: "ReasoningGroup",
178
+ type: "ComponentType<PropsWithChildren<{ group: ThreadGroupPart }>>",
179
+ description: "Wraps runs of consecutive reasoning parts. Receives the group part and the rendered children.",
180
+ },
181
+ ]}
182
+ />
183
+
184
+ Define the `components` object once at module scope (or memoize it) so message subtrees do not re-render whenever the parent re-renders.
185
+
186
+ For per-tool UI, prefer registering a renderer by tool name over overriding `ToolFallback`: put `render` on the matching toolkit entry (see [Tool UI](/docs/tools/tool-ui)). `data` message parts render through renderers registered with `useAssistantDataUI`; parts without a registered renderer are not displayed.
187
+
136
188
  ## API Reference
137
189
 
138
190
  The following primitives are used within the Thread component and can be customized in your `/components/assistant-ui/thread.tsx` file.