@assistant-ui/mcp-docs-server 0.1.36 → 0.1.39
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 +8 -9
- package/.docs/organized/code-examples/with-a2a.md +16 -11
- package/.docs/organized/code-examples/with-ag-ui.md +16 -11
- package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +32 -21
- package/.docs/organized/code-examples/with-artifacts.md +17 -10
- package/.docs/organized/code-examples/with-assistant-transport.md +15 -8
- package/.docs/organized/code-examples/with-browser-extension.md +15 -8
- package/.docs/organized/code-examples/with-chain-of-thought.md +17 -10
- package/.docs/organized/code-examples/with-cloud-standalone.md +10 -9
- package/.docs/organized/code-examples/with-cloud.md +18 -13
- package/.docs/organized/code-examples/with-custom-thread-list.md +17 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +20 -13
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +20 -13
- package/.docs/organized/code-examples/with-eve.md +16 -9
- package/.docs/organized/code-examples/with-expo.md +26 -23
- package/.docs/organized/code-examples/with-external-store.md +16 -11
- package/.docs/organized/code-examples/with-ffmpeg.md +18 -13
- package/.docs/organized/code-examples/with-generative-ui.md +20 -15
- package/.docs/organized/code-examples/with-google-adk.md +16 -9
- package/.docs/organized/code-examples/with-heat-graph.md +8 -9
- package/.docs/organized/code-examples/with-image-generation.md +17 -10
- package/.docs/organized/code-examples/with-interactables.md +19 -15
- package/.docs/organized/code-examples/with-langchain.md +17 -10
- package/.docs/organized/code-examples/with-langgraph.md +17 -10
- package/.docs/organized/code-examples/with-livekit.md +21 -14
- package/.docs/organized/code-examples/with-mcp.md +40 -18
- package/.docs/organized/code-examples/with-opencode.md +22 -17
- package/.docs/organized/code-examples/with-pi.md +47 -12
- package/.docs/organized/code-examples/with-react-hook-form.md +19 -14
- package/.docs/organized/code-examples/with-react-ink-web.md +6 -6
- package/.docs/organized/code-examples/with-react-ink.md +2 -2
- package/.docs/organized/code-examples/with-react-router.md +20 -15
- package/.docs/organized/code-examples/with-resumable-stream.md +19 -12
- package/.docs/organized/code-examples/with-store.md +8 -9
- package/.docs/organized/code-examples/with-tanstack.md +17 -11
- package/.docs/organized/code-examples/with-tap-runtime.md +16 -11
- package/.docs/organized/code-examples/with-virtualized-thread.md +17 -12
- package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
- package/.docs/raw/docs/(docs)/cli.mdx +18 -2
- package/.docs/raw/docs/(docs)/installation.mdx +15 -1
- package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +19 -1
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +4 -2
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +29 -29
- package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
- package/.docs/raw/docs/cloud/langgraph.mdx +4 -2
- package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
- package/.docs/raw/docs/guides/dictation.mdx +185 -257
- package/.docs/raw/docs/guides/index.mdx +10 -0
- package/.docs/raw/docs/guides/mentions.mdx +31 -3
- package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
- package/.docs/raw/docs/guides/speech.mdx +47 -29
- package/.docs/raw/docs/guides/suggestions.mdx +70 -1
- package/.docs/raw/docs/guides/voice.mdx +197 -267
- package/.docs/raw/docs/ink/primitives.mdx +35 -1
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +13 -7
- package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
- package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
- package/.docs/raw/docs/integrations/index.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
- package/.docs/raw/docs/migrations/index.mdx +50 -0
- package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +2 -2
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
- package/.docs/raw/docs/primitives/composer.mdx +16 -0
- package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
- package/.docs/raw/docs/primitives/thread-list.mdx +30 -6
- package/.docs/raw/docs/react-native/primitives.mdx +23 -0
- package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
- package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +13 -2
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +13 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +11 -11
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
- package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +8 -6
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
- package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +2 -0
- package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
- package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
- package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -1
- package/.docs/raw/docs/tools/backend.mdx +6 -3
- package/.docs/raw/docs/tools/defining-tools.mdx +12 -1
- package/.docs/raw/docs/tools/generative-ui.mdx +95 -0
- package/.docs/raw/docs/tools/mcp-apps.mdx +37 -10
- package/.docs/raw/docs/tools/mcp.mdx +105 -3
- package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +41 -4
- package/.docs/raw/docs/ui/accordion.mdx +16 -10
- package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
- package/.docs/raw/docs/ui/attachment.mdx +5 -1
- package/.docs/raw/docs/ui/badge.mdx +23 -12
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +80 -0
- package/.docs/raw/docs/ui/model-selector.mdx +69 -6
- package/.docs/raw/docs/ui/select.mdx +22 -14
- package/.docs/raw/docs/ui/sources.mdx +1 -1
- package/.docs/raw/docs/ui/tabs.mdx +25 -14
- package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
- package/README.md +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -0
- package/dist/index.js.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/prepare-docs/copy-raw.js +0 -4
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prompts/xulux-playground.d.ts +12 -0
- package/dist/prompts/xulux-playground.d.ts.map +1 -0
- package/dist/prompts/xulux-playground.js +33 -0
- package/dist/prompts/xulux-playground.js.map +1 -0
- package/dist/tools/docs.d.ts +2 -4
- package/dist/tools/docs.d.ts.map +1 -1
- package/dist/tools/docs.js +38 -10
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.d.ts +5 -5
- package/dist/tools/examples.d.ts.map +1 -1
- package/dist/tools/examples.js +10 -7
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/resources.d.ts +6 -0
- package/dist/tools/resources.d.ts.map +1 -0
- package/dist/tools/resources.js +74 -0
- package/dist/tools/resources.js.map +1 -0
- package/dist/tools/search.d.ts +30 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +39 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/tests/test-setup.d.ts.map +1 -1
- package/dist/tools/tests/test-setup.js +7 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/tools/xulux-templates.d.ts +72 -0
- package/dist/tools/xulux-templates.d.ts.map +1 -0
- package/dist/tools/xulux-templates.js +82 -0
- package/dist/tools/xulux-templates.js.map +1 -0
- package/dist/utils/cache.d.ts +5 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +18 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/mcp-format.d.ts +1 -0
- package/dist/utils/mcp-format.d.ts.map +1 -1
- package/dist/utils/mcp-format.js +7 -4
- package/dist/utils/mcp-format.js.map +1 -1
- package/dist/utils/mdx.d.ts +2 -1
- package/dist/utils/mdx.d.ts.map +1 -1
- package/dist/utils/mdx.js +19 -2
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +20 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/search.d.ts +10 -0
- package/dist/utils/search.d.ts.map +1 -0
- package/dist/utils/search.js +97 -0
- package/dist/utils/search.js.map +1 -0
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/xulux/catalog-client.d.ts +14 -0
- package/dist/xulux/catalog-client.d.ts.map +1 -0
- package/dist/xulux/catalog-client.js +67 -0
- package/dist/xulux/catalog-client.js.map +1 -0
- package/dist/xulux/fallback-catalog.d.ts +7 -0
- package/dist/xulux/fallback-catalog.d.ts.map +1 -0
- package/dist/xulux/fallback-catalog.js +47 -0
- package/dist/xulux/fallback-catalog.js.map +1 -0
- package/dist/xulux/fetch-sandbox.d.ts +5 -0
- package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
- package/dist/xulux/fetch-sandbox.js +40 -0
- package/dist/xulux/fetch-sandbox.js.map +1 -0
- package/dist/xulux/template-service.d.ts +84 -0
- package/dist/xulux/template-service.d.ts.map +1 -0
- package/dist/xulux/template-service.js +223 -0
- package/dist/xulux/template-service.js.map +1 -0
- package/dist/xulux/types.d.ts +55 -0
- package/dist/xulux/types.d.ts.map +1 -0
- package/dist/xulux/types.js +6 -0
- package/dist/xulux/types.js.map +1 -0
- package/package.json +5 -5
- package/src/constants.ts +2 -0
- package/src/index.ts +67 -0
- package/src/prepare-docs/copy-raw.ts +0 -5
- package/src/prompts/xulux-playground.ts +36 -0
- package/src/tools/docs.ts +52 -4
- package/src/tools/examples.ts +18 -11
- package/src/tools/resources.ts +114 -0
- package/src/tools/search.ts +46 -0
- package/src/tools/tests/completions.test.ts +46 -0
- package/src/tools/tests/directory-size-cap.test.ts +50 -0
- package/src/tools/tests/docs.test.ts +20 -0
- package/src/tools/tests/examples.test.ts +5 -5
- package/src/tools/tests/listings-cache.test.ts +19 -0
- package/src/tools/tests/mcp-protocol.test.ts +92 -1
- package/src/tools/tests/resources.test.ts +102 -0
- package/src/tools/tests/search.test.ts +37 -0
- package/src/tools/tests/test-setup.ts +10 -0
- package/src/tools/tests/xulux-templates.test.ts +262 -0
- package/src/tools/xulux-templates.ts +141 -0
- package/src/utils/cache.ts +20 -0
- package/src/utils/mcp-format.ts +8 -6
- package/src/utils/mdx.ts +21 -1
- package/src/utils/paths.ts +25 -0
- package/src/utils/search.ts +131 -0
- package/src/utils/tests/cache.test.ts +51 -0
- package/src/utils/tests/mcp-format.test.ts +22 -0
- package/src/utils/tests/security.test.ts +1 -1
- package/src/xulux/catalog-client.ts +105 -0
- package/src/xulux/fallback-catalog.ts +63 -0
- package/src/xulux/fetch-sandbox.ts +56 -0
- package/src/xulux/template-service.ts +406 -0
- package/src/xulux/types.ts +60 -0
- package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -64
- package/.docs/raw/blog/2024-09-11/index.mdx +0 -10
- package/.docs/raw/blog/2024-12-15/index.mdx +0 -10
- package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -127
- package/.docs/raw/blog/2026-03-launch-week/index.mdx +0 -258
- package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +0 -464
|
@@ -1,60 +1,192 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Realtime Voice Chat
|
|
3
|
-
description: Build bidirectional voice conversations with AI in React
|
|
3
|
+
description: Build bidirectional voice conversations with AI in React. Realtime audio streaming, interruption handling, and visual state, integrated via assistant-ui.
|
|
4
4
|
platforms: ["react"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
import { VoiceSample } from "@/components/docs/samples/voice";
|
|
8
8
|
|
|
9
|
-
assistant-ui supports realtime bidirectional voice
|
|
9
|
+
assistant-ui supports realtime bidirectional voice through the `RealtimeVoiceAdapter` interface. Users speak into their microphone, the agent answers with audio, and transcripts appear in the thread while the session runs.
|
|
10
10
|
|
|
11
11
|
<VoiceSample />
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Three voice modes
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
assistant-ui ships three related but distinct voice capabilities. Choose the mode that matches the product surface:
|
|
16
16
|
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
|
20
|
-
| [Dictation](/docs/guides/dictation) | `DictationAdapter` | Audio →
|
|
21
|
-
| **
|
|
17
|
+
| Mode | Guide | Adapter | Direction |
|
|
18
|
+
|------|-------|---------|-----------|
|
|
19
|
+
| **Realtime duplex** | this page | `RealtimeVoiceAdapter` | Audio ↔ audio, live session |
|
|
20
|
+
| **Push-to-talk dictation** | [Dictation](/docs/guides/dictation) | `DictationAdapter` | Audio → text into the composer |
|
|
21
|
+
| **Read-aloud** | [Speech](/docs/guides/speech) | `SpeechSynthesisAdapter` | Text → audio for one message |
|
|
22
|
+
|
|
23
|
+
Realtime voice is the only mode that owns both directions at once. Dictation fills the composer; speech reads a finished message aloud.
|
|
24
|
+
|
|
25
|
+
## RealtimeVoiceAdapter
|
|
26
|
+
|
|
27
|
+
Implement `RealtimeVoiceAdapter` to connect any voice provider. The contract lives in `@assistant-ui/core` and is re-exported from `@assistant-ui/react`:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import type { RealtimeVoiceAdapter } from "@assistant-ui/react";
|
|
31
|
+
|
|
32
|
+
type RealtimeVoiceAdapter = {
|
|
33
|
+
connect: (options: {
|
|
34
|
+
abortSignal?: AbortSignal;
|
|
35
|
+
}) => RealtimeVoiceAdapter.Session;
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A session exposes connection state, mute controls, and event subscriptions:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
namespace RealtimeVoiceAdapter {
|
|
43
|
+
type Status =
|
|
44
|
+
| { type: "starting" | "running" }
|
|
45
|
+
| {
|
|
46
|
+
type: "ended";
|
|
47
|
+
reason: "finished" | "cancelled" | "error";
|
|
48
|
+
error?: unknown;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type Mode = "listening" | "speaking";
|
|
52
|
+
|
|
53
|
+
type TranscriptItem = {
|
|
54
|
+
role: "user" | "assistant";
|
|
55
|
+
text: string;
|
|
56
|
+
isFinal?: boolean;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type Session = {
|
|
60
|
+
status: Status;
|
|
61
|
+
isMuted: boolean;
|
|
62
|
+
|
|
63
|
+
disconnect: () => void;
|
|
64
|
+
mute: () => void;
|
|
65
|
+
unmute: () => void;
|
|
66
|
+
|
|
67
|
+
onStatusChange: (callback: (status: Status) => void) => Unsubscribe;
|
|
68
|
+
onTranscript: (
|
|
69
|
+
callback: (transcript: TranscriptItem) => void,
|
|
70
|
+
) => Unsubscribe;
|
|
71
|
+
onModeChange: (callback: (mode: Mode) => void) => Unsubscribe;
|
|
72
|
+
onVolumeChange: (callback: (volume: number) => void) => Unsubscribe;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Session status moves `starting` → `running` → `ended`. The `ended` status includes a reason: `"finished"`, `"cancelled"`, or `"error"` (with an optional `error` field).
|
|
78
|
+
|
|
79
|
+
Transcripts from `onTranscript` are appended to the message thread automatically:
|
|
80
|
+
|
|
81
|
+
- User transcripts (`role: "user"`, `isFinal: true`) become user messages.
|
|
82
|
+
- Assistant transcripts (`role: "assistant"`) stream into an assistant message. The message stays in a running status until `isFinal: true`.
|
|
83
|
+
|
|
84
|
+
`onModeChange` reports `"listening"` (user's turn) or `"speaking"` (agent's turn). `onVolumeChange` reports a real-time level from `0` to `1` for visual feedback such as the voice orb.
|
|
85
|
+
|
|
86
|
+
## createVoiceSession
|
|
87
|
+
|
|
88
|
+
`createVoiceSession` removes the manual callback-set boilerplate when you implement an adapter. Pass it the connect options and an async `setup` function that receives `VoiceSessionHelpers` and returns `VoiceSessionControls`:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import {
|
|
92
|
+
createVoiceSession,
|
|
93
|
+
type RealtimeVoiceAdapter,
|
|
94
|
+
type VoiceSessionControls,
|
|
95
|
+
type VoiceSessionHelpers,
|
|
96
|
+
} from "@assistant-ui/react";
|
|
97
|
+
|
|
98
|
+
export class MyVoiceAdapter implements RealtimeVoiceAdapter {
|
|
99
|
+
connect(options: {
|
|
100
|
+
abortSignal?: AbortSignal;
|
|
101
|
+
}): RealtimeVoiceAdapter.Session {
|
|
102
|
+
return createVoiceSession(options, async (helpers: VoiceSessionHelpers) => {
|
|
103
|
+
const client = await MyVoiceClient.connect();
|
|
104
|
+
|
|
105
|
+
client.on("open", () => helpers.setStatus({ type: "running" }));
|
|
106
|
+
client.on("close", () => helpers.end("finished"));
|
|
107
|
+
client.on("error", (err: unknown) => helpers.end("error", err));
|
|
108
|
+
client.on("transcript", (item) => helpers.emitTranscript(item));
|
|
109
|
+
client.on("mode", (mode) => helpers.emitMode(mode));
|
|
110
|
+
client.on("volume", (v: number) => helpers.emitVolume(v));
|
|
111
|
+
|
|
112
|
+
const controls: VoiceSessionControls = {
|
|
113
|
+
disconnect: () => client.close(),
|
|
114
|
+
mute: () => client.setMuted(true),
|
|
115
|
+
unmute: () => client.setMuted(false),
|
|
116
|
+
};
|
|
117
|
+
return controls;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`VoiceSessionHelpers` provides:
|
|
124
|
+
|
|
125
|
+
| Helper | Role |
|
|
126
|
+
|--------|------|
|
|
127
|
+
| `setStatus(status)` | Update session status (for example to `{ type: "running" }`) |
|
|
128
|
+
| `end(reason, error?)` | End the session and clean up subscribers |
|
|
129
|
+
| `emitTranscript(item)` | Push a transcript into the thread |
|
|
130
|
+
| `emitMode(mode)` | Report `"listening"` or `"speaking"` |
|
|
131
|
+
| `emitVolume(volume)` | Report a level from `0` to `1` |
|
|
132
|
+
| `isDisposed()` | Skip events after teardown |
|
|
133
|
+
|
|
134
|
+
`createVoiceSession` wires status tracking, mute state, abort-signal disconnect, and all `on*` subscriptions for you.
|
|
22
135
|
|
|
23
136
|
## Configuration
|
|
24
137
|
|
|
25
|
-
Pass a `RealtimeVoiceAdapter` implementation
|
|
138
|
+
Pass a `RealtimeVoiceAdapter` implementation on the runtime:
|
|
26
139
|
|
|
27
140
|
```tsx
|
|
28
141
|
const runtime = useChatRuntime({
|
|
29
142
|
adapters: {
|
|
30
|
-
voice: new MyVoiceAdapter({ /*
|
|
143
|
+
voice: new MyVoiceAdapter({ /* provider options */ }),
|
|
31
144
|
},
|
|
32
145
|
});
|
|
33
146
|
```
|
|
34
147
|
|
|
35
|
-
When a voice adapter is provided, `capabilities.voice` is
|
|
148
|
+
When a voice adapter is provided, `capabilities.voice` is set to `true` automatically.
|
|
149
|
+
|
|
150
|
+
## React hooks
|
|
36
151
|
|
|
37
|
-
|
|
152
|
+
These hooks are exported from `@assistant-ui/react` and read the active voice session on the current thread.
|
|
38
153
|
|
|
39
154
|
### useVoiceState
|
|
40
155
|
|
|
41
|
-
Returns the current
|
|
156
|
+
Returns the current `VoiceSessionState`, or `undefined` when no session is active:
|
|
42
157
|
|
|
43
158
|
```tsx
|
|
44
|
-
import { useVoiceState
|
|
159
|
+
import { useVoiceState } from "@assistant-ui/react";
|
|
45
160
|
|
|
46
161
|
const voiceState = useVoiceState();
|
|
47
|
-
// voiceState?.status.type
|
|
48
|
-
// voiceState?.isMuted
|
|
49
|
-
// voiceState?.mode
|
|
162
|
+
// voiceState?.status.type: "starting" | "running" | "ended"
|
|
163
|
+
// voiceState?.isMuted: boolean
|
|
164
|
+
// voiceState?.mode: "listening" | "speaking"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`VoiceSessionState` is:
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
type VoiceSessionState = {
|
|
171
|
+
readonly status: RealtimeVoiceAdapter.Status;
|
|
172
|
+
readonly isMuted: boolean;
|
|
173
|
+
readonly mode: RealtimeVoiceAdapter.Mode;
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### useVoiceVolume
|
|
178
|
+
|
|
179
|
+
Subscribes to real-time audio level independently of session state:
|
|
50
180
|
|
|
51
|
-
|
|
52
|
-
|
|
181
|
+
```tsx
|
|
182
|
+
import { useVoiceVolume } from "@assistant-ui/react";
|
|
183
|
+
|
|
184
|
+
const volume = useVoiceVolume(); // number from 0 to 1
|
|
53
185
|
```
|
|
54
186
|
|
|
55
187
|
### useVoiceControls
|
|
56
188
|
|
|
57
|
-
Returns methods
|
|
189
|
+
Returns methods that drive the session:
|
|
58
190
|
|
|
59
191
|
```tsx
|
|
60
192
|
import { useVoiceControls } from "@assistant-ui/react";
|
|
@@ -62,11 +194,9 @@ import { useVoiceControls } from "@assistant-ui/react";
|
|
|
62
194
|
const { connect, disconnect, mute, unmute } = useVoiceControls();
|
|
63
195
|
```
|
|
64
196
|
|
|
65
|
-
|
|
197
|
+
### Headless control bar
|
|
66
198
|
|
|
67
|
-
|
|
68
|
-
For a ready-made control bar with a voice orb and call controls, see the [Voice component](/docs/ui/voice).
|
|
69
|
-
</Callout>
|
|
199
|
+
Build your own controls from the hooks when you need a custom layout:
|
|
70
200
|
|
|
71
201
|
```tsx
|
|
72
202
|
import { useVoiceState, useVoiceControls } from "@assistant-ui/react";
|
|
@@ -102,263 +232,50 @@ function VoiceControls() {
|
|
|
102
232
|
}
|
|
103
233
|
```
|
|
104
234
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
Implement the `RealtimeVoiceAdapter` interface to integrate with any voice provider.
|
|
235
|
+
## Voice UI component
|
|
108
236
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```tsx
|
|
112
|
-
import type { RealtimeVoiceAdapter } from "@assistant-ui/react";
|
|
113
|
-
|
|
114
|
-
class MyVoiceAdapter implements RealtimeVoiceAdapter {
|
|
115
|
-
connect(options: {
|
|
116
|
-
abortSignal?: AbortSignal;
|
|
117
|
-
}): RealtimeVoiceAdapter.Session {
|
|
118
|
-
// Establish connection to your voice service
|
|
119
|
-
return {
|
|
120
|
-
get status() { /* ... */ },
|
|
121
|
-
get isMuted() { /* ... */ },
|
|
122
|
-
|
|
123
|
-
disconnect: () => { /* ... */ },
|
|
124
|
-
mute: () => { /* ... */ },
|
|
125
|
-
unmute: () => { /* ... */ },
|
|
126
|
-
|
|
127
|
-
onStatusChange: (callback) => {
|
|
128
|
-
// Status: { type: "starting" } → { type: "running" } → { type: "ended", reason }
|
|
129
|
-
return () => {}; // Return unsubscribe
|
|
130
|
-
},
|
|
237
|
+
The fastest path is the styled `voice` registry component. It ships a `VoiceControl` bar, mute and disconnect buttons, status indicator, and animated `VoiceOrb` built on the hooks above.
|
|
131
238
|
|
|
132
|
-
|
|
133
|
-
// callback({ role: "user" | "assistant", text: "...", isFinal: true })
|
|
134
|
-
// Transcripts are automatically appended as messages in the thread.
|
|
135
|
-
return () => {};
|
|
136
|
-
},
|
|
239
|
+
### Install
|
|
137
240
|
|
|
138
|
-
|
|
139
|
-
onModeChange: (callback) => {
|
|
140
|
-
// callback("listening") — user's turn
|
|
141
|
-
// callback("speaking") — agent's turn
|
|
142
|
-
return () => {};
|
|
143
|
-
},
|
|
241
|
+
<InstallCommand shadcn={["voice"]} />
|
|
144
242
|
|
|
145
|
-
|
|
146
|
-
onVolumeChange: (callback) => {
|
|
147
|
-
// callback(0.72) — drives VoiceOrb amplitude and waveform bar heights
|
|
148
|
-
return () => {};
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
243
|
+
This adds `/components/assistant-ui/voice.tsx` to your project. Adjust styling as needed.
|
|
154
244
|
|
|
155
|
-
###
|
|
245
|
+
### Use with a runtime
|
|
156
246
|
|
|
157
|
-
|
|
247
|
+
```tsx title="app/page.tsx"
|
|
248
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
249
|
+
import { VoiceControl } from "@/components/assistant-ui/voice";
|
|
250
|
+
import { AuiIf } from "@assistant-ui/react";
|
|
158
251
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
All adapters must implement `onModeChange` and `onVolumeChange`. If your provider doesn't support these, return a no-op unsubscribe:
|
|
172
|
-
|
|
173
|
-
- **`onModeChange`** — Reports `"listening"` (user's turn) or `"speaking"` (agent's turn). The `VoiceOrb` switches to the active speaking animation.
|
|
174
|
-
- **`onVolumeChange`** — Reports a real-time audio level (`0`–`1`). The `VoiceOrb` modulates its amplitude and glow, and waveform bars scale to match.
|
|
175
|
-
|
|
176
|
-
When using `createVoiceSession`, these are handled automatically — call `session.emitMode()` and `session.emitVolume()` when your provider delivers data.
|
|
177
|
-
|
|
178
|
-
### Transcript Handling
|
|
179
|
-
|
|
180
|
-
Transcripts emitted via `onTranscript` are automatically appended to the message thread:
|
|
181
|
-
|
|
182
|
-
- **User transcripts** (`role: "user"`, `isFinal: true`) are appended as user messages.
|
|
183
|
-
- **Assistant transcripts** (`role: "assistant"`) are streamed into an assistant message. The message shows a "running" status until `isFinal: true` is received.
|
|
184
|
-
|
|
185
|
-
## Example: ElevenLabs Conversational AI
|
|
186
|
-
|
|
187
|
-
[ElevenLabs Conversational AI](https://elevenlabs.io/docs/agents-platform/overview) provides realtime voice agents via WebRTC.
|
|
188
|
-
|
|
189
|
-
### Install Dependencies
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
npm install @elevenlabs/client
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Adapter
|
|
196
|
-
|
|
197
|
-
```tsx title="lib/elevenlabs-voice-adapter.ts"
|
|
198
|
-
import type { RealtimeVoiceAdapter, Unsubscribe } from "@assistant-ui/react";
|
|
199
|
-
import { VoiceConversation } from "@elevenlabs/client";
|
|
200
|
-
|
|
201
|
-
export class ElevenLabsVoiceAdapter implements RealtimeVoiceAdapter {
|
|
202
|
-
private _agentId: string;
|
|
203
|
-
|
|
204
|
-
constructor(options: { agentId: string }) {
|
|
205
|
-
this._agentId = options.agentId;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
connect(options: {
|
|
209
|
-
abortSignal?: AbortSignal;
|
|
210
|
-
}): RealtimeVoiceAdapter.Session {
|
|
211
|
-
const statusCallbacks = new Set<(s: RealtimeVoiceAdapter.Status) => void>();
|
|
212
|
-
const transcriptCallbacks = new Set<(t: RealtimeVoiceAdapter.TranscriptItem) => void>();
|
|
213
|
-
const modeCallbacks = new Set<(m: RealtimeVoiceAdapter.Mode) => void>();
|
|
214
|
-
const volumeCallbacks = new Set<(v: number) => void>();
|
|
215
|
-
|
|
216
|
-
let currentStatus: RealtimeVoiceAdapter.Status = { type: "starting" };
|
|
217
|
-
let isMuted = false;
|
|
218
|
-
let conversation: VoiceConversation | null = null;
|
|
219
|
-
let disposed = false;
|
|
220
|
-
|
|
221
|
-
const updateStatus = (status: RealtimeVoiceAdapter.Status) => {
|
|
222
|
-
if (disposed) return;
|
|
223
|
-
currentStatus = status;
|
|
224
|
-
for (const cb of statusCallbacks) cb(status);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const cleanup = () => {
|
|
228
|
-
disposed = true;
|
|
229
|
-
conversation = null;
|
|
230
|
-
statusCallbacks.clear();
|
|
231
|
-
transcriptCallbacks.clear();
|
|
232
|
-
modeCallbacks.clear();
|
|
233
|
-
volumeCallbacks.clear();
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const session: RealtimeVoiceAdapter.Session = {
|
|
237
|
-
get status() { return currentStatus; },
|
|
238
|
-
get isMuted() { return isMuted; },
|
|
239
|
-
disconnect: () => { conversation?.endSession(); cleanup(); },
|
|
240
|
-
mute: () => { conversation?.setMicMuted(true); isMuted = true; },
|
|
241
|
-
unmute: () => { conversation?.setMicMuted(false); isMuted = false; },
|
|
242
|
-
onStatusChange: (cb): Unsubscribe => {
|
|
243
|
-
statusCallbacks.add(cb);
|
|
244
|
-
return () => statusCallbacks.delete(cb);
|
|
245
|
-
},
|
|
246
|
-
onTranscript: (cb): Unsubscribe => {
|
|
247
|
-
transcriptCallbacks.add(cb);
|
|
248
|
-
return () => transcriptCallbacks.delete(cb);
|
|
249
|
-
},
|
|
250
|
-
onModeChange: (cb): Unsubscribe => {
|
|
251
|
-
modeCallbacks.add(cb);
|
|
252
|
-
return () => modeCallbacks.delete(cb);
|
|
253
|
-
},
|
|
254
|
-
onVolumeChange: (cb): Unsubscribe => {
|
|
255
|
-
volumeCallbacks.add(cb);
|
|
256
|
-
return () => volumeCallbacks.delete(cb);
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
if (options.abortSignal) {
|
|
261
|
-
options.abortSignal.addEventListener("abort", () => {
|
|
262
|
-
conversation?.endSession(); cleanup();
|
|
263
|
-
}, { once: true });
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const doConnect = async () => {
|
|
267
|
-
if (disposed) return;
|
|
268
|
-
try {
|
|
269
|
-
conversation = await VoiceConversation.startSession({
|
|
270
|
-
agentId: this._agentId,
|
|
271
|
-
onConnect: () => updateStatus({ type: "running" }),
|
|
272
|
-
onDisconnect: () => { updateStatus({ type: "ended", reason: "finished" }); cleanup(); },
|
|
273
|
-
onError: (msg) => { updateStatus({ type: "ended", reason: "error", error: new Error(msg) }); cleanup(); },
|
|
274
|
-
onModeChange: ({ mode }) => {
|
|
275
|
-
if (disposed) return;
|
|
276
|
-
for (const cb of modeCallbacks) cb(mode === "speaking" ? "speaking" : "listening");
|
|
277
|
-
},
|
|
278
|
-
onMessage: (msg) => {
|
|
279
|
-
if (disposed) return;
|
|
280
|
-
for (const cb of transcriptCallbacks) {
|
|
281
|
-
cb({ role: msg.role === "user" ? "user" : "assistant", text: msg.message, isFinal: true });
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
} catch (error) {
|
|
286
|
-
updateStatus({ type: "ended", reason: "error", error }); cleanup();
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
doConnect();
|
|
291
|
-
return session;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### Usage
|
|
297
|
-
|
|
298
|
-
```tsx
|
|
299
|
-
import { ElevenLabsVoiceAdapter } from "@/lib/elevenlabs-voice-adapter";
|
|
300
|
-
|
|
301
|
-
const runtime = useChatRuntime({
|
|
302
|
-
adapters: {
|
|
303
|
-
voice: new ElevenLabsVoiceAdapter({
|
|
304
|
-
agentId: process.env.NEXT_PUBLIC_ELEVENLABS_AGENT_ID!,
|
|
305
|
-
}),
|
|
306
|
-
},
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## Using `createVoiceSession`
|
|
311
|
-
|
|
312
|
-
`createVoiceSession` is a helper that eliminates the manual `Set<callback>` boilerplate shown in the ElevenLabs example above. Pass it an async `setup` function that receives a `helpers` object and returns `{ disconnect, mute, unmute }`. The helper wires up all callback sets, status tracking, and abort-signal handling for you.
|
|
313
|
-
|
|
314
|
-
```tsx title="lib/my-voice-adapter.ts"
|
|
315
|
-
import { createVoiceSession, type RealtimeVoiceAdapter } from "@assistant-ui/react";
|
|
316
|
-
|
|
317
|
-
export class MyVoiceAdapter implements RealtimeVoiceAdapter {
|
|
318
|
-
connect(options: { abortSignal?: AbortSignal }): RealtimeVoiceAdapter.Session {
|
|
319
|
-
return createVoiceSession(options, async (helpers) => {
|
|
320
|
-
// Connect to your provider
|
|
321
|
-
const client = await MyVoiceClient.connect();
|
|
322
|
-
|
|
323
|
-
client.on("open", () => helpers.setStatus({ type: "running" }));
|
|
324
|
-
client.on("close", () => helpers.end("finished"));
|
|
325
|
-
client.on("error", (err) => helpers.end("error", err));
|
|
326
|
-
|
|
327
|
-
client.on("transcript", (item) => helpers.emitTranscript(item));
|
|
328
|
-
client.on("mode", (mode) => helpers.emitMode(mode));
|
|
329
|
-
client.on("volume", (v) => helpers.emitVolume(v));
|
|
330
|
-
|
|
331
|
-
// Return controls — createVoiceSession calls these on disconnect/mute/unmute
|
|
332
|
-
return {
|
|
333
|
-
disconnect: () => client.close(),
|
|
334
|
-
mute: () => client.setMuted(true),
|
|
335
|
-
unmute: () => client.setMuted(false),
|
|
336
|
-
};
|
|
337
|
-
});
|
|
338
|
-
}
|
|
252
|
+
export default function Chat() {
|
|
253
|
+
return (
|
|
254
|
+
<div className="flex h-full flex-col">
|
|
255
|
+
<AuiIf condition={(s) => s.thread.capabilities.voice}>
|
|
256
|
+
<VoiceControl />
|
|
257
|
+
</AuiIf>
|
|
258
|
+
<div className="min-h-0 flex-1">
|
|
259
|
+
<Thread />
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
339
263
|
}
|
|
340
264
|
```
|
|
341
265
|
|
|
342
|
-
|
|
266
|
+
See the [Voice UI page](/docs/ui/voice) for anatomy, variants, and state samples.
|
|
343
267
|
|
|
344
268
|
## Example: LiveKit
|
|
345
269
|
|
|
346
|
-
[LiveKit](https://livekit.io/) provides realtime voice
|
|
347
|
-
|
|
348
|
-
### Prerequisites
|
|
349
|
-
|
|
350
|
-
1. **A LiveKit server** — create a [LiveKit Cloud](https://cloud.livekit.io/) project (grab the URL, API Key, and API Secret from the project settings) or self-host `livekit-server`.
|
|
351
|
-
2. **An agent worker** — built with the [LiveKit Agents SDK](https://docs.livekit.io/agents/) (Python or Node). The worker connects to your LiveKit server and is automatically dispatched into new rooms.
|
|
270
|
+
[LiveKit](https://livekit.io/) provides realtime voice over WebRTC rooms with transcription support. The browser adapter joins a room; a separate agent worker (STT, LLM, TTS) joins the same room. Without an agent in the room, the client connects but has nothing to talk to.
|
|
352
271
|
|
|
353
|
-
|
|
272
|
+
The [`with-livekit`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-livekit) example shows the full wiring:
|
|
354
273
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
`livekit-client` powers the browser adapter; `livekit-server-sdk` is used server-side to mint access tokens.
|
|
274
|
+
1. **Adapter** (`examples/with-livekit/lib/livekit-voice-adapter.ts`): `LiveKitVoiceAdapter` implements `RealtimeVoiceAdapter`. `connect` calls `createVoiceSession`, connects a LiveKit `Room`, enables the local microphone, attaches remote audio tracks, and maps room events to helpers (`setStatus`, `end`, `emitMode`, `emitVolume`, `emitTranscript`).
|
|
275
|
+
2. **Token route** (`examples/with-livekit/app/api/livekit-token/route.ts`): mints a short-lived room token server-side so secrets stay off the client.
|
|
276
|
+
3. **Agent worker** (`examples/with-livekit/agent/`): a Python LiveKit Agents process that joins the room and runs the voice pipeline.
|
|
360
277
|
|
|
361
|
-
|
|
278
|
+
Minimal runtime wiring:
|
|
362
279
|
|
|
363
280
|
```tsx
|
|
364
281
|
import { LiveKitVoiceAdapter } from "@/lib/livekit-voice-adapter";
|
|
@@ -377,4 +294,17 @@ const runtime = useChatRuntime({
|
|
|
377
294
|
});
|
|
378
295
|
```
|
|
379
296
|
|
|
380
|
-
|
|
297
|
+
Clone the example for the adapter source, token endpoint, and agent worker rather than re-implementing the full LiveKit event map from scratch.
|
|
298
|
+
|
|
299
|
+
You can also scaffold it with the CLI:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
npx assistant-ui create my-app -e with-livekit
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Related guides
|
|
306
|
+
|
|
307
|
+
- [Dictation](/docs/guides/dictation): push-to-talk speech-to-text into the composer (`DictationAdapter`, `ComposerPrimitive.Dictate`).
|
|
308
|
+
- [Speech](/docs/guides/speech): read-aloud for assistant messages (`SpeechSynthesisAdapter`, `ActionBarPrimitive.Speak`).
|
|
309
|
+
- [Voice UI](/docs/ui/voice): registry component reference for `VoiceControl` and `VoiceOrb`.
|
|
310
|
+
- [Voice API reference](/docs/api-reference/voice): generated types for sessions, speech, and dictation.
|
|
@@ -3,7 +3,7 @@ title: Primitives
|
|
|
3
3
|
description: Composable terminal components for building chat UIs with Ink.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Primitives are thin wrappers around Ink components (`Box`, `Text
|
|
6
|
+
Primitives are thin wrappers around Ink components (`Box`, `Text`) that integrate with the assistant-ui runtime. They accept all standard Ink props and add runtime-aware behavior.
|
|
7
7
|
|
|
8
8
|
Primitives use namespace imports, the same pattern as the web package (e.g. `ThreadPrimitive.Root`, `ComposerPrimitive.Input`).
|
|
9
9
|
|
|
@@ -1215,3 +1215,37 @@ Container `Box` for the status bar. Forwards all `Box` props; pass `gap` to spac
|
|
|
1215
1215
|
<StatusBarPrimitive.Latency />
|
|
1216
1216
|
</StatusBarPrimitive.Root>
|
|
1217
1217
|
```
|
|
1218
|
+
|
|
1219
|
+
## TextInput
|
|
1220
|
+
|
|
1221
|
+
```tsx
|
|
1222
|
+
import { TextInput } from "@assistant-ui/react-ink";
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
Store-free, controlled single/multi-line text field. Ink ships no native text input, so this is the terminal equivalent of the DOM `<textarea>` or React Native's `<TextInput>` — the editing engine (buffer, grapheme-aware cursor, and the full emacs/arrow keymap) without any runtime or composer coupling. `ComposerPrimitive.Input` is a thin adapter that wraps `TextInput` and binds it to the composer store; reach for `TextInput` directly when you need an editable field outside the thread composer (for example, editing a value before confirming an action).
|
|
1226
|
+
|
|
1227
|
+
It is controlled on `value`/`onChange`, so an external owner can override the text at any time (the buffer absorbs the echo without thrashing the cursor). An owner may also transform the emitted text in `onChange` (uppercase, mask, reject); the buffer applies the corrected value while keeping the cursor at the edit position, snapped to a grapheme boundary. A replacement that does not respond to an edit (a programmatic set while idle) still places the cursor at the end:
|
|
1228
|
+
|
|
1229
|
+
```tsx
|
|
1230
|
+
const [command, setCommand] = useState(approval.action.command);
|
|
1231
|
+
|
|
1232
|
+
<TextInput
|
|
1233
|
+
value={command}
|
|
1234
|
+
onChange={setCommand}
|
|
1235
|
+
submitOnEnter
|
|
1236
|
+
onSubmit={(text) => runEdited(text)}
|
|
1237
|
+
/>
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
| Prop | Type | Description |
|
|
1241
|
+
|------|------|-------------|
|
|
1242
|
+
| `value` | `string` | Current text (controlled) |
|
|
1243
|
+
| `onChange` | `(text: string) => void` | Called with the next text whenever the buffer changes |
|
|
1244
|
+
| `onSubmit` | `(text: string) => void` | Called on Enter when `submitOnEnter` is set, with the text as displayed; owners that validate in `onChange` should validate again in `onSubmit` |
|
|
1245
|
+
| `submitOnEnter` | `boolean` | Whether Enter calls `onSubmit` (default: `false`) |
|
|
1246
|
+
| `placeholder` | `string` | Placeholder text when empty (default: `""`) |
|
|
1247
|
+
| `autoFocus` | `boolean` | Auto-focus on mount (default: `true`) |
|
|
1248
|
+
| `multiLine` | `boolean` | When true, Enter inserts a newline unless `submitOnEnter` is enabled |
|
|
1249
|
+
| `...rest` | `ComponentProps<typeof Box>` | Forwarded to the underlying Ink `Box` |
|
|
1250
|
+
|
|
1251
|
+
Keyboard bindings, grapheme handling, and terminal-capability caveats are identical to [`ComposerPrimitive.Input`](#input).
|
|
@@ -5,10 +5,10 @@ description: Wire the Vercel AI SDK into a React chat UI with assistant-ui — u
|
|
|
5
5
|
|
|
6
6
|
import { VercelIcon } from "@/components/icons/vercel";
|
|
7
7
|
|
|
8
|
-
[Vercel AI SDK](https://ai-sdk.dev/) is the most common framework people pair with assistant-ui. The full setup, attachments, persistence, tool-call patterns, and version notes are documented under [runtimes/ai-sdk](/docs/runtimes/ai-sdk); this page is the entry point in the integrations tree for discoverability and architecture context.
|
|
8
|
+
[Vercel AI SDK](https://ai-sdk.dev/) is the most common framework people pair with assistant-ui. The full setup, attachments, persistence, tool-call patterns, and version notes are documented under [runtimes/ai-sdk](/docs/runtimes/ai-sdk/overview); this page is the entry point in the integrations tree for discoverability and architecture context.
|
|
9
9
|
|
|
10
10
|
<Callout type="info">
|
|
11
|
-
If you arrived here looking to wire up your first chat: jump to [AI SDK
|
|
11
|
+
If you arrived here looking to wire up your first chat: jump to [AI SDK v7 quickstart](/docs/runtimes/ai-sdk/v7). This page is a high-level pointer.
|
|
12
12
|
</Callout>
|
|
13
13
|
|
|
14
14
|
## Where it slots in
|
|
@@ -27,14 +27,20 @@ flowchart LR
|
|
|
27
27
|
|
|
28
28
|
## Pick a version
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Four versions of `ai` are supported. New projects should pick **v7**; v6, v5, and v4 are documented for migration and existing apps that haven't upgraded.
|
|
31
31
|
|
|
32
32
|
<Cards>
|
|
33
33
|
<Card
|
|
34
34
|
icon={<VercelIcon width={20} height={20} />}
|
|
35
|
-
title="AI SDK
|
|
35
|
+
title="AI SDK v7 (current)"
|
|
36
|
+
description="Requires ai@^7 and @ai-sdk/react@^4. Async convertToModelMessages, tool inputSchema, toUIMessageStreamResponse."
|
|
37
|
+
href="/docs/runtimes/ai-sdk/v7"
|
|
38
|
+
/>
|
|
39
|
+
<Card
|
|
40
|
+
icon={<VercelIcon width={20} height={20} />}
|
|
41
|
+
title="AI SDK v6 (legacy)"
|
|
36
42
|
description="Requires ai@^6 and @ai-sdk/react@^3. Async convertToModelMessages, tool inputSchema, toUIMessageStreamResponse."
|
|
37
|
-
href="/docs/runtimes/ai-sdk/v6"
|
|
43
|
+
href="/docs/runtimes/ai-sdk/v6-legacy"
|
|
38
44
|
/>
|
|
39
45
|
<Card
|
|
40
46
|
icon={<VercelIcon width={20} height={20} />}
|
|
@@ -58,7 +64,7 @@ AI SDK is the default choice for new projects on Next.js, Remix, or any framewor
|
|
|
58
64
|
- You will compose with a framework like [Mastra](/docs/integrations/frameworks/mastra/overview), an [observability tool](/docs/integrations/observability/helicone), an [LLM gateway](/docs/integrations/gateways), or [tools through MCP](/docs/tools/mcp), all of which assume an AI SDK route.
|
|
59
65
|
- You want first-party `frontendTools`, attachments, multi-step tool calls, token-usage metadata, and persisted history via `withFormat`.
|
|
60
66
|
|
|
61
|
-
If you need streaming agent state (subgraph events, generative UI messages), look at [LangGraph](/docs/runtimes/langgraph) instead. If you have a different protocol-shaped backend (A2A, AG-UI, OpenCode), see [pick a runtime](/docs/runtimes/pick-a-runtime).
|
|
67
|
+
If you need streaming agent state (subgraph events, generative UI messages), look at [LangGraph](/docs/runtimes/langgraph/overview) instead. If you have a different protocol-shaped backend (A2A, AG-UI, OpenCode), see [pick a runtime](/docs/runtimes/pick-a-runtime).
|
|
62
68
|
|
|
63
69
|
## Related
|
|
64
70
|
|
|
@@ -67,7 +73,7 @@ If you need streaming agent state (subgraph events, generative UI messages), loo
|
|
|
67
73
|
icon={<VercelIcon width={20} height={20} />}
|
|
68
74
|
title="AI SDK runtime overview"
|
|
69
75
|
description="The full runtime documentation and version selector."
|
|
70
|
-
href="/docs/runtimes/ai-sdk"
|
|
76
|
+
href="/docs/runtimes/ai-sdk/overview"
|
|
71
77
|
/>
|
|
72
78
|
<Card
|
|
73
79
|
title="Pick a runtime"
|