@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.
- package/.docs/organized/code-examples/waterfall.md +7 -7
- package/.docs/organized/code-examples/with-a2a.md +8 -8
- package/.docs/organized/code-examples/with-ag-ui.md +12 -12
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
- package/.docs/organized/code-examples/with-artifacts.md +40 -34
- package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
- package/.docs/organized/code-examples/with-browser-extension.md +9 -9
- package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
- package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
- package/.docs/organized/code-examples/with-cloud.md +10 -10
- package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
- package/.docs/organized/code-examples/with-expo.md +66 -31
- package/.docs/organized/code-examples/with-external-store.md +8 -8
- package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
- package/.docs/organized/code-examples/with-generative-ui.md +98 -368
- package/.docs/organized/code-examples/with-google-adk.md +9 -9
- package/.docs/organized/code-examples/with-heat-graph.md +7 -7
- package/.docs/organized/code-examples/with-image-generation.md +10 -10
- package/.docs/organized/code-examples/with-interactables.md +10 -10
- package/.docs/organized/code-examples/with-langchain.md +10 -10
- package/.docs/organized/code-examples/with-langgraph.md +33 -29
- package/.docs/organized/code-examples/with-livekit.md +12 -12
- package/.docs/organized/code-examples/with-mcp.md +11 -11
- package/.docs/organized/code-examples/with-opencode.md +109 -583
- package/.docs/organized/code-examples/with-pi.md +2044 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
- package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
- package/.docs/organized/code-examples/with-react-ink.md +309 -102
- package/.docs/organized/code-examples/with-react-router.md +14 -14
- package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
- package/.docs/organized/code-examples/with-store.md +70 -66
- package/.docs/organized/code-examples/with-tanstack.md +25 -11
- package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
- package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
- package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- package/.docs/raw/docs/guides/index.mdx +3 -0
- package/.docs/raw/docs/guides/input-history.mdx +55 -0
- package/.docs/raw/docs/guides/virtualization.mdx +63 -0
- package/.docs/raw/docs/ink/adapters.mdx +23 -1
- package/.docs/raw/docs/ink/hooks.mdx +22 -19
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
- package/.docs/raw/docs/react-native/hooks.mdx +26 -18
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
- package/.docs/raw/docs/tools/backend.mdx +19 -11
- package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
- package/.docs/raw/docs/tools/index.mdx +7 -12
- package/.docs/raw/docs/tools/mcp.mdx +83 -15
- package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
- package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
- package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
- package/.docs/raw/docs/ui/mermaid.mdx +16 -9
- package/.docs/raw/docs/ui/model-selector.mdx +219 -52
- package/.docs/raw/docs/ui/number-roll.mdx +154 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +2 -2
- package/.docs/raw/docs/ui/reasoning.mdx +3 -3
- package/.docs/raw/docs/ui/streamdown.mdx +2 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
- package/.docs/raw/docs/ui/thread.mdx +52 -0
- package/.docs/raw/docs/ui/tool-fallback.mdx +16 -0
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prepare-docs/prepare.js.map +1 -1
- package/dist/stdio.js.map +1 -1
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +4 -4
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description:
|
|
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
|
|
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-
|
|
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
|
|
53
|
+
### Read the selection in your API route
|
|
53
54
|
|
|
54
|
-
The selected model's `id`
|
|
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
|
-
##
|
|
81
|
+
## Reasoning Efforts
|
|
73
82
|
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
+
### Sticky Selection
|
|
89
99
|
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
+
## Search
|
|
116
145
|
|
|
117
|
-
|
|
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
|
-
|
|
148
|
+
```tsx
|
|
149
|
+
<ModelSelector models={models} searchable />
|
|
150
|
+
```
|
|
122
151
|
|
|
123
|
-
|
|
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
|
-
|
|
154
|
+
## Composition
|
|
126
155
|
|
|
127
|
-
|
|
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
|
|
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
|
|
148
|
-
| `ModelSelector.
|
|
149
|
-
| `ModelSelector.
|
|
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
|
-
}
|
|
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 `
|
|
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
|
|
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
|
|
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.
|