@assistant-ui/mcp-docs-server 0.1.12 → 0.1.14
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/with-ag-ui.md +1089 -0
- package/.docs/organized/code-examples/with-ai-sdk-v5.md +12 -21
- package/.docs/organized/code-examples/with-assistant-transport.md +10 -19
- package/.docs/organized/code-examples/with-cloud.md +7 -16
- package/.docs/organized/code-examples/with-external-store.md +6 -15
- package/.docs/organized/code-examples/with-ffmpeg.md +14 -21
- package/.docs/organized/code-examples/with-langgraph.md +5 -14
- package/.docs/organized/code-examples/with-parent-id-grouping.md +6 -15
- package/.docs/organized/code-examples/with-react-hook-form.md +10 -19
- package/.docs/raw/docs/api-reference/context-providers/AssistantRuntimeProvider.mdx +6 -1
- package/.docs/raw/docs/api-reference/integrations/vercel-ai-sdk.mdx +179 -70
- package/.docs/raw/docs/cloud/authorization.mdx +2 -2
- package/.docs/raw/docs/copilots/model-context.mdx +4 -5
- package/.docs/raw/docs/copilots/motivation.mdx +4 -4
- package/.docs/raw/docs/getting-started.mdx +8 -4
- package/.docs/raw/docs/guides/Attachments.mdx +2 -2
- package/.docs/raw/docs/guides/Tools.mdx +5 -5
- package/.docs/raw/docs/guides/context-api.mdx +5 -5
- package/.docs/raw/docs/migrations/v0-12.mdx +2 -2
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +6 -2
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +9 -0
- package/.docs/raw/docs/runtimes/custom/local.mdx +77 -4
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +7 -4
- package/.docs/raw/docs/runtimes/langserve.mdx +3 -8
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +13 -11
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +50 -31
- package/.docs/raw/docs/ui/PartGrouping.mdx +2 -2
- package/.docs/raw/docs/ui/Reasoning.mdx +174 -0
- package/dist/chunk-M2RKUM66.js +3 -3
- package/dist/chunk-NVNFQ5ZO.js +2 -2
- package/package.json +5 -6
|
@@ -5,19 +5,29 @@ title: "@assistant-ui/react-ai-sdk"
|
|
|
5
5
|
Vercel AI SDK integration for assistant-ui.
|
|
6
6
|
|
|
7
7
|
import { ParametersTable } from "@/components/docs";
|
|
8
|
+
import { Callout } from "fumadocs-ui/components/callout";
|
|
9
|
+
|
|
10
|
+
<Callout type="info">
|
|
11
|
+
This package provides integration with AI SDK v5. For AI SDK v4, see the [AI
|
|
12
|
+
SDK v4 (Legacy)](/docs/runtimes/ai-sdk/v4-legacy) documentation.
|
|
13
|
+
</Callout>
|
|
8
14
|
|
|
9
15
|
## API Reference
|
|
10
16
|
|
|
11
|
-
### `
|
|
17
|
+
### `useChatRuntime`
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
Creates a runtime directly with AI SDK v5's `useChat` hook integration. This is the recommended approach for most use cases.
|
|
14
20
|
|
|
15
21
|
```tsx
|
|
16
|
-
import {
|
|
22
|
+
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
|
|
23
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
17
24
|
|
|
18
25
|
const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
19
|
-
const
|
|
20
|
-
|
|
26
|
+
const runtime = useChatRuntime({
|
|
27
|
+
transport: new AssistantChatTransport({
|
|
28
|
+
api: "/api/chat",
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
21
31
|
|
|
22
32
|
return (
|
|
23
33
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -27,26 +37,86 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
|
27
37
|
};
|
|
28
38
|
```
|
|
29
39
|
|
|
40
|
+
To customize the API endpoint, simply change the `api` parameter:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
const runtime = useChatRuntime({
|
|
44
|
+
transport: new AssistantChatTransport({
|
|
45
|
+
api: "/my-custom-api/chat",
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
30
50
|
<ParametersTable
|
|
31
51
|
parameters={[
|
|
32
52
|
{
|
|
33
|
-
name: "
|
|
34
|
-
type: "
|
|
35
|
-
description: "
|
|
53
|
+
name: "options",
|
|
54
|
+
type: "UseChatRuntimeOptions",
|
|
55
|
+
description: "Configuration options for the chat runtime.",
|
|
56
|
+
children: [
|
|
57
|
+
{
|
|
58
|
+
type: "UseChatRuntimeOptions",
|
|
59
|
+
parameters: [
|
|
60
|
+
{
|
|
61
|
+
name: "api",
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "The API endpoint URL. Defaults to '/api/chat'.",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "transport",
|
|
67
|
+
type: "ChatTransport",
|
|
68
|
+
description:
|
|
69
|
+
"Custom transport implementation. Defaults to AssistantChatTransport which forwards system messages and tools.",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "cloud",
|
|
73
|
+
type: "AssistantCloud",
|
|
74
|
+
description:
|
|
75
|
+
"Optional AssistantCloud instance for chat persistence.",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "initialMessages",
|
|
79
|
+
type: "UIMessage[]",
|
|
80
|
+
description: "Initial messages to populate the chat.",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "onFinish",
|
|
84
|
+
type: "(message: UIMessage) => void",
|
|
85
|
+
description: "Callback when a message completes streaming.",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "onError",
|
|
89
|
+
type: "(error: Error) => void",
|
|
90
|
+
description: "Callback for handling errors.",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
36
95
|
},
|
|
37
96
|
]}
|
|
38
97
|
/>
|
|
39
98
|
|
|
40
|
-
|
|
99
|
+
<Callout type="info">
|
|
100
|
+
By default, `useChatRuntime` uses `AssistantChatTransport` which automatically
|
|
101
|
+
forwards system messages and frontend tools to your backend API. This enables
|
|
102
|
+
your backend to receive the full context from the assistant-ui.
|
|
103
|
+
</Callout>
|
|
104
|
+
|
|
105
|
+
### `useAISDKRuntime`
|
|
41
106
|
|
|
42
|
-
|
|
107
|
+
For advanced use cases where you need direct access to the `useChat` hook from AI SDK.
|
|
43
108
|
|
|
44
109
|
```tsx
|
|
45
|
-
import {
|
|
110
|
+
import { useChat } from "@ai-sdk/react";
|
|
111
|
+
import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
|
|
112
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
46
113
|
|
|
47
114
|
const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
48
|
-
const
|
|
49
|
-
|
|
115
|
+
const chat = useChat({
|
|
116
|
+
api: "/api/chat",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const runtime = useAISDKRuntime(chat);
|
|
50
120
|
|
|
51
121
|
return (
|
|
52
122
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -59,83 +129,76 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
|
59
129
|
<ParametersTable
|
|
60
130
|
parameters={[
|
|
61
131
|
{
|
|
62
|
-
name: "
|
|
63
|
-
type: "ReturnType<typeof
|
|
64
|
-
description: "The
|
|
132
|
+
name: "chat",
|
|
133
|
+
type: "ReturnType<typeof useChat>",
|
|
134
|
+
description: "The chat helpers from @ai-sdk/react's useChat hook.",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "options",
|
|
138
|
+
type: "AISDKRuntimeOptions",
|
|
139
|
+
description: "Optional configuration options.",
|
|
140
|
+
children: [
|
|
141
|
+
{
|
|
142
|
+
type: "AISDKRuntimeOptions",
|
|
143
|
+
parameters: [
|
|
144
|
+
{
|
|
145
|
+
name: "cloud",
|
|
146
|
+
type: "AssistantCloud",
|
|
147
|
+
description:
|
|
148
|
+
"Optional AssistantCloud instance for chat persistence.",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "adapters",
|
|
152
|
+
type: "RuntimeAdapters",
|
|
153
|
+
description:
|
|
154
|
+
"Optional runtime adapters for attachments, feedback, speech, etc.",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
65
159
|
},
|
|
66
160
|
]}
|
|
67
161
|
/>
|
|
68
162
|
|
|
69
|
-
### `
|
|
163
|
+
### `AssistantChatTransport`
|
|
70
164
|
|
|
71
|
-
|
|
165
|
+
A transport that extends the default AI SDK transport to automatically forward system messages and frontend tools to your backend.
|
|
72
166
|
|
|
73
167
|
```tsx
|
|
74
|
-
import {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const input = m.content[0].text;
|
|
84
|
-
setMessages((currentConversation) => [
|
|
85
|
-
...currentConversation,
|
|
86
|
-
{ id: nanoid(), role: "user", display: input },
|
|
87
|
-
]);
|
|
88
|
-
|
|
89
|
-
const message = await continueConversation(input);
|
|
90
|
-
|
|
91
|
-
setMessages((currentConversation) => [...currentConversation, message]);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const runtime = useVercelRSCRuntime({ messages, onNew });
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
98
|
-
{children}
|
|
99
|
-
</AssistantRuntimeProvider>
|
|
100
|
-
);
|
|
101
|
-
};
|
|
168
|
+
import { AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
|
|
169
|
+
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
170
|
+
|
|
171
|
+
const runtime = useChatRuntime({
|
|
172
|
+
transport: new AssistantChatTransport({
|
|
173
|
+
api: "/my-custom-api/chat",
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
102
176
|
```
|
|
103
177
|
|
|
104
178
|
<ParametersTable
|
|
105
179
|
parameters={[
|
|
106
180
|
{
|
|
107
|
-
name: "
|
|
108
|
-
type: "
|
|
109
|
-
description: "
|
|
181
|
+
name: "options",
|
|
182
|
+
type: "HttpChatTransportInitOptions",
|
|
183
|
+
description: "Transport configuration options.",
|
|
110
184
|
children: [
|
|
111
185
|
{
|
|
112
|
-
type: "
|
|
186
|
+
type: "HttpChatTransportInitOptions",
|
|
113
187
|
parameters: [
|
|
114
188
|
{
|
|
115
|
-
name: "
|
|
116
|
-
type: "
|
|
117
|
-
description: "The
|
|
189
|
+
name: "api",
|
|
190
|
+
type: "string",
|
|
191
|
+
description: "The API endpoint URL.",
|
|
118
192
|
},
|
|
119
193
|
{
|
|
120
|
-
name: "
|
|
121
|
-
type: "
|
|
122
|
-
description: "
|
|
194
|
+
name: "headers",
|
|
195
|
+
type: "Record<string, string> | Headers",
|
|
196
|
+
description: "Optional headers to include in requests.",
|
|
123
197
|
},
|
|
124
198
|
{
|
|
125
|
-
name: "
|
|
126
|
-
type: "
|
|
127
|
-
description: "
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
name: "onReload",
|
|
131
|
-
type: "(parentId: string | null) => Promise<void>",
|
|
132
|
-
description: "A function to reload a message.",
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
name: "convertMessage",
|
|
136
|
-
type: "(message: TMessage) => VercelRSCMessage",
|
|
137
|
-
description:
|
|
138
|
-
"A function to convert messages to the VercelRSCMessage format. Only required if your message objects are not already compatible with Vercel RSC.",
|
|
199
|
+
name: "credentials",
|
|
200
|
+
type: "RequestCredentials",
|
|
201
|
+
description: "Optional credentials mode for fetch requests.",
|
|
139
202
|
},
|
|
140
203
|
],
|
|
141
204
|
},
|
|
@@ -143,3 +206,49 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
|
143
206
|
},
|
|
144
207
|
]}
|
|
145
208
|
/>
|
|
209
|
+
|
|
210
|
+
### `frontendTools`
|
|
211
|
+
|
|
212
|
+
Helper function to convert frontend tool definitions to AI SDK format for use in your backend.
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { frontendTools } from "@assistant-ui/react-ai-sdk";
|
|
216
|
+
import { streamText, convertToModelMessages } from "ai";
|
|
217
|
+
import { openai } from "@ai-sdk/openai";
|
|
218
|
+
|
|
219
|
+
export async function POST(req: Request) {
|
|
220
|
+
const { messages, system, tools } = await req.json();
|
|
221
|
+
|
|
222
|
+
const result = streamText({
|
|
223
|
+
model: openai("gpt-4o"),
|
|
224
|
+
system,
|
|
225
|
+
messages: convertToModelMessages(messages),
|
|
226
|
+
tools: {
|
|
227
|
+
// Wrap frontend tools with the helper
|
|
228
|
+
...frontendTools(tools),
|
|
229
|
+
// Your backend tools
|
|
230
|
+
myBackendTool: tool({
|
|
231
|
+
// ...
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return result.toUIMessageStreamResponse();
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
<ParametersTable
|
|
241
|
+
parameters={[
|
|
242
|
+
{
|
|
243
|
+
name: "tools",
|
|
244
|
+
type: "Record<string, unknown>",
|
|
245
|
+
description:
|
|
246
|
+
"Frontend tools object forwarded from AssistantChatTransport.",
|
|
247
|
+
},
|
|
248
|
+
]}
|
|
249
|
+
/>
|
|
250
|
+
|
|
251
|
+
<Callout type="info">
|
|
252
|
+
The `frontendTools` helper converts frontend tool definitions to the AI SDK
|
|
253
|
+
format and ensures they are properly handled by the streaming response.
|
|
254
|
+
</Callout>
|
|
@@ -70,7 +70,7 @@ export const POST = async (req: Request) => {
|
|
|
70
70
|
|
|
71
71
|
if (!userId) throw new Error("User not authenticated");
|
|
72
72
|
|
|
73
|
-
const workspaceId = orgId ? `${orgId}
|
|
73
|
+
const workspaceId = orgId ? `${orgId}_${userId}` : userId;
|
|
74
74
|
const assistantCloud = new AssistantCloud({
|
|
75
75
|
apiKey: process.env["ASSISTANT_API_KEY"]!,
|
|
76
76
|
userId,
|
|
@@ -91,7 +91,7 @@ const cloud = new AssistantCloud({
|
|
|
91
91
|
baseUrl: process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"]!,
|
|
92
92
|
authToken: () =>
|
|
93
93
|
fetch("/api/assistant-ui-token", { method: "POST" }).then((r) =>
|
|
94
|
-
r.
|
|
94
|
+
r.text(),
|
|
95
95
|
),
|
|
96
96
|
});
|
|
97
97
|
|
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
makeAssistantVisible,
|
|
34
34
|
makeAssistantTool,
|
|
35
35
|
tool,
|
|
36
|
-
useAssistantRuntime,
|
|
37
36
|
} from "@assistant-ui/react";
|
|
38
37
|
import { z } from "zod";
|
|
39
38
|
|
|
@@ -75,12 +74,12 @@ function Form() {
|
|
|
75
74
|
The context provider system allows components to contribute to the model context. Here's a typical usage pattern:
|
|
76
75
|
|
|
77
76
|
```tsx
|
|
78
|
-
import {
|
|
77
|
+
import { useAssistantApi, tool } from "@assistant-ui/react";
|
|
79
78
|
import { useEffect } from "react";
|
|
80
79
|
import { z } from "zod";
|
|
81
80
|
|
|
82
81
|
function MyComponent() {
|
|
83
|
-
const
|
|
82
|
+
const api = useAssistantApi();
|
|
84
83
|
|
|
85
84
|
// Define tool using the tool() helper
|
|
86
85
|
const myTool = tool({
|
|
@@ -95,13 +94,13 @@ function MyComponent() {
|
|
|
95
94
|
|
|
96
95
|
useEffect(() => {
|
|
97
96
|
// Register context provider
|
|
98
|
-
return
|
|
97
|
+
return api.modelContext().register({
|
|
99
98
|
getModelContext: () => ({
|
|
100
99
|
system: "You are a helpful search assistant...",
|
|
101
100
|
tools: { myTool },
|
|
102
101
|
}),
|
|
103
102
|
});
|
|
104
|
-
}, [
|
|
103
|
+
}, [api]); // Re-register if api changes
|
|
105
104
|
|
|
106
105
|
return <div>{/* component content */}</div>;
|
|
107
106
|
}
|
|
@@ -143,14 +143,14 @@ function SmartTransactionHistory() {
|
|
|
143
143
|
Finally, let's add dynamic context based on the user's transaction patterns:
|
|
144
144
|
|
|
145
145
|
```tsx
|
|
146
|
-
import {
|
|
146
|
+
import { useAssistantApi } from "@assistant-ui/react";
|
|
147
147
|
import { useEffect } from "react";
|
|
148
148
|
|
|
149
149
|
function SmartTransactionHistory({ userProfile }) {
|
|
150
|
-
const
|
|
150
|
+
const api = useAssistantApi();
|
|
151
151
|
|
|
152
152
|
useEffect(() => {
|
|
153
|
-
return
|
|
153
|
+
return api.modelContext().register({
|
|
154
154
|
getModelContext: () => ({
|
|
155
155
|
system: `
|
|
156
156
|
User spending patterns:
|
|
@@ -160,7 +160,7 @@ function SmartTransactionHistory({ userProfile }) {
|
|
|
160
160
|
`,
|
|
161
161
|
}),
|
|
162
162
|
});
|
|
163
|
-
}, [
|
|
163
|
+
}, [api, userProfile]);
|
|
164
164
|
|
|
165
165
|
// Previous components...
|
|
166
166
|
}
|
|
@@ -1453,13 +1453,15 @@ If you aren't using Next.js, you can also deploy this endpoint to Cloudflare Wor
|
|
|
1453
1453
|
|
|
1454
1454
|
```tsx title="/app/page.tsx" tab="Thread"
|
|
1455
1455
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
1456
|
-
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
1456
|
+
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
|
|
1457
1457
|
import { ThreadList } from "@/components/assistant-ui/thread-list";
|
|
1458
1458
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
1459
1459
|
|
|
1460
1460
|
const MyApp = () => {
|
|
1461
1461
|
const runtime = useChatRuntime({
|
|
1462
|
-
|
|
1462
|
+
transport: new AssistantChatTransport({
|
|
1463
|
+
api: "/api/chat",
|
|
1464
|
+
}),
|
|
1463
1465
|
});
|
|
1464
1466
|
|
|
1465
1467
|
return (
|
|
@@ -1477,12 +1479,14 @@ const MyApp = () => {
|
|
|
1477
1479
|
// run `npx shadcn@latest add "https://r.assistant-ui.com/assistant-modal"`
|
|
1478
1480
|
|
|
1479
1481
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
1480
|
-
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
1482
|
+
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
|
|
1481
1483
|
import { AssistantModal } from "@/components/assistant-ui/assistant-modal";
|
|
1482
1484
|
|
|
1483
1485
|
const MyApp = () => {
|
|
1484
1486
|
const runtime = useChatRuntime({
|
|
1485
|
-
|
|
1487
|
+
transport: new AssistantChatTransport({
|
|
1488
|
+
api: "/api/chat",
|
|
1489
|
+
}),
|
|
1486
1490
|
});
|
|
1487
1491
|
|
|
1488
1492
|
return (
|
|
@@ -508,14 +508,14 @@ class ValidatedImageAdapter implements AttachmentAdapter {
|
|
|
508
508
|
Enable multi-file selection with custom limits:
|
|
509
509
|
|
|
510
510
|
```tsx
|
|
511
|
-
const
|
|
511
|
+
const api = useAssistantApi();
|
|
512
512
|
|
|
513
513
|
const handleMultipleFiles = async (files: FileList) => {
|
|
514
514
|
const maxFiles = 5;
|
|
515
515
|
const filesToAdd = Array.from(files).slice(0, maxFiles);
|
|
516
516
|
|
|
517
517
|
for (const file of filesToAdd) {
|
|
518
|
-
await composer.addAttachment({ file });
|
|
518
|
+
await api.composer().addAttachment({ file });
|
|
519
519
|
}
|
|
520
520
|
};
|
|
521
521
|
```
|
|
@@ -165,15 +165,15 @@ function App() {
|
|
|
165
165
|
|
|
166
166
|
### 4. Advanced: Direct Context Registration
|
|
167
167
|
|
|
168
|
-
Use `
|
|
168
|
+
Use `api.modelContext().register()` when you need to configure more than just tools:
|
|
169
169
|
|
|
170
170
|
```tsx
|
|
171
|
-
import { tool,
|
|
171
|
+
import { tool, useAssistantApi } from "@assistant-ui/react";
|
|
172
172
|
import { useEffect, useState } from "react";
|
|
173
173
|
import { z } from "zod";
|
|
174
174
|
|
|
175
175
|
function MyComponent() {
|
|
176
|
-
const
|
|
176
|
+
const api = useAssistantApi();
|
|
177
177
|
const [isCreativeMode, setIsCreativeMode] = useState(false);
|
|
178
178
|
|
|
179
179
|
useEffect(() => {
|
|
@@ -188,7 +188,7 @@ function MyComponent() {
|
|
|
188
188
|
});
|
|
189
189
|
|
|
190
190
|
// Register tools with model configuration
|
|
191
|
-
return
|
|
191
|
+
return api.modelContext().register({
|
|
192
192
|
getModelContext: () => ({
|
|
193
193
|
tools: { calculate: calculateTool },
|
|
194
194
|
callSettings: {
|
|
@@ -198,7 +198,7 @@ function MyComponent() {
|
|
|
198
198
|
priority: 10, // Higher priority overrides other providers
|
|
199
199
|
}),
|
|
200
200
|
});
|
|
201
|
-
}, [
|
|
201
|
+
}, [api, isCreativeMode]);
|
|
202
202
|
|
|
203
203
|
return <div>{/* Your component */}</div>;
|
|
204
204
|
}
|
|
@@ -35,7 +35,7 @@ assistant-ui organizes state into **scopes** - logical boundaries that provide a
|
|
|
35
35
|
└── ✏️ Composer (composer) - New message input
|
|
36
36
|
└── 📎 Attachment (attachment) - Files being added
|
|
37
37
|
|
|
38
|
-
🔧
|
|
38
|
+
🔧 Tools (tools) - Custom UI components for tool calls
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
**How scopes work:**
|
|
@@ -212,9 +212,9 @@ api.threadListItem().unarchive();
|
|
|
212
212
|
api.threadListItem().delete();
|
|
213
213
|
api.threads().getState();
|
|
214
214
|
|
|
215
|
-
//
|
|
216
|
-
api.
|
|
217
|
-
api.
|
|
215
|
+
// Tools actions
|
|
216
|
+
api.tools().setToolUI(toolName, render);
|
|
217
|
+
api.tools().getState();
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
### useAssistantEvent
|
|
@@ -272,7 +272,7 @@ Each scope provides access to specific state and actions:
|
|
|
272
272
|
- **Part** (`part`): Content part within a message (text, tool calls, etc.)
|
|
273
273
|
- **Composer** (`composer`): Text input for sending or editing messages
|
|
274
274
|
- **Attachment** (`attachment`): File or media attached to a message or composer
|
|
275
|
-
- **
|
|
275
|
+
- **Tools** (`tools`): Tool UI components
|
|
276
276
|
|
|
277
277
|
### Scope Resolution
|
|
278
278
|
|
|
@@ -20,8 +20,8 @@ The following hooks have been removed:
|
|
|
20
20
|
|
|
21
21
|
- `useMessageUtils` → Use `useAssistantState(({ message }) => message.isHovering)` / `useAssistantState(({ message }) => message.isCopied)`
|
|
22
22
|
- `useMessageUtilsStore` → Use `useAssistantApi()` with `api.message().setIsHovering()` / `api.message().setIsCopied()`
|
|
23
|
-
- `useToolUIs` → Use `useAssistantState(({
|
|
24
|
-
- `useToolUIsStore` → Use `useAssistantApi()` with `api.
|
|
23
|
+
- `useToolUIs` → Use `useAssistantState(({ tools }) => tools)` and `useAssistantApi()` with `api.tools()`
|
|
24
|
+
- `useToolUIsStore` → Use `useAssistantApi()` with `api.tools()`
|
|
25
25
|
|
|
26
26
|
**Deprecated Hooks:**
|
|
27
27
|
|
|
@@ -94,10 +94,14 @@ export async function POST(req: Request) {
|
|
|
94
94
|
|
|
95
95
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
96
96
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
97
|
-
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
97
|
+
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
|
|
98
98
|
|
|
99
99
|
export default function Home() {
|
|
100
|
-
const runtime = useChatRuntime(
|
|
100
|
+
const runtime = useChatRuntime({
|
|
101
|
+
transport: new AssistantChatTransport({
|
|
102
|
+
api: "/api/chat",
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
101
105
|
|
|
102
106
|
return (
|
|
103
107
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
@@ -109,6 +109,15 @@ When the hook mounts it calls `list()` on your adapter, hydrates existing thread
|
|
|
109
109
|
async delete(remoteId) {
|
|
110
110
|
await fetch(`/api/threads/${remoteId}`, { method: "DELETE" });
|
|
111
111
|
},
|
|
112
|
+
async fetch(remoteId) {
|
|
113
|
+
const response = await fetch(`/api/threads/${remoteId}`);
|
|
114
|
+
const thread = await response.json();
|
|
115
|
+
return {
|
|
116
|
+
status: thread.is_archived ? "archived" : "regular",
|
|
117
|
+
remoteId: thread.id,
|
|
118
|
+
title: thread.title,
|
|
119
|
+
};
|
|
120
|
+
},
|
|
112
121
|
async generateTitle(remoteId, messages) {
|
|
113
122
|
return createAssistantStream(async (controller) => {
|
|
114
123
|
const response = await fetch(`/api/threads/${remoteId}/title`, {
|
|
@@ -538,7 +538,12 @@ export function MyRuntimeProvider({ children }) {
|
|
|
538
538
|
```
|
|
539
539
|
|
|
540
540
|
<Callout type="info" title="Returning a title from generateTitle">
|
|
541
|
-
The `generateTitle` method must return an <code>AssistantStream</code>
|
|
541
|
+
The `generateTitle` method must return an <code>AssistantStream</code>{" "}
|
|
542
|
+
containing the title text. The easiest, type-safe way is to use{" "}
|
|
543
|
+
<code>createAssistantStream</code> and call{" "}
|
|
544
|
+
<code>controller.appendText(newTitle)</code> followed by{" "}
|
|
545
|
+
<code>controller.close()</code>. Returning a raw <code>ReadableStream</code>{" "}
|
|
546
|
+
won't update the thread list UI.
|
|
542
547
|
</Callout>
|
|
543
548
|
|
|
544
549
|
#### Understanding the Architecture
|
|
@@ -854,9 +859,9 @@ function MyCustomRuntimeProvider({ children }) {
|
|
|
854
859
|
</Callout>
|
|
855
860
|
|
|
856
861
|
<Callout type="warning">
|
|
857
|
-
**`useThreadRuntime` vs `useLocalThreadRuntime`:**
|
|
858
|
-
|
|
859
|
-
|
|
862
|
+
**`useThreadRuntime` vs `useLocalThreadRuntime`:** - `useThreadRuntime` -
|
|
863
|
+
Access the current thread's runtime from within components -
|
|
864
|
+
`useLocalThreadRuntime` - Create a new single-thread runtime instance
|
|
860
865
|
</Callout>
|
|
861
866
|
|
|
862
867
|
## Integration Examples
|
|
@@ -999,6 +1004,74 @@ async run({ messages }) { // ❌ Wrong for streaming
|
|
|
999
1004
|
```
|
|
1000
1005
|
</Callout>
|
|
1001
1006
|
|
|
1007
|
+
### Tool UI Flickers or Disappears During Streaming
|
|
1008
|
+
|
|
1009
|
+
A common issue when implementing a streaming `ChatModelAdapter` is seeing a tool's UI appear for a moment and then disappear. This is caused by failing to accumulate the `tool_calls` correctly across multiple stream chunks. State must be stored **outside** the streaming loop to persist.
|
|
1010
|
+
|
|
1011
|
+
**❌ Incorrect: Forgetting Previous Tool Calls**
|
|
1012
|
+
|
|
1013
|
+
This implementation incorrectly re-creates the `content` array for every chunk. If a later chunk contains only text, tool calls from previous chunks are lost, causing the UI to disappear.
|
|
1014
|
+
|
|
1015
|
+
```tsx
|
|
1016
|
+
// This implementation incorrectly re-creates the `content` array for every chunk.
|
|
1017
|
+
// If a later chunk contains only text, tool calls from previous chunks are lost.
|
|
1018
|
+
async *run({ messages, abortSignal, context }) {
|
|
1019
|
+
const stream = await backendApi({ messages, abortSignal, context });
|
|
1020
|
+
let text = "";
|
|
1021
|
+
|
|
1022
|
+
for await (const chunk of stream) {
|
|
1023
|
+
// ❌ DON'T: This overwrites toolCalls with only the current chunk's data
|
|
1024
|
+
const toolCalls = chunk.tool_calls || [];
|
|
1025
|
+
const content = [{ type: "text", text }];
|
|
1026
|
+
for (const toolCall of toolCalls) {
|
|
1027
|
+
content.push({
|
|
1028
|
+
type: "tool-call",
|
|
1029
|
+
toolName: toolCall.name,
|
|
1030
|
+
toolCallId: toolCall.id,
|
|
1031
|
+
args: toolCall.args,
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
yield { content }; // This yield might not contain the tool call anymore
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
**✅ Correct: Accumulating State**
|
|
1040
|
+
|
|
1041
|
+
This implementation uses a `Map` outside the loop to remember all tool calls.
|
|
1042
|
+
|
|
1043
|
+
```tsx
|
|
1044
|
+
// This implementation uses a Map outside the loop to remember all tool calls.
|
|
1045
|
+
async *run({ messages, abortSignal, context }) {
|
|
1046
|
+
const stream = await backendApi({ messages, abortSignal, context });
|
|
1047
|
+
let text = "";
|
|
1048
|
+
// ✅ DO: Declare state outside the loop
|
|
1049
|
+
const toolCallsMap = new Map();
|
|
1050
|
+
|
|
1051
|
+
for await (const chunk of stream) {
|
|
1052
|
+
text += chunk.content || "";
|
|
1053
|
+
|
|
1054
|
+
// ✅ DO: Add/update tool calls in the persistent map
|
|
1055
|
+
for (const toolCall of chunk.tool_calls || []) {
|
|
1056
|
+
toolCallsMap.set(toolCall.toolCallId, {
|
|
1057
|
+
type: "tool-call",
|
|
1058
|
+
toolName: toolCall.name,
|
|
1059
|
+
toolCallId: toolCall.toolCallId,
|
|
1060
|
+
args: toolCall.args,
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// ✅ DO: Build content from accumulated state
|
|
1065
|
+
const content = [
|
|
1066
|
+
...(text ? [{ type: "text", text }] : []),
|
|
1067
|
+
...Array.from(toolCallsMap.values()),
|
|
1068
|
+
];
|
|
1069
|
+
|
|
1070
|
+
yield { content }; // Yield the complete, correct state every time
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1002
1075
|
### Debug Tips
|
|
1003
1076
|
|
|
1004
1077
|
1. **Log adapter calls** to trace execution:
|