@assistant-ui/react-langchain 0.0.35 → 0.0.36

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 (36) hide show
  1. package/dist/LangChainThreadController.d.ts +9 -0
  2. package/dist/LangChainThreadController.d.ts.map +1 -0
  3. package/dist/LangChainThreadController.js +21 -0
  4. package/dist/LangChainThreadController.js.map +1 -0
  5. package/dist/convertMessages.d.ts.map +1 -1
  6. package/dist/convertMessages.js +6 -11
  7. package/dist/convertMessages.js.map +1 -1
  8. package/dist/converter.d.ts +24 -1
  9. package/dist/converter.d.ts.map +1 -1
  10. package/dist/converter.js +84 -18
  11. package/dist/converter.js.map +1 -1
  12. package/dist/langChainThreadState.d.ts +54 -0
  13. package/dist/langChainThreadState.d.ts.map +1 -0
  14. package/dist/langChainThreadState.js +135 -0
  15. package/dist/langChainThreadState.js.map +1 -0
  16. package/dist/sdkIdentity.js +1 -1
  17. package/dist/types.d.ts +9 -0
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/useStreamRuntime.d.ts +3 -2
  20. package/dist/useStreamRuntime.d.ts.map +1 -1
  21. package/dist/useStreamRuntime.js +152 -139
  22. package/dist/useStreamRuntime.js.map +1 -1
  23. package/dist/useSubagentTranscripts.js +1 -1
  24. package/package.json +8 -8
  25. package/src/LangChainThreadController.ts +24 -0
  26. package/src/convertMessages.test.ts +23 -0
  27. package/src/convertMessages.ts +9 -16
  28. package/src/converter.test.ts +322 -0
  29. package/src/converter.ts +138 -27
  30. package/src/getPendingToolCalls.test.ts +35 -0
  31. package/src/langChainThreadState.test.ts +203 -0
  32. package/src/langChainThreadState.ts +239 -0
  33. package/src/types.ts +9 -0
  34. package/src/useStreamRuntime.cloud-options.test.tsx +61 -0
  35. package/src/useStreamRuntime.test.tsx +227 -1
  36. package/src/useStreamRuntime.ts +210 -239
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langChainThreadState.js","names":[],"sources":["../src/langChainThreadState.ts"],"sourcesContent":["import type { AppendMessage } from \"@assistant-ui/core\";\nimport type { LangChainBaseMessage } from \"./types\";\nimport { getMessageType } from \"./convertMessages\";\n\nexport type StagedMessage = LangChainBaseMessage & { id: string };\n\nexport type StagedEntry = {\n message: StagedMessage;\n runConfig: AppendMessage[\"runConfig\"];\n reconcileOnEcho: boolean;\n baseMessageCount: number;\n transcriptStatus?: \"unsent\" | \"sent\";\n};\n\nexport type LangChainThreadState = {\n forkGeneration: number;\n forkPending: boolean;\n stagedEntries: ReadonlyMap<string, StagedEntry>;\n stagedBaseMessages: LangChainBaseMessage[] | null;\n visibleStagedMessages: LangChainBaseMessage[] | null;\n};\n\nexport type LangChainThreadAction =\n | { type: \"supersedeFork\" }\n | { type: \"finishFork\"; generation: number }\n | { type: \"cancelFork\" }\n | {\n type: \"stage\";\n entry: StagedEntry;\n visibleMessages: readonly LangChainBaseMessage[];\n }\n | {\n type: \"stageEdit\";\n entry: StagedEntry;\n baseMessages: LangChainBaseMessage[];\n }\n | {\n type: \"reconcile\";\n messages: LangChainBaseMessage[];\n visibleMessages: readonly LangChainBaseMessage[];\n }\n | {\n type: \"markTranscript\";\n messages: readonly LangChainBaseMessage[];\n status: \"unsent\" | \"sent\";\n }\n | {\n type: \"remove\";\n id: string;\n visibleMessages: readonly LangChainBaseMessage[];\n }\n | {\n type: \"promote\";\n messages: readonly LangChainBaseMessage[];\n visibleMessages: readonly LangChainBaseMessage[];\n };\n\nexport const createLangChainThreadState = (): LangChainThreadState => ({\n forkGeneration: 0,\n forkPending: false,\n stagedEntries: new Map(),\n stagedBaseMessages: null,\n visibleStagedMessages: null,\n});\n\nconst humanContentText = (content: LangChainBaseMessage[\"content\"]) => {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .filter(\n (part): part is { type: \"text\"; text: string } =>\n typeof part === \"object\" &&\n part !== null &&\n part.type === \"text\" &&\n typeof part.text === \"string\",\n )\n .map((part) => part.text)\n .join(\"\");\n};\n\nconst hasSameMessageContent = (\n a: LangChainBaseMessage,\n b: LangChainBaseMessage,\n) => humanContentText(a.content) === humanContentText(b.content);\n\nexport const reduceLangChainThreadState = (\n state: LangChainThreadState,\n action: LangChainThreadAction,\n): LangChainThreadState => {\n switch (action.type) {\n case \"supersedeFork\":\n return {\n ...state,\n forkGeneration: state.forkGeneration + 1,\n forkPending: true,\n };\n case \"finishFork\":\n return state.forkGeneration === action.generation\n ? { ...state, forkPending: false }\n : state;\n case \"cancelFork\":\n return {\n ...state,\n forkGeneration: state.forkGeneration + 1,\n forkPending: false,\n };\n case \"stage\": {\n const stagedEntries = new Map(state.stagedEntries);\n stagedEntries.set(action.entry.message.id, action.entry);\n return {\n ...state,\n stagedEntries,\n visibleStagedMessages: [\n ...action.visibleMessages,\n action.entry.message,\n ],\n };\n }\n case \"stageEdit\": {\n const stagedEntries = new Map(state.stagedEntries);\n stagedEntries.set(action.entry.message.id, action.entry);\n return {\n ...state,\n stagedEntries,\n stagedBaseMessages: action.baseMessages,\n visibleStagedMessages: [...action.baseMessages, action.entry.message],\n };\n }\n case \"reconcile\": {\n if (state.stagedEntries.size === 0) return state;\n // Staged edits must keep their truncated base while stream updates arrive before promotion.\n const baseMessages = state.stagedBaseMessages ?? action.messages;\n const stagedEntries = new Map(state.stagedEntries);\n const remainingStagedMessages: LangChainBaseMessage[] = [];\n const matchedBaseMessageIndexes = new Set<number>();\n const visibleStagedIds = new Set(\n action.visibleMessages.flatMap((message) =>\n message.id ? [message.id] : [],\n ),\n );\n for (const [id, staged] of state.stagedEntries) {\n if (!visibleStagedIds.has(id)) continue;\n const echoed = baseMessages.some((message, index) => {\n if (matchedBaseMessageIndexes.has(index)) return false;\n if (message.id === id) {\n matchedBaseMessageIndexes.add(index);\n return true;\n }\n if (\n !staged.reconcileOnEcho ||\n index < staged.baseMessageCount ||\n getMessageType(message) !== \"human\" ||\n !hasSameMessageContent(message, staged.message)\n ) {\n return false;\n }\n matchedBaseMessageIndexes.add(index);\n return true;\n });\n if (echoed) stagedEntries.delete(id);\n else remainingStagedMessages.push(staged.message);\n }\n if (remainingStagedMessages.length === 0) {\n if (\n stagedEntries.size === state.stagedEntries.size &&\n state.stagedBaseMessages === null &&\n state.visibleStagedMessages === null\n ) {\n return state;\n }\n return {\n ...state,\n stagedEntries,\n stagedBaseMessages: null,\n visibleStagedMessages: null,\n };\n }\n return {\n ...state,\n stagedEntries,\n visibleStagedMessages: [...baseMessages, ...remainingStagedMessages],\n };\n }\n case \"markTranscript\": {\n let stagedEntries: Map<string, StagedEntry> | null = null;\n for (const message of action.messages) {\n const entry = message.id\n ? (stagedEntries ?? state.stagedEntries).get(message.id)\n : undefined;\n if (entry?.transcriptStatus) {\n stagedEntries ??= new Map(state.stagedEntries);\n stagedEntries.set(message.id!, {\n ...entry,\n transcriptStatus: action.status,\n });\n }\n }\n return stagedEntries ? { ...state, stagedEntries } : state;\n }\n case \"remove\": {\n if (!state.stagedEntries.has(action.id)) return state;\n const stagedEntries = new Map(state.stagedEntries);\n stagedEntries.delete(action.id);\n return {\n ...state,\n stagedEntries,\n stagedBaseMessages:\n stagedEntries.size === 0 ? null : state.stagedBaseMessages,\n visibleStagedMessages:\n stagedEntries.size === 0\n ? null\n : action.visibleMessages.filter(\n (message) => message.id !== action.id,\n ),\n };\n }\n case \"promote\": {\n const stagedEntries = new Map(state.stagedEntries);\n const promotedIds = new Set<string>();\n for (const message of action.messages) {\n if (!message.id || stagedEntries.get(message.id)?.transcriptStatus)\n continue;\n promotedIds.add(message.id);\n stagedEntries.delete(message.id);\n }\n return {\n ...state,\n stagedEntries,\n stagedBaseMessages: null,\n visibleStagedMessages:\n stagedEntries.size > 0\n ? action.visibleMessages.filter(\n (message) => !message.id || !promotedIds.has(message.id),\n )\n : null,\n };\n }\n }\n};\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  //#region src/sdkIdentity.ts
2
2
  const LANGCHAIN_SDK = {
3
3
  name: "@assistant-ui/react-langchain",
4
- version: "0.0.35"
4
+ version: "0.0.36"
5
5
  };
