@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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Input History
|
|
3
|
+
description: Terminal-style ArrowUp/ArrowDown recall of previously sent messages in the assistant-ui React composer.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Input history lets users press ArrowUp in an empty composer to recall previously sent messages, newest first, like a shell prompt. ArrowDown steps back toward the newest entry and finally restores the draft that was being typed when browsing started.
|
|
8
|
+
|
|
9
|
+
<Callout type="warn">
|
|
10
|
+
This API is marked unstable and may change without notice.
|
|
11
|
+
</Callout>
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Spread the hook's bundle onto `ComposerPrimitive.Input`:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import {
|
|
19
|
+
ComposerPrimitive,
|
|
20
|
+
unstable_useComposerInputHistory,
|
|
21
|
+
} from "@assistant-ui/react";
|
|
22
|
+
|
|
23
|
+
const Composer = () => {
|
|
24
|
+
const history = unstable_useComposerInputHistory();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ComposerPrimitive.Root>
|
|
28
|
+
<ComposerPrimitive.Input {...history} />
|
|
29
|
+
<ComposerPrimitive.Send />
|
|
30
|
+
</ComposerPrimitive.Root>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The history ring is derived live from the current thread's user messages (trimmed, with adjacent duplicates collapsed), so it needs no persistence or configuration.
|
|
36
|
+
|
|
37
|
+
## Behavior
|
|
38
|
+
|
|
39
|
+
- ArrowUp starts recall only when the composer is empty (whitespace-only drafts count as empty and are restored on the way back down).
|
|
40
|
+
- While a recalled multi-line message is shown, arrows move the caret line by line; recall only steps when the caret is on the first line (ArrowUp) or last line (ArrowDown).
|
|
41
|
+
- An open mention or slash-command popover keeps owning the arrow keys; the hook yields whenever a trigger popover is active.
|
|
42
|
+
- IME composition, modifier keys, text selections, and events a preceding handler already `preventDefault`ed are left untouched.
|
|
43
|
+
- Switching threads or sending a message resets the browse position.
|
|
44
|
+
- The hook is inert on edit composers.
|
|
45
|
+
|
|
46
|
+
To interleave your own ArrowUp handling ahead of history (for example, stepping through queued messages), compose your handler before the hook's and call `preventDefault` when you consume the key:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<ComposerPrimitive.Input
|
|
50
|
+
onKeyDown={(e) => {
|
|
51
|
+
myHandler(e);
|
|
52
|
+
history.onKeyDown(e);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Thread Virtualization
|
|
3
|
+
description: Render very long threads with @tanstack/react-virtual and ThreadPrimitive.MessageByIndex.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Virtualization mounts only the messages near the viewport and represents the rest as empty space, so a thread with thousands of messages scrolls like one with twenty. assistant-ui does not ship a virtualized thread component; this guide shows the supported composition, extracted from a production consumer and available as a runnable example: [`examples/with-virtualized-thread`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-virtualized-thread).
|
|
8
|
+
|
|
9
|
+
## Do you need this?
|
|
10
|
+
|
|
11
|
+
Probably not. The default kit already renders message bodies with `content-visibility: auto` and `contain-intrinsic-size`, which skips paint work for off-screen messages, and `ThreadPrimitive.Viewport` handles auto-scroll. That covers typical threads. Reach for virtualization when React mount and update cost itself becomes the bottleneck: threads with hundreds to thousands of messages, or very heavy per-message content, where typing latency degrades because every message stays mounted.
|
|
12
|
+
|
|
13
|
+
## Rendering messages by index
|
|
14
|
+
|
|
15
|
+
`ThreadPrimitive.MessageByIndex` renders a single message at a given index and memoizes on the index plus per-field `components` identity. Pass a module-level components object so the memo holds and a streaming delta only re-renders the message it belongs to:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
const MESSAGE_COMPONENTS = { UserMessage, AssistantMessage };
|
|
19
|
+
|
|
20
|
+
<ThreadPrimitive.MessageByIndex index={index} components={MESSAGE_COMPONENTS} />;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Grouping into turns
|
|
24
|
+
|
|
25
|
+
Virtualizing per user turn (a user message plus the responses that follow it) gives the virtualizer stable, meaningfully sized items. Subscribe to a single signature string so the selector returns a primitive and the turn array only rebuilds when membership actually changes:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
const signature = useAuiState((s) =>
|
|
29
|
+
s.thread.messages.map((m, i) => `${i}:${m.role}:${m.id}`).join("\n"),
|
|
30
|
+
);
|
|
31
|
+
const turns = useMemo(() => buildTurns(signature), [signature]);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Padding spacers, not absolute positioning
|
|
35
|
+
|
|
36
|
+
Render the virtual items in normal document flow inside a spacer div whose `paddingTop`/`paddingBottom` represent the unmounted regions. Items remain regular flow children, so message CSS (including `position: sticky` patterns and the kit styling) keeps working, and `virtualizer.measureElement` records real heights as items mount:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
const items = virtualizer.getVirtualItems();
|
|
40
|
+
const paddingTop = items[0]?.start ?? 0;
|
|
41
|
+
const paddingBottom = Math.max(
|
|
42
|
+
0,
|
|
43
|
+
virtualizer.getTotalSize() - (items.at(-1)?.end ?? 0),
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Owning the scroll element
|
|
48
|
+
|
|
49
|
+
The composition owns its scroll container instead of using `ThreadPrimitive.Viewport`: the built-in auto-scroll assumes every message is mounted, and its resize-driven re-pin can fight the virtualizer's measurement adjustments. Three pieces replace it:
|
|
50
|
+
|
|
51
|
+
1. **Auto-follow.** A ResizeObserver on the content wrapper re-pins the scroller to the bottom while a sticky flag is armed. The flag disarms when the user scrolls up (detected as `scrollTop` decreasing while `scrollHeight` and `clientHeight` are stable, the same heuristic the built-in viewport uses, plus wheel-up and touchmove) and re-arms when the user returns to the bottom.
|
|
52
|
+
2. **Measurement guard.** While pinned at the bottom, the virtualizer's own scroll adjustments on item re-measurement are suppressed via a custom `scrollToFn`; without this the two scroll writers fight and the view rubber-bands during streaming.
|
|
53
|
+
3. **Run-start jump.** A `useLayoutEffect` observes `s.thread.isRunning` flipping to true and jumps to the bottom before paint, so a just-sent message never flashes below the fold. The `thread.runStart` event is deprecated; deriving the transition from state is the supported path.
|
|
54
|
+
|
|
55
|
+
The production consumer this is extracted from disables streaming auto-follow entirely as a product choice; the example keeps following because it matches `ThreadPrimitive.Viewport`'s default behavior. Both are valid, and the disarm guard makes either safe.
|
|
56
|
+
|
|
57
|
+
## Try it
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npx assistant-ui@latest create my-app
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then copy `app/VirtualizedThread.tsx`, `app/MyRuntimeProvider.tsx`, and `app/seed-messages.ts` from [the example](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-virtualized-thread), or clone the repo and run `pnpm --filter with-virtualized-thread dev`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Adapters
|
|
3
|
-
description:
|
|
3
|
+
description: Attachment, title generation, and storage adapters for React Ink.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Adapters customize runtime behavior. They can be passed as options to `useLocalRuntime` or `useRemoteThreadListRuntime`.
|
|
@@ -17,6 +17,28 @@ const adapter = createFileStorageAdapter({
|
|
|
17
17
|
});
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## Attachment adapters
|
|
21
|
+
|
|
22
|
+
`SimpleTextAttachmentAdapter` and `SimpleImageAttachmentAdapter` work the same in the terminal as on web and React Native. Text contents are sent to the model wrapped in `<attachment name=...>` tags, and images are sent as base64 data URLs.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
CompositeAttachmentAdapter,
|
|
27
|
+
SimpleImageAttachmentAdapter,
|
|
28
|
+
SimpleTextAttachmentAdapter,
|
|
29
|
+
useLocalRuntime,
|
|
30
|
+
} from "@assistant-ui/react-ink";
|
|
31
|
+
|
|
32
|
+
const runtime = useLocalRuntime(chatModelAdapter, {
|
|
33
|
+
adapters: {
|
|
34
|
+
attachments: new CompositeAttachmentAdapter([
|
|
35
|
+
new SimpleTextAttachmentAdapter(),
|
|
36
|
+
new SimpleImageAttachmentAdapter(),
|
|
37
|
+
]),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
20
42
|
## TitleGenerationAdapter
|
|
21
43
|
|
|
22
44
|
Produces a thread title from a thread's messages. Pass one as the `titleGenerator` option to `createFileStorageAdapter`, or call it from a custom `RemoteThreadListAdapter`.
|
|
@@ -62,7 +62,7 @@ useAuiEvent("thread.runStart", (payload) => {
|
|
|
62
62
|
|
|
63
63
|
### useNotification
|
|
64
64
|
|
|
65
|
-
Ring the terminal bell and emit an OSC desktop notification when the assistant finishes a run, stops with an error, or pauses for
|
|
65
|
+
Ring the terminal bell and emit an OSC desktop notification when the assistant finishes a run, stops with an error, or pauses for user input.
|
|
66
66
|
|
|
67
67
|
```tsx
|
|
68
68
|
import { useNotification } from "@assistant-ui/react-ink";
|
|
@@ -130,7 +130,7 @@ const runtime = useLocalRuntime(chatModel, {
|
|
|
130
130
|
| `adapters.feedback` | `FeedbackAdapter` | Adapter for message feedback (thumbs up/down) |
|
|
131
131
|
| `adapters.suggestion` | `SuggestionAdapter` | Adapter for suggested prompts |
|
|
132
132
|
| `cloud` | `AssistantCloud` | Cloud instance for thread persistence via `@assistant-ui/cloud` |
|
|
133
|
-
| `unstable_humanToolNames` | `string[]` | Tool names that
|
|
133
|
+
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run until a result is added via `addResult` |
|
|
134
134
|
|
|
135
135
|
### useRemoteThreadListRuntime
|
|
136
136
|
|
|
@@ -155,37 +155,40 @@ const runtime = useRemoteThreadListRuntime({
|
|
|
155
155
|
|
|
156
156
|
### Tools
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
Author tools with `defineToolkit`, the same API as on the web ([Defining Tools](/docs/tools/defining-tools)). The tool definition is forwarded to the model; when the model calls it, the `execute` function runs and the `render` component displays the result.
|
|
159
|
+
|
|
160
|
+
<Callout type="info">
|
|
161
|
+
An Ink app runs in a single Node process, so there is no client/server
|
|
162
|
+
boundary to split and no build step. `defineToolkit` from
|
|
163
|
+
`@assistant-ui/react-ink` runs at runtime: import it as a value, with no
|
|
164
|
+
`"use generative"` directive and no `"use client"` inside `execute`.
|
|
165
|
+
</Callout>
|
|
159
166
|
|
|
160
167
|
```tsx title="weather-toolkit.tsx"
|
|
161
|
-
import
|
|
168
|
+
import { defineToolkit } from "@assistant-ui/react-ink";
|
|
162
169
|
import { Text } from "ink";
|
|
170
|
+
import { z } from "zod";
|
|
163
171
|
|
|
164
|
-
export
|
|
172
|
+
export default defineToolkit({
|
|
165
173
|
get_weather: {
|
|
166
|
-
type: "frontend",
|
|
167
174
|
description: "Get the current weather for a city",
|
|
168
|
-
parameters: {
|
|
169
|
-
type: "object",
|
|
170
|
-
properties: {
|
|
171
|
-
city: { type: "string" },
|
|
172
|
-
},
|
|
173
|
-
required: ["city"],
|
|
174
|
-
},
|
|
175
|
+
parameters: z.object({ city: z.string() }),
|
|
175
176
|
execute: async ({ city }) => {
|
|
176
177
|
const res = await fetch(`https://api.weather.example/${city}`);
|
|
177
178
|
return res.json();
|
|
178
179
|
},
|
|
179
180
|
render: ({ args, result }) => (
|
|
180
|
-
<Text>
|
|
181
|
+
<Text>
|
|
182
|
+
{args.city}: {result?.temperature}°F
|
|
183
|
+
</Text>
|
|
181
184
|
),
|
|
182
185
|
},
|
|
183
|
-
}
|
|
186
|
+
});
|
|
184
187
|
```
|
|
185
188
|
|
|
186
189
|
```tsx title="ToolProvider.tsx"
|
|
187
190
|
import { AuiProvider, Tools, useAui } from "@assistant-ui/react-ink";
|
|
188
|
-
import
|
|
191
|
+
import toolkit from "./weather-toolkit";
|
|
189
192
|
|
|
190
193
|
function ToolProvider({ children }: { children: React.ReactNode }) {
|
|
191
194
|
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
@@ -246,7 +249,7 @@ const WeatherCardUI = makeAssistantDataUI({
|
|
|
246
249
|
Wrap a render function component so that it always uses the latest version without re-creating a stable reference. Useful when passing a render prop inline and the function closes over changing state.
|
|
247
250
|
|
|
248
251
|
```tsx title="weather-toolkit.tsx"
|
|
249
|
-
import {
|
|
252
|
+
import { defineToolkit, useInlineRender } from "@assistant-ui/react-ink";
|
|
250
253
|
import { Text } from "ink";
|
|
251
254
|
import { useMemo } from "react";
|
|
252
255
|
|
|
@@ -257,12 +260,12 @@ export function useWeatherToolkit() {
|
|
|
257
260
|
|
|
258
261
|
return useMemo(
|
|
259
262
|
() =>
|
|
260
|
-
({
|
|
263
|
+
defineToolkit({
|
|
261
264
|
get_weather: {
|
|
262
265
|
type: "backend",
|
|
263
266
|
render,
|
|
264
267
|
},
|
|
265
|
-
})
|
|
268
|
+
}),
|
|
266
269
|
[render],
|
|
267
270
|
);
|
|
268
271
|
}
|
|
@@ -103,25 +103,31 @@ export function App() {
|
|
|
103
103
|
|
|
104
104
|
## UI-Only Tool Renderers
|
|
105
105
|
|
|
106
|
-
If you used `makeAssistantToolUI` or `useAssistantToolUI` for a backend, MCP, or
|
|
106
|
+
If you used `makeAssistantToolUI` or `useAssistantToolUI` for a backend, MCP, or
|
|
107
|
+
LangGraph tool, the tool executes elsewhere. Prefer a `"use generative"` toolkit
|
|
108
|
+
with `execute: externalTool()` so the compiler omits the server entry, while the
|
|
109
|
+
client keeps your renderer as `type: "backend"`:
|
|
107
110
|
|
|
108
111
|
```tsx
|
|
109
|
-
// app/
|
|
110
|
-
"use
|
|
112
|
+
// app/toolkit.tsx
|
|
113
|
+
"use generative";
|
|
111
114
|
|
|
112
|
-
import
|
|
115
|
+
import { defineToolkit, externalTool } from "@assistant-ui/react";
|
|
113
116
|
|
|
114
|
-
export
|
|
117
|
+
export default defineToolkit({
|
|
115
118
|
web_search: {
|
|
116
|
-
|
|
119
|
+
execute: externalTool(),
|
|
117
120
|
render: ({ args, result }) => (
|
|
118
121
|
<SearchResults query={args.query} results={result?.results ?? []} />
|
|
119
122
|
),
|
|
120
123
|
},
|
|
121
|
-
}
|
|
124
|
+
});
|
|
122
125
|
```
|
|
123
126
|
|
|
124
|
-
Register it like any toolkit: `useAui({ tools: Tools({ toolkit }) })`.
|
|
127
|
+
Register it like any toolkit: `useAui({ tools: Tools({ toolkit }) })`.
|
|
128
|
+
Render-only entries upload no schema and run no browser code — they only attach
|
|
129
|
+
UI for matching tool-call message parts. For MCP server catalogs, spread
|
|
130
|
+
`defineMcpToolkit({ ... })` in the same generative toolkit.
|
|
125
131
|
|
|
126
132
|
For a one-off renderer that should only affect a particular message surface, use `MessagePrimitive.Parts` inline tool render overrides instead of a global registration.
|
|
127
133
|
|
|
@@ -80,7 +80,7 @@ const runtime = useLocalRuntime(chatModel, {
|
|
|
80
80
|
| `maxSteps` | `number` | Maximum tool call steps per run |
|
|
81
81
|
| `cloud` | `AssistantCloud` | Optional cloud instance for persistence |
|
|
82
82
|
| `adapters` | `object` | Optional adapter overrides (see below) |
|
|
83
|
-
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run
|
|
83
|
+
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run until a result is added via `addResult` |
|
|
84
84
|
|
|
85
85
|
The `adapters` option accepts the following fields (all optional):
|
|
86
86
|
|
|
@@ -116,39 +116,47 @@ const runtime = useRemoteThreadListRuntime({
|
|
|
116
116
|
|
|
117
117
|
### Tools
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Author tools in a `"use generative"` file with `defineToolkit`, the same API as on the web ([Defining Tools](/docs/tools/defining-tools)). The tool definition is forwarded to the model; when the model calls it, the `execute` function runs and the `render` component displays the result.
|
|
120
|
+
|
|
121
|
+
Add the [`@assistant-ui/metro`](https://www.npmjs.com/package/@assistant-ui/metro) plugin so Metro compiles the `"use generative"` directive:
|
|
122
|
+
|
|
123
|
+
```js title="metro.config.js"
|
|
124
|
+
const { getDefaultConfig } = require("expo/metro-config");
|
|
125
|
+
const { withAui } = require("@assistant-ui/metro");
|
|
126
|
+
|
|
127
|
+
module.exports = withAui(getDefaultConfig(__dirname));
|
|
128
|
+
```
|
|
120
129
|
|
|
121
130
|
```tsx title="weather-toolkit.tsx"
|
|
122
|
-
|
|
131
|
+
"use generative";
|
|
132
|
+
|
|
133
|
+
import { defineToolkit } from "@assistant-ui/react-native";
|
|
123
134
|
import { Text, View } from "react-native";
|
|
135
|
+
import { z } from "zod";
|
|
124
136
|
|
|
125
|
-
export
|
|
137
|
+
export default defineToolkit({
|
|
126
138
|
get_weather: {
|
|
127
|
-
type: "frontend",
|
|
128
139
|
description: "Get the current weather for a city",
|
|
129
|
-
parameters: {
|
|
130
|
-
type: "object",
|
|
131
|
-
properties: {
|
|
132
|
-
city: { type: "string" },
|
|
133
|
-
},
|
|
134
|
-
required: ["city"],
|
|
135
|
-
},
|
|
140
|
+
parameters: z.object({ city: z.string() }),
|
|
136
141
|
execute: async ({ city }) => {
|
|
142
|
+
"use client";
|
|
137
143
|
const res = await fetch(`https://api.weather.example/${city}`);
|
|
138
144
|
return res.json();
|
|
139
145
|
},
|
|
140
146
|
render: ({ args, result }) => (
|
|
141
147
|
<View>
|
|
142
|
-
<Text>
|
|
148
|
+
<Text>
|
|
149
|
+
{args.city}: {result?.temperature}°F
|
|
150
|
+
</Text>
|
|
143
151
|
</View>
|
|
144
152
|
),
|
|
145
153
|
},
|
|
146
|
-
}
|
|
154
|
+
});
|
|
147
155
|
```
|
|
148
156
|
|
|
149
157
|
```tsx title="ToolProvider.tsx"
|
|
150
158
|
import { AuiProvider, Tools, useAui } from "@assistant-ui/react-native";
|
|
151
|
-
import
|
|
159
|
+
import toolkit from "./weather-toolkit";
|
|
152
160
|
|
|
153
161
|
function ToolProvider({ children }: { children: React.ReactNode }) {
|
|
154
162
|
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
@@ -188,7 +196,7 @@ useAssistantInstructions("You are a helpful weather assistant.");
|
|
|
188
196
|
Wrap a tool UI component so that inline state updates (from a parent component's render) are reflected without remounting. Use this when the render function closes over props that change over time.
|
|
189
197
|
|
|
190
198
|
```tsx title="my-tool-toolkit.tsx"
|
|
191
|
-
import {
|
|
199
|
+
import { defineToolkit, useInlineRender } from "@assistant-ui/react-native";
|
|
192
200
|
import { Text } from "react-native";
|
|
193
201
|
import { useMemo } from "react";
|
|
194
202
|
|
|
@@ -199,12 +207,12 @@ export function useMyToolToolkit(someOuterProp: string) {
|
|
|
199
207
|
|
|
200
208
|
return useMemo(
|
|
201
209
|
() =>
|
|
202
|
-
({
|
|
210
|
+
defineToolkit({
|
|
203
211
|
my_tool: {
|
|
204
212
|
type: "backend",
|
|
205
213
|
render: stableRender,
|
|
206
214
|
},
|
|
207
|
-
})
|
|
215
|
+
}),
|
|
208
216
|
[stableRender],
|
|
209
217
|
);
|
|
210
218
|
}
|
|
@@ -29,6 +29,45 @@ Reference for the runtime's API surface. Start with [quickstart](/docs/runtimes/
|
|
|
29
29
|
| History | `adapters.history` | Per-thread message persistence. |
|
|
30
30
|
| Thread list | `adapters.threadList` | Multi-thread switching (experimental, see below). |
|
|
31
31
|
|
|
32
|
+
## Loading conversation history
|
|
33
|
+
|
|
34
|
+
If your backend exposes the persisted AG-UI messages of a conversation (for
|
|
35
|
+
example a `GET /agents/state` endpoint), use `fromAgUiMessages` to convert them
|
|
36
|
+
to assistant-ui messages and return them from the history adapter so the thread
|
|
37
|
+
is restored on page load:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { fromAgUiMessages } from "@assistant-ui/react-ag-ui";
|
|
41
|
+
import { ExportedMessageRepository } from "@assistant-ui/react";
|
|
42
|
+
|
|
43
|
+
const runtime = useAgUiRuntime({
|
|
44
|
+
agent,
|
|
45
|
+
adapters: {
|
|
46
|
+
history: {
|
|
47
|
+
async load() {
|
|
48
|
+
const { messages } = await fetch("/agents/state").then((r) => r.json());
|
|
49
|
+
return ExportedMessageRepository.fromArray(fromAgUiMessages(messages));
|
|
50
|
+
},
|
|
51
|
+
async append({ message }) {
|
|
52
|
+
// persist the newly sent message on your backend
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Messages sent during the session are always forwarded to the agent through the
|
|
60
|
+
run input, independent of `append`. A no-op `append` is therefore only safe when
|
|
61
|
+
your backend already persists the conversation on its own; otherwise those
|
|
62
|
+
messages are gone on the next page load.
|
|
63
|
+
|
|
64
|
+
`fromAgUiMessages` accepts an optional second argument: pass
|
|
65
|
+
`{ showThinking: false }` to match a runtime configured with
|
|
66
|
+
`showThinking: false`, so imported reasoning messages are dropped at conversion
|
|
67
|
+
time, the same way a live run never stores them.
|
|
68
|
+
|
|
69
|
+
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each message. Multimodal user input (`image`, `audio`, `video`, and `document` parts, as well as legacy `binary` parts) is restored as attachments on the user message, so a backend that persists multimodal messages shows them again on reload and re-sends them on the next run. Legacy `binary` parts that only reference a file id are not restored.
|
|
70
|
+
|
|
32
71
|
## Thread list (experimental)
|
|
33
72
|
|
|
34
73
|
<Callout type="warn">
|
|
@@ -353,15 +353,15 @@ Render the gate with a toolkit entry. The `approval` field carries the gate stat
|
|
|
353
353
|
import { useChat } from "@ai-sdk/react";
|
|
354
354
|
import {
|
|
355
355
|
AssistantRuntimeProvider,
|
|
356
|
+
defineToolkit,
|
|
356
357
|
Tools,
|
|
357
|
-
type Toolkit,
|
|
358
358
|
useAui,
|
|
359
359
|
} from "@assistant-ui/react";
|
|
360
360
|
import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
|
|
361
361
|
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
362
362
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
363
363
|
|
|
364
|
-
const toolkit = {
|
|
364
|
+
const toolkit = defineToolkit({
|
|
365
365
|
deploy: {
|
|
366
366
|
type: "backend",
|
|
367
367
|
render: ({ args, approval, respondToApproval, result }) => {
|
|
@@ -387,7 +387,7 @@ const toolkit = {
|
|
|
387
387
|
return <p>Deployed {result.deployed}</p>;
|
|
388
388
|
},
|
|
389
389
|
},
|
|
390
|
-
}
|
|
390
|
+
});
|
|
391
391
|
|
|
392
392
|
export default function Page() {
|
|
393
393
|
const chat = useChat({
|
|
@@ -7,8 +7,8 @@ assistant-ui exposes runtime integrations at three layers. Understanding which l
|
|
|
7
7
|
|
|
8
8
|
## The three layers
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
graph TD
|
|
10
|
+
<Flow.Root
|
|
11
|
+
llm={`graph TD
|
|
12
12
|
subgraph Framework["Framework adapters"]
|
|
13
13
|
A1[react-ai-sdk]
|
|
14
14
|
A2[react-langgraph]
|
|
@@ -34,8 +34,50 @@ graph TD
|
|
|
34
34
|
A6 --> C2
|
|
35
35
|
A7 --> C2
|
|
36
36
|
P1 --> C1
|
|
37
|
-
P2 --> C2
|
|
38
|
-
|
|
37
|
+
P2 --> C2`}
|
|
38
|
+
>
|
|
39
|
+
<Flow.Canvas
|
|
40
|
+
edges={[
|
|
41
|
+
{ from: "datastream", to: "local", route: "down", midFrac: 0.4, laneOffset: -60 },
|
|
42
|
+
{ from: "transport", to: "external", route: "down", midFrac: 0.65, toOffset: -14 },
|
|
43
|
+
{ from: "adapters", to: "external", route: "down", midFrac: 0.4, toOffset: 14 },
|
|
44
|
+
]}
|
|
45
|
+
>
|
|
46
|
+
<div className="grid max-w-2xl grid-cols-[1fr_1.6fr] gap-4">
|
|
47
|
+
<Flow.Group>
|
|
48
|
+
<Flow.GroupLabel>Protocol layers</Flow.GroupLabel>
|
|
49
|
+
<Flow.Column className="items-stretch gap-5">
|
|
50
|
+
<Flow.Row className="justify-start">
|
|
51
|
+
<Flow.Node flowId="datastream">DataStream</Flow.Node>
|
|
52
|
+
</Flow.Row>
|
|
53
|
+
<Flow.Row className="justify-end">
|
|
54
|
+
<Flow.Node flowId="transport">AssistantTransport</Flow.Node>
|
|
55
|
+
</Flow.Row>
|
|
56
|
+
</Flow.Column>
|
|
57
|
+
</Flow.Group>
|
|
58
|
+
<Flow.Group flowId="adapters">
|
|
59
|
+
<Flow.GroupLabel>Framework adapters</Flow.GroupLabel>
|
|
60
|
+
<Flow.Row className="flex-wrap justify-start">
|
|
61
|
+
<Flow.Node>react-ai-sdk</Flow.Node>
|
|
62
|
+
<Flow.Node>react-langgraph</Flow.Node>
|
|
63
|
+
<Flow.Node>react-langchain</Flow.Node>
|
|
64
|
+
<Flow.Node>react-google-adk</Flow.Node>
|
|
65
|
+
<Flow.Node>react-a2a</Flow.Node>
|
|
66
|
+
<Flow.Node>react-ag-ui</Flow.Node>
|
|
67
|
+
<Flow.Node>react-opencode</Flow.Node>
|
|
68
|
+
</Flow.Row>
|
|
69
|
+
</Flow.Group>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="h-14" aria-hidden />
|
|
72
|
+
<Flow.Group className="mx-auto w-fit">
|
|
73
|
+
<Flow.GroupLabel>Core runtimes</Flow.GroupLabel>
|
|
74
|
+
<Flow.Row className="gap-10">
|
|
75
|
+
<Flow.Node flowId="local">LocalRuntime</Flow.Node>
|
|
76
|
+
<Flow.Node flowId="external">ExternalStoreRuntime</Flow.Node>
|
|
77
|
+
</Flow.Row>
|
|
78
|
+
</Flow.Group>
|
|
79
|
+
</Flow.Canvas>
|
|
80
|
+
</Flow.Root>
|
|
39
81
|
|
|
40
82
|
Each upper layer is implemented in terms of a lower one. You can drop down a layer whenever you need more control, but most users start at the framework layer and never touch the others.
|
|
41
83
|
|
|
@@ -30,7 +30,7 @@ A non-exhaustive list of `unstable_` exports surfaced in the runtime docs.
|
|
|
30
30
|
| API | Package | Notes |
|
|
31
31
|
| --- | --- | --- |
|
|
32
32
|
| `unstable_createMessageConverter` | `@assistant-ui/react` | Message-format converter used by AssistantTransport and DataStream. |
|
|
33
|
-
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause
|
|
33
|
+
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause the run until a result is added via `addResult`. Only available on LocalRuntime; not supported in DataStream. |
|
|
34
34
|
| `unstable_threadListAdapter` | `@assistant-ui/react-langgraph` | LangGraph thread-list adapter slot on `useLangGraphRuntime`. |
|
|
35
35
|
| `unstable_createLangGraphStream` | `@assistant-ui/react-langgraph` | End-to-end cancellation primitive. |
|
|
36
36
|
| `unstable_Provider` | Various adapters | Thread-scoped provider on `RemoteThreadListAdapter`. Must render children synchronously. |
|
|
@@ -25,11 +25,17 @@ If you only need message streaming, [DataStream](/docs/runtimes/custom/data-stre
|
|
|
25
25
|
|
|
26
26
|
## Mental model
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
graph LR
|
|
28
|
+
<Flow.Root
|
|
29
|
+
llm={`graph LR
|
|
30
30
|
Frontend -->|Commands| Agent[Agent server]
|
|
31
|
-
Agent -->|State snapshots| Frontend
|
|
32
|
-
|
|
31
|
+
Agent -->|State snapshots| Frontend`}
|
|
32
|
+
>
|
|
33
|
+
<Flow.Row>
|
|
34
|
+
<Flow.Node>Frontend</Flow.Node>
|
|
35
|
+
<Flow.Arrow label="Commands" reverseLabel="State snapshots" length={150} />
|
|
36
|
+
<Flow.Node>Agent server</Flow.Node>
|
|
37
|
+
</Flow.Row>
|
|
38
|
+
</Flow.Root>
|
|
33
39
|
|
|
34
40
|
The frontend receives state snapshots and converts them to React components. The UI is a stateless view on top of the agent state.
|
|
35
41
|
|
|
@@ -37,21 +43,57 @@ The agent server receives commands from the frontend. When a user interacts with
|
|
|
37
43
|
|
|
38
44
|
### Command lifecycle
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
graph LR
|
|
46
|
+
<Flow.Root
|
|
47
|
+
llm={`graph LR
|
|
42
48
|
queued -->|sent to backend| in_transit
|
|
43
|
-
in_transit -->|backend processes| applied
|
|
44
|
-
|
|
49
|
+
in_transit -->|backend processes| applied`}
|
|
50
|
+
>
|
|
51
|
+
<Flow.Row>
|
|
52
|
+
<Flow.Node>queued</Flow.Node>
|
|
53
|
+
<Flow.Arrow label="sent to backend" length={120} />
|
|
54
|
+
<Flow.Node>in_transit</Flow.Node>
|
|
55
|
+
<Flow.Arrow label="backend processes" length={132} />
|
|
56
|
+
<Flow.Node>applied</Flow.Node>
|
|
57
|
+
</Flow.Row>
|
|
58
|
+
</Flow.Root>
|
|
45
59
|
|
|
46
60
|
The runtime alternates between **idle** (no active backend request) and **sending** (request in flight). When a new command is created while idle, it is sent immediately; otherwise it is queued until the current request completes.
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
graph LR
|
|
62
|
+
<Flow.Root
|
|
63
|
+
llm={`graph LR
|
|
50
64
|
idle -->|new command| sending
|
|
51
65
|
sending -->|request completes| check{check queue}
|
|
52
66
|
check -->|queue has commands| sending
|
|
53
|
-
check -->|queue empty| idle
|
|
54
|
-
|
|
67
|
+
check -->|queue empty| idle`}
|
|
68
|
+
>
|
|
69
|
+
<Flow.Canvas
|
|
70
|
+
edges={[
|
|
71
|
+
{
|
|
72
|
+
from: "check",
|
|
73
|
+
to: "sending",
|
|
74
|
+
route: "loop-bottom",
|
|
75
|
+
label: "queue has commands",
|
|
76
|
+
laneOffset: 28,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
from: "check",
|
|
80
|
+
to: "idle",
|
|
81
|
+
route: "loop-bottom",
|
|
82
|
+
label: "queue empty",
|
|
83
|
+
laneOffset: 60,
|
|
84
|
+
},
|
|
85
|
+
]}
|
|
86
|
+
>
|
|
87
|
+
<Flow.Row>
|
|
88
|
+
<Flow.Node flowId="idle">idle</Flow.Node>
|
|
89
|
+
<Flow.Arrow label="new command" length={108} />
|
|
90
|
+
<Flow.Node flowId="sending">sending</Flow.Node>
|
|
91
|
+
<Flow.Arrow label="request completes" length={132} />
|
|
92
|
+
<Flow.Node flowId="check" variant="decision">check queue</Flow.Node>
|
|
93
|
+
</Flow.Row>
|
|
94
|
+
<div className="h-20" aria-hidden />
|
|
95
|
+
</Flow.Canvas>
|
|
96
|
+
</Flow.Root>
|
|
55
97
|
|
|
56
98
|
To implement this you build two pieces:
|
|
57
99
|
|
|
@@ -206,7 +206,7 @@ const runtime = useDataStreamRuntime({
|
|
|
206
206
|
## Tool integration
|
|
207
207
|
|
|
208
208
|
<Callout type="warn">
|
|
209
|
-
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime) directly if you need
|
|
209
|
+
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime#human-in-the-loop-tools) directly if you need them.
|
|
210
210
|
</Callout>
|
|
211
211
|
|
|
212
212
|
### Frontend tools
|