@assistant-ui/mcp-docs-server 0.1.29 → 0.1.30

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.
Files changed (53) hide show
  1. package/.docs/organized/code-examples/waterfall.md +15 -7
  2. package/.docs/organized/code-examples/with-a2a.md +8 -20
  3. package/.docs/organized/code-examples/with-ag-ui.md +9 -6
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +8 -8
  5. package/.docs/organized/code-examples/with-artifacts.md +10 -8
  6. package/.docs/organized/code-examples/with-assistant-transport.md +9 -10
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +7 -7
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +13 -10
  9. package/.docs/organized/code-examples/with-cloud.md +8 -9
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +8 -8
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +15 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +11 -11
  13. package/.docs/organized/code-examples/with-expo.md +20 -16
  14. package/.docs/organized/code-examples/with-external-store.md +7 -7
  15. package/.docs/organized/code-examples/with-ffmpeg.md +15 -10
  16. package/.docs/organized/code-examples/with-generative-ui.md +7 -7
  17. package/.docs/organized/code-examples/with-google-adk.md +6 -6
  18. package/.docs/organized/code-examples/with-heat-graph.md +5 -5
  19. package/.docs/organized/code-examples/with-interactables.md +8 -23
  20. package/.docs/organized/code-examples/with-langchain.md +437 -0
  21. package/.docs/organized/code-examples/with-langgraph.md +15 -15
  22. package/.docs/organized/code-examples/with-livekit.md +15 -10
  23. package/.docs/organized/code-examples/with-opencode.md +8 -10
  24. package/.docs/organized/code-examples/with-parent-id-grouping.md +8 -8
  25. package/.docs/organized/code-examples/with-react-hook-form.md +219 -147
  26. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  27. package/.docs/organized/code-examples/with-react-router.md +10 -10
  28. package/.docs/organized/code-examples/with-store.md +8 -5
  29. package/.docs/organized/code-examples/with-tanstack.md +8 -8
  30. package/.docs/organized/code-examples/with-tap-runtime.md +9 -5
  31. package/.docs/raw/docs/(docs)/guides/mentions.mdx +248 -109
  32. package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +112 -92
  33. package/.docs/raw/docs/(docs)/rtl.mdx +79 -0
  34. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
  36. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +4 -0
  37. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  38. package/.docs/raw/docs/primitives/composer.mdx +94 -62
  39. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +57 -0
  40. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +47 -1
  41. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  42. package/.docs/raw/docs/runtimes/langchain/comparison.mdx +60 -0
  43. package/.docs/raw/docs/runtimes/langchain/index.mdx +210 -0
  44. package/.docs/raw/docs/runtimes/langgraph/index.mdx +155 -63
  45. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -4
  46. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +199 -0
  47. package/.docs/raw/docs/ui/directive-text.mdx +113 -0
  48. package/.docs/raw/docs/ui/reasoning.mdx +13 -9
  49. package/dist/utils/logger.js +1 -1
  50. package/dist/utils/logger.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/utils/logger.ts +1 -1
  53. package/.docs/raw/docs/ui/mention.mdx +0 -168
@@ -1447,7 +1447,7 @@ The main interface for connecting your state to assistant-ui.
1447
1447
  name: "isRunning",
1448
1448
  type: "boolean",
1449
1449
  description:
1450
- "Whether the assistant is currently generating a response. When true, shows optimistic assistant message",
1450
+ "Whether the assistant is currently generating a response. When true, shows an optimistic assistant message and flows directly to `thread.isRunning`, so the thread stays in a running state even after the last assistant message has completed (e.g. while suggestions or metadata chunks are still arriving). When omitted, `thread.isRunning` falls back to the last-message-status heuristic.",
1451
1451
  default: "false",
1452
1452
  },