6
6
  //#endregion
7
7
  export { LANGCHAIN_SDK };
package/dist/types.d.ts CHANGED
@@ -89,6 +89,11 @@ export type LangChainRuntimeExtraOptions = ExternalStoreSharedOptions & {
89
89
  */
90
90
  onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
91
91
  cloud?: AssistantCloud | undefined;
92
+ /**
93
+ * Stable identity for the account or workspace owning Cloud runtime state.
94
+ * Provide it from the first render and change it when that scope changes.
95
+ */
96
+ scopeId?: string | undefined;
92
97
  adapters?: {
93
98
  attachments?: AttachmentAdapter | undefined;
94
99
  speech?: SpeechSynthesisAdapter | undefined;
@@ -106,11 +111,15 @@ export type LangChainRuntimeExtraOptions = ExternalStoreSharedOptions & {
106
111
  /**
107
112
  * Routes the Cancel button's click to `useStream().stop()`. On by
108
113
  * default. Pass `false` to disable the Cancel button.
114
+ *
115
+ * @deprecated Experimental since 2025-01-03. Not scheduled for removal; the API may change in any release.
109
116
  */
110
117
  unstable_allowCancellation?: boolean | undefined;
111
118
  /**
112
119
  * Custom `RemoteThreadListAdapter`. When provided, replaces the
113
120
  * cloud-backed thread list adapter.
121
+ *
122
+ * @deprecated Experimental since 2026-04-16. Not scheduled for removal; the API may change in any release.
114
123
  */
115
124
  unstable_threadListAdapter?: RemoteThreadListAdapter | undefined;
116
125
  /** Custom thread-creation hook, forwarded to the cloud adapter. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,17 @@
1
1
  /// <reference types="@assistant-ui/core/store" preserve="true" />
2
2
  import type { AppendMessage } from "@assistant-ui/core";
3
- import type { UseStreamRuntimeOptions } from "./types.js";
3
+ import type { LangChainBaseMessage, LangChainToolCall, UseStreamRuntimeOptions } from "./types.js";
4
4
  export { groupUIMessagesByParent } from "./converter.js";
5
5
  export declare const runConfigToSubmitOptions: (runConfig: AppendMessage["runConfig"]) => {
6
6
  config: {
7
7
  configurable: Record<string, unknown>;
8
8
  };
9
9
  } | undefined;
10
+ export declare const getPendingToolCalls: (messages: readonly LangChainBaseMessage[]) => LangChainToolCall[];
10
11
  /**
11
12
  * Creates an assistant-ui runtime backed by LangChain's `useStream` hook.
12
13
  * Accepts the same options as `useStream` from `@langchain/react`, plus
13
- * `cloud` and `adapters`.
14
+ * `cloud`, `scopeId`, and `adapters`.
14
15
  *
15
16
  * @example
16
17
  * ```tsx
@@ -1 +1 @@
1
- {"version":3,"file":"useStreamRuntime.d.ts","sourceRoot":"","sources":["../src/useStreamRuntime.ts"],"names":[],"mappings":";AAWA,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,oBAAoB,CAAC;AAqB7E,OAAO,KAAK,EAIV,uBAAuB,EACxB,mBAAgB;AAEjB,OAAO,EAAE,uBAAuB,EAAE,uBAAoB;AA0BtD,eAAO,MAAM,wBAAwB,GACnC,WAAW,aAAa,CAAC,WAAW,CAAC;;;;aAIxB,CAAC;AA+rBhB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gBAAgB,GAAI,YAAY,uBAAuB,kDA8BnE,CAAC"}
1
+ {"version":3,"file":"useStreamRuntime.d.ts","sourceRoot":"","sources":["../src/useStreamRuntime.ts"],"names":[],"mappings":";AAYA,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,oBAAoB,CAAC;AAqB7E,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EAEjB,uBAAuB,EACxB,mBAAgB;AAEjB,OAAO,EAAE,uBAAuB,EAAE,uBAAoB;AA4BtD,eAAO,MAAM,wBAAwB,GACnC,WAAW,aAAa,CAAC,WAAW,CAAC;;;;aAIxB,CAAC;AAMhB,eAAO,MAAM,mBAAmB,GAC9B,UAAU,SAAS,oBAAoB,EAAE,KACxC,iBAAiB,EAkBjB,CAAC;AAmoBJ;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gBAAgB,GAAI,YAAY,uBAAuB,kDAgCnE,CAAC"}
@@ -1,24 +1,25 @@
1
1
  "use client";
2
- import { attachSubagentTranscripts, createAttachMemo } from "./attachSubagentTranscripts.js";
3
2
  import { getMessageContent, getMessageModality, groupUIMessagesByParent } from "./converter.js";
4
3
  import { convertLangChainBaseMessage, createLangChainMetadataKey, getMessageType } from "./convertMessages.js";
4
+ import { LangChainThreadController } from "./LangChainThreadController.js";
5
+ import { attachSubagentTranscripts, createAttachMemo } from "./attachSubagentTranscripts.js";
5
6
  import { langChainExtras } from "./runtimeExtras.js";
6
7
  import { useLangChainStreamingTiming } from "./streamingTiming.js";
7
8
  import { UI_CUSTOM_CHANNELS, createUIFoldMemo, createUISnapshotMemo, foldUIUpdates, mergeUIMessages, reconcileUISnapshot } from "./uiMessages.js";
8
9
  import { useSubagentTranscripts } from "./useSubagentTranscripts.js";
9
10
  import { resolveForkCheckpoint } from "./resolveForkCheckpoint.js";
10
11
  import { LANGCHAIN_SDK } from "./sdkIdentity.js";
11
- import { generateId, getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
12
12
  import { createCloudThreadListAdapterCreateFallback, createToolCallCancellationStub, getThreadMessageText, scanPendingToolCalls } from "@assistant-ui/core/internal";
13
+ import { generateId, getExternalStoreMessages, pickExternalStoreSharedOptions } from "@assistant-ui/core";
13
14
  import { useAui, useAuiState } from "@assistant-ui/store";
14
15
  import { useCloudThreadListAdapter, useExternalMessageConverter, useExternalStoreRuntime, useRemoteThreadListRuntime } from "@assistant-ui/core/react";
15
- import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "react";
16
+ import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
16
17
  import { STREAM_CONTROLLER, useChannel, useStream } from "@langchain/react";
17
18
  //#region src/useStreamRuntime.ts
18
19
  const runConfigToSubmitOptions = (runConfig) => runConfig?.custom ? { config: { configurable: runConfig.custom } } : void 0;
19
20
  const getPendingToolCalls = (messages) => scanPendingToolCalls(messages, (message) => {
20
21
  const type = getMessageType(message);
21
- if (type === "ai") return { toolCalls: message.tool_calls ?? [] };
22
+ if (type === "ai") return { toolCalls: (message.tool_calls ?? []).filter((toolCall) => typeof toolCall === "object" && toolCall !== null) };
22
23
  if (type === "tool" && message.tool_call_id) return { toolCallId: message.tool_call_id };
23
24
  }, (toolCall) => toolCall.id);
24
25
  const toStagedHumanMessage = (msg, id = generateId()) => ({
@@ -32,12 +33,6 @@ const toStagedMessageInput = (message) => ({
32
33
  content: message.content,
33
34
  ...message.additional_kwargs && { additional_kwargs: message.additional_kwargs }
34
35
  });
35
- const humanContentText = (content) => {
36
- if (typeof content === "string") return content;
37
- if (!Array.isArray(content)) return "";
38
- return content.filter((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
39
- };
40
- const hasSameMessageContent = (a, b) => humanContentText(a.content) === humanContentText(b.content);
41
36
  const truncateLangChainBaseMessages = (threadMessages, parentId) => {
42
37
  if (parentId === null) return [];
43
38
  const parentIndex = threadMessages.findIndex((m) => m.id === parentId);
@@ -53,7 +48,10 @@ const useStreamThreadRuntime = (options) => {
53
48
  const uiStateKey = options.uiStateKey ?? "ui";
54
49
  const externalId = useAuiState((s) => s.threadListItem.externalId);
55
50
  const stream = useStream(Object.assign({}, options, { threadId: externalId }));
56
- const [stagedMessages, setStagedMessages] = useState(null);
51
+ const [threadController] = useState(() => new LangChainThreadController());
52
+ const getVisibleStagedMessages = () => threadController.getState().visibleStagedMessages;
53
+ const visibleStagedMessages = useSyncExternalStore(threadController.subscribe, getVisibleStagedMessages, getVisibleStagedMessages);
54
+ const forkPending = useSyncExternalStore(threadController.subscribe, () => threadController.getState().forkPending, () => threadController.getState().forkPending);
57
55
  const [toolStatuses, setToolStatuses] = useState({});
58
56
  const hasExecutingTools = Object.values(toolStatuses).some((s) => s?.type === "executing");
59
57
  const effectiveIsRunning = stream.isLoading || hasExecutingTools;
@@ -64,7 +62,7 @@ const useStreamThreadRuntime = (options) => {
64
62
  const liveUiMessages = useMemo(() => foldUIUpdates(customEvents, uiFoldMemo), [customEvents, uiFoldMemo]);
65
63
  const mergedUiMessages = useMemo(() => mergeUIMessages(liveUiMessages, uiStateValue), [liveUiMessages, uiStateValue]);
66
64
  const uiMessagesByParent = useMemo(() => groupUIMessagesByParent(mergedUiMessages), [mergedUiMessages]);
67
- const visibleMessages = stagedMessages ?? stream.messages;
65
+ const visibleMessages = visibleStagedMessages ?? stream.messages;
68
66
  const messageTiming = useLangChainStreamingTiming(visibleMessages, effectiveIsRunning);
69
67
  const subagentTranscripts = useSubagentTranscripts(stream, uiMessagesByParent);
70
68
  const [getConverterMetadataKey] = useState(createLangChainMetadataKey);
@@ -128,48 +126,35 @@ const useStreamThreadRuntime = (options) => {
128
126
  useInsertionEffect(() => {
129
127
  visibleMessagesRef.current = visibleMessages;
130
128
  }, [visibleMessages]);
129
+ const dispatchStaging = useCallback((action, fallback = visibleMessagesRef.current) => {
130
+ threadController.dispatch(action);
131
+ visibleMessagesRef.current = threadController.getState().visibleStagedMessages ?? fallback;
132
+ }, [threadController]);
131
133
  const threadMessagesRef = useRef(messagesWithTranscripts);
132
134
  useInsertionEffect(() => {
133
135
  threadMessagesRef.current = messagesWithTranscripts;
134
136
  }, [messagesWithTranscripts]);
135
- const stagedMessagesRef = useRef(/* @__PURE__ */ new Map());
136
- const stagedBaseMessagesRef = useRef(null);
137
137
  useEffect(() => {
138
- if (stagedMessagesRef.current.size === 0) return;
139
- const baseMessages = stagedBaseMessagesRef.current ?? stream.messages;
140
- const remainingStagedMessages = [];
141
- const matchedBaseMessageIndexes = /* @__PURE__ */ new Set();
142
- const visibleStagedIds = new Set(visibleMessagesRef.current.flatMap((m) => m.id ? [m.id] : []));
143
- for (const [id, staged] of stagedMessagesRef.current) {
144
- if (!visibleStagedIds.has(id)) continue;
145
- if (baseMessages.some((message, index) => {
146
- if (matchedBaseMessageIndexes.has(index)) return false;
147
- if (message.id === id) {
148
- matchedBaseMessageIndexes.add(index);
149
- return true;
150
- }
151
- if (!staged.reconcileOnEcho || index < staged.baseMessageCount || getMessageType(message) !== "human" || !hasSameMessageContent(message, staged.message)) return false;
152
- matchedBaseMessageIndexes.add(index);
153
- return true;
154
- })) stagedMessagesRef.current.delete(id);
155
- else remainingStagedMessages.push(staged.message);
156
- }
157
- if (remainingStagedMessages.length === 0) {
158
- stagedBaseMessagesRef.current = null;
159
- visibleMessagesRef.current = baseMessages;
160
- setStagedMessages(null);
161
- return;
162
- }
163
- const nextMessages = [...baseMessages, ...remainingStagedMessages];
164
- visibleMessagesRef.current = nextMessages;
165
- setStagedMessages(nextMessages);
166
- }, [stream.messages]);
138
+ const state = threadController.getState();
139
+ if (state.stagedEntries.size === 0) return;
140
+ const messages = stream.messages;
141
+ dispatchStaging({
142
+ type: "reconcile",
143
+ messages,
144
+ visibleMessages: visibleMessagesRef.current
145
+ }, state.stagedBaseMessages ?? messages);
146
+ }, [
147
+ stream.messages,
148
+ threadController,
149
+ dispatchStaging
150
+ ]);
167
151
  const getStagedRun = (parentId) => {
168
- const parent = parentId ? stagedMessagesRef.current.get(parentId) : void 0;
152
+ const stagedEntries = threadController.getState().stagedEntries;
153
+ const parent = parentId ? stagedEntries.get(parentId) : void 0;
169
154
  if (!parent || parent.transcriptStatus === "sent") return null;
170
155
  const staged = [];
171
156
  for (const message of visibleMessagesRef.current) {
172
- const entry = message.id ? stagedMessagesRef.current.get(message.id) : void 0;
157
+ const entry = message.id ? stagedEntries.get(message.id) : void 0;
173
158
  if (entry && entry.transcriptStatus !== "sent") staged.push(entry.message);
174
159
  if (message.id === parentId) break;
175
160
  }
@@ -185,23 +170,28 @@ const useStreamThreadRuntime = (options) => {
185
170
  content: getThreadMessageText(message),
186
171
  ...message.metadata.modality && { additional_kwargs: { modality: message.metadata.modality } }
187
172
  };
188
- stagedMessagesRef.current.set(transcript.id, {
189
- message: transcript,
190
- runConfig: void 0,
191
- reconcileOnEcho: false,
192
- baseMessageCount: streamRef.current.messages.length,
193
- transcriptStatus: "unsent"
173
+ dispatchStaging({
174
+ type: "stage",
175
+ entry: {
176
+ message: transcript,
177
+ runConfig: void 0,
178
+ reconcileOnEcho: false,
179
+ baseMessageCount: streamRef.current.messages.length,
180
+ transcriptStatus: "unsent"
181
+ },
182
+ visibleMessages: visibleMessagesRef.current
194
183
  });
195
- const nextMessages = [...visibleMessagesRef.current, transcript];
196
- visibleMessagesRef.current = nextMessages;
197
- setStagedMessages(nextMessages);
198
184
  };
199
- const getUnsentTranscripts = () => visibleMessagesRef.current.filter((message) => message.id !== void 0 && stagedMessagesRef.current.get(message.id)?.transcriptStatus === "unsent");
185
+ const getUnsentTranscripts = () => {
186
+ const stagedEntries = threadController.getState().stagedEntries;
187
+ return visibleMessagesRef.current.filter((message) => message.id !== void 0 && stagedEntries.get(message.id)?.transcriptStatus === "unsent");
188
+ };
200
189
  const setTranscriptStatus = (messages, status) => {
201
- for (const message of messages) {
202
- const staged = message.id ? stagedMessagesRef.current.get(message.id) : void 0;
203
- if (staged?.transcriptStatus) staged.transcriptStatus = status;
204
- }
190
+ dispatchStaging({
191
+ type: "markTranscript",
192
+ messages,
193
+ status
194
+ });
205
195
  };
206
196
  const submitCarryingTranscripts = async (transcripts, submit) => {
207
197
  setTranscriptStatus(transcripts, "sent");
@@ -213,10 +203,10 @@ const useStreamThreadRuntime = (options) => {
213
203
  }
214
204
  };
215
205
  const dropTranscripts = (messages) => {
216
- for (const message of messages) if (message.id && stagedMessagesRef.current.get(message.id)?.transcriptStatus) removeStagedMessage(message.id);
206
+ for (const message of messages) if (message.id && threadController.getState().stagedEntries.get(message.id)?.transcriptStatus) removeStagedMessage(message.id);
217
207
  };
218
208
  const isTranscriptMessage = (message) => {
219
- if ((message.id ? stagedMessagesRef.current.get(message.id) : void 0)?.transcriptStatus === "unsent") return true;
209
+ if ((message.id ? threadController.getState().stagedEntries.get(message.id) : void 0)?.transcriptStatus === "unsent") return true;
220
210
  const type = getMessageType(message);
221
211
  return (type === "human" || type === "ai") && getMessageModality(message.additional_kwargs) !== void 0;
222
212
  };
@@ -238,25 +228,31 @@ const useStreamThreadRuntime = (options) => {
238
228
  };
239
229
  const stageUserMessage = (msg, reconcileOnEcho = false) => {
240
230
  const stagedMessage = toStagedHumanMessage(msg);
241
- stagedMessagesRef.current.set(stagedMessage.id, {
242
- message: stagedMessage,
243
- runConfig: msg.runConfig,
244
- reconcileOnEcho,
245
- baseMessageCount: streamRef.current.messages.length
231
+ dispatchStaging({
232
+ type: "stage",
233
+ entry: {
234
+ message: stagedMessage,
235
+ runConfig: msg.runConfig,
236
+ reconcileOnEcho,
237
+ baseMessageCount: streamRef.current.messages.length
238
+ },
239
+ visibleMessages: visibleMessagesRef.current
246
240
  });
247
- const nextMessages = [...visibleMessagesRef.current, stagedMessage];
248
- visibleMessagesRef.current = nextMessages;
249
- setStagedMessages(nextMessages);
250
241
  return stagedMessage;
251
242
  };
252
243
  const removeStagedMessage = (id) => {
253
- if (!stagedMessagesRef.current.delete(id)) return;
254
- const nextMessages = visibleMessagesRef.current.filter((message) => message.id !== id);
255
- visibleMessagesRef.current = nextMessages;
256
- if (stagedMessagesRef.current.size === 0) {
257
- stagedBaseMessagesRef.current = null;
258
- setStagedMessages(null);
259
- } else setStagedMessages(nextMessages);
244
+ if (!threadController.getState().stagedEntries.has(id)) return;
245
+ const visible = visibleMessagesRef.current;
246
+ const nextMessages = visible.filter((message) => message.id !== id);
247
+ dispatchStaging({
248
+ type: "remove",
249
+ id,
250
+ visibleMessages: visible
251
+ }, nextMessages);
252
+ };
253
+ const supersedeFork = () => {
254
+ threadController.dispatch({ type: "supersedeFork" });
255
+ return threadController.getState().forkGeneration;
260
256
  };
261
257
  const extras = useMemo(() => langChainExtras.provide({
262
258
  interrupt: stream.interrupt,
@@ -281,7 +277,7 @@ const useStreamThreadRuntime = (options) => {
281
277
  ]);
282
278
  return useExternalStoreRuntime({
283
279
  ...pickExternalStoreSharedOptions(options),
284
- isRunning: stream.isLoading,
280
+ isRunning: stream.isLoading || forkPending,
285
281
  isLoading: stream.isThreadLoading,
286
282
  messages: messagesWithTranscripts,
287
283
  adapters,
@@ -330,73 +326,89 @@ const useStreamThreadRuntime = (options) => {
330
326
  }] }, runConfig === void 0 ? void 0 : { config: runConfig });
331
327
  },
332
328
  onReload: async (parentId, config) => {
333
- const stagedRun = getStagedRun(parentId);
334
- if (stagedRun) {
335
- if (config.sourceId && stagedMessagesRef.current.get(config.sourceId)?.transcriptStatus) removeStagedMessage(config.sourceId);
336
- const promotedIds = /* @__PURE__ */ new Set();
337
- for (const message of stagedRun.messages) {
338
- if (!message.id || stagedMessagesRef.current.get(message.id)?.transcriptStatus) continue;
339
- promotedIds.add(message.id);
340
- stagedMessagesRef.current.delete(message.id);
329
+ const forkGeneration = supersedeFork();
330
+ try {
331
+ await streamRef.current.stop();
332
+ if (threadController.getState().forkGeneration !== forkGeneration) return;
333
+ const stagedRun = getStagedRun(parentId);
334
+ if (stagedRun) {
335
+ if (config.sourceId && threadController.getState().stagedEntries.get(config.sourceId)?.transcriptStatus) removeStagedMessage(config.sourceId);
336
+ dispatchStaging({
337
+ type: "promote",
338
+ messages: stagedRun.messages,
339
+ visibleMessages: visibleMessagesRef.current
340
+ });
341
+ const runConfig = config.runConfig ?? stagedRun.runConfig;
342
+ setActiveRunConfig(runConfig);
343
+ await submitCarryingTranscripts(stagedRun.messages, () => stream.submit({ [messagesKey]: stagedRun.messages.map(toStagedMessageInput) }, runConfigToSubmitOptions(runConfig)));
344
+ return;
341
345
  }
342
- stagedBaseMessagesRef.current = null;
343
- if (stagedMessagesRef.current.size > 0) {
344
- const nextMessages = visibleMessagesRef.current.filter((message) => !message.id || !promotedIds.has(message.id));
345
- visibleMessagesRef.current = nextMessages;
346
- setStagedMessages(nextMessages);
347
- } else setStagedMessages(null);
348
- const runConfig = config.runConfig ?? stagedRun.runConfig;
349
- setActiveRunConfig(runConfig);
350
- await submitCarryingTranscripts(stagedRun.messages, () => stream.submit({ [messagesKey]: stagedRun.messages.map(toStagedMessageInput) }, runConfigToSubmitOptions(runConfig)));
351
- return;
346
+ const threadId = externalId;
347
+ if (!threadId || parentId == null) return;
348
+ const s = streamRef.current;
349
+ const fork = planForkTranscripts(parentId);
350
+ const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, config.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
351
+ if (threadController.getState().forkGeneration !== forkGeneration) return;
352
+ if (!checkpointId) return;
353
+ dropTranscripts(fork.truncated);
354
+ setActiveRunConfig(config.runConfig);
355
+ await submitCarryingTranscripts(fork.transcripts, () => s.submit(fork.transcripts.length > 0 ? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) } : null, {
356
+ forkFrom: checkpointId,
357
+ ...runConfigToSubmitOptions(config.runConfig)
358
+ }));
359
+ } finally {
360
+ threadController.dispatch({
361
+ type: "finishFork",
362
+ generation: forkGeneration
363
+ });
352
364
  }
353
- const threadId = externalId;
354
- if (!threadId || parentId == null) return;
355
- const s = streamRef.current;
356
- const fork = planForkTranscripts(parentId);
357
- const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, config.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
358
- if (!checkpointId) return;
359
- dropTranscripts(fork.truncated);
360
- setActiveRunConfig(config.runConfig);
361
- await submitCarryingTranscripts(fork.transcripts, () => s.submit(fork.transcripts.length > 0 ? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) } : null, {
362
- forkFrom: checkpointId,
363
- ...runConfigToSubmitOptions(config.runConfig)
364
- }));
365
365
  },
366
366
  onEdit: async (message) => {
367
- if (!(message.startRun ?? message.role === "user")) {
368
- const truncated = truncateLangChainBaseMessages(threadMessagesRef.current, message.parentId);
369
- const stagedMessage = toStagedHumanMessage(message);
370
- stagedMessagesRef.current.set(stagedMessage.id, {
371
- message: stagedMessage,
372
- runConfig: message.runConfig,
373
- reconcileOnEcho: false,
374
- baseMessageCount: 0
367
+ const forkGeneration = supersedeFork();
368
+ try {
369
+ await streamRef.current.stop();
370
+ if (threadController.getState().forkGeneration !== forkGeneration) return;
371
+ if (!(message.startRun ?? message.role === "user")) {
372
+ const truncated = truncateLangChainBaseMessages(threadMessagesRef.current, message.parentId);
373
+ const stagedMessage = toStagedHumanMessage(message);
374
+ dispatchStaging({
375
+ type: "stageEdit",
376
+ entry: {
377
+ message: stagedMessage,
378
+ runConfig: message.runConfig,
379
+ reconcileOnEcho: false,
380
+ baseMessageCount: 0
381
+ },
382
+ baseMessages: truncated
383
+ });
384
+ return;
385
+ }
386
+ const threadId = externalId;
387
+ if (!threadId) return;
388
+ const s = streamRef.current;
389
+ const fork = planForkTranscripts(message.parentId);
390
+ const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, message.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
391
+ if (threadController.getState().forkGeneration !== forkGeneration) return;
392
+ if (!checkpointId) return;
393
+ dropTranscripts(fork.truncated);
394
+ const content = getMessageContent(message);
395
+ setActiveRunConfig(message.runConfig);
396
+ await submitCarryingTranscripts(fork.transcripts, () => s.submit({ [messagesKey]: [...fork.transcripts.map(toStagedMessageInput), {
397
+ type: "human",
398
+ content
399
+ }] }, {
400
+ forkFrom: checkpointId,
401
+ ...runConfigToSubmitOptions(message.runConfig)
402
+ }));
403
+ } finally {
404
+ threadController.dispatch({
405
+ type: "finishFork",
406
+ generation: forkGeneration
375
407
  });
376
- stagedBaseMessagesRef.current = truncated;
377
- const nextMessages = [...truncated, stagedMessage];
378
- visibleMessagesRef.current = nextMessages;
379
- setStagedMessages(nextMessages);
380
- return;
381
408
  }
382
- const threadId = externalId;
383
- if (!threadId) return;
384
- const s = streamRef.current;
385
- const fork = planForkTranscripts(message.parentId);
386
- const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, message.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
387
- if (!checkpointId) return;
388
- dropTranscripts(fork.truncated);
389
- const content = getMessageContent(message);
390
- setActiveRunConfig(message.runConfig);
391
- await submitCarryingTranscripts(fork.transcripts, () => s.submit({ [messagesKey]: [...fork.transcripts.map(toStagedMessageInput), {
392
- type: "human",
393
- content
394
- }] }, {
395
- forkFrom: checkpointId,
396
- ...runConfigToSubmitOptions(message.runConfig)
397
- }));
398
409
  },
399
410
  onCancel: unstable_allowCancellation !== false ? async () => {
411
+ threadController.dispatch({ type: "cancelFork" });
400
412
  activeRunConfigRef.current = void 0;
401
413
  await stream.stop();
402
414
  } : void 0
@@ -405,7 +417,7 @@ const useStreamThreadRuntime = (options) => {
405
417
  /**
406
418
  * Creates an assistant-ui runtime backed by LangChain's `useStream` hook.
407
419
  * Accepts the same options as `useStream` from `@langchain/react`, plus
408
- * `cloud` and `adapters`.
420
+ * `cloud`, `scopeId`, and `adapters`.
409
421
  *
410
422
  * @example
411
423
  * ```tsx
@@ -427,11 +439,12 @@ const useStreamThreadRuntime = (options) => {
427
439
  * ```
428
440
  */
429
441
  const useStreamRuntime = (rawOptions) => {
430
- const { cloud, unstable_threadListAdapter, create, delete: deleteFn, onThreadIdChange, ...options } = rawOptions;
442
+ const { cloud, scopeId, unstable_threadListAdapter, create, delete: deleteFn, onThreadIdChange, ...options } = rawOptions;
431
443
  const aui = useAui();
432
444
  const cloudAdapter = useCloudThreadListAdapter({
433
445
  sdk: LANGCHAIN_SDK,
434
446
  cloud,
447
+ scopeId,
435
448
  create: createCloudThreadListAdapterCreateFallback(create, aui.threadListItem),
436
449
  delete: deleteFn
437
450
  });
@@ -445,6 +458,6 @@ const useStreamRuntime = (rawOptions) => {
445
458
  });
446
459
  };
447
460
  //#endregion
448
- export { groupUIMessagesByParent, runConfigToSubmitOptions, useStreamRuntime };
461
+ export { getPendingToolCalls, groupUIMessagesByParent, runConfigToSubmitOptions, useStreamRuntime };
449
462
 
450
463
  //# sourceMappingURL=useStreamRuntime.js.map