@assistant-ui/mcp-docs-server 0.1.32 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/organized/code-examples/waterfall.md +15 -17
- package/.docs/organized/code-examples/with-a2a.md +19 -19
- package/.docs/organized/code-examples/with-ag-ui.md +25 -20
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +6 -4
- package/.docs/organized/code-examples/with-artifacts.md +34 -28
- package/.docs/organized/code-examples/with-assistant-transport.md +59 -47
- package/.docs/organized/code-examples/with-browser-extension.md +17 -5
- package/.docs/organized/code-examples/with-chain-of-thought.md +376 -78
- package/.docs/organized/code-examples/with-cloud-standalone.md +20 -19
- package/.docs/organized/code-examples/with-cloud.md +6 -4
- package/.docs/organized/code-examples/with-custom-thread-list.md +9 -7
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +18 -16
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +9 -8
- package/.docs/organized/code-examples/with-expo.md +23 -17
- package/.docs/organized/code-examples/with-external-store.md +4 -2
- package/.docs/organized/code-examples/with-ffmpeg.md +317 -280
- package/.docs/organized/code-examples/with-generative-ui.md +1018 -214
- package/.docs/organized/code-examples/with-google-adk.md +4 -2
- package/.docs/organized/code-examples/with-heat-graph.md +8 -6
- package/.docs/organized/code-examples/with-image-generation.md +19 -17
- package/.docs/organized/code-examples/with-interactables.md +312 -234
- package/.docs/organized/code-examples/with-langchain.md +9 -7
- package/.docs/organized/code-examples/with-langgraph.md +82 -78
- package/.docs/organized/code-examples/with-livekit.md +19 -17
- package/.docs/organized/code-examples/with-mcp.md +13 -7
- package/.docs/organized/code-examples/with-opencode.md +61 -61
- package/.docs/organized/code-examples/with-react-hook-form.md +14 -12
- package/.docs/organized/code-examples/with-react-ink.md +2 -2
- package/.docs/organized/code-examples/with-react-router.md +11 -9
- package/.docs/organized/code-examples/with-resumable-stream.md +10 -9
- package/.docs/organized/code-examples/with-store.md +14 -12
- package/.docs/organized/code-examples/with-tanstack.md +8 -9
- package/.docs/organized/code-examples/with-tap-runtime.md +21 -19
- package/.docs/raw/docs/(docs)/architecture.mdx +43 -2
- package/.docs/raw/docs/(docs)/cli.mdx +1 -2
- package/.docs/raw/docs/(docs)/copilots/model-context.mdx +34 -26
- package/.docs/raw/docs/(docs)/copilots/motivation.mdx +32 -26
- package/.docs/raw/docs/(docs)/installation.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +5 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +14 -14
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +11 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +18 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +33 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +45 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +41 -41
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +19 -1
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +122 -122
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +32 -2
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +6 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +4 -6
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +30 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +20 -3
- package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +8 -8
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +52 -4
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +6 -7
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +58 -46
- package/.docs/raw/docs/cloud/ai-sdk.mdx +0 -2
- package/.docs/raw/docs/guides/chain-of-thought.mdx +7 -9
- package/.docs/raw/docs/guides/context-api.mdx +2 -1
- package/.docs/raw/docs/guides/index.mdx +3 -12
- package/.docs/raw/docs/guides/mentions.mdx +4 -4
- package/.docs/raw/docs/guides/slash-commands.mdx +1 -1
- package/.docs/raw/docs/guides/suggestions.mdx +1 -1
- package/.docs/raw/docs/ink/hooks.mdx +98 -85
- package/.docs/raw/docs/ink/migration.mdx +1 -1
- package/.docs/raw/docs/ink/primitives.mdx +2 -2
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +1 -1
- package/.docs/raw/docs/integrations/index.mdx +2 -2
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +17 -2
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +226 -0
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +10 -16
- package/.docs/raw/docs/primitives/message.mdx +9 -10
- package/.docs/raw/docs/react-native/hooks.mdx +57 -82
- package/.docs/raw/docs/react-native/migration.mdx +1 -1
- package/.docs/raw/docs/react-native/primitives.mdx +2 -2
- package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +3 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +122 -1
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +7 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +18 -7
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +108 -38
- package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +64 -50
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +98 -86
- package/.docs/raw/docs/tools/backend.mdx +136 -0
- package/.docs/raw/docs/tools/defining-tools.mdx +413 -0
- package/.docs/raw/docs/tools/dynamic-tools.mdx +110 -0
- package/.docs/raw/docs/tools/generative-ui.mdx +214 -0
- package/.docs/raw/docs/tools/index.mdx +76 -0
- package/.docs/raw/docs/{guides → tools}/interactables.mdx +1 -1
- package/.docs/raw/docs/{integrations/tools → tools}/mcp.mdx +77 -50
- package/.docs/raw/docs/{guides → tools}/multi-agent.mdx +17 -19
- package/.docs/raw/docs/tools/tool-ui.mdx +967 -0
- package/.docs/raw/docs/{integrations/tools/react-mcp.mdx → tools/user-managed-mcp.mdx} +3 -3
- package/.docs/raw/docs/ui/directive-text.mdx +3 -3
- package/.docs/raw/docs/ui/mcp-config.mdx +4 -4
- package/.docs/raw/docs/ui/part-grouping.mdx +84 -50
- package/.docs/raw/docs/ui/reasoning.mdx +4 -5
- package/.docs/raw/docs/ui/tool-group.mdx +5 -6
- package/.docs/raw/docs/utilities/heat-graph.mdx +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/prepare-docs/code-examples.d.ts.map +1 -1
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/package.json +3 -3
- package/.docs/organized/code-examples/with-parent-id-grouping.md +0 -596
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -151
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -230
- package/.docs/raw/docs/guides/generative-ui.mdx +0 -142
- package/.docs/raw/docs/guides/tool-ui.mdx +0 -858
- package/.docs/raw/docs/guides/tools.mdx +0 -736
- /package/.docs/raw/docs/{guides → tools}/mcp-apps.mdx +0 -0
|
@@ -254,7 +254,7 @@ export async function POST(req: Request) {
|
|
|
254
254
|
}
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
-
Frontend tools are registered through `useAui` (see the [tools guide](/docs/
|
|
257
|
+
Frontend tools are registered through `useAui` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
|
|
258
258
|
|
|
259
259
|
## Multi-step tool calls
|
|
260
260
|
|
|
@@ -286,6 +286,127 @@ export async function POST(req: Request) {
|
|
|
286
286
|
|
|
287
287
|
Without a `stopWhen`, AI SDK runs a single inference step. Set `stepCountIs` (or one of AI SDK's other stop conditions) when your tools chain multiple calls.
|
|
288
288
|
|
|
289
|
+
## Server-side tool approval
|
|
290
|
+
|
|
291
|
+
AI SDK v6 lets a tool gate its own execution with `needsApproval`. The server pauses, emits an `approval-requested` part, and resumes once the client posts an approval response. assistant-ui surfaces the gate as `approval` on the tool part and adds a `respondToApproval` prop on the renderer, so tool components stay decoupled from `chatHelpers`.
|
|
292
|
+
|
|
293
|
+
On the backend, mark the tool with `needsApproval` (boolean or function):
|
|
294
|
+
|
|
295
|
+
```ts title="@/app/api/chat/route.ts"
|
|
296
|
+
import { openai } from "@ai-sdk/openai";
|
|
297
|
+
import { streamText, convertToModelMessages, tool } from "ai";
|
|
298
|
+
import { z } from "zod";
|
|
299
|
+
|
|
300
|
+
export async function POST(req: Request) {
|
|
301
|
+
const { messages } = await req.json();
|
|
302
|
+
|
|
303
|
+
const result = streamText({
|
|
304
|
+
model: openai("gpt-5.4-mini"),
|
|
305
|
+
messages: await convertToModelMessages(messages),
|
|
306
|
+
tools: {
|
|
307
|
+
deploy: tool({
|
|
308
|
+
description: "Deploy the current build to an environment.",
|
|
309
|
+
inputSchema: z.object({ target: z.string() }),
|
|
310
|
+
needsApproval: ({ input }) => input.target === "production",
|
|
311
|
+
execute: async ({ target }) => ({ deployed: target }),
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return result.toUIMessageStreamResponse();
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
On the client, configure `useChat` with `sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses` so the response is sent back to the server when the user decides:
|
|
321
|
+
|
|
322
|
+
```tsx title="@/app/page.tsx"
|
|
323
|
+
"use client";
|
|
324
|
+
|
|
325
|
+
import { useChat } from "@ai-sdk/react";
|
|
326
|
+
import {
|
|
327
|
+
AssistantRuntimeProvider,
|
|
328
|
+
useAISDKRuntime,
|
|
329
|
+
} from "@assistant-ui/react-ai-sdk";
|
|
330
|
+
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
331
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
332
|
+
|
|
333
|
+
export default function Page() {
|
|
334
|
+
const chat = useChat({
|
|
335
|
+
api: "/api/chat",
|
|
336
|
+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
337
|
+
});
|
|
338
|
+
const runtime = useAISDKRuntime(chat);
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
342
|
+
<Thread />
|
|
343
|
+
</AssistantRuntimeProvider>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Render the gate with a toolkit entry. The `approval` field carries the gate state, and `respondToApproval` is the only correct way to acknowledge it (it reads the approval id from the part):
|
|
349
|
+
|
|
350
|
+
```tsx title="@/app/page.tsx"
|
|
351
|
+
"use client";
|
|
352
|
+
|
|
353
|
+
import { useChat } from "@ai-sdk/react";
|
|
354
|
+
import {
|
|
355
|
+
AssistantRuntimeProvider,
|
|
356
|
+
Tools,
|
|
357
|
+
type Toolkit,
|
|
358
|
+
useAui,
|
|
359
|
+
} from "@assistant-ui/react";
|
|
360
|
+
import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
|
|
361
|
+
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
362
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
363
|
+
|
|
364
|
+
const toolkit = {
|
|
365
|
+
deploy: {
|
|
366
|
+
type: "backend",
|
|
367
|
+
render: ({ args, approval, respondToApproval, result }) => {
|
|
368
|
+
if (approval?.approved === undefined) {
|
|
369
|
+
return (
|
|
370
|
+
<div>
|
|
371
|
+
<p>Approve deploy to {args.target}?</p>
|
|
372
|
+
<button onClick={() => respondToApproval({ approved: true })}>
|
|
373
|
+
Approve
|
|
374
|
+
</button>
|
|
375
|
+
<button
|
|
376
|
+
onClick={() =>
|
|
377
|
+
respondToApproval({ approved: false, reason: "user denied" })
|
|
378
|
+
}
|
|
379
|
+
>
|
|
380
|
+
Deny
|
|
381
|
+
</button>
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (approval?.approved === false) return <p>Denied</p>;
|
|
386
|
+
if (result === undefined) return <p>Approved, deploying…</p>;
|
|
387
|
+
return <p>Deployed {result.deployed}</p>;
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
} satisfies Toolkit;
|
|
391
|
+
|
|
392
|
+
export default function Page() {
|
|
393
|
+
const chat = useChat({
|
|
394
|
+
api: "/api/chat",
|
|
395
|
+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
396
|
+
});
|
|
397
|
+
const runtime = useAISDKRuntime(chat);
|
|
398
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
399
|
+
|
|
400
|
+
return (
|
|
401
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
402
|
+
<Thread />
|
|
403
|
+
</AssistantRuntimeProvider>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
See the [tool UI guide](/docs/tools/tool-ui#server-side-approval-gates) for the full three-state semantics of `approval.approved` and the `isAutomatic` flag.
|
|
409
|
+
|
|
289
410
|
## Quote context
|
|
290
411
|
|
|
291
412
|
assistant-ui's composer can attach quote metadata to user messages (e.g. when the user selects text and clicks "Quote"). On the server, `injectQuoteContext` flattens that metadata into a markdown blockquote prefix so the LLM sees the quoted text:
|
|
@@ -287,6 +287,12 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
|
|
|
287
287
|
description: "Persist title changes from the UI.",
|
|
288
288
|
required: true,
|
|
289
289
|
},
|
|
290
|
+
{
|
|
291
|
+
name: "updateCustom",
|
|
292
|
+
type: "(remoteId: string, custom: Record<string, unknown> | undefined) => Promise<void>",
|
|
293
|
+
description:
|
|
294
|
+
"Optional. Persist replacement custom metadata from `aui.threadListItem().updateCustom(custom)`.",
|
|
295
|
+
},
|
|
290
296
|
{
|
|
291
297
|
name: "archive",
|
|
292
298
|
type: "(remoteId: string) => Promise<void>",
|
|
@@ -355,7 +361,7 @@ function ThreadListItemMeta() {
|
|
|
355
361
|
}
|
|
356
362
|
```
|
|
357
363
|
|
|
358
|
-
`custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To
|
|
364
|
+
`custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem().updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads().reload()` to re-run `list()`.
|
|
359
365
|
|
|
360
366
|
## ExternalStoreThreadListAdapter
|
|
361
367
|
|
|
@@ -417,24 +417,35 @@ If you build the `content` array fresh from the current chunk each iteration, to
|
|
|
417
417
|
|
|
418
418
|
`LocalRuntime` supports OpenAI-compatible function calling. Register tools through `useAui` so the runtime exposes them to your adapter via `context.tools`:
|
|
419
419
|
|
|
420
|
-
```tsx
|
|
421
|
-
|
|
420
|
+
```tsx title="app/toolkit.tsx"
|
|
421
|
+
"use generative";
|
|
422
|
+
|
|
423
|
+
import { defineToolkit } from "@assistant-ui/react";
|
|
422
424
|
import { z } from "zod";
|
|
423
425
|
|
|
424
|
-
|
|
426
|
+
export default defineToolkit({
|
|
425
427
|
getWeather: {
|
|
426
428
|
description: "Get the current weather in a location",
|
|
427
429
|
parameters: z.object({
|
|
428
430
|
location: z.string(),
|
|
429
431
|
unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
|
|
430
432
|
}),
|
|
431
|
-
execute: async ({ location, unit }) =>
|
|
433
|
+
execute: async ({ location, unit }) => {
|
|
434
|
+
"use client";
|
|
435
|
+
return fetchWeather(location, unit);
|
|
436
|
+
},
|
|
437
|
+
renderText: { running: "Checking the weather…", complete: "Weather ready" },
|
|
432
438
|
},
|
|
433
|
-
};
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
```tsx title="app/MyRuntimeProvider.tsx"
|
|
443
|
+
import { useAui, Tools } from "@assistant-ui/react";
|
|
444
|
+
import toolkit from "./toolkit";
|
|
434
445
|
|
|
435
446
|
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
436
447
|
const runtime = useLocalRuntime(MyModelAdapter);
|
|
437
|
-
const aui = useAui({ tools: Tools({ toolkit
|
|
448
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
438
449
|
|
|
439
450
|
return (
|
|
440
451
|
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
@@ -444,7 +455,7 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
444
455
|
}
|
|
445
456
|
```
|
|
446
457
|
|
|
447
|
-
See the [tools guide](/docs/
|
|
458
|
+
See the [tools guide](/docs/tools/defining-tools) for advanced patterns.
|
|
448
459
|
|
|
449
460
|
### Human-in-the-loop approval
|
|
450
461
|
|
|
@@ -257,7 +257,7 @@ When an ADK Python 2.0+ Workflow's `RequestInput` node pauses execution to ask t
|
|
|
257
257
|
<Tab value="React">
|
|
258
258
|
|
|
259
259
|
```tsx
|
|
260
|
-
import {
|
|
260
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
261
261
|
import { useAdkSubmitInput } from "@assistant-ui/react-google-adk";
|
|
262
262
|
|
|
263
263
|
type RequestInputArgs = {
|
|
@@ -267,12 +267,10 @@ type RequestInputArgs = {
|
|
|
267
267
|
response_schema?: unknown;
|
|
268
268
|
};
|
|
269
269
|
|
|
270
|
-
export const RequestInputToolUI
|
|
270
|
+
export const RequestInputToolUI: ToolCallMessagePartComponent<
|
|
271
271
|
RequestInputArgs,
|
|
272
272
|
unknown
|
|
273
|
-
>({
|
|
274
|
-
toolName: "adk_request_input",
|
|
275
|
-
render: function RequestInputUI({ toolCallId, args, result }) {
|
|
273
|
+
> = function RequestInputUI({ toolCallId, args, result }) {
|
|
276
274
|
const submitInput = useAdkSubmitInput();
|
|
277
275
|
|
|
278
276
|
if (result !== undefined) {
|
|
@@ -294,15 +292,14 @@ export const RequestInputToolUI = makeAssistantToolUI<
|
|
|
294
292
|
<button type="submit">Submit</button>
|
|
295
293
|
</form>
|
|
296
294
|
);
|
|
297
|
-
|
|
298
|
-
});
|
|
295
|
+
};
|
|
299
296
|
```
|
|
300
297
|
|
|
301
298
|
</Tab>
|
|
302
299
|
<Tab value="React Native">
|
|
303
300
|
|
|
304
301
|
```tsx
|
|
305
|
-
import {
|
|
302
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react-native";
|
|
306
303
|
import { useAdkSubmitInput } from "@assistant-ui/react-google-adk";
|
|
307
304
|
import { useState } from "react";
|
|
308
305
|
import { Pressable, Text, TextInput, View } from "react-native";
|
|
@@ -314,12 +311,10 @@ type RequestInputArgs = {
|
|
|
314
311
|
response_schema?: unknown;
|
|
315
312
|
};
|
|
316
313
|
|
|
317
|
-
export const RequestInputToolUI
|
|
314
|
+
export const RequestInputToolUI: ToolCallMessagePartComponent<
|
|
318
315
|
RequestInputArgs,
|
|
319
316
|
unknown
|
|
320
|
-
>({
|
|
321
|
-
toolName: "adk_request_input",
|
|
322
|
-
render: function RequestInputUI({ toolCallId, args, result }) {
|
|
317
|
+
> = function RequestInputUI({ toolCallId, args, result }) {
|
|
323
318
|
const submitInput = useAdkSubmitInput();
|
|
324
319
|
const [value, setValue] = useState("");
|
|
325
320
|
|
|
@@ -336,15 +331,14 @@ export const RequestInputToolUI = makeAssistantToolUI<
|
|
|
336
331
|
</Pressable>
|
|
337
332
|
</View>
|
|
338
333
|
);
|
|
339
|
-
|
|
340
|
-
});
|
|
334
|
+
};
|
|
341
335
|
```
|
|
342
336
|
|
|
343
337
|
</Tab>
|
|
344
338
|
<Tab value="React Ink">
|
|
345
339
|
|
|
346
340
|
```tsx
|
|
347
|
-
import {
|
|
341
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react-ink";
|
|
348
342
|
import { useAdkSubmitInput } from "@assistant-ui/react-google-adk";
|
|
349
343
|
import { useState } from "react";
|
|
350
344
|
import { Box, Text, useInput } from "ink";
|
|
@@ -356,12 +350,10 @@ type RequestInputArgs = {
|
|
|
356
350
|
response_schema?: unknown;
|
|
357
351
|
};
|
|
358
352
|
|
|
359
|
-
export const RequestInputToolUI
|
|
353
|
+
export const RequestInputToolUI: ToolCallMessagePartComponent<
|
|
360
354
|
RequestInputArgs,
|
|
361
355
|
unknown
|
|
362
|
-
>({
|
|
363
|
-
toolName: "adk_request_input",
|
|
364
|
-
render: function RequestInputUI({ toolCallId, args, result }) {
|
|
356
|
+
> = function RequestInputUI({ toolCallId, args, result }) {
|
|
365
357
|
const submitInput = useAdkSubmitInput();
|
|
366
358
|
const [value, setValue] = useState("");
|
|
367
359
|
|
|
@@ -383,53 +375,131 @@ export const RequestInputToolUI = makeAssistantToolUI<
|
|
|
383
375
|
<Text dimColor>Press enter to submit.</Text>
|
|
384
376
|
</Box>
|
|
385
377
|
);
|
|
386
|
-
|
|
387
|
-
});
|
|
378
|
+
};
|
|
388
379
|
```
|
|
389
380
|
|
|
390
381
|
</Tab>
|
|
391
382
|
</PlatformTabs>
|
|
392
383
|
|
|
393
|
-
Register the
|
|
384
|
+
Register the renderer with a backend toolkit entry inside `AssistantRuntimeProvider`:
|
|
394
385
|
|
|
395
386
|
<PlatformTabs>
|
|
396
387
|
<Tab value="React">
|
|
397
388
|
|
|
398
389
|
```tsx
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
390
|
+
import {
|
|
391
|
+
AssistantRuntimeProvider,
|
|
392
|
+
Tools,
|
|
393
|
+
type Toolkit,
|
|
394
|
+
useAui,
|
|
395
|
+
} from "@assistant-ui/react";
|
|
396
|
+
import {
|
|
397
|
+
createAdkStream,
|
|
398
|
+
useAdkRuntime,
|
|
399
|
+
} from "@assistant-ui/react-google-adk";
|
|
400
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
401
|
+
|
|
402
|
+
const toolkit = {
|
|
403
|
+
adk_request_input: {
|
|
404
|
+
type: "backend",
|
|
405
|
+
render: RequestInputToolUI,
|
|
406
|
+
},
|
|
407
|
+
} satisfies Toolkit;
|
|
408
|
+
|
|
409
|
+
function App() {
|
|
410
|
+
const runtime = useAdkRuntime({
|
|
411
|
+
stream: createAdkStream({ api: "/api/chat" }),
|
|
412
|
+
});
|
|
413
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
417
|
+
<Thread />
|
|
418
|
+
</AssistantRuntimeProvider>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
403
421
|
```
|
|
404
422
|
|
|
405
423
|
</Tab>
|
|
406
424
|
<Tab value="React Native">
|
|
407
425
|
|
|
408
426
|
```tsx
|
|
427
|
+
import {
|
|
428
|
+
AssistantRuntimeProvider,
|
|
429
|
+
Tools,
|
|
430
|
+
type Toolkit,
|
|
431
|
+
useAui,
|
|
432
|
+
} from "@assistant-ui/react-native";
|
|
433
|
+
import {
|
|
434
|
+
createAdkStream,
|
|
435
|
+
useAdkRuntime,
|
|
436
|
+
} from "@assistant-ui/react-google-adk";
|
|
409
437
|
import { View } from "react-native";
|
|
410
438
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
411
439
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
440
|
+
const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
|
|
441
|
+
|
|
442
|
+
const toolkit = {
|
|
443
|
+
adk_request_input: {
|
|
444
|
+
type: "backend",
|
|
445
|
+
render: RequestInputToolUI,
|
|
446
|
+
},
|
|
447
|
+
} satisfies Toolkit;
|
|
448
|
+
|
|
449
|
+
function App() {
|
|
450
|
+
const runtime = useAdkRuntime({
|
|
451
|
+
stream: createAdkStream({ api: `${API_URL}/api/chat` }),
|
|
452
|
+
});
|
|
453
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
457
|
+
<View style={{ flex: 1 }}>
|
|
458
|
+
<Thread />
|
|
459
|
+
</View>
|
|
460
|
+
</AssistantRuntimeProvider>
|
|
461
|
+
);
|
|
462
|
+
}
|
|
418
463
|
```
|
|
419
464
|
|
|
420
465
|
</Tab>
|
|
421
466
|
<Tab value="React Ink">
|
|
422
467
|
|
|
423
468
|
```tsx
|
|
469
|
+
import {
|
|
470
|
+
AssistantRuntimeProvider,
|
|
471
|
+
Tools,
|
|
472
|
+
type Toolkit,
|
|
473
|
+
useAui,
|
|
474
|
+
} from "@assistant-ui/react-ink";
|
|
475
|
+
import {
|
|
476
|
+
createAdkStream,
|
|
477
|
+
useAdkRuntime,
|
|
478
|
+
} from "@assistant-ui/react-google-adk";
|
|
424
479
|
import { Box } from "ink";
|
|
425
480
|
import { Thread } from "./components/thread.js";
|
|
426
481
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
482
|
+
const toolkit = {
|
|
483
|
+
adk_request_input: {
|
|
484
|
+
type: "backend",
|
|
485
|
+
render: RequestInputToolUI,
|
|
486
|
+
},
|
|
487
|
+
} satisfies Toolkit;
|
|
488
|
+
|
|
489
|
+
function App() {
|
|
490
|
+
const runtime = useAdkRuntime({
|
|
491
|
+
stream: createAdkStream({ api: "http://localhost:3000/api/chat" }),
|
|
492
|
+
});
|
|
493
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
494
|
+
|
|
495
|
+
return (
|
|
496
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
497
|
+
<Box flexDirection="column">
|
|
498
|
+
<Thread />
|
|
499
|
+
</Box>
|
|
500
|
+
</AssistantRuntimeProvider>
|
|
501
|
+
);
|
|
502
|
+
}
|
|
433
503
|
```
|
|
434
504
|
|
|
435
505
|
</Tab>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Generative UI
|
|
2
|
+
title: LangGraph Generative UI
|
|
3
3
|
description: Render structured UI components emitted by LangGraph alongside assistant messages.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
LangGraph's [Generative UI](https://docs.langchain.com/langsmith/generative-ui-react) lets your graph emit structured UI components alongside assistant messages via `push_ui_message` (Python) or `typedUi().push()` (TypeScript). The assistant-ui adapter translates these into [`DataMessagePart`s](/docs/
|
|
6
|
+
LangGraph's [Generative UI](https://docs.langchain.com/langsmith/generative-ui-react) lets your graph emit structured UI components alongside assistant messages via `push_ui_message` (Python) or `typedUi().push()` (TypeScript). The assistant-ui adapter translates these into [`DataMessagePart`s](/docs/tools/tool-ui) on the associated assistant message, which you render with the existing `makeAssistantDataUI` API.
|
|
7
7
|
|
|
8
8
|
## Enable the `custom` stream mode
|
|
9
9
|
|
|
@@ -102,7 +102,7 @@ function MyComponent() {
|
|
|
102
102
|
|
|
103
103
|
## Generative UI
|
|
104
104
|
|
|
105
|
-
LangGraph can emit structured UI components alongside assistant messages via `push_ui_message` (Python) or `typedUi().push()` (TypeScript). The assistant-ui adapter translates these into [`DataMessagePart`s](/docs/
|
|
105
|
+
LangGraph can emit structured UI components alongside assistant messages via `push_ui_message` (Python) or `typedUi().push()` (TypeScript). The assistant-ui adapter translates these into [`DataMessagePart`s](/docs/tools/tool-ui) on the associated assistant message, which you render with the existing `makeAssistantDataUI` API.
|
|
106
106
|
|
|
107
107
|
See [Generative UI](/docs/runtimes/langgraph/generative-ui) for full setup: enabling the `custom` stream channel, emitting UI messages, registering renderers, dynamic loading, and persisting UI state across thread switches.
|
|
108
108
|
|
|
@@ -64,43 +64,52 @@ type PriceSnapshotToolResult = {
|
|
|
64
64
|
};
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
Then, we
|
|
67
|
+
Then, we define a tool UI renderer:
|
|
68
68
|
|
|
69
69
|
```tsx title="@/components/tools/price-snapshot/PriceSnapshotTool.tsx"
|
|
70
70
|
"use client";
|
|
71
71
|
|
|
72
|
-
import {
|
|
72
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
73
73
|
|
|
74
|
-
export const
|
|
74
|
+
export const PriceSnapshotToolUI: ToolCallMessagePartComponent<
|
|
75
75
|
PriceSnapshotToolArgs,
|
|
76
76
|
string
|
|
77
|
-
>({
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
);
|
|
87
|
-
},
|
|
88
|
-
});
|
|
77
|
+
> = function PriceSnapshotUI({ args, result }) {
|
|
78
|
+
return (
|
|
79
|
+
<div className="mb-4 flex flex-col items-center">
|
|
80
|
+
<pre className="whitespace-pre-wrap break-all text-center">
|
|
81
|
+
price_snapshot({JSON.stringify(args)})
|
|
82
|
+
</pre>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
89
86
|
```
|
|
90
87
|
|
|
91
88
|
This simply displays the tool name and arguments passed to it, but not the result.
|
|
92
89
|
|
|
93
90
|
### Bind tool UI
|
|
94
91
|
|
|
95
|
-
```tsx title="@/app/page.tsx" {1,
|
|
96
|
-
|
|
92
|
+
```tsx title="@/app/page.tsx" {1-3,10}
|
|
93
|
+
"use client";
|
|
94
|
+
|
|
95
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
96
|
+
import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
|
|
97
|
+
import { AuiProvider, Tools, type Toolkit, useAui } from "@assistant-ui/react";
|
|
98
|
+
|
|
99
|
+
const toolkit = {
|
|
100
|
+
price_snapshot: {
|
|
101
|
+
type: "backend",
|
|
102
|
+
render: PriceSnapshotToolUI,
|
|
103
|
+
},
|
|
104
|
+
} satisfies Toolkit;
|
|
97
105
|
|
|
98
106
|
export default function Home() {
|
|
107
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
108
|
+
|
|
99
109
|
return (
|
|
100
|
-
<
|
|
101
|
-
<PriceSnapshotTool />
|
|
110
|
+
<AuiProvider value={aui}>
|
|
102
111
|
<Thread />
|
|
103
|
-
</
|
|
112
|
+
</AuiProvider>
|
|
104
113
|
);
|
|
105
114
|
}
|
|
106
115
|
```
|
|
@@ -201,7 +210,7 @@ We will import the new `<PriceSnapshot />` component and use it in the `render`
|
|
|
201
210
|
"use client";
|
|
202
211
|
|
|
203
212
|
import { PriceSnapshot } from "./price-snapshot";
|
|
204
|
-
import {
|
|
213
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
205
214
|
|
|
206
215
|
type PriceSnapshotToolArgs = {
|
|
207
216
|
ticker: string;
|
|
@@ -216,34 +225,31 @@ type PriceSnapshotToolResult = {
|
|
|
216
225
|
};
|
|
217
226
|
};
|
|
218
227
|
|
|
219
|
-
export const
|
|
228
|
+
export const PriceSnapshotToolUI: ToolCallMessagePartComponent<
|
|
220
229
|
PriceSnapshotToolArgs,
|
|
221
230
|
string
|
|
222
|
-
>({
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
);
|
|
245
|
-
},
|
|
246
|
-
});
|
|
231
|
+
> = function PriceSnapshotUI({ args, result }) {
|
|
232
|
+
let resultObj: PriceSnapshotToolResult | { error: string };
|
|
233
|
+
try {
|
|
234
|
+
resultObj = result ? JSON.parse(result) : {};
|
|
235
|
+
} catch (e) {
|
|
236
|
+
resultObj = { error: result! };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<div className="mb-4 flex flex-col items-center gap-2">
|
|
241
|
+
<pre className="whitespace-pre-wrap break-all text-center">
|
|
242
|
+
price_snapshot({JSON.stringify(args)})
|
|
243
|
+
</pre>
|
|
244
|
+
{"snapshot" in resultObj && (
|
|
245
|
+
<PriceSnapshot ticker={args.ticker} {...resultObj.snapshot} />
|
|
246
|
+
)}
|
|
247
|
+
{"error" in resultObj && (
|
|
248
|
+
<p className="text-red-500">{resultObj.error}</p>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
247
253
|
```
|
|
248
254
|
|
|
249
255
|
### Try it out!
|
|
@@ -320,12 +326,20 @@ export const ToolFallback: ToolCallMessagePartComponent = ({
|
|
|
320
326
|
### Bind fallback UI
|
|
321
327
|
|
|
322
328
|
```tsx title="@/app/page.tsx"
|
|
329
|
+
const toolkit = {
|
|
330
|
+
price_snapshot: {
|
|
331
|
+
type: "backend",
|
|
332
|
+
render: PriceSnapshotToolUI,
|
|
333
|
+
},
|
|
334
|
+
} satisfies Toolkit;
|
|
335
|
+
|
|
323
336
|
export default function Home() {
|
|
337
|
+
const aui = useAui({ tools: Tools({ toolkit }) });
|
|
338
|
+
|
|
324
339
|
return (
|
|
325
|
-
<
|
|
326
|
-
<PriceSnapshotTool />
|
|
340
|
+
<AuiProvider value={aui}>
|
|
327
341
|
<Thread />
|
|
328
|
-
</
|
|
342
|
+
</AuiProvider>
|
|
329
343
|
);
|
|
330
344
|
}
|
|
331
345
|
```
|