@assistant-ui/mcp-docs-server 0.1.1
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/local-ollama.md +1135 -0
- package/.docs/organized/code-examples/search-agent-for-e-commerce.md +1721 -0
- package/.docs/organized/code-examples/with-ai-sdk.md +1081 -0
- package/.docs/organized/code-examples/with-cloud.md +1164 -0
- package/.docs/organized/code-examples/with-external-store.md +1064 -0
- package/.docs/organized/code-examples/with-ffmpeg.md +1305 -0
- package/.docs/organized/code-examples/with-langgraph.md +1819 -0
- package/.docs/organized/code-examples/with-openai-assistants.md +1175 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +1727 -0
- package/.docs/organized/code-examples/with-vercel-ai-rsc.md +1157 -0
- package/.docs/raw/blog/2024-07-29-hello/index.mdx +65 -0
- package/.docs/raw/blog/2024-09-11/index.mdx +10 -0
- package/.docs/raw/blog/2024-12-15/index.mdx +10 -0
- package/.docs/raw/blog/2025-01-31-changelog/index.mdx +129 -0
- package/.docs/raw/docs/about-assistantui.mdx +44 -0
- package/.docs/raw/docs/api-reference/context-providers/AssistantRuntimeProvider.mdx +30 -0
- package/.docs/raw/docs/api-reference/context-providers/TextContentPartProvider.mdx +26 -0
- package/.docs/raw/docs/api-reference/integrations/react-hook-form.mdx +103 -0
- package/.docs/raw/docs/api-reference/integrations/vercel-ai-sdk.mdx +145 -0
- package/.docs/raw/docs/api-reference/overview.mdx +583 -0
- package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +264 -0
- package/.docs/raw/docs/api-reference/primitives/AssistantModal.mdx +129 -0
- package/.docs/raw/docs/api-reference/primitives/Attachment.mdx +96 -0
- package/.docs/raw/docs/api-reference/primitives/BranchPicker.mdx +87 -0
- package/.docs/raw/docs/api-reference/primitives/Composer.mdx +204 -0
- package/.docs/raw/docs/api-reference/primitives/ContentPart.mdx +173 -0
- package/.docs/raw/docs/api-reference/primitives/Error.mdx +70 -0
- package/.docs/raw/docs/api-reference/primitives/Message.mdx +181 -0
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +197 -0
- package/.docs/raw/docs/api-reference/primitives/composition.mdx +21 -0
- package/.docs/raw/docs/api-reference/runtimes/AssistantRuntime.mdx +33 -0
- package/.docs/raw/docs/api-reference/runtimes/AttachmentRuntime.mdx +46 -0
- package/.docs/raw/docs/api-reference/runtimes/ComposerRuntime.mdx +69 -0
- package/.docs/raw/docs/api-reference/runtimes/ContentPartRuntime.mdx +22 -0
- package/.docs/raw/docs/api-reference/runtimes/MessageRuntime.mdx +49 -0
- package/.docs/raw/docs/api-reference/runtimes/ThreadListItemRuntime.mdx +32 -0
- package/.docs/raw/docs/api-reference/runtimes/ThreadListRuntime.mdx +31 -0
- package/.docs/raw/docs/api-reference/runtimes/ThreadRuntime.mdx +48 -0
- package/.docs/raw/docs/architecture.mdx +92 -0
- package/.docs/raw/docs/cloud/authorization.mdx +152 -0
- package/.docs/raw/docs/cloud/overview.mdx +55 -0
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +54 -0
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +123 -0
- package/.docs/raw/docs/concepts/architecture.mdx +19 -0
- package/.docs/raw/docs/concepts/runtime-layer.mdx +163 -0
- package/.docs/raw/docs/concepts/why.mdx +9 -0
- package/.docs/raw/docs/copilots/make-assistant-readable.mdx +71 -0
- package/.docs/raw/docs/copilots/make-assistant-tool-ui.mdx +76 -0
- package/.docs/raw/docs/copilots/make-assistant-tool.mdx +117 -0
- package/.docs/raw/docs/copilots/model-context.mdx +135 -0
- package/.docs/raw/docs/copilots/motivation.mdx +191 -0
- package/.docs/raw/docs/copilots/use-assistant-instructions.mdx +62 -0
- package/.docs/raw/docs/getting-started.mdx +1133 -0
- package/.docs/raw/docs/guides/Attachments.mdx +640 -0
- package/.docs/raw/docs/guides/Branching.mdx +59 -0
- package/.docs/raw/docs/guides/Editing.mdx +56 -0
- package/.docs/raw/docs/guides/Speech.mdx +43 -0
- package/.docs/raw/docs/guides/ToolUI.mdx +663 -0
- package/.docs/raw/docs/guides/Tools.mdx +496 -0
- package/.docs/raw/docs/index.mdx +7 -0
- package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +85 -0
- package/.docs/raw/docs/legacy/styled/Decomposition.mdx +633 -0
- package/.docs/raw/docs/legacy/styled/Markdown.mdx +86 -0
- package/.docs/raw/docs/legacy/styled/Scrollbar.mdx +71 -0
- package/.docs/raw/docs/legacy/styled/Thread.mdx +84 -0
- package/.docs/raw/docs/legacy/styled/ThreadWidth.mdx +21 -0
- package/.docs/raw/docs/mcp-docs-server.mdx +324 -0
- package/.docs/raw/docs/migrations/deprecation-policy.mdx +41 -0
- package/.docs/raw/docs/migrations/v0-7.mdx +188 -0
- package/.docs/raw/docs/migrations/v0-8.mdx +160 -0
- package/.docs/raw/docs/migrations/v0-9.mdx +75 -0
- package/.docs/raw/docs/react-compatibility.mdx +208 -0
- package/.docs/raw/docs/runtimes/ai-sdk/rsc.mdx +226 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-assistant-hook.mdx +195 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat-hook.mdx +138 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +136 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +1624 -0
- package/.docs/raw/docs/runtimes/custom/local.mdx +1185 -0
- package/.docs/raw/docs/runtimes/helicone.mdx +60 -0
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +320 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +11 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +28 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +120 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +336 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +385 -0
- package/.docs/raw/docs/runtimes/langserve.mdx +126 -0
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +218 -0
- package/.docs/raw/docs/runtimes/mastra/overview.mdx +17 -0
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +196 -0
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +222 -0
- package/.docs/raw/docs/ui/AssistantModal.mdx +46 -0
- package/.docs/raw/docs/ui/AssistantSidebar.mdx +42 -0
- package/.docs/raw/docs/ui/Attachment.mdx +82 -0
- package/.docs/raw/docs/ui/Markdown.mdx +72 -0
- package/.docs/raw/docs/ui/Mermaid.mdx +79 -0
- package/.docs/raw/docs/ui/Scrollbar.mdx +59 -0
- package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +253 -0
- package/.docs/raw/docs/ui/Thread.mdx +47 -0
- package/.docs/raw/docs/ui/ThreadList.mdx +49 -0
- package/.docs/raw/docs/ui/ToolFallback.mdx +64 -0
- package/.docs/raw/docs/ui/primitives/Thread.mdx +197 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/chunk-C7O7EFKU.js +38 -0
- package/dist/chunk-CZCDQ3YH.js +420 -0
- package/dist/index.js +1 -0
- package/dist/prepare-docs/prepare.js +199 -0
- package/dist/stdio.js +8 -0
- package/package.json +43 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Part 3: Approval UI"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Background: LangGraph implementation details
|
|
6
|
+
|
|
7
|
+
import Image from "next/image";
|
|
8
|
+
import approval from "./images/stockbroker-langgraph.png";
|
|
9
|
+
|
|
10
|
+
<Image
|
|
11
|
+
src={approval}
|
|
12
|
+
alt="LangChain LangGraph"
|
|
13
|
+
width={600}
|
|
14
|
+
className="mx-auto rounded-lg border shadow"
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
Our LangGraph backend interrupts the `purchase_stock` tool execution in order to ensure the user confirms the purchase. The user confirms the purchase by submitting a tool message with the `approve` field set to `true`.
|
|
18
|
+
|
|
19
|
+
```ts title="assistant-ui-stockbroker/backend/src/index.ts" {6,18-19,32-35}
|
|
20
|
+
const purchaseApproval = async (state: typeof GraphAnnotation.State) => {
|
|
21
|
+
const { messages } = state;
|
|
22
|
+
const lastMessage = messages[messages.length - 1];
|
|
23
|
+
if (!(lastMessage instanceof ToolMessage)) {
|
|
24
|
+
// Interrupt the node to request permission to execute the purchase.
|
|
25
|
+
throw new NodeInterrupt("Please confirm the purchase before executing.");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const shouldExecutePurchase = (state: typeof GraphAnnotation.State) => {
|
|
30
|
+
const { messages } = state;
|
|
31
|
+
const lastMessage = messages[messages.length - 1];
|
|
32
|
+
if (!(lastMessage instanceof ToolMessage)) {
|
|
33
|
+
// Interrupt the node to request permission to execute the purchase.
|
|
34
|
+
throw new NodeInterrupt("Please confirm the purchase before executing.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { approve } = JSON.parse(lastMessage.content as string);
|
|
38
|
+
return approve ? "execute_purchase" : "agent";
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const workflow = new StateGraph(GraphAnnotation)
|
|
42
|
+
.addNode("agent", callModel)
|
|
43
|
+
.addEdge(START, "agent")
|
|
44
|
+
.addNode("tools", toolNode)
|
|
45
|
+
.addNode("prepare_purchase_details", preparePurchaseDetails)
|
|
46
|
+
.addNode("purchase_approval", purchaseApproval)
|
|
47
|
+
.addNode("execute_purchase", executePurchase)
|
|
48
|
+
.addEdge("prepare_purchase_details", "purchase_approval")
|
|
49
|
+
.addEdge("execute_purchase", END)
|
|
50
|
+
.addEdge("tools", "agent")
|
|
51
|
+
.addConditionalEdges("purchase_approval", shouldExecutePurchase, [
|
|
52
|
+
"agent",
|
|
53
|
+
"execute_purchase",
|
|
54
|
+
])
|
|
55
|
+
.addConditionalEdges("agent", shouldContinue, [
|
|
56
|
+
"tools",
|
|
57
|
+
END,
|
|
58
|
+
"prepare_purchase_details",
|
|
59
|
+
]);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Add approval UI
|
|
63
|
+
|
|
64
|
+
We create a new file under `/components/tools/purchase-stock/PurchaseStockTool.tsx` to define the tool.
|
|
65
|
+
|
|
66
|
+
First, we define the tool arguments and result types:
|
|
67
|
+
|
|
68
|
+
```ts title="@/components/tools/purchase-stock/PurchaseStockTool.tsx"
|
|
69
|
+
type PurchaseStockArgs = {
|
|
70
|
+
ticker: string;
|
|
71
|
+
companyName: string;
|
|
72
|
+
quantity: number;
|
|
73
|
+
maxPurchasePrice: number;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
type PurchaseStockResult = {
|
|
77
|
+
approve?: boolean;
|
|
78
|
+
cancelled?: boolean;
|
|
79
|
+
error?: string;
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Then we use `makeAssistantToolUI` to define the tool UI:
|
|
84
|
+
|
|
85
|
+
```tsx title="@/components/tools/purchase-stock/PurchaseStockTool.tsx"
|
|
86
|
+
"use client";
|
|
87
|
+
|
|
88
|
+
import { TransactionConfirmationPending } from "./transaction-confirmation-pending";
|
|
89
|
+
import { TransactionConfirmationFinal } from "./transaction-confirmation-final";
|
|
90
|
+
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
91
|
+
import { updateState } from "@/lib/chatApi";
|
|
92
|
+
|
|
93
|
+
export const PurchaseStockTool = makeAssistantToolUI<PurchaseStockArgs, string>(
|
|
94
|
+
{
|
|
95
|
+
toolName: "purchase_stock",
|
|
96
|
+
render: function PurchaseStockUI({ args, result, status, addResult }) {
|
|
97
|
+
const handleReject = async () => {
|
|
98
|
+
addResult({ approve: false });
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const handleConfirm = async () => {
|
|
102
|
+
addResult({ approve: true });
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className="mb-4 flex flex-col items-center gap-2">
|
|
107
|
+
<div>
|
|
108
|
+
<pre className="whitespace-pre-wrap break-all text-center">
|
|
109
|
+
purchase_stock({JSON.stringify(args)})
|
|
110
|
+
</pre>
|
|
111
|
+
</div>
|
|
112
|
+
{!result && status.type !== "running" && (
|
|
113
|
+
<TransactionConfirmationPending
|
|
114
|
+
{...args}
|
|
115
|
+
onConfirm={handleConfirm}
|
|
116
|
+
onReject={handleReject}
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Finally, we add a `TransactionConfirmationPending` component to ask for approval.
|
|
127
|
+
|
|
128
|
+
This requires shadcn/ui's `Card` and `Button` components. We will install them as a dependency.
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
npx shadcn@latest add card button
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Then create a new file under `/components/tools/purchase-stock/transaction-confirmation-pending.tsx` to define the approval UI.
|
|
135
|
+
|
|
136
|
+
```tsx title="@/components/tools/purchase-stock/transaction-confirmation-pending.tsx"
|
|
137
|
+
"use client";
|
|
138
|
+
|
|
139
|
+
import { CheckIcon, XIcon } from "lucide-react";
|
|
140
|
+
|
|
141
|
+
import { Button } from "@/components/ui/button";
|
|
142
|
+
import {
|
|
143
|
+
Card,
|
|
144
|
+
CardContent,
|
|
145
|
+
CardFooter,
|
|
146
|
+
CardHeader,
|
|
147
|
+
CardTitle,
|
|
148
|
+
} from "@/components/ui/card";
|
|
149
|
+
|
|
150
|
+
type TransactionConfirmation = {
|
|
151
|
+
ticker: string;
|
|
152
|
+
companyName: string;
|
|
153
|
+
quantity: number;
|
|
154
|
+
maxPurchasePrice: number;
|
|
155
|
+
onConfirm: () => void;
|
|
156
|
+
onReject: () => void;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export function TransactionConfirmationPending(props: TransactionConfirmation) {
|
|
160
|
+
const {
|
|
161
|
+
ticker,
|
|
162
|
+
companyName,
|
|
163
|
+
quantity,
|
|
164
|
+
maxPurchasePrice,
|
|
165
|
+
onConfirm,
|
|
166
|
+
onReject,
|
|
167
|
+
} = props;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<Card className="mx-auto w-full max-w-md">
|
|
171
|
+
<CardHeader>
|
|
172
|
+
<CardTitle className="text-2xl font-bold">
|
|
173
|
+
Confirm Transaction
|
|
174
|
+
</CardTitle>
|
|
175
|
+
</CardHeader>
|
|
176
|
+
<CardContent className="space-y-4">
|
|
177
|
+
<div className="grid grid-cols-2 gap-2">
|
|
178
|
+
<p className="text-muted-foreground text-sm font-medium">Ticker:</p>
|
|
179
|
+
<p className="text-sm font-bold">{ticker}</p>
|
|
180
|
+
<p className="text-muted-foreground text-sm font-medium">Company:</p>
|
|
181
|
+
<p className="text-sm">{companyName}</p>
|
|
182
|
+
<p className="text-muted-foreground text-sm font-medium">Quantity:</p>
|
|
183
|
+
<p className="text-sm">{quantity} shares</p>
|
|
184
|
+
<p className="text-muted-foreground text-sm font-medium">
|
|
185
|
+
Max Purchase Price:
|
|
186
|
+
</p>
|
|
187
|
+
<p className="text-sm">${maxPurchasePrice?.toFixed(2)}</p>
|
|
188
|
+
</div>
|
|
189
|
+
<div className="bg-muted rounded-md p-3">
|
|
190
|
+
<p className="text-sm font-medium">Total Maximum Cost:</p>
|
|
191
|
+
<p className="text-lg font-bold">
|
|
192
|
+
${(quantity * maxPurchasePrice)?.toFixed(2)}
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
</CardContent>
|
|
196
|
+
<CardFooter className="flex justify-end">
|
|
197
|
+
<Button variant="outline" onClick={onReject}>
|
|
198
|
+
<XIcon className="mr-2 h-4 w-4" />
|
|
199
|
+
Reject
|
|
200
|
+
</Button>
|
|
201
|
+
<Button onClick={onConfirm}>
|
|
202
|
+
<CheckIcon className="mr-2 h-4 w-4" />
|
|
203
|
+
Confirm
|
|
204
|
+
</Button>
|
|
205
|
+
</CardFooter>
|
|
206
|
+
</Card>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Bind approval UI
|
|
212
|
+
|
|
213
|
+
```tsx title="@/app/page.tsx" {1,8}
|
|
214
|
+
import { PurchaseStockTool } from "@/components/tools/purchase-stock/PurchaseStockTool";
|
|
215
|
+
|
|
216
|
+
export default function Home() {
|
|
217
|
+
return (
|
|
218
|
+
<div className="flex h-full flex-col">
|
|
219
|
+
<Thread
|
|
220
|
+
...
|
|
221
|
+
tools={[PriceSnapshotTool, PurchaseStockTool]}
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Try it out!
|
|
229
|
+
|
|
230
|
+
Ask the assistant to buy 5 shares of Tesla. You should see the following appear:
|
|
231
|
+
|
|
232
|
+
import purchase from "./images/acme-approve.png";
|
|
233
|
+
|
|
234
|
+
<Image
|
|
235
|
+
src={purchase}
|
|
236
|
+
alt="Approval UI"
|
|
237
|
+
width={600}
|
|
238
|
+
className="mx-auto rounded-lg border shadow"
|
|
239
|
+
/>
|
|
240
|
+
|
|
241
|
+
## Add `TransactionConfirmationFinal` to show approval result
|
|
242
|
+
|
|
243
|
+
We will add a component to display the approval result.
|
|
244
|
+
|
|
245
|
+
```ts title="@/components/tools/purchase-stock/transaction-confirmation-final.tsx"
|
|
246
|
+
"use client";
|
|
247
|
+
|
|
248
|
+
import { CheckCircle } from "lucide-react";
|
|
249
|
+
|
|
250
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
251
|
+
|
|
252
|
+
type TransactionConfirmation = {
|
|
253
|
+
ticker: string;
|
|
254
|
+
companyName: string;
|
|
255
|
+
quantity: number;
|
|
256
|
+
maxPurchasePrice: number;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export function TransactionConfirmationFinal(props: TransactionConfirmation) {
|
|
260
|
+
const { ticker, companyName, quantity, maxPurchasePrice } = props;
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<Card className="mx-auto w-full max-w-md">
|
|
264
|
+
<CardHeader className="text-center">
|
|
265
|
+
<CheckCircle className="mx-auto mb-4 h-16 w-16 text-green-500" />
|
|
266
|
+
<CardTitle className="text-2xl font-bold text-green-700">
|
|
267
|
+
Transaction Confirmed
|
|
268
|
+
</CardTitle>
|
|
269
|
+
</CardHeader>
|
|
270
|
+
<CardContent className="space-y-4">
|
|
271
|
+
<div className="rounded-md border border-green-200 bg-green-50 p-4">
|
|
272
|
+
<h3 className="mb-2 text-lg font-semibold text-green-800">
|
|
273
|
+
Purchase Summary
|
|
274
|
+
</h3>
|
|
275
|
+
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
276
|
+
<p className="font-medium text-green-700">Ticker:</p>
|
|
277
|
+
<p className="font-bold text-green-900">{ticker}</p>
|
|
278
|
+
<p className="font-medium text-green-700">Company:</p>
|
|
279
|
+
<p className="text-green-900">{companyName}</p>
|
|
280
|
+
<p className="font-medium text-green-700">Quantity:</p>
|
|
281
|
+
<p className="text-green-900">{quantity} shares</p>
|
|
282
|
+
<p className="font-medium text-green-700">Price per Share:</p>
|
|
283
|
+
<p className="text-green-900">${maxPurchasePrice?.toFixed(2)}</p>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
<div className="rounded-md border border-green-300 bg-green-100 p-4">
|
|
287
|
+
<p className="text-lg font-semibold text-green-800">Total Cost:</p>
|
|
288
|
+
<p className="text-2xl font-bold text-green-900">
|
|
289
|
+
${(quantity * maxPurchasePrice)?.toFixed(2)}
|
|
290
|
+
</p>
|
|
291
|
+
</div>
|
|
292
|
+
<p className="text-center text-sm text-green-600">
|
|
293
|
+
Your purchase of {quantity} shares of {companyName} ({ticker}) has
|
|
294
|
+
been successfully processed.
|
|
295
|
+
</p>
|
|
296
|
+
</CardContent>
|
|
297
|
+
</Card>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Update `PurchaseStockTool`
|
|
303
|
+
|
|
304
|
+
We will import the new `<TransactionConfirmationFinal />` component and use it in the `render` function whenever an approval result is available.
|
|
305
|
+
|
|
306
|
+
```tsx title="@/components/tools/purchase-stock/PurchaseStockTool.tsx" {3,25-30,37-42}
|
|
307
|
+
"use client";
|
|
308
|
+
|
|
309
|
+
import { TransactionConfirmationPending } from "./transaction-confirmation-pending";
|
|
310
|
+
import { TransactionConfirmationFinal } from "./transaction-confirmation-final";
|
|
311
|
+
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
312
|
+
import { updateState } from "@/lib/chatApi";
|
|
313
|
+
|
|
314
|
+
type PurchaseStockArgs = {
|
|
315
|
+
ticker: string;
|
|
316
|
+
companyName: string;
|
|
317
|
+
quantity: number;
|
|
318
|
+
maxPurchasePrice: number;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
type PurchaseStockResult = {
|
|
322
|
+
approve?: boolean;
|
|
323
|
+
cancelled?: boolean;
|
|
324
|
+
error?: string;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export const PurchaseStockTool = makeAssistantToolUI<PurchaseStockArgs, string>(
|
|
328
|
+
{
|
|
329
|
+
toolName: "purchase_stock",
|
|
330
|
+
render: function PurchaseStockUI({ args, result, status, addResult }) {
|
|
331
|
+
let resultObj: PurchaseStockResult;
|
|
332
|
+
try {
|
|
333
|
+
resultObj = result ? JSON.parse(result) : {};
|
|
334
|
+
} catch (e) {
|
|
335
|
+
resultObj = { error: result! };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const handleReject = () => {
|
|
339
|
+
addResult({ cancelled: true });
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const handleConfirm = async () => {
|
|
343
|
+
addResult({ approve: true });
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<div className="mb-4 flex flex-col items-center gap-2">
|
|
348
|
+
<div>
|
|
349
|
+
<pre className="whitespace-pre-wrap break-all text-center">
|
|
350
|
+
purchase_stock({JSON.stringify(args)})
|
|
351
|
+
</pre>
|
|
352
|
+
</div>
|
|
353
|
+
{!result && status.type !== "running" && (
|
|
354
|
+
<TransactionConfirmationPending
|
|
355
|
+
{...args}
|
|
356
|
+
onConfirm={handleConfirm}
|
|
357
|
+
onReject={handleReject}
|
|
358
|
+
/>
|
|
359
|
+
)}
|
|
360
|
+
{resultObj.approve && <TransactionConfirmationFinal {...args} />}
|
|
361
|
+
{resultObj.approve === false && (
|
|
362
|
+
<pre className="font-bold text-red-600">User rejected purchase</pre>
|
|
363
|
+
)}
|
|
364
|
+
{resultObj.cancelled && (
|
|
365
|
+
<pre className="font-bold text-red-600">Cancelled</pre>
|
|
366
|
+
)}
|
|
367
|
+
</div>
|
|
368
|
+
);
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
);
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Try it out!
|
|
375
|
+
|
|
376
|
+
Confirm the purchase of shares. You should see the approval confimration UI appear.
|
|
377
|
+
|
|
378
|
+
import purchase2 from "./images/acme-confirmed.png";
|
|
379
|
+
|
|
380
|
+
<Image
|
|
381
|
+
src={purchase2}
|
|
382
|
+
alt="Approval result"
|
|
383
|
+
width={600}
|
|
384
|
+
className="mx-auto rounded-lg border shadow"
|
|
385
|
+
/>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: LangChain LangServe
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Integration with a LangServe server via Vercel AI SDK.
|
|
8
|
+
|
|
9
|
+
## Getting Started
|
|
10
|
+
|
|
11
|
+
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
12
|
+
|
|
13
|
+
<Steps>
|
|
14
|
+
<Step>
|
|
15
|
+
### Create a Next.JS project
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npx create-next-app@latest my-app
|
|
19
|
+
cd my-app
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
</Step>
|
|
23
|
+
<Step>
|
|
24
|
+
|
|
25
|
+
### Install `@langchain/core`, `ai-sdk` and `@assistant-ui/react`
|
|
26
|
+
|
|
27
|
+
```sh npm2yarn
|
|
28
|
+
npm install @assistant-ui/react @assistant-ui/react-ai-sdk ai @ai-sdk/react @langchain/core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
</Step>
|
|
32
|
+
<Step>
|
|
33
|
+
|
|
34
|
+
### Setup a backend route under `/api/chat`
|
|
35
|
+
|
|
36
|
+
```tsx twoslash title="@/app/api/chat/route.ts"
|
|
37
|
+
// @errors: 2558 2345
|
|
38
|
+
import { RemoteRunnable } from "@langchain/core/runnables/remote";
|
|
39
|
+
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
40
|
+
import { streamText, LangChainAdapter, type Message } from "ai";
|
|
41
|
+
|
|
42
|
+
export const maxDuration = 30;
|
|
43
|
+
|
|
44
|
+
export async function POST(req: Request) {
|
|
45
|
+
const { messages } = (await req.json()) as { messages: Message[] };
|
|
46
|
+
|
|
47
|
+
// TODO replace with your own langserve URL
|
|
48
|
+
const remoteChain = new RemoteRunnable<
|
|
49
|
+
{ messages: Message[] },
|
|
50
|
+
string,
|
|
51
|
+
RunnableConfig
|
|
52
|
+
>({
|
|
53
|
+
url: "<YOUR_LANGSERVE_URL>",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const stream = await remoteChain.stream({
|
|
57
|
+
messages,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return LangChainAdapter.toDataStreamResponse(stream);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
</Step>
|
|
65
|
+
<Step>
|
|
66
|
+
|
|
67
|
+
### Define a `MyRuntimeProvider` component
|
|
68
|
+
|
|
69
|
+
```tsx twoslash include MyRuntimeProvider title="@/app/MyRuntimeProvider.tsx"
|
|
70
|
+
// @filename: /app/MyRuntimeProvider.tsx
|
|
71
|
+
// ---cut---
|
|
72
|
+
"use client";
|
|
73
|
+
|
|
74
|
+
import { useChat } from "@ai-sdk/react";
|
|
75
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
76
|
+
import { useVercelUseChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
77
|
+
|
|
78
|
+
export function MyRuntimeProvider({
|
|
79
|
+
children,
|
|
80
|
+
}: Readonly<{
|
|
81
|
+
children: React.ReactNode;
|
|
82
|
+
}>) {
|
|
83
|
+
const chat = useChat({
|
|
84
|
+
api: "/api/chat",
|
|
85
|
+
unstable_AISDKInterop: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const runtime = useVercelUseChatRuntime(chat);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
92
|
+
{children}
|
|
93
|
+
</AssistantRuntimeProvider>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</Step>
|
|
99
|
+
<Step>
|
|
100
|
+
|
|
101
|
+
### Wrap your app in `MyRuntimeProvider`
|
|
102
|
+
|
|
103
|
+
```tsx twoslash title="@/app/layout.tsx"
|
|
104
|
+
// @include: MyRuntimeProvider
|
|
105
|
+
// @filename: /app/layout.tsx
|
|
106
|
+
// ---cut---
|
|
107
|
+
import type { ReactNode } from "react";
|
|
108
|
+
import { MyRuntimeProvider } from "@/app/MyRuntimeProvider";
|
|
109
|
+
|
|
110
|
+
export default function RootLayout({
|
|
111
|
+
children,
|
|
112
|
+
}: Readonly<{
|
|
113
|
+
children: ReactNode;
|
|
114
|
+
}>) {
|
|
115
|
+
return (
|
|
116
|
+
<MyRuntimeProvider>
|
|
117
|
+
<html lang="en">
|
|
118
|
+
<body>{children}</body>
|
|
119
|
+
</html>
|
|
120
|
+
</MyRuntimeProvider>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
</Step>
|
|
126
|
+
</Steps>
|