@assistant-ui/mcp-docs-server 0.1.8 → 0.1.10
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 -17
- package/.docs/organized/code-examples/with-assistant-transport.md +1599 -0
- package/.docs/organized/code-examples/with-cloud.md +11 -11
- package/.docs/organized/code-examples/with-external-store.md +9 -9
- package/.docs/organized/code-examples/with-ffmpeg.md +17 -16
- package/.docs/organized/code-examples/with-langgraph.md +33 -110
- package/.docs/organized/code-examples/with-parent-id-grouping.md +9 -9
- package/.docs/organized/code-examples/with-react-hook-form.md +17 -16
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +40 -8
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +42 -66
- package/.docs/raw/docs/copilots/assistant-frame.mdx +18 -16
- package/.docs/raw/docs/devtools.mdx +51 -0
- package/.docs/raw/docs/getting-started.mdx +2 -4
- package/.docs/raw/docs/guides/ToolUI.mdx +112 -37
- package/.docs/raw/docs/guides/Tools.mdx +170 -6
- package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +324 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +2 -2
- package/.docs/raw/docs/runtimes/custom/local.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +55 -20
- 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/ui/Thread.mdx +368 -5
- package/package.json +8 -8
- package/.docs/raw/docs/migrations/v0-7.mdx +0 -188
- package/.docs/raw/docs/migrations/v0-8.mdx +0 -160
- package/.docs/raw/docs/migrations/v0-9.mdx +0 -75
- package/.docs/raw/docs/ui/primitives/Thread.mdx +0 -197
|
@@ -359,13 +359,167 @@ const RefundTool = makeAssistantTool({
|
|
|
359
359
|
});
|
|
360
360
|
```
|
|
361
361
|
|
|
362
|
+
### Tool Human Input
|
|
363
|
+
|
|
364
|
+
Tools can pause their execution to request user input or approval before continuing. This is useful for:
|
|
365
|
+
|
|
366
|
+
- Requesting user confirmation for sensitive operations
|
|
367
|
+
- Collecting additional information mid-execution
|
|
368
|
+
- Implementing progressive disclosure workflows
|
|
369
|
+
- Building interactive, multi-step tool experiences
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
import { makeAssistantTool, tool } from "@assistant-ui/react";
|
|
373
|
+
import { z } from "zod";
|
|
374
|
+
|
|
375
|
+
const confirmationTool = tool({
|
|
376
|
+
description: "Send an email with confirmation",
|
|
377
|
+
parameters: z.object({
|
|
378
|
+
to: z.string(),
|
|
379
|
+
subject: z.string(),
|
|
380
|
+
body: z.string(),
|
|
381
|
+
}),
|
|
382
|
+
execute: async ({ to, subject, body }, { human }) => {
|
|
383
|
+
// Request user confirmation before sending
|
|
384
|
+
const confirmed = await human({
|
|
385
|
+
type: "confirmation",
|
|
386
|
+
action: "send-email",
|
|
387
|
+
details: { to, subject },
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (!confirmed) {
|
|
391
|
+
return {
|
|
392
|
+
status: "cancelled",
|
|
393
|
+
message: "Email sending cancelled by user",
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Proceed with sending the email
|
|
398
|
+
await sendEmail({ to, subject, body });
|
|
399
|
+
return { status: "sent", message: `Email sent to ${to}` };
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const EmailTool = makeAssistantTool({
|
|
404
|
+
...confirmationTool,
|
|
405
|
+
toolName: "sendEmail",
|
|
406
|
+
render: ({ args, result, interrupt, resume }) => {
|
|
407
|
+
// The interrupt payload is available when the tool is waiting for user input
|
|
408
|
+
if (interrupt) {
|
|
409
|
+
return (
|
|
410
|
+
<div className="confirmation-dialog">
|
|
411
|
+
<h3>Confirm Email</h3>
|
|
412
|
+
<p>Send email to: {interrupt.payload.details.to}</p>
|
|
413
|
+
<p>Subject: {interrupt.payload.details.subject}</p>
|
|
414
|
+
<div className="actions">
|
|
415
|
+
<button onClick={() => resume(true)}>Confirm</button>
|
|
416
|
+
<button onClick={() => resume(false)}>Cancel</button>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Show the result after completion
|
|
423
|
+
if (result) {
|
|
424
|
+
return (
|
|
425
|
+
<div className="email-result">
|
|
426
|
+
<p>{result.message}</p>
|
|
427
|
+
</div>
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Show loading state
|
|
432
|
+
return <div>Preparing email...</div>;
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### Human Input Behavior
|
|
438
|
+
|
|
439
|
+
- **Payload**: The object passed to `human()` is available in the `render` function as `interrupt.payload`
|
|
440
|
+
- **Type**: The `interrupt` object has the structure `{ type: "human", payload: ... }`
|
|
441
|
+
- **Resume**: Call `resume(payload)` to continue execution - the payload becomes the resolved value of the `human()` call
|
|
442
|
+
- **Multiple Requests**: If `human()` is called multiple times, previous requests are automatically rejected with an error
|
|
443
|
+
- **Cancellation**: If the tool execution is aborted (e.g., user cancels the message), all pending requests are rejected
|
|
444
|
+
|
|
445
|
+
#### Advanced Human Input Patterns
|
|
446
|
+
|
|
447
|
+
You can use human input for complex multi-step interactions:
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
const wizardTool = tool({
|
|
451
|
+
description: "Multi-step data processing wizard",
|
|
452
|
+
parameters: z.object({
|
|
453
|
+
dataSource: z.string(),
|
|
454
|
+
}),
|
|
455
|
+
execute: async ({ dataSource }, { human }) => {
|
|
456
|
+
// Step 1: Load data
|
|
457
|
+
const data = await loadData(dataSource);
|
|
458
|
+
|
|
459
|
+
// Step 2: Request user to select columns
|
|
460
|
+
const selectedColumns = await human({
|
|
461
|
+
type: "column-selection",
|
|
462
|
+
availableColumns: data.columns,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Step 3: Request processing options
|
|
466
|
+
const options = await human({
|
|
467
|
+
type: "processing-options",
|
|
468
|
+
columns: selectedColumns,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Step 4: Process data with user selections
|
|
472
|
+
const result = await processData(data, selectedColumns, options);
|
|
473
|
+
return result;
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const WizardTool = makeAssistantTool({
|
|
478
|
+
...wizardTool,
|
|
479
|
+
toolName: "dataWizard",
|
|
480
|
+
render: ({ args, result, interrupt, resume }) => {
|
|
481
|
+
if (interrupt?.payload.type === "column-selection") {
|
|
482
|
+
return (
|
|
483
|
+
<ColumnSelector
|
|
484
|
+
columns={interrupt.payload.availableColumns}
|
|
485
|
+
onSelect={(cols) => resume(cols)}
|
|
486
|
+
/>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (interrupt?.payload.type === "processing-options") {
|
|
491
|
+
return (
|
|
492
|
+
<ProcessingOptions
|
|
493
|
+
columns={interrupt.payload.columns}
|
|
494
|
+
onConfirm={(opts) => resume(opts)}
|
|
495
|
+
/>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (result) {
|
|
500
|
+
return <ResultDisplay data={result} />;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return <div>Loading...</div>;
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
<Callout type="note">
|
|
509
|
+
When a tool calls `human()` multiple times (e.g., for multi-step
|
|
510
|
+
workflows), each new request automatically rejects any previous pending
|
|
511
|
+
request. Make sure to handle potential errors if you need to support
|
|
512
|
+
cancellation of earlier steps.
|
|
513
|
+
</Callout>
|
|
514
|
+
|
|
362
515
|
### MCP (Model Context Protocol) Tools
|
|
363
516
|
|
|
364
517
|
Integration with MCP servers using AI SDK v5's experimental MCP support:
|
|
365
518
|
|
|
366
519
|
<Callout type="warning">
|
|
367
|
-
MCP support in AI SDK v5 is experimental. The API may change in future
|
|
368
|
-
Make sure to install the MCP SDK: `npm install
|
|
520
|
+
MCP support in AI SDK v5 is experimental. The API may change in future
|
|
521
|
+
releases. Make sure to install the MCP SDK: `npm install
|
|
522
|
+
@modelcontextprotocol/sdk`
|
|
369
523
|
</Callout>
|
|
370
524
|
|
|
371
525
|
```tsx
|
|
@@ -414,15 +568,13 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
|
414
568
|
// HTTP transport
|
|
415
569
|
const httpClient = await experimental_createMCPClient({
|
|
416
570
|
transport: new StreamableHTTPClientTransport(
|
|
417
|
-
new URL("http://localhost:3000/mcp")
|
|
571
|
+
new URL("http://localhost:3000/mcp"),
|
|
418
572
|
),
|
|
419
573
|
});
|
|
420
574
|
|
|
421
575
|
// Server-Sent Events transport
|
|
422
576
|
const sseClient = await experimental_createMCPClient({
|
|
423
|
-
transport: new SSEClientTransport(
|
|
424
|
-
new URL("http://localhost:3000/sse")
|
|
425
|
-
),
|
|
577
|
+
transport: new SSEClientTransport(new URL("http://localhost:3000/sse")),
|
|
426
578
|
});
|
|
427
579
|
```
|
|
428
580
|
|
|
@@ -560,9 +712,21 @@ Tools receive additional context during execution:
|
|
|
560
712
|
execute: async (args, context) => {
|
|
561
713
|
// context.abortSignal - AbortSignal for cancellation
|
|
562
714
|
// context.toolCallId - Unique identifier for this invocation
|
|
715
|
+
// context.human - Function to request human input
|
|
716
|
+
|
|
717
|
+
// Example: Request user confirmation
|
|
718
|
+
const userResponse = await context.human({
|
|
719
|
+
message: "Are you sure?",
|
|
720
|
+
});
|
|
563
721
|
};
|
|
564
722
|
```
|
|
565
723
|
|
|
724
|
+
The execution context provides:
|
|
725
|
+
|
|
726
|
+
- **`abortSignal`**: An `AbortSignal` that triggers when the tool execution is cancelled
|
|
727
|
+
- **`toolCallId`**: A unique identifier for this specific tool invocation
|
|
728
|
+
- **`human`**: A function that pauses execution and requests user input. The payload passed to `human()` becomes available in the render function, and the value passed to `resume()` becomes the resolved value of the `human()` call
|
|
729
|
+
|
|
566
730
|
## Runtime Integration
|
|
567
731
|
|
|
568
732
|
Each integration handles tools differently:
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Migrating to react-langgraph v0.7
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import { Callout } from "fumadocs-ui/components/callout";
|
|
6
|
+
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This guide helps you migrate from the previous LangGraph integration pattern to the new simplified API introduced in `@assistant-ui/react-langgraph` v0.7. The new API consolidates thread management directly into `useLangGraphRuntime`, eliminating the need for separate runtime hooks and manual thread state management.
|
|
11
|
+
|
|
12
|
+
## Key Changes
|
|
13
|
+
|
|
14
|
+
### 1. Simplified Thread Management
|
|
15
|
+
|
|
16
|
+
The `useLangGraphRuntime` hook now directly handles thread lifecycle:
|
|
17
|
+
- No more `useRemoteThreadListRuntime` wrapper
|
|
18
|
+
- No more separate runtime hook functions
|
|
19
|
+
- Thread management is built into the core runtime
|
|
20
|
+
|
|
21
|
+
### 2. New `initialize` Parameter
|
|
22
|
+
|
|
23
|
+
The `stream` function now receives an `initialize` parameter that handles thread creation and loading automatically.
|
|
24
|
+
|
|
25
|
+
### 3. Direct Cloud Integration
|
|
26
|
+
|
|
27
|
+
Cloud persistence can now be configured directly in `useLangGraphRuntime` with the `cloud` parameter.
|
|
28
|
+
|
|
29
|
+
## Migration Steps
|
|
30
|
+
|
|
31
|
+
<Steps>
|
|
32
|
+
|
|
33
|
+
<Step>
|
|
34
|
+
|
|
35
|
+
### Update Your Runtime Implementation
|
|
36
|
+
|
|
37
|
+
#### Before (Old Pattern)
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import {
|
|
41
|
+
useCloudThreadListRuntime,
|
|
42
|
+
useThreadListItemRuntime,
|
|
43
|
+
} from "@assistant-ui/react";
|
|
44
|
+
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
45
|
+
|
|
46
|
+
const useMyLangGraphRuntime = () => {
|
|
47
|
+
const threadListItemRuntime = useThreadListItemRuntime();
|
|
48
|
+
|
|
49
|
+
const runtime = useLangGraphRuntime({
|
|
50
|
+
stream: async function* (messages) {
|
|
51
|
+
const { externalId } = await threadListItemRuntime.initialize();
|
|
52
|
+
if (!externalId) throw new Error("Thread not found");
|
|
53
|
+
|
|
54
|
+
return sendMessage({
|
|
55
|
+
threadId: externalId,
|
|
56
|
+
messages,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
onSwitchToThread: async (externalId) => {
|
|
60
|
+
const state = await getThreadState(externalId);
|
|
61
|
+
return {
|
|
62
|
+
messages: state.values.messages,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return runtime;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// In your component:
|
|
71
|
+
const runtime = useCloudThreadListRuntime({
|
|
72
|
+
cloud,
|
|
73
|
+
runtimeHook: useMyLangGraphRuntime,
|
|
74
|
+
create: async () => {
|
|
75
|
+
const { thread_id } = await createThread();
|
|
76
|
+
return { externalId: thread_id };
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### After (New Pattern)
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
85
|
+
|
|
86
|
+
// Directly in your component:
|
|
87
|
+
const runtime = useLangGraphRuntime({
|
|
88
|
+
cloud, // Optional: for cloud persistence
|
|
89
|
+
stream: async function* (messages, { initialize }) {
|
|
90
|
+
const { externalId } = await initialize();
|
|
91
|
+
if (!externalId) throw new Error("Thread not found");
|
|
92
|
+
|
|
93
|
+
return sendMessage({
|
|
94
|
+
threadId: externalId,
|
|
95
|
+
messages,
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
create: async () => {
|
|
99
|
+
const { thread_id } = await createThread();
|
|
100
|
+
return { externalId: thread_id };
|
|
101
|
+
},
|
|
102
|
+
load: async (externalId) => {
|
|
103
|
+
const state = await getThreadState(externalId);
|
|
104
|
+
return {
|
|
105
|
+
messages: state.values.messages,
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
</Step>
|
|
112
|
+
|
|
113
|
+
<Step>
|
|
114
|
+
|
|
115
|
+
### Update Import Statements
|
|
116
|
+
|
|
117
|
+
Remove unused imports:
|
|
118
|
+
|
|
119
|
+
```diff
|
|
120
|
+
- import {
|
|
121
|
+
- useCloudThreadListRuntime,
|
|
122
|
+
- useThreadListItemRuntime,
|
|
123
|
+
- } from "@assistant-ui/react";
|
|
124
|
+
+ import { AssistantCloud } from "@assistant-ui/react";
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
</Step>
|
|
128
|
+
|
|
129
|
+
<Step>
|
|
130
|
+
|
|
131
|
+
### Simplify Component Structure
|
|
132
|
+
|
|
133
|
+
You no longer need a separate runtime hook function. Everything can be defined directly in your component or provider:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
export function MyRuntimeProvider({ children }) {
|
|
137
|
+
const cloud = useMemo(
|
|
138
|
+
() => new AssistantCloud({
|
|
139
|
+
baseUrl: process.env.NEXT_PUBLIC_ASSISTANT_BASE_URL,
|
|
140
|
+
}),
|
|
141
|
+
[]
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const runtime = useLangGraphRuntime({
|
|
145
|
+
cloud,
|
|
146
|
+
// All configuration inline
|
|
147
|
+
stream: async function* (messages, { initialize }) {
|
|
148
|
+
// Your stream implementation
|
|
149
|
+
},
|
|
150
|
+
create: async () => {
|
|
151
|
+
// Your create implementation
|
|
152
|
+
},
|
|
153
|
+
load: async (externalId) => {
|
|
154
|
+
// Your load implementation
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
160
|
+
{children}
|
|
161
|
+
</AssistantRuntimeProvider>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
</Step>
|
|
167
|
+
|
|
168
|
+
<Step>
|
|
169
|
+
|
|
170
|
+
### Update Method Names
|
|
171
|
+
|
|
172
|
+
Rename methods to match the new API:
|
|
173
|
+
|
|
174
|
+
- `onSwitchToThread` → `load`
|
|
175
|
+
- `onSwitchToNewThread` → handled automatically via `create`
|
|
176
|
+
- Thread ID management is now automatic
|
|
177
|
+
|
|
178
|
+
</Step>
|
|
179
|
+
|
|
180
|
+
</Steps>
|
|
181
|
+
|
|
182
|
+
## API Reference Changes
|
|
183
|
+
|
|
184
|
+
### Old API
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
type OldLangGraphRuntimeOptions = {
|
|
188
|
+
threadId?: string;
|
|
189
|
+
stream: (messages: Message[]) => AsyncGenerator;
|
|
190
|
+
onSwitchToNewThread?: () => Promise<void>;
|
|
191
|
+
onSwitchToThread?: (threadId: string) => Promise<ThreadState>;
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### New API
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
type NewLangGraphRuntimeOptions = {
|
|
199
|
+
cloud?: AssistantCloud;
|
|
200
|
+
stream: (
|
|
201
|
+
messages: Message[],
|
|
202
|
+
context: {
|
|
203
|
+
initialize: () => Promise<{
|
|
204
|
+
remoteId: string;
|
|
205
|
+
externalId: string | undefined;
|
|
206
|
+
}>
|
|
207
|
+
}
|
|
208
|
+
) => AsyncGenerator;
|
|
209
|
+
create?: () => Promise<{ externalId: string }>;
|
|
210
|
+
load?: (externalId: string) => Promise<ThreadState>;
|
|
211
|
+
delete?: (externalId: string) => Promise<void>;
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Benefits of the New API
|
|
216
|
+
|
|
217
|
+
1. **Simpler Setup**: No need for multiple runtime hooks and wrappers
|
|
218
|
+
2. **Cleaner Code**: All configuration in one place
|
|
219
|
+
3. **Better Type Safety**: More explicit types for thread management
|
|
220
|
+
4. **Automatic Thread Handling**: The runtime manages thread lifecycle internally
|
|
221
|
+
5. **Optional Cloud Integration**: Add cloud persistence with a single parameter
|
|
222
|
+
|
|
223
|
+
## Common Migration Issues
|
|
224
|
+
|
|
225
|
+
<Callout type="warning">
|
|
226
|
+
**Breaking Change**: The `threadId` and `onSwitchToNewThread` parameters are no longer supported. Use the new `create` and `load` methods instead.
|
|
227
|
+
</Callout>
|
|
228
|
+
|
|
229
|
+
### Issue: `threadListItemRuntime` is not defined
|
|
230
|
+
|
|
231
|
+
**Solution**: Remove references to `useThreadListItemRuntime()`. Use the `initialize` parameter in the stream function instead.
|
|
232
|
+
|
|
233
|
+
### Issue: Thread switching doesn't work
|
|
234
|
+
|
|
235
|
+
**Solution**: Ensure you've implemented both `create` and `load` functions. The runtime needs both to manage thread lifecycle.
|
|
236
|
+
|
|
237
|
+
### Issue: Cloud persistence not working
|
|
238
|
+
|
|
239
|
+
**Solution**: Pass the `AssistantCloud` instance directly to `useLangGraphRuntime` via the `cloud` parameter.
|
|
240
|
+
|
|
241
|
+
## Example: Complete Migration
|
|
242
|
+
|
|
243
|
+
Here's a complete before and after example for a typical LangGraph integration:
|
|
244
|
+
|
|
245
|
+
### Before
|
|
246
|
+
|
|
247
|
+
```tsx title="runtime-provider.tsx"
|
|
248
|
+
import {
|
|
249
|
+
AssistantCloud,
|
|
250
|
+
AssistantRuntimeProvider,
|
|
251
|
+
useCloudThreadListRuntime,
|
|
252
|
+
useThreadListItemRuntime,
|
|
253
|
+
} from "@assistant-ui/react";
|
|
254
|
+
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
255
|
+
|
|
256
|
+
const useMyRuntime = () => {
|
|
257
|
+
const threadListItemRuntime = useThreadListItemRuntime();
|
|
258
|
+
|
|
259
|
+
return useLangGraphRuntime({
|
|
260
|
+
stream: async function* (messages) {
|
|
261
|
+
const { externalId } = await threadListItemRuntime.initialize();
|
|
262
|
+
// ... implementation
|
|
263
|
+
},
|
|
264
|
+
onSwitchToThread: async (externalId) => {
|
|
265
|
+
// ... implementation
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export function Provider({ children }) {
|
|
271
|
+
const cloud = new AssistantCloud({ /* config */ });
|
|
272
|
+
|
|
273
|
+
const runtime = useCloudThreadListRuntime({
|
|
274
|
+
cloud,
|
|
275
|
+
runtimeHook: useMyRuntime,
|
|
276
|
+
create: async () => { /* ... */ },
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
281
|
+
{children}
|
|
282
|
+
</AssistantRuntimeProvider>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### After
|
|
288
|
+
|
|
289
|
+
```tsx title="runtime-provider.tsx"
|
|
290
|
+
import {
|
|
291
|
+
AssistantCloud,
|
|
292
|
+
AssistantRuntimeProvider,
|
|
293
|
+
} from "@assistant-ui/react";
|
|
294
|
+
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
295
|
+
|
|
296
|
+
export function Provider({ children }) {
|
|
297
|
+
const cloud = new AssistantCloud({ /* config */ });
|
|
298
|
+
|
|
299
|
+
const runtime = useLangGraphRuntime({
|
|
300
|
+
cloud,
|
|
301
|
+
stream: async function* (messages, { initialize }) {
|
|
302
|
+
const { externalId } = await initialize();
|
|
303
|
+
// ... implementation
|
|
304
|
+
},
|
|
305
|
+
create: async () => { /* ... */ },
|
|
306
|
+
load: async (externalId) => {
|
|
307
|
+
// ... implementation (formerly onSwitchToThread)
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
313
|
+
{children}
|
|
314
|
+
</AssistantRuntimeProvider>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Need Help?
|
|
320
|
+
|
|
321
|
+
If you encounter issues during migration:
|
|
322
|
+
1. Check the updated [LangGraph documentation](/docs/runtimes/langgraph)
|
|
323
|
+
2. Review the [example implementation](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-langgraph)
|
|
324
|
+
3. Report issues on [GitHub](https://github.com/assistant-ui/assistant-ui/issues)
|
|
@@ -130,7 +130,7 @@ const runtime = useChatRuntime({
|
|
|
130
130
|
<Callout type="info">
|
|
131
131
|
By default, `useChatRuntime` uses `AssistantChatTransport` which automatically
|
|
132
132
|
forwards system messages and frontend tools to your backend API. This enables
|
|
133
|
-
your backend to receive the full context from the
|
|
133
|
+
your backend to receive the full context from the assistant-ui.
|
|
134
134
|
</Callout>
|
|
135
135
|
|
|
136
136
|
### Custom Transport Configuration
|
|
@@ -165,7 +165,7 @@ const runtime = useChatRuntime({
|
|
|
165
165
|
|
|
166
166
|
#### Transport Options
|
|
167
167
|
|
|
168
|
-
- **`AssistantChatTransport`** (default): Automatically forwards system messages and frontend tools from the
|
|
168
|
+
- **`AssistantChatTransport`** (default): Automatically forwards system messages and frontend tools from the assistant-ui context to your backend
|
|
169
169
|
- **`DefaultChatTransport`**: Standard AI SDK transport without automatic forwarding
|
|
170
170
|
|
|
171
171
|
### Using Frontend Tools with `frontendTools`
|
|
@@ -732,7 +732,7 @@ Provide follow-up suggestions:
|
|
|
732
732
|
|
|
733
733
|
```tsx
|
|
734
734
|
const suggestionAdapter: SuggestionAdapter = {
|
|
735
|
-
async *
|
|
735
|
+
async *generate({ messages }) {
|
|
736
736
|
// Analyze conversation context
|
|
737
737
|
const lastMessage = messages[messages.length - 1];
|
|
738
738
|
|
|
@@ -61,7 +61,6 @@ npm install @assistant-ui/react @assistant-ui/react-ui @assistant-ui/react-langg
|
|
|
61
61
|
```tsx twoslash title="@/api/api/[...path]/route.ts"
|
|
62
62
|
import { NextRequest, NextResponse } from "next/server";
|
|
63
63
|
|
|
64
|
-
|
|
65
64
|
function getCorsHeaders() {
|
|
66
65
|
return {
|
|
67
66
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -188,7 +187,6 @@ export const sendMessage = async (params: {
|
|
|
188
187
|
// ---cut---
|
|
189
188
|
"use client";
|
|
190
189
|
|
|
191
|
-
import { useRef } from "react";
|
|
192
190
|
import { Thread } from "@/components/assistant-ui";
|
|
193
191
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
194
192
|
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
@@ -196,27 +194,21 @@ import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
|
|
|
196
194
|
import { createThread, getThreadState, sendMessage } from "@/lib/chatApi";
|
|
197
195
|
|
|
198
196
|
export function MyAssistant() {
|
|
199
|
-
const threadIdRef = useRef<string | undefined>();
|
|
200
197
|
const runtime = useLangGraphRuntime({
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (!
|
|
204
|
-
const { thread_id } = await createThread();
|
|
205
|
-
threadIdRef.current = thread_id;
|
|
206
|
-
}
|
|
207
|
-
const threadId = threadIdRef.current;
|
|
198
|
+
stream: async (messages, { initialize }) => {
|
|
199
|
+
const { externalId } = await initialize();
|
|
200
|
+
if (!externalId) throw new Error("Thread not found");
|
|
208
201
|
return sendMessage({
|
|
209
|
-
threadId,
|
|
202
|
+
threadId: externalId,
|
|
210
203
|
messages,
|
|
211
204
|
});
|
|
212
205
|
},
|
|
213
|
-
|
|
206
|
+
create: async () => {
|
|
214
207
|
const { thread_id } = await createThread();
|
|
215
|
-
|
|
208
|
+
return { externalId: thread_id };
|
|
216
209
|
},
|
|
217
|
-
|
|
218
|
-
const state = await getThreadState(
|
|
219
|
-
threadIdRef.current = threadId;
|
|
210
|
+
load: async (externalId) => {
|
|
211
|
+
const state = await getThreadState(externalId);
|
|
220
212
|
return {
|
|
221
213
|
messages: state.values.messages,
|
|
222
214
|
interrupts: state.tasks[0]?.interrupts,
|
|
@@ -306,14 +298,57 @@ import { convertLangChainMessages } from "@assistant-ui/react-langgraph";
|
|
|
306
298
|
const threadMessage = convertLangChainMessages(langChainMessage);
|
|
307
299
|
```
|
|
308
300
|
|
|
309
|
-
##
|
|
301
|
+
## Thread Management
|
|
302
|
+
|
|
303
|
+
### Basic Thread Support
|
|
304
|
+
|
|
305
|
+
The `useLangGraphRuntime` hook now includes built-in thread management capabilities:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
const runtime = useLangGraphRuntime({
|
|
309
|
+
stream: async (messages, { initialize }) => {
|
|
310
|
+
// initialize() creates or loads a thread and returns its IDs
|
|
311
|
+
const { remoteId, externalId } = await initialize();
|
|
312
|
+
// Use externalId (your backend's thread ID) for API calls
|
|
313
|
+
return sendMessage({ threadId: externalId, messages });
|
|
314
|
+
},
|
|
315
|
+
create: async () => {
|
|
316
|
+
// Called when creating a new thread
|
|
317
|
+
const { thread_id } = await createThread();
|
|
318
|
+
return { externalId: thread_id };
|
|
319
|
+
},
|
|
320
|
+
load: async (externalId) => {
|
|
321
|
+
// Called when loading an existing thread
|
|
322
|
+
const state = await getThreadState(externalId);
|
|
323
|
+
return {
|
|
324
|
+
messages: state.values.messages,
|
|
325
|
+
interrupts: state.tasks[0]?.interrupts,
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
```
|
|
310
330
|
|
|
311
|
-
|
|
331
|
+
### Cloud Persistence
|
|
332
|
+
|
|
333
|
+
For persistent thread history across sessions, integrate with assistant-cloud:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const runtime = useLangGraphRuntime({
|
|
337
|
+
cloud: new AssistantCloud({
|
|
338
|
+
baseUrl: process.env.NEXT_PUBLIC_ASSISTANT_BASE_URL,
|
|
339
|
+
}),
|
|
340
|
+
// ... stream, create, load functions
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
See the [Cloud Persistence guide](/docs/cloud/persistence/langgraph) for detailed setup instructions.
|
|
345
|
+
|
|
346
|
+
## Interrupt Persistence
|
|
312
347
|
|
|
313
|
-
|
|
348
|
+
LangGraph supports interrupting the execution flow to request user input or handle specific interactions. These interrupts can be persisted and restored when switching between threads:
|
|
314
349
|
|
|
315
350
|
1. Make sure your thread state type includes the `interrupts` field
|
|
316
|
-
2. Return the interrupts from `
|
|
351
|
+
2. Return the interrupts from the `load` function along with the messages
|
|
317
352
|
3. The runtime will automatically restore the interrupt state when switching threads
|
|
318
353
|
|
|
319
354
|
This feature is particularly useful for applications that require user approval flows, multi-step forms, or any other interactive elements that might span multiple thread switches.
|