@assistant-ui/mcp-docs-server 0.1.7 → 0.1.9
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-ai-sdk-v5.md +24 -15
- package/.docs/organized/code-examples/with-assistant-transport.md +1599 -0
- package/.docs/organized/code-examples/with-cloud.md +12 -10
- package/.docs/organized/code-examples/with-external-store.md +10 -8
- package/.docs/organized/code-examples/with-ffmpeg.md +17 -14
- package/.docs/organized/code-examples/with-langgraph.md +83 -47
- package/.docs/organized/code-examples/with-parent-id-grouping.md +10 -8
- package/.docs/organized/code-examples/with-react-hook-form.md +17 -14
- package/.docs/raw/docs/api-reference/integrations/react-data-stream.mdx +194 -0
- package/.docs/raw/docs/api-reference/overview.mdx +6 -0
- package/.docs/raw/docs/api-reference/primitives/Composer.mdx +31 -0
- package/.docs/raw/docs/api-reference/primitives/Message.mdx +108 -3
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +59 -0
- package/.docs/raw/docs/api-reference/primitives/ThreadList.mdx +128 -0
- package/.docs/raw/docs/api-reference/primitives/ThreadListItem.mdx +160 -0
- package/.docs/raw/docs/api-reference/runtimes/AssistantRuntime.mdx +0 -11
- package/.docs/raw/docs/api-reference/runtimes/ComposerRuntime.mdx +3 -3
- package/.docs/raw/docs/copilots/assistant-frame.mdx +399 -0
- package/.docs/raw/docs/devtools.mdx +51 -0
- package/.docs/raw/docs/getting-started.mdx +20 -19
- package/.docs/raw/docs/guides/Attachments.mdx +6 -13
- package/.docs/raw/docs/guides/Tools.mdx +56 -13
- package/.docs/raw/docs/guides/context-api.mdx +574 -0
- package/.docs/raw/docs/migrations/v0-12.mdx +125 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +2 -2
- package/.docs/raw/docs/runtimes/custom/local.mdx +17 -4
- package/.docs/raw/docs/runtimes/data-stream.mdx +287 -0
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +6 -5
- package/.docs/raw/docs/runtimes/mastra/overview.mdx +3 -3
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +13 -13
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +5 -0
- package/.docs/raw/docs/ui/ThreadList.mdx +54 -16
- package/dist/{chunk-L4K23SWI.js → chunk-NVNFQ5ZO.js} +4 -1
- package/dist/index.js +1 -1
- package/dist/prepare-docs/prepare.js +1 -1
- package/dist/stdio.js +1 -1
- package/package.json +7 -7
- package/.docs/raw/docs/concepts/architecture.mdx +0 -19
- package/.docs/raw/docs/concepts/runtime-layer.mdx +0 -163
- package/.docs/raw/docs/concepts/why.mdx +0 -9
|
@@ -41,29 +41,22 @@ This adds `/components/assistant-ui/attachment.tsx` to your project.
|
|
|
41
41
|
</Step>
|
|
42
42
|
<Step>
|
|
43
43
|
|
|
44
|
-
###
|
|
44
|
+
### Set up Runtime (No Configuration Required)
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
For `useChatRuntime`, attachments work automatically without additional configuration:
|
|
47
47
|
|
|
48
48
|
```tsx title="/app/MyRuntimeProvider.tsx"
|
|
49
49
|
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
50
|
-
import {
|
|
51
|
-
CompositeAttachmentAdapter,
|
|
52
|
-
SimpleImageAttachmentAdapter,
|
|
53
|
-
SimpleTextAttachmentAdapter,
|
|
54
|
-
} from "@assistant-ui/react";
|
|
55
50
|
|
|
56
51
|
const runtime = useChatRuntime({
|
|
57
52
|
api: "/api/chat",
|
|
58
|
-
adapters: {
|
|
59
|
-
attachments: new CompositeAttachmentAdapter([
|
|
60
|
-
new SimpleImageAttachmentAdapter(),
|
|
61
|
-
new SimpleTextAttachmentAdapter(),
|
|
62
|
-
]),
|
|
63
|
-
},
|
|
64
53
|
});
|
|
65
54
|
```
|
|
66
55
|
|
|
56
|
+
<Callout type="info">
|
|
57
|
+
**Note:** The AI SDK runtime handles attachments automatically. For other runtimes like `useLocalRuntime`, you may still need to configure attachment adapters as shown in the [Creating Custom Attachment Adapters](#creating-custom-attachment-adapters) section below.
|
|
58
|
+
</Callout>
|
|
59
|
+
|
|
67
60
|
</Step>
|
|
68
61
|
<Step>
|
|
69
62
|
|
|
@@ -361,25 +361,68 @@ const RefundTool = makeAssistantTool({
|
|
|
361
361
|
|
|
362
362
|
### MCP (Model Context Protocol) Tools
|
|
363
363
|
|
|
364
|
-
Integration with MCP servers:
|
|
364
|
+
Integration with MCP servers using AI SDK v5's experimental MCP support:
|
|
365
|
+
|
|
366
|
+
<Callout type="warning">
|
|
367
|
+
MCP support in AI SDK v5 is experimental. The API may change in future releases.
|
|
368
|
+
Make sure to install the MCP SDK: `npm install @modelcontextprotocol/sdk`
|
|
369
|
+
</Callout>
|
|
365
370
|
|
|
366
371
|
```tsx
|
|
367
|
-
//
|
|
368
|
-
import {
|
|
372
|
+
// Server-side usage (e.g., in your API route)
|
|
373
|
+
import { experimental_createMCPClient, streamText } from "ai";
|
|
374
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
369
375
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
376
|
+
export async function POST(req: Request) {
|
|
377
|
+
// Create MCP client with stdio transport
|
|
378
|
+
const client = await experimental_createMCPClient({
|
|
379
|
+
transport: new StdioClientTransport({
|
|
373
380
|
command: "npx",
|
|
374
381
|
args: ["@modelcontextprotocol/server-github"],
|
|
375
|
-
},
|
|
376
|
-
}
|
|
377
|
-
|
|
382
|
+
}),
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
// Get tools from the MCP server
|
|
387
|
+
const tools = await client.tools();
|
|
388
|
+
|
|
389
|
+
const result = streamText({
|
|
390
|
+
model: openai("gpt-4o"),
|
|
391
|
+
tools,
|
|
392
|
+
messages: convertToModelMessages(messages),
|
|
393
|
+
});
|
|
378
394
|
|
|
379
|
-
|
|
395
|
+
return result.toUIMessageStreamResponse();
|
|
396
|
+
} finally {
|
|
397
|
+
// Always close the client to release resources
|
|
398
|
+
await client.close();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Frontend usage with assistant-ui
|
|
380
403
|
const runtime = useChatRuntime({
|
|
381
|
-
api: "/api/chat",
|
|
382
|
-
|
|
404
|
+
api: "/api/chat", // Your API route that uses MCP tools
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Alternative transport options:
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
412
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
413
|
+
|
|
414
|
+
// HTTP transport
|
|
415
|
+
const httpClient = await experimental_createMCPClient({
|
|
416
|
+
transport: new StreamableHTTPClientTransport(
|
|
417
|
+
new URL("http://localhost:3000/mcp")
|
|
418
|
+
),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Server-Sent Events transport
|
|
422
|
+
const sseClient = await experimental_createMCPClient({
|
|
423
|
+
transport: new SSEClientTransport(
|
|
424
|
+
new URL("http://localhost:3000/sse")
|
|
425
|
+
),
|
|
383
426
|
});
|
|
384
427
|
```
|
|
385
428
|
|
|
@@ -483,7 +526,7 @@ const resilientTool = tool({
|
|
|
483
526
|
} catch (error) {
|
|
484
527
|
lastError = error;
|
|
485
528
|
if (abortSignal.aborted) throw error; // Don't retry on abort
|
|
486
|
-
await new Promise((resolve) => setTimeout(resolve, 1000 * i));
|
|
529
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
|
|
487
530
|
}
|
|
488
531
|
}
|
|
489
532
|
|
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Context API
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The Context API provides direct access to assistant-ui's state management system, enabling you to build custom components that integrate seamlessly with the assistant runtime.
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
The Context API is assistant-ui's powerful state management system that enables you to build custom components with full access to the assistant's state and capabilities. It provides:
|
|
10
|
+
|
|
11
|
+
- **Reactive state access** - Subscribe to state changes with automatic re-renders
|
|
12
|
+
- **Action execution** - Trigger operations like sending messages or reloading responses
|
|
13
|
+
- **Event listening** - React to user interactions and system events
|
|
14
|
+
- **Scope-aware design** - Components automatically know their context (message, thread, etc.)
|
|
15
|
+
|
|
16
|
+
It's the foundation that powers all assistant-ui primitives. When the built-in components don't meet your needs, you can use the Context API to create custom components with the same capabilities.
|
|
17
|
+
|
|
18
|
+
The Context API is backed by the runtime you provide to `<AssistantRuntimeProvider>`. This runtime acts as a unified store that manages all assistant state, handles actions, and dispatches events across your entire application.
|
|
19
|
+
|
|
20
|
+
## Core Concepts
|
|
21
|
+
|
|
22
|
+
### Scopes and Hierarchy
|
|
23
|
+
|
|
24
|
+
assistant-ui organizes state into **scopes** - logical boundaries that provide access to relevant data and actions. Each scope corresponds to a specific part of the chat interface and automatically provides context-aware functionality.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
🗂️ ThreadList (threads) - Manages the list of conversations
|
|
28
|
+
├── 📄 ThreadListItem (threadListItem) - Individual thread in the list
|
|
29
|
+
└── 💬 Thread (thread) - Active conversation with messages
|
|
30
|
+
├── 🔵 Message (message) - User or assistant message
|
|
31
|
+
│ ├── 📝 Part (part) - Content within a message (text, tool calls, etc.)
|
|
32
|
+
│ ├── 📎 Attachment (attachment) - Files attached to messages
|
|
33
|
+
│ └── ✏️ Composer (composer) - Edit mode for existing messages
|
|
34
|
+
│ └── 📎 Attachment (attachment) - Files in edit mode
|
|
35
|
+
└── ✏️ Composer (composer) - New message input
|
|
36
|
+
└── 📎 Attachment (attachment) - Files being added
|
|
37
|
+
|
|
38
|
+
🔧 ToolUIs (toolUIs) - Custom UI components for tool calls
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**How scopes work:**
|
|
42
|
+
|
|
43
|
+
- Scopes are **automatically determined** by where your component is rendered
|
|
44
|
+
- A button inside a `<ThreadPrimitive.Messages>` automatically gets `message` scope
|
|
45
|
+
- A button inside a `<ComposerPrimitive.Attachments>` automatically gets `attachment` scope
|
|
46
|
+
- Child scopes can access parent scope data (e.g., a `message` component can access `thread` data)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// Inside a message component
|
|
50
|
+
function MessageButton() {
|
|
51
|
+
// ✅ Available: message scope (current message)
|
|
52
|
+
const role = useAssistantState(({ message }) => message.role);
|
|
53
|
+
|
|
54
|
+
// ✅ Available: thread scope (parent)
|
|
55
|
+
const isRunning = useAssistantState(({ thread }) => thread.isRunning);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### State Management Model
|
|
60
|
+
|
|
61
|
+
The Context API follows a predictable state management pattern:
|
|
62
|
+
|
|
63
|
+
1. **State** is immutable and flows down through scopes
|
|
64
|
+
2. **Actions** are methods that trigger state changes
|
|
65
|
+
3. **Events** notify components of state changes and user interactions
|
|
66
|
+
4. **Subscriptions** let components react to changes
|
|
67
|
+
|
|
68
|
+
## Essential Hooks
|
|
69
|
+
|
|
70
|
+
### useAssistantState
|
|
71
|
+
|
|
72
|
+
Read state reactively with automatic re-renders when values change. This hook works like Zustand's selector pattern - you provide a function that extracts the specific data you need, and your component only re-renders when that data changes.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { useAssistantState } from "@assistant-ui/react";
|
|
76
|
+
|
|
77
|
+
// Basic usage - extract a single property
|
|
78
|
+
const role = useAssistantState(({ message }) => message.role); // "user" | "assistant"
|
|
79
|
+
const isRunning = useAssistantState(({ thread }) => thread.isRunning); // boolean
|
|
80
|
+
|
|
81
|
+
// Access nested data
|
|
82
|
+
const attachmentCount = useAssistantState(
|
|
83
|
+
({ composer }) => composer.attachments.length,
|
|
84
|
+
);
|
|
85
|
+
const lastMessage = useAssistantState(({ thread }) => thread.messages.at(-1));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The selector function receives all available scopes for your component's location and should return a specific value. The component re-renders only when that returned value changes.
|
|
89
|
+
|
|
90
|
+
**Common patterns:**
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// Access multiple scopes
|
|
94
|
+
const canSend = useAssistantState(
|
|
95
|
+
({ thread, composer }) => !thread.isRunning && composer.text.length > 0,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Compute derived state
|
|
99
|
+
const messageCount = useAssistantState(({ thread }) => thread.messages.length);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Important:** Never create new objects in selectors. Return primitive values or stable references to avoid infinite re-renders.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// ❌ Bad - creates new object every time
|
|
106
|
+
const data = useAssistantState(({ message }) => ({
|
|
107
|
+
role: message.role,
|
|
108
|
+
content: message.content,
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
// ✅ Good - returns stable values
|
|
112
|
+
const role = useAssistantState(({ message }) => message.role);
|
|
113
|
+
const content = useAssistantState(({ message }) => message.content);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### useAssistantApi
|
|
117
|
+
|
|
118
|
+
Access the API instance for imperative operations and actions. Unlike `useAssistantState`, this hook returns a stable object that never changes, making it perfect for event handlers and imperative operations.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { useAssistantApi } from "@assistant-ui/react";
|
|
122
|
+
|
|
123
|
+
function CustomMessageActions() {
|
|
124
|
+
const api = useAssistantApi();
|
|
125
|
+
|
|
126
|
+
// Perform actions in event handlers
|
|
127
|
+
const handleSend = () => {
|
|
128
|
+
api.composer().send();
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleReload = () => {
|
|
132
|
+
api.message().reload();
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Read state imperatively when needed
|
|
136
|
+
const handleConditionalAction = () => {
|
|
137
|
+
const { isRunning } = api.thread().getState();
|
|
138
|
+
const { text } = api.composer().getState();
|
|
139
|
+
|
|
140
|
+
if (!isRunning && text.length > 0) {
|
|
141
|
+
api.composer().send();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div>
|
|
147
|
+
<button onClick={handleSend}>Send</button>
|
|
148
|
+
<button onClick={handleReload}>Reload</button>
|
|
149
|
+
<button onClick={handleConditionalAction}>Smart Send</button>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The API object is stable and doesn't cause re-renders. Use it for:
|
|
156
|
+
|
|
157
|
+
- **Triggering actions** in event handlers and callbacks
|
|
158
|
+
- **Reading current state** imperatively when you don't need subscriptions
|
|
159
|
+
- **Accessing nested scopes** programmatically
|
|
160
|
+
- **Checking scope availability** before performing actions
|
|
161
|
+
|
|
162
|
+
**Available actions by scope:**
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// Thread actions
|
|
166
|
+
api.thread().append(message);
|
|
167
|
+
api.thread().startRun(config);
|
|
168
|
+
api.thread().cancelRun();
|
|
169
|
+
api.thread().switchToNewThread();
|
|
170
|
+
api.thread().switchToThread(threadId);
|
|
171
|
+
api.thread().getState();
|
|
172
|
+
api.thread().message(idOrIndex);
|
|
173
|
+
api.thread().composer;
|
|
174
|
+
|
|
175
|
+
// Message actions
|
|
176
|
+
api.message().reload();
|
|
177
|
+
api.message().speak();
|
|
178
|
+
api.message().stopSpeaking();
|
|
179
|
+
api.message().submitFeedback({ type: "positive" | "negative" });
|
|
180
|
+
api.message().switchToBranch({ position, branchId });
|
|
181
|
+
api.message().getState();
|
|
182
|
+
api.message().part(indexOrToolCallId);
|
|
183
|
+
api.message().composer;
|
|
184
|
+
|
|
185
|
+
// Part actions
|
|
186
|
+
api.part().addResult(result);
|
|
187
|
+
api.part().getState();
|
|
188
|
+
|
|
189
|
+
// Composer actions
|
|
190
|
+
api.composer().send();
|
|
191
|
+
api.composer().setText(text);
|
|
192
|
+
api.composer().setRole(role);
|
|
193
|
+
api.composer().addAttachment(file);
|
|
194
|
+
api.composer().clearAttachments();
|
|
195
|
+
api.composer().reset();
|
|
196
|
+
api.composer().getState();
|
|
197
|
+
|
|
198
|
+
// Attachment actions
|
|
199
|
+
api.attachment().remove();
|
|
200
|
+
api.attachment().getState();
|
|
201
|
+
|
|
202
|
+
// ThreadList actions
|
|
203
|
+
api.threads().switchToNewThread();
|
|
204
|
+
api.threads().switchToThread(threadId);
|
|
205
|
+
api.threads().getState();
|
|
206
|
+
|
|
207
|
+
// ThreadListItem actions
|
|
208
|
+
api.threadListItem().switchTo();
|
|
209
|
+
api.threadListItem().rename(title);
|
|
210
|
+
api.threadListItem().archive();
|
|
211
|
+
api.threadListItem().unarchive();
|
|
212
|
+
api.threadListItem().delete();
|
|
213
|
+
api.threads().getState();
|
|
214
|
+
|
|
215
|
+
// ToolUIs actions
|
|
216
|
+
api.toolUIs().setToolUI(toolName, render);
|
|
217
|
+
api.toolUIs().getState();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### useAssistantEvent
|
|
221
|
+
|
|
222
|
+
Subscribe to events with automatic cleanup on unmount. This hook is perfect for reacting to user interactions, system events, or integrating with external analytics.
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
import { useAssistantEvent } from "@assistant-ui/react";
|
|
226
|
+
|
|
227
|
+
// Listen to current scope events (most common)
|
|
228
|
+
useAssistantEvent("composer.send", (event) => {
|
|
229
|
+
console.log("Current composer sent message:", event.message);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Listen to all events of a type across all scopes
|
|
233
|
+
useAssistantEvent({ event: "composer.send", scope: "*" }, (event) => {
|
|
234
|
+
console.log("Any composer sent a message:", event);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Listen to ALL events (useful for debugging or analytics)
|
|
238
|
+
useAssistantEvent("*", (event) => {
|
|
239
|
+
console.log("Event occurred:", event.type, "from:", event.source);
|
|
240
|
+
// Send to analytics, logging, etc.
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Practical example: Track user interactions
|
|
244
|
+
function AnalyticsTracker() {
|
|
245
|
+
useAssistantEvent("composer.send", (event) => {
|
|
246
|
+
analytics.track("message_sent", {
|
|
247
|
+
messageLength: event.message.content.length,
|
|
248
|
+
hasAttachments: event.message.attachments.length > 0,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
return null; // This component only tracks events
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Event name patterns:**
|
|
257
|
+
|
|
258
|
+
- Event names follow `source.action` format (e.g., `composer.send`, `thread.run-start`)
|
|
259
|
+
- Use `"*"` as the event name to listen to all events
|
|
260
|
+
- The `scope` parameter controls which instances trigger the event
|
|
261
|
+
|
|
262
|
+
## Working with Scopes
|
|
263
|
+
|
|
264
|
+
### Available Scopes
|
|
265
|
+
|
|
266
|
+
Each scope provides access to specific state and actions:
|
|
267
|
+
|
|
268
|
+
- **ThreadList** (`threads`): Collection and management of threads
|
|
269
|
+
- **ThreadListItem** (`threadListItem`): Individual thread in the list
|
|
270
|
+
- **Thread** (`thread`): Conversation with messages
|
|
271
|
+
- **Message** (`message`): Individual message (user or assistant)
|
|
272
|
+
- **Part** (`part`): Content part within a message (text, tool calls, etc.)
|
|
273
|
+
- **Composer** (`composer`): Text input for sending or editing messages
|
|
274
|
+
- **Attachment** (`attachment`): File or media attached to a message or composer
|
|
275
|
+
- **ToolUIs** (`toolUIs`): Tool UI components
|
|
276
|
+
|
|
277
|
+
### Scope Resolution
|
|
278
|
+
|
|
279
|
+
The Context API automatically resolves the current scope based on component location:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
function MessageButton() {
|
|
283
|
+
const api = useAssistantApi();
|
|
284
|
+
|
|
285
|
+
// Automatically uses the current message scope
|
|
286
|
+
const handleReload = () => {
|
|
287
|
+
api.message().reload();
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return <button onClick={handleReload}>Reload</button>;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Checking Scope Availability
|
|
295
|
+
|
|
296
|
+
Before accessing a scope, check if it's available:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
const api = useAssistantApi();
|
|
300
|
+
|
|
301
|
+
// Check if message scope exists
|
|
302
|
+
if (api.message.source) {
|
|
303
|
+
// Safe to use message scope
|
|
304
|
+
const { role } = api.message().getState();
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Accessing Nested Scopes
|
|
309
|
+
|
|
310
|
+
Navigate through the scope hierarchy programmatically:
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
const api = useAssistantApi();
|
|
314
|
+
|
|
315
|
+
// Access specific message by ID or index
|
|
316
|
+
const messageById = api.thread().message({ id: "msg_123" });
|
|
317
|
+
const messageByIndex = api.thread().message({ index: 0 });
|
|
318
|
+
|
|
319
|
+
// Access part by index or tool call ID
|
|
320
|
+
const partByIndex = api.message().part({ index: 0 });
|
|
321
|
+
const partByToolCall = api.message().part({ toolCallId: "call_123" });
|
|
322
|
+
|
|
323
|
+
// Access attachment by index
|
|
324
|
+
const attachment = api.composer().attachment({ index: 0 }).getState();
|
|
325
|
+
|
|
326
|
+
// Access thread from thread list
|
|
327
|
+
const thread = api.threads().thread("main");
|
|
328
|
+
const threadItem = api.threads().item({ id: "thread_123" });
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Common Patterns
|
|
332
|
+
|
|
333
|
+
### Conditional Rendering
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
function RunIndicator() {
|
|
337
|
+
const isRunning = useAssistantState(({ thread }) => thread.isRunning);
|
|
338
|
+
|
|
339
|
+
if (!isRunning) return null;
|
|
340
|
+
return <div>Assistant is thinking...</div>;
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Custom Action Buttons
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
function CopyButton() {
|
|
348
|
+
const api = useAssistantApi();
|
|
349
|
+
|
|
350
|
+
const handleCopy = () => {
|
|
351
|
+
navigator.clipboard.writeText(api.message().getCopyText());
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
return <button onClick={handleCopy}>Copy</button>;
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### State-Aware Components
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
function SmartComposer() {
|
|
362
|
+
const api = useAssistantApi();
|
|
363
|
+
const isRunning = useAssistantState(({ thread }) => thread.isRunning);
|
|
364
|
+
const text = useAssistantState(({ composer }) => composer.text);
|
|
365
|
+
|
|
366
|
+
const canSend = !isRunning && text.length > 0;
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<div>
|
|
370
|
+
<textarea
|
|
371
|
+
value={text}
|
|
372
|
+
onChange={(e) => api.composer().setText(e.target.value)}
|
|
373
|
+
disabled={isRunning}
|
|
374
|
+
/>
|
|
375
|
+
<button onClick={() => api.composer().send()} disabled={!canSend}>
|
|
376
|
+
Send
|
|
377
|
+
</button>
|
|
378
|
+
</div>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Event-Driven Updates
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
function MessageCounter() {
|
|
387
|
+
const [sendCount, setSendCount] = useState(0);
|
|
388
|
+
|
|
389
|
+
useAssistantEvent("composer.send", () => {
|
|
390
|
+
setSendCount((c) => c + 1);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
return <div>Messages sent: {sendCount}</div>;
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Advanced Topics
|
|
398
|
+
|
|
399
|
+
### Resolution Dynamics
|
|
400
|
+
|
|
401
|
+
When you call `api.scope()`, the API resolves the current scope at that moment. This resolution happens each time you call the function, which matters when dealing with changing contexts:
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
const api = useAssistantApi();
|
|
405
|
+
|
|
406
|
+
// Get current thread
|
|
407
|
+
const thread1 = api.thread();
|
|
408
|
+
thread1.append({ role: "user", content: "Hello" });
|
|
409
|
+
|
|
410
|
+
// User might switch threads here
|
|
411
|
+
|
|
412
|
+
// This could be a different thread
|
|
413
|
+
const thread2 = api.thread();
|
|
414
|
+
thread2.cancelRun(); // Cancels the current thread's run, not necessarily thread1's
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
For most use cases, this behavior is intuitive. In advanced scenarios where you need to track specific instances, store the resolved reference.
|
|
418
|
+
|
|
419
|
+
### Performance Optimization
|
|
420
|
+
|
|
421
|
+
**Selector optimization:**
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
// ❌ Expensive computation in selector (runs on every store update)
|
|
425
|
+
const result = useAssistantState(
|
|
426
|
+
({ thread }) => thread.messages.filter((m) => m.role === "user").length,
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// ✅ Memoize expensive computations
|
|
430
|
+
const messages = useAssistantState(({ thread }) => thread.messages);
|
|
431
|
+
const userCount = useMemo(
|
|
432
|
+
() => messages.filter((m) => m.role === "user").length,
|
|
433
|
+
[messages],
|
|
434
|
+
);
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Minimize re-renders:**
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
// ❌ Subscribes to entire thread state
|
|
441
|
+
const thread = useAssistantState(({ thread }) => thread);
|
|
442
|
+
|
|
443
|
+
// ✅ Subscribe only to needed values
|
|
444
|
+
const isRunning = useAssistantState(({ thread }) => thread.isRunning);
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## API Reference
|
|
448
|
+
|
|
449
|
+
### Hooks
|
|
450
|
+
|
|
451
|
+
| Hook | Purpose | Returns |
|
|
452
|
+
| ----------------------------------- | -------------------------- | -------------- |
|
|
453
|
+
| `useAssistantState(selector)` | Subscribe to state changes | Selected value |
|
|
454
|
+
| `useAssistantApi()` | Get API instance | API object |
|
|
455
|
+
| `useAssistantEvent(event, handler)` | Subscribe to events | void |
|
|
456
|
+
|
|
457
|
+
### Scope States
|
|
458
|
+
|
|
459
|
+
| Scope | Key State Properties | Description |
|
|
460
|
+
| -------------- | --------------------------------------------------------------------------------- | ------------------------------------------------ |
|
|
461
|
+
| ThreadList | `mainThreadId`, `threadIds`, `isLoading`, `threadItems` | Manages all available conversation threads |
|
|
462
|
+
| ThreadListItem | `id`, `title`, `status`, `remoteId`, `externalId` | Individual thread metadata and status |
|
|
463
|
+
| Thread | `isRunning`, `isLoading`, `isDisabled`, `messages`, `capabilities`, `suggestions` | Active conversation state and message history |
|
|
464
|
+
| Message | `role`, `content`, `status`, `attachments`, `parentId`, `branchNumber`, `isLast` | Individual message content and metadata |
|
|
465
|
+
| Composer | `text`, `role`, `attachments`, `isEmpty`, `canCancel`, `type`, `isEditing` | Text input state for new/edited messages |
|
|
466
|
+
| Part | `type`, `content`, `status`, `text`, `toolCallId`, `toolName` | Content parts within messages (text, tool calls) |
|
|
467
|
+
| Attachment | `id`, `type`, `name`, `url`, `size`, `mimeType` | File attachments metadata and content |
|
|
468
|
+
|
|
469
|
+
### Available Actions by Scope
|
|
470
|
+
|
|
471
|
+
| Scope | Actions | Use Cases |
|
|
472
|
+
| -------------- | --------------------------------------------------------------------- | ----------------------------------------- |
|
|
473
|
+
| ThreadList | `switchToNewThread()`, `switchToThread(id)`, `getState()` | Thread navigation and creation |
|
|
474
|
+
| ThreadListItem | `switchTo()`, `rename(title)`, `archive()`, `unarchive()`, `delete()` | Thread management operations |
|
|
475
|
+
| Thread | `append(message)`, `startRun()`, `cancelRun()`, `switchToNewThread()` | Message handling and conversation control |
|
|
476
|
+
| Message | `reload()`, `speak()`, `stopSpeaking()`, `submitFeedback(feedback)` | Message interactions and regeneration |
|
|
477
|
+
| Composer | `send()`, `setText(text)`, `addAttachment(file)`, `reset()` | Text input and message composition |
|
|
478
|
+
| Part | `addResult(result)`, `getState()` | Tool call result handling |
|
|
479
|
+
| Attachment | `remove()`, `getState()` | File management |
|
|
480
|
+
|
|
481
|
+
### Common Events
|
|
482
|
+
|
|
483
|
+
| Event | Description |
|
|
484
|
+
| -------------------------------- | ----------------------------- |
|
|
485
|
+
| `thread.run-start` | Assistant starts generating |
|
|
486
|
+
| `thread.run-end` | Assistant finishes generating |
|
|
487
|
+
| `thread.initialize` | Thread is initialized |
|
|
488
|
+
| `thread.model-context-update` | Model context is updated |
|
|
489
|
+
| `composer.send` | Message is sent |
|
|
490
|
+
| `composer.attachment-add` | Attachment added to composer |
|
|
491
|
+
| `thread-list-item.switched-to` | Switched to a thread |
|
|
492
|
+
| `thread-list-item.switched-away` | Switched away from a thread |
|
|
493
|
+
|
|
494
|
+
## Troubleshooting
|
|
495
|
+
|
|
496
|
+
### Common Errors
|
|
497
|
+
|
|
498
|
+
**"Cannot access [scope] outside of [scope] context"**
|
|
499
|
+
|
|
500
|
+
```tsx
|
|
501
|
+
// ❌ This will throw if not inside a message component
|
|
502
|
+
const role = useAssistantState(({ message }) => message.role);
|
|
503
|
+
|
|
504
|
+
// ✅ Check scope availability first
|
|
505
|
+
function SafeMessageButton() {
|
|
506
|
+
const api = useAssistantApi();
|
|
507
|
+
|
|
508
|
+
const role = useAssistantState(({ message }) =>
|
|
509
|
+
api.message.source !== undefined ? message.role : "none",
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
return <div>Role: {role}</div>;
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**"Maximum update depth exceeded" / Infinite re-renders**
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
// ❌ Creating new objects in selectors causes infinite re-renders
|
|
520
|
+
const data = useAssistantState(({ message }) => ({
|
|
521
|
+
role: message.role,
|
|
522
|
+
content: message.content, // New object every time!
|
|
523
|
+
}));
|
|
524
|
+
|
|
525
|
+
// ✅ Return primitive values or use separate selectors
|
|
526
|
+
const role = useAssistantState(({ message }) => message.role);
|
|
527
|
+
const content = useAssistantState(({ message }) => message.content);
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**"Scope resolution failed" / Stale scope references**
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
// ❌ Storing scope references can lead to stale data
|
|
534
|
+
const api = useAssistantApi();
|
|
535
|
+
const thread = api.thread(); // This reference might become stale
|
|
536
|
+
|
|
537
|
+
useEffect(() => {
|
|
538
|
+
// This might reference the wrong thread if user switched
|
|
539
|
+
thread.cancelRun();
|
|
540
|
+
}, [thread]);
|
|
541
|
+
|
|
542
|
+
// ✅ Resolve scopes fresh each time
|
|
543
|
+
const api = useAssistantApi();
|
|
544
|
+
|
|
545
|
+
useEffect(() => {
|
|
546
|
+
// Always gets the current thread
|
|
547
|
+
api.thread().cancelRun();
|
|
548
|
+
}, [api]);
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
## Quick Reference
|
|
552
|
+
|
|
553
|
+
```tsx
|
|
554
|
+
// Read state
|
|
555
|
+
const value = useAssistantState(({ scope }) => scope.property);
|
|
556
|
+
|
|
557
|
+
// Perform action
|
|
558
|
+
const api = useAssistantApi();
|
|
559
|
+
api.scope().action();
|
|
560
|
+
|
|
561
|
+
// Listen to events
|
|
562
|
+
useAssistantEvent("source.event", (e) => {});
|
|
563
|
+
|
|
564
|
+
// Check scope availability
|
|
565
|
+
if (api.scope.source) {
|
|
566
|
+
/* scope exists */
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Get state imperatively
|
|
570
|
+
const state = api.scope().getState();
|
|
571
|
+
|
|
572
|
+
// Navigate scopes
|
|
573
|
+
api.thread().message({ id: "..." }).getState();
|
|
574
|
+
```
|