1453
1453
  {
@@ -0,0 +1,60 @@
1
+ ---
2
+ title: Comparison with `react-langgraph`
3
+ description: How `@assistant-ui/react-langchain` differs from `@assistant-ui/react-langgraph`.
4
+ ---
5
+
6
+ Both packages connect assistant-ui to LangGraph backends. They are **independent adapters for different upstream libraries** — one is not a successor to the other.
7
+
8
+ | Aspect | `@assistant-ui/react-langgraph` | `@assistant-ui/react-langchain` |
9
+ | ------------------------------ | ------------------------------------------------------------ | ----------------------------------------------------- |
10
+ | Wraps | `@langchain/langgraph-sdk` (raw SDK) | `@langchain/react` (`useStream` hook) |
11
+ | Age | Sept 2024 onward | April 2026 onward |
12
+ | Version | `0.13.x` | `0.0.x` |
13
+ | Lines of source | ~7,500 | ~600 |
14
+ | Built on | `useExternalStoreRuntime` | `useExternalStoreRuntime` |
15
+ | `create-assistant-ui` template | `-t langgraph` ships this package | No template yet |
16
+
17
+ ## Feature coverage
18
+
19
+ | Feature | `react-langgraph` | `react-langchain` |
20
+ | --------------------------------------- | -------------------------------- | ----------------------------------- |
21
+ | Stream messages | ✅ `useLangGraphRuntime` | ✅ `useStreamRuntime` |
22
+ | Interrupt state | ✅ `useLangGraphInterruptState` | ✅ `useLangChainInterruptState` |
23
+ | Send raw state update / resume command | ✅ `useLangGraphSendCommand` | ✅ `useLangChainSubmit` |
24
+ | Read arbitrary custom state key | ❌ | ✅ `useLangChainState<T>(key)` |
25
+ | Per-message metadata (`messages-tuple`) | ✅ `useLangGraphMessageMetadata` | ❌ not exposed |
26
+ | Generative UI messages (LangSmith) | ✅ `useLangGraphUIMessages` | ❌ not exposed |
27
+ | Subgraph / namespaced stream events | ✅ via `eventHandlers` | ❌ not exposed |
28
+ | End-to-end cancellation primitive | ✅ `unstable_createLangGraphStream` | ❌ not exposed |
29
+ | Message accumulator utility | ✅ `LangGraphMessageAccumulator` | ❌ not exposed |
30
+ | Cloud thread persistence | ✅ `cloud` option | ✅ `cloud` option |
31
+
32
+ `react-langchain` is the newer, thinner wrapper — it delegates to the upstream `useStream` hook rather than re-implementing the stream plumbing. That is why its footprint is smaller and its surface area is narrower today. Features that exist in `react-langgraph` but not `react-langchain` are absent because they have not yet been ported, not because they are deprecated.
33
+
34
+ ## Choosing between them
35
+
36
+ Use `@assistant-ui/react-langgraph` when:
37
+
38
+ - You are scaffolding via `npx create-assistant-ui -t langgraph`.
39
+ - You want the broader feature set today (subgraph events, UI messages, message metadata, cancellation).
40
+ - You prefer integrating with `@langchain/langgraph-sdk` directly.
41
+
42
+ Use `@assistant-ui/react-langchain` when:
43
+
44
+ - Your app already depends on `@langchain/react` and uses `useStream` elsewhere.
45
+ - You want to read custom state keys (`todos`, `files`, plans, ...) with `useLangChainState<T>(key)` without reconstructing them from tool-call streams.
46
+ - You prefer a thin wrapper that stays pinned to upstream behavior.
47
+
48
+ ## Hook name mapping
49
+
50
+ If you are moving code between the two adapters, most hooks have a counterpart — but note the feature gaps above.
51
+
52
+ | `react-langgraph` | `react-langchain` | Notes |
53
+ | ---------------------------------- | ----------------------------------- | ----------------------------------------------------------- |
54
+ | `useLangGraphRuntime` | `useStreamRuntime` | Options extend upstream `UseStreamOptions`; no `stream` / `create` / `load` to write. |
55
+ | `useLangGraphInterruptState` | `useLangChainInterruptState` | Same return shape: `{ value?: unknown } \| undefined`. |
56
+ | `useLangGraphSendCommand` | `useLangChainSubmit` | `submit(values, { command })` replaces the dedicated hook. |
57
+ | `useLangGraphSend` | _(use `runtime.thread.append`)_ | No direct equivalent; send turns through the runtime. |
58
+ | `useLangGraphMessageMetadata` | _(not available)_ | Open an issue if you rely on this. |
59
+ | `useLangGraphUIMessages` | _(not available)_ | Open an issue if you rely on this. |
60
+ | _(none)_ | `useLangChainState<T>(key)` | New — reads any custom state key reactively. |
@@ -0,0 +1,210 @@
1
+ ---
2
+ title: Getting Started
3
+ description: Adapter for LangChain's `useStream` hook, exposed as an assistant-ui runtime.
4
+ ---
5
+
6
+ `@assistant-ui/react-langchain` wraps [`useStream`](https://docs.langchain.com/oss/javascript/langgraph-sdk/react-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. Use this package if you are already integrating your app with `@langchain/react` and want assistant-ui on top of the upstream hook.
7
+
8
+ <Callout type="info">
9
+ assistant-ui ships two adapters for LangGraph backends:
10
+ - **[`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph)** integrates with `@langchain/langgraph-sdk` directly and exposes features like subgraph events, UI messages, message metadata, and end-to-end cancellation.
11
+ - **`@assistant-ui/react-langchain`** (this page) wraps `@langchain/react`'s `useStream`. It is lighter-weight and stays aligned with upstream, but currently does not expose every `react-langgraph` feature.
12
+
13
+ See [the comparison doc](/docs/runtimes/langchain/comparison) for a feature gap table.
14
+ </Callout>
15
+
16
+ ## Requirements
17
+
18
+ You need a LangGraph Cloud API server. You can start a server locally via [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio) or use [LangSmith](https://www.langchain.com/langsmith) for a hosted version.
19
+
20
+ The state of the graph you are using must have a `messages` key with a list of LangChain-alike messages (or pass a custom `messagesKey`).
21
+
22
+ ## Installation
23
+
24
+ <Steps>
25
+ <Step>
26
+
27
+ ### Install dependencies
28
+
29
+ <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-langchain", "@langchain/react", "@langchain/langgraph-sdk"]} />
30
+
31
+ </Step>
32
+ <Step>
33
+
34
+ ### Define a `MyAssistant` component
35
+
36
+ ```tsx title="@/components/MyAssistant.tsx"
37
+ "use client";
38
+
39
+ import { Thread } from "@/components/assistant-ui/thread";
40
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
41
+ import { useStreamRuntime } from "@assistant-ui/react-langchain";
42
+
43
+ export function MyAssistant() {
44
+ const runtime = useStreamRuntime({
45
+ assistantId: process.env["NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID"]!,
46
+ apiUrl: process.env["NEXT_PUBLIC_LANGGRAPH_API_URL"],
47
+ });
48
+
49
+ return (
50
+ <AssistantRuntimeProvider runtime={runtime}>
51
+ <Thread />
52
+ </AssistantRuntimeProvider>
53
+ );
54
+ }
55
+ ```
56
+
57
+ </Step>
58
+ <Step>
59
+
60
+ ### Use the `MyAssistant` component
61
+
62
+ ```tsx title="@/app/page.tsx"
63
+ import { MyAssistant } from "@/components/MyAssistant";
64
+
65
+ export default function Home() {
66
+ return (
67
+ <main className="h-dvh">
68
+ <MyAssistant />
69
+ </main>
70
+ );
71
+ }
72
+ ```
73
+
74
+ </Step>
75
+ <Step>
76
+
77
+ ### Set environment variables
78
+
79
+ Create a `.env.local` file in your project with the following variables:
80
+
81
+ ```sh
82
+ NEXT_PUBLIC_LANGGRAPH_API_URL=http://localhost:2024
83
+ NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID=your_graph_id
84
+ ```
85
+
86
+ </Step>
87
+ <Step>
88
+
89
+ ### Set up UI components
90
+
91
+ Follow the [UI Components](/docs/ui/thread) guide to set up the UI components.
92
+
93
+ </Step>
94
+ </Steps>
95
+
96
+ ## `useStreamRuntime` options
97
+
98
+ `useStreamRuntime` accepts every option [`useStream`](https://docs.langchain.com/oss/javascript/langgraph-sdk/react-stream) from `@langchain/react` does, plus three assistant-ui-specific fields:
99
+
100
+ | Option | Type | Description |
101
+ | ------------- | ---------------------------------------------- | ------------------------------------------------------------------------ |
102
+ | `cloud` | `AssistantCloud` | Optional — persists threads via assistant-cloud. |
103
+ | `adapters` | `{ attachments?, speech?, feedback? }` | Optional — attachment, speech, and feedback adapters. |
104
+ | `messagesKey` | `string` | The state key that holds messages. Defaults to `"messages"`. |
105
+
106
+ ## Reading custom state keys
107
+
108
+ LangGraph agents often expose structured state beyond messages (plans, todos, scratch files, generative-UI artifacts). Read them directly with `useLangChainState`. It mirrors `useStream().values[key]` upstream and updates when the stream emits new state.
109
+
110
+ ```tsx
111
+ import { useLangChainState } from "@assistant-ui/react-langchain";
112
+
113
+ type Todo = { id: string; title: string; done: boolean };
114
+
115
+ function TodoList() {
116
+ const todos = useLangChainState<Todo[]>("todos", []);
117
+
118
+ return (
119
+ <ul>
120
+ {todos.map((t) => (
121
+ <li key={t.id}>
122
+ {t.done ? "✓" : "○"} {t.title}
123
+ </li>
124
+ ))}
125
+ </ul>
126
+ );
127
+ }
128
+ ```
129
+
130
+ Signatures:
131
+
132
+ ```ts
133
+ useLangChainState<T>(key: string): T | undefined;
134
+ useLangChainState<T>(key: string, defaultValue: T): T;
135
+ ```
136
+
137
+ This hook is especially useful with the [`deepagents`](https://docs.langchain.com/oss/python/deepagents) middleware, whose `write_todos` step updates `state.todos` alongside the tool-call stream. Reading the state key directly avoids reconstructing the list from partial tool-call args.
138
+
139
+ <Callout type="info">
140
+ Added in v0.0.2 — see issue [#3862](https://github.com/assistant-ui/assistant-ui/issues/3862) for motivation.
141
+ </Callout>
142
+
143
+ ## Interrupts
144
+
145
+ LangGraph interrupts pause the graph and wait for client input. `useLangChainInterruptState` exposes the current interrupt; `useLangChainSubmit` resumes the graph with a raw state update.
146
+
147
+ ```tsx
148
+ import {
149
+ useLangChainInterruptState,
150
+ useLangChainSubmit,
151
+ } from "@assistant-ui/react-langchain";
152
+ import { Command } from "@langchain/langgraph-sdk";
153
+
154
+ function InterruptPrompt() {
155
+ const interrupt = useLangChainInterruptState();
156
+ const submit = useLangChainSubmit();
157
+
158
+ if (!interrupt) return null;
159
+
160
+ return (
161
+ <div>
162
+ <pre>{JSON.stringify(interrupt.value, null, 2)}</pre>
163
+ <button
164
+ onClick={() =>
165
+ submit(null, { command: new Command({ resume: "approved" }) })
166
+ }
167
+ >
168
+ Approve
169
+ </button>
170
+ </div>
171
+ );
172
+ }
173
+ ```
174
+
175
+ ## Message conversion
176
+
177
+ `convertLangChainBaseMessage` transforms a LangChain `BaseMessage` into an assistant-ui message. Use it when building a custom `ExternalStoreAdapter` that needs to consume LangChain messages outside of `useStreamRuntime`.
178
+
179
+ ```ts
180
+ import { convertLangChainBaseMessage } from "@assistant-ui/react-langchain";
181
+ ```
182
+
183
+ ## Cloud persistence
184
+
185
+ Pass an `AssistantCloud` instance to persist threads across sessions. The runtime automatically wires thread list management and resumes state from the cloud.
186
+
187
+ ```tsx
188
+ import { AssistantCloud } from "assistant-cloud";
189
+ import { useStreamRuntime } from "@assistant-ui/react-langchain";
190
+
191
+ const cloud = new AssistantCloud({ baseUrl: "/api/cloud" });
192
+
193
+ const runtime = useStreamRuntime({
194
+ cloud,
195
+ assistantId: "agent",
196
+ apiUrl: "http://localhost:2024",
197
+ });
198
+ ```
199
+
200
+ ## Custom `messagesKey`
201
+
202
+ If your graph stores messages under a non-default key, pass `messagesKey` so the runtime submits tool results and human turns to the correct state slot:
203
+
204
+ ```tsx
205
+ const runtime = useStreamRuntime({
206
+ assistantId: "agent",
207
+ apiUrl: "http://localhost:2024",
208
+ messagesKey: "chat_messages",
209
+ });
210
+ ```
@@ -3,6 +3,10 @@ title: Getting Started
3
3
  description: Connect to LangGraph Cloud API for agent workflows with streaming.
4
4
  ---
5
5
 
6
+ <Callout type="info">
7
+ If you are already using `@langchain/react`'s `useStream` hook, the alternative [`@assistant-ui/react-langchain`](/docs/runtimes/langchain) adapter may fit better. `@assistant-ui/react-langgraph` (this page) integrates with `@langchain/langgraph-sdk` directly and has the broader feature set — subgraph events, UI messages, message metadata, end-to-end cancellation. See the [comparison](/docs/runtimes/langchain/comparison).
8
+ </Callout>
9
+
6
10
  ## Requirements
7
11
 
8
12
  You need a LangGraph Cloud API server. You can start a server locally via [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio) or use [LangSmith](https://www.langchain.com/langsmith) for a hosted version.
@@ -60,6 +64,8 @@ NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID=your_graph_id
60
64
  ```tsx twoslash title="@/app/api/[...path]/route.ts"
61
65
  import { NextRequest, NextResponse } from "next/server";
62
66
 
67
+ export const runtime = "edge";
68
+
63
69
  function getCorsHeaders() {
64
70
  return {
65
71
  "Access-Control-Allow-Origin": "*",
@@ -84,6 +90,7 @@ async function handleRequest(req: NextRequest, method: string) {
84
90
  headers: {
85
91
  "x-api-key": process.env["LANGCHAIN_API_KEY"] || "",
86
92
  },
93
+ signal: req.signal,
87
94
  };
88
95
 
89
96
  if (["POST", "PUT", "PATCH"].includes(method)) {
@@ -142,48 +149,15 @@ export const OPTIONS = () =>
142
149
  // @filename: /lib/chatApi.ts
143
150
 
144
151
  // ---cut---
145
- import { Client, type ThreadState } from "@langchain/langgraph-sdk";
146
- import { LangChainMessage, LangGraphCommand } from "@assistant-ui/react-langgraph";
147
-
148
- const createClient = () => {
149
- const apiUrl = process.env["NEXT_PUBLIC_LANGGRAPH_API_URL"] || "/api";
150
- return new Client({
151
- apiUrl,
152
- });
153
- };
154
-
155
- export const createThread = async () => {
156
- const client = createClient();
157
- return client.threads.create();
158
- };
159
-
160
- export const getThreadState = async (
161
- threadId: string,
162
- ): Promise<ThreadState<{ messages: LangChainMessage[] }>> => {
163
- const client = createClient();
164
- return client.threads.getState(threadId);
165
- };
166
-
167
- export const sendMessage = async (params: {
168
- threadId: string;
169
- messages?: LangChainMessage[];
170
- command?: LangGraphCommand;
171
- }) => {
172
- const client = createClient();
173
- return client.runs.stream(
174
- params.threadId,
175
- process.env["NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID"]!,
176
- {
177
- input: params.messages?.length
178
- ? { messages: params.messages }
179
- : null,
180
- command: params.command,
181
- // Include "custom" if you use LangSmith Generative UI
182
- // (`push_ui_message` / `typedUi().push()`). See the Generative UI
183
- // section below for details.
184
- streamMode: ["messages", "updates", "custom"],
185
- },
186
- );
152
+ import { Client } from "@langchain/langgraph-sdk";
153
+
154
+ export const createClient = () => {
155
+ const apiUrl =
156
+ process.env["NEXT_PUBLIC_LANGGRAPH_API_URL"] ||
157
+ (typeof window !== "undefined"
158
+ ? new URL("/api", window.location.href).href
159
+ : "/api");
160
+ return new Client({ apiUrl });
187
161
  };
188
162
  ```
189
163
 
@@ -199,32 +173,41 @@ export const sendMessage = async (params: {
199
173
  // ---cut---
200
174
  "use client";
201
175
 
176
+ import { useMemo } from "react";
202
177
  import { Thread } from "@/components/assistant-ui/thread";
203
178
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
204
- import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
179
+ import {
180
+ unstable_createLangGraphStream,
181
+ useLangGraphRuntime,
182
+ type LangChainMessage,
183
+ } from "@assistant-ui/react-langgraph";
205
184
 
206
- import { createThread, getThreadState, sendMessage } from "@/lib/chatApi";
185
+ import { createClient } from "@/lib/chatApi";
207
186
 
208
- export function MyAssistant() {
209
- const runtime = useLangGraphRuntime({
210
- stream: async function* (messages, { initialize, command }) {
211
- const { externalId } = await initialize();
212
- if (!externalId) throw new Error("Thread not found");
187
+ const ASSISTANT_ID = process.env["NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID"]!;
213
188
 
214
- const generator = await sendMessage({
215
- threadId: externalId,
216
- messages,
217
- command,
218
- });
189
+ export function MyAssistant() {
190
+ const client = useMemo(() => createClient(), []);
191
+ const stream = useMemo(
192
+ () =>
193
+ unstable_createLangGraphStream({
194
+ client,
195
+ assistantId: ASSISTANT_ID,
196
+ }),
197
+ [client],
198
+ );
219
199
 
220
- yield* generator;
221
- },
200
+ const runtime = useLangGraphRuntime({
201
+ unstable_allowCancellation: true,
202
+ stream,
222
203
  create: async () => {
223
- const { thread_id } = await createThread();
204
+ const { thread_id } = await client.threads.create();
224
205
  return { externalId: thread_id };
225
206
  },
226
207
  load: async (externalId) => {
227
- const state = await getThreadState(externalId);
208
+ const state = await client.threads.getState<{
209
+ messages: LangChainMessage[];
210
+ }>(externalId);
228
211
  return {
229
212
  messages: state.values.messages,
230
213
  interrupts: state.tasks[0]?.interrupts,
@@ -323,18 +306,38 @@ const runtime = useLangGraphRuntime({
323
306
  stream: async (messages, { initialize, ...config }) => { /* ... */ },
324
307
  eventHandlers: {
325
308
  onMessageChunk: (chunk, metadata) => {
326
- // Fired for each chunk in messages-tuple mode
327
- // metadata contains langgraph_step, langgraph_node, ls_model_name, etc.
309
+ // Fired for each chunk in messages-tuple mode.
310
+ // `metadata` contains langgraph_step, langgraph_node, ls_model_name, etc.
311
+ // For pipe-namespaced events emitted by subgraphs (e.g. `messages|tools:call_abc`),
312
+ // `metadata.namespace` holds the suffix ("tools:call_abc"). Use it to attribute
313
+ // a chunk to a specific subgraph.
328
314
  },
329
315
  onValues: (values) => {
330
- // Fired when a "values" event is received
316
+ // Fired when a top-level `values` event is received.
317
+ // Subgraph `values` events are routed to `onSubgraphValues` instead.
331
318
  },
332
319
  onUpdates: (updates) => {
333
- // Fired when an "updates" event is received
320
+ // Fired when a top-level `updates` event is received.
321
+ // Subgraph `updates` events are routed to `onSubgraphUpdates` instead.
322
+ },
323
+ onSubgraphValues: (namespace, values) => {
324
+ // Fired when a subgraph `values|<namespace>` event is received
325
+ // (e.g. `namespace === "tools:call_abc"`). Use this to observe
326
+ // subgraph-internal state without mixing it into `onValues`.
327
+ },
328
+ onSubgraphUpdates: (namespace, updates) => {
329
+ // Fired when a subgraph `updates|<namespace>` event is received.
334
330
  },
335
331
  onMetadata: (metadata) => { /* thread metadata */ },
336
332
  onInfo: (info) => { /* informational messages */ },
337
- onError: (error) => { /* stream errors */ },
333
+ onError: (error) => {
334
+ // Fired for both top-level and subgraph errors.
335
+ },
336
+ onSubgraphError: (namespace, error) => {
337
+ // Additionally fired for subgraph errors with the namespace.
338
+ // Use to attribute a subgraph failure to its source without marking
339
+ // the parent message as incomplete (that only happens for top-level errors).
340
+ },
338
341
  onCustomEvent: (type, data) => { /* custom events */ },
339
342
  },
340
343
  });
@@ -399,6 +402,51 @@ const runtime = useLangGraphRuntime({
399
402
 
400
403
  See the [Cloud Persistence guide](/docs/cloud/langgraph) for detailed setup instructions.
401
404
 
405
+ ### Custom Thread List
406
+
407
+ To surface pre-existing LangGraph `thread_id`s in the thread picker without running assistant-cloud, pass a `RemoteThreadListAdapter` via `unstable_threadListAdapter`. A common implementation backs `list()` with `client.threads.search()` and `initialize()` with `client.threads.create()`.
408
+
409
+ ```typescript
410
+ import type { RemoteThreadListAdapter } from "@assistant-ui/react";
411
+ import { Client } from "@langchain/langgraph-sdk";
412
+
413
+ const client = new Client({ apiUrl: process.env.NEXT_PUBLIC_LANGGRAPH_API_URL });
414
+
415
+ const threadListAdapter: RemoteThreadListAdapter = {
416
+ async list() {
417
+ const threads = await client.threads.search({ limit: 50 });
418
+ return {
419
+ threads: threads.map((t) => ({
420
+ status: "regular",
421
+ remoteId: t.thread_id,
422
+ externalId: t.thread_id,
423
+ title: (t.metadata as { title?: string } | undefined)?.title,
424
+ })),
425
+ };
426
+ },
427
+ async initialize() {
428
+ const t = await client.threads.create();
429
+ return { remoteId: t.thread_id, externalId: t.thread_id };
430
+ },
431
+ async delete(remoteId) {
432
+ await client.threads.delete(remoteId);
433
+ },
434
+ // rename, archive, unarchive, fetch, generateTitle — see link below
435
+ };
436
+
437
+ const runtime = useLangGraphRuntime({
438
+ stream: async function* (messages, { initialize }) { /* ... */ },
439
+ load: async (externalId) => { /* ... */ },
440
+ unstable_threadListAdapter: threadListAdapter,
441
+ });
442
+ ```
443
+
444
+ Setting `remoteId === externalId` keeps the ids assistant-ui stores aligned with the LangGraph thread ids your `load` and `stream` callbacks receive. See the [Custom Thread List guide](/docs/runtimes/custom/custom-thread-list) for the full adapter contract.
445
+
446
+ <Callout type="info">
447
+ When `unstable_threadListAdapter` is provided, the `cloud`, `create`, and `delete` options are ignored — the adapter owns the full thread-list lifecycle.
448
+ </Callout>
449
+
402
450
  ## Message Editing & Regeneration
403
451
 
404
452
  LangGraph uses server-side checkpoints for state management. To support message editing (branching) and regeneration, you need to provide a `getCheckpointId` callback that resolves the appropriate checkpoint for server-side forking.
@@ -565,6 +613,50 @@ Mount the component once somewhere inside the `AssistantRuntimeProvider` tree. I
565
613
 
566
614
  When a matching UI message arrives, the adapter appends a `{ type: "data", name: "chart", data: { series, title } }` part to the parent assistant message and the registered component renders inline.
567
615
 
616
+ ### Register renderers via `uiComponents`
617
+
618
+ Instead of mounting separate `makeAssistantDataUI` components, you can register renderers directly on the runtime hook via the `uiComponents` option:
619
+
620
+ ```tsx title="@/components/MyAssistant.tsx"
621
+ const runtime = useLangGraphRuntime({
622
+ stream: async function* (messages, { initialize }) { /* ... */ },
623
+ uiComponents: {
624
+ renderers: {
625
+ chart: ({ data }) => <Chart series={data.series} title={data.title} />,
626
+ table: ({ data }) => <DataTable rows={data.rows} />,
627
+ },
628
+ },
629
+ });
630
+ ```
631
+
632
+ Static `renderers` are matched by `ui_message` name. If no match is found, the part renders nothing unless a `fallback` is provided.
633
+
634
+ ### Dynamic loading with `fallback`
635
+
636
+ LangSmith's [Generative UI](https://docs.langchain.com/langsmith/generative-ui-react) supports colocating UI code with your graph and loading it at runtime via `LoadExternalComponent`. The `fallback` option handles any `ui_message` name that has no static renderer:
637
+
638
+ ```tsx title="@/components/MyAssistant.tsx"
639
+ import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui";
640
+
641
+ const runtime = useLangGraphRuntime({
642
+ stream: async function* (messages, { initialize }) { /* ... */ },
643
+ uiComponents: {
644
+ fallback: ({ name, data }) => (
645
+ <LoadExternalComponent name={name} props={data} />
646
+ ),
647
+ renderers: {
648
+ chart: ({ data }) => <Chart {...data} />,
649
+ },
650
+ },
651
+ });
652
+ ```
653
+
654
+ With this setup:
655
+ - A `ui_message` with `name: "chart"` renders the static `Chart` component
656
+ - Any other name (e.g. `"dashboard"`, `"form"`) is handled by `fallback`, which fetches the component from LangSmith at runtime
657
+
658
+ The `fallback` component receives the same props as any data renderer: `name`, `data`, and part state metadata. This lets you pass the component name and props straight through to `LoadExternalComponent`.
659
+
568
660
  ### Semantics
569
661
 
570
662
  The adapter mirrors the reducer in `@langchain/langgraph-sdk/react-ui` exactly:
@@ -11,7 +11,8 @@ Choosing the right runtime is crucial for your assistant-ui implementation. This
11
11
  graph TD
12
12
  A[What's your starting point?] --> B{Existing Framework?}
13
13
  B -->|Vercel AI SDK| C[Use AI SDK Integration]
14
- B -->|LangGraph| D[Use LangGraph Runtime]
14
+ B -->|LangGraph via langgraph-sdk| D1[Use react-langgraph]
15
+ B -->|LangChain via @langchain/react useStream| D2[Use react-langchain]
15
16
  B -->|LangServe| E[Use LangServe Runtime]
16
17
  B -->|Mastra| F[Use Mastra Runtime]
17
18
  B -->|AG-UI Protocol| J[Use AG-UI Runtime]
@@ -55,9 +56,14 @@ For popular frameworks, we provide ready-to-use integrations built on top of our
55
56
  />
56
57
  <Card
57
58
  title="LangGraph"
58
- description="For complex agent workflows with LangChain's graph framework"
59
+ description="Integrates with `@langchain/langgraph-sdk` directly. Broader feature set: subgraph events, UI messages, message metadata, cancellation."
59
60
  href="/docs/runtimes/langgraph"
60
61
  />
62
+ <Card
63
+ title="LangChain useStream"
64
+ description="Wraps `useStream` from `@langchain/react`. Lighter-weight, stays aligned with upstream. Fewer features today."
65
+ href="/docs/runtimes/langchain"
66
+ />
61
67
  <Card
62
68
  title="LangServe"
63
69
  description="For LangChain applications deployed with LangServe"
@@ -87,13 +93,14 @@ For popular frameworks, we provide ready-to-use integrations built on top of our
87
93
  The pre-built integrations (AI SDK, LangGraph, etc.) are **not separate runtime types**. They're convenient wrappers built on top of our core runtimes:
88
94
 
89
95
  - **AI SDK Integration** → Built on `LocalRuntime` with streaming adapter
90
- - **LangGraph Runtime** → Built on `LocalRuntime` with graph execution adapter
96
+ - **LangGraph Runtime** → Built on `ExternalStoreRuntime`, integrates with `@langchain/langgraph-sdk`
97
+ - **LangChain useStream Runtime** → Built on `ExternalStoreRuntime`, wraps `useStream` from `@langchain/react`
91
98
  - **LangServe Runtime** → Built on `LocalRuntime` with LangServe client adapter
92
99
  - **Mastra Runtime** → Built on `LocalRuntime` with workflow adapter
93
100
  - **AG-UI Runtime** → Built on `LocalRuntime` with AG-UI protocol adapter
94
101
  - **A2A Runtime** → Built on `LocalRuntime` with Agent-to-Agent protocol adapter
95
102
 
96
- This means you get all the benefits of `LocalRuntime` (automatic state management, built-in features) with zero configuration for your specific framework.
103
+ This means pre-built integrations give you assistant-ui's features — state management, streaming, UI primitives — with zero configuration for your specific framework, regardless of whether the adapter happens to build on `LocalRuntime` or `ExternalStoreRuntime` internally. The list above tells you which core runtime each adapter uses.
97
104
 
98
105
  ### When to Use Pre-Built vs Core Runtimes
99
106
 
@@ -228,6 +235,7 @@ Explore our implementation examples:
228
235
  - [`LocalRuntime` Guide](/docs/runtimes/custom/local)
229
236
  - [`ExternalStoreRuntime` Guide](/docs/runtimes/custom/external-store)
230
237
  - [LangGraph Integration](/docs/runtimes/langgraph)
238
+ - [LangChain useStream Integration](/docs/runtimes/langchain)
231
239
  3. **Start with an example** from our [examples repository](https://github.com/assistant-ui/assistant-ui/tree/main/examples)
232
240
  4. **Add features progressively** using adapters
233
241
  5. **Consider Assistant Cloud** for production persistence