@assistant-ui/react-langchain 0.0.34 → 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 (37) 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 +2 -1
  6. package/dist/convertMessages.d.ts.map +1 -1
  7. package/dist/convertMessages.js +12 -12
  8. package/dist/convertMessages.js.map +1 -1
  9. package/dist/converter.d.ts +24 -1
  10. package/dist/converter.d.ts.map +1 -1
  11. package/dist/converter.js +84 -18
  12. package/dist/converter.js.map +1 -1
  13. package/dist/langChainThreadState.d.ts +54 -0
  14. package/dist/langChainThreadState.d.ts.map +1 -0
  15. package/dist/langChainThreadState.js +135 -0
  16. package/dist/langChainThreadState.js.map +1 -0
  17. package/dist/sdkIdentity.js +1 -1
  18. package/dist/types.d.ts +9 -0
  19. package/dist/types.d.ts.map +1 -1
  20. package/dist/useStreamRuntime.d.ts +3 -2
  21. package/dist/useStreamRuntime.d.ts.map +1 -1
  22. package/dist/useStreamRuntime.js +159 -144
  23. package/dist/useStreamRuntime.js.map +1 -1
  24. package/dist/useSubagentTranscripts.js +1 -1
  25. package/package.json +9 -8
  26. package/src/LangChainThreadController.ts +24 -0
  27. package/src/convertMessages.test.ts +23 -0
  28. package/src/convertMessages.ts +43 -17
  29. package/src/converter.test.ts +322 -0
  30. package/src/converter.ts +138 -27
  31. package/src/getPendingToolCalls.test.ts +35 -0
  32. package/src/langChainThreadState.test.ts +203 -0
  33. package/src/langChainThreadState.ts +239 -0
  34. package/src/types.ts +9 -0
  35. package/src/useStreamRuntime.cloud-options.test.tsx +61 -0
  36. package/src/useStreamRuntime.test.tsx +296 -8
  37. package/src/useStreamRuntime.ts +221 -249
@@ -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.34"
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;AAwBtD,eAAO,MAAM,wBAAwB,GACnC,WAAW,aAAa,CAAC,WAAW,CAAC;;;;aAIxB,CAAC;AAgsBhB;;;;;;;;;;;;;;;;;;;;;;;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
- import { convertLangChainBaseMessage, getMessageType } from "./convertMessages.js";
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,18 +62,20 @@ 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
- const convertWithUI = useMemo(() => (message, metadata) => convertLangChainBaseMessage(message, {
71
- ...metadata,
68
+ const [getConverterMetadataKey] = useState(createLangChainMetadataKey);
69
+ const converterMetadata = useMemo(() => ({
72
70
  uiMessagesByParent,
73
71
  messageTiming
74
72
  }), [uiMessagesByParent, messageTiming]);
75
73
  const threadMessages = useExternalMessageConverter({
76
- callback: convertWithUI,
74
+ callback: convertLangChainBaseMessage,
77
75
  messages: visibleMessages,
78
- isRunning: effectiveIsRunning
76
+ isRunning: effectiveIsRunning,
77
+ metadata: converterMetadata,
78
+ getMetadataKey: getConverterMetadataKey
79
79
  });
80
80
  const [memo] = useState(createAttachMemo);
81
81
  const messagesWithTranscripts = useMemo(() => attachSubagentTranscripts(threadMessages, subagentTranscripts, memo), [
@@ -126,48 +126,35 @@ const useStreamThreadRuntime = (options) => {
126
126
  useInsertionEffect(() => {
127
127
  visibleMessagesRef.current = visibleMessages;
128
128
  }, [visibleMessages]);
129
+ const dispatchStaging = useCallback((action, fallback = visibleMessagesRef.current) => {
130
+ threadController.dispatch(action);
131
+ visibleMessagesRef.current = threadController.getState().visibleStagedMessages ?? fallback;
132
+ }, [threadController]);
129
133
  const threadMessagesRef = useRef(messagesWithTranscripts);
130
134
  useInsertionEffect(() => {
131
135
  threadMessagesRef.current = messagesWithTranscripts;
132
136
  }, [messagesWithTranscripts]);
133
- const stagedMessagesRef = useRef(/* @__PURE__ */ new Map());
134
- const stagedBaseMessagesRef = useRef(null);
135
137
  useEffect(() => {
136
- if (stagedMessagesRef.current.size === 0) return;
137
- const baseMessages = stagedBaseMessagesRef.current ?? stream.messages;
138
- const remainingStagedMessages = [];
139
- const matchedBaseMessageIndexes = /* @__PURE__ */ new Set();
140
- const visibleStagedIds = new Set(visibleMessagesRef.current.flatMap((m) => m.id ? [m.id] : []));
141
- for (const [id, staged] of stagedMessagesRef.current) {
142
- if (!visibleStagedIds.has(id)) continue;
143
- if (baseMessages.some((message, index) => {
144
- if (matchedBaseMessageIndexes.has(index)) return false;
145
- if (message.id === id) {
146
- matchedBaseMessageIndexes.add(index);
147
- return true;
148
- }
149
- if (!staged.reconcileOnEcho || index < staged.baseMessageCount || getMessageType(message) !== "human" || !hasSameMessageContent(message, staged.message)) return false;
150
- matchedBaseMessageIndexes.add(index);
151
- return true;
152
- })) stagedMessagesRef.current.delete(id);
153
- else remainingStagedMessages.push(staged.message);
154
- }
155
- if (remainingStagedMessages.length === 0) {
156
- stagedBaseMessagesRef.current = null;
157
- visibleMessagesRef.current = baseMessages;
158
- setStagedMessages(null);
159
- return;
160
- }
161
- const nextMessages = [...baseMessages, ...remainingStagedMessages];
162
- visibleMessagesRef.current = nextMessages;
163
- setStagedMessages(nextMessages);
164
- }, [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
+ ]);
165
151
  const getStagedRun = (parentId) => {
166
- const parent = parentId ? stagedMessagesRef.current.get(parentId) : void 0;
152
+ const stagedEntries = threadController.getState().stagedEntries;
153
+ const parent = parentId ? stagedEntries.get(parentId) : void 0;
167
154
  if (!parent || parent.transcriptStatus === "sent") return null;
168
155
  const staged = [];
169
156
  for (const message of visibleMessagesRef.current) {
170
- const entry = message.id ? stagedMessagesRef.current.get(message.id) : void 0;
157
+ const entry = message.id ? stagedEntries.get(message.id) : void 0;
171
158
  if (entry && entry.transcriptStatus !== "sent") staged.push(entry.message);
172
159
  if (message.id === parentId) break;
173
160
  }
@@ -183,23 +170,28 @@ const useStreamThreadRuntime = (options) => {
183
170
  content: getThreadMessageText(message),
184
171
  ...message.metadata.modality && { additional_kwargs: { modality: message.metadata.modality } }
185
172
  };
186
- stagedMessagesRef.current.set(transcript.id, {
187
- message: transcript,
188
- runConfig: void 0,
189
- reconcileOnEcho: false,
190
- baseMessageCount: streamRef.current.messages.length,
191
- 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
192
183
  });
193
- const nextMessages = [...visibleMessagesRef.current, transcript];
194
- visibleMessagesRef.current = nextMessages;
195
- setStagedMessages(nextMessages);
196
184
  };
197
- 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
+ };
198
189
  const setTranscriptStatus = (messages, status) => {
199
- for (const message of messages) {
200
- const staged = message.id ? stagedMessagesRef.current.get(message.id) : void 0;
201
- if (staged?.transcriptStatus) staged.transcriptStatus = status;
202
- }
190
+ dispatchStaging({
191
+ type: "markTranscript",
192
+ messages,
193
+ status
194
+ });
203
195
  };
204
196
  const submitCarryingTranscripts = async (transcripts, submit) => {
205
197
  setTranscriptStatus(transcripts, "sent");
@@ -211,10 +203,10 @@ const useStreamThreadRuntime = (options) => {
211
203
  }
212
204
  };
213
205
  const dropTranscripts = (messages) => {
214
- 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);
215
207
  };
216
208
  const isTranscriptMessage = (message) => {
217
- 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;
218
210
  const type = getMessageType(message);
219
211
  return (type === "human" || type === "ai") && getMessageModality(message.additional_kwargs) !== void 0;
220
212
  };
@@ -236,25 +228,31 @@ const useStreamThreadRuntime = (options) => {
236
228
  };
237
229
  const stageUserMessage = (msg, reconcileOnEcho = false) => {
238
230
  const stagedMessage = toStagedHumanMessage(msg);
239
- stagedMessagesRef.current.set(stagedMessage.id, {
240
- message: stagedMessage,
241
- runConfig: msg.runConfig,
242
- reconcileOnEcho,
243
- 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
244
240
  });
245
- const nextMessages = [...visibleMessagesRef.current, stagedMessage];
246
- visibleMessagesRef.current = nextMessages;
247
- setStagedMessages(nextMessages);
248
241
  return stagedMessage;
249
242
  };
250
243
  const removeStagedMessage = (id) => {
251
- if (!stagedMessagesRef.current.delete(id)) return;
252
- const nextMessages = visibleMessagesRef.current.filter((message) => message.id !== id);
253
- visibleMessagesRef.current = nextMessages;
254
- if (stagedMessagesRef.current.size === 0) {
255
- stagedBaseMessagesRef.current = null;
256
- setStagedMessages(null);
257
- } 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;
258
256
  };
259
257
  const extras = useMemo(() => langChainExtras.provide({
260
258
  interrupt: stream.interrupt,
@@ -279,7 +277,7 @@ const useStreamThreadRuntime = (options) => {
279
277
  ]);
280
278
  return useExternalStoreRuntime({
281
279
  ...pickExternalStoreSharedOptions(options),
282
- isRunning: stream.isLoading,
280
+ isRunning: stream.isLoading || forkPending,
283
281
  isLoading: stream.isThreadLoading,
284
282
  messages: messagesWithTranscripts,
285
283
  adapters,
@@ -328,73 +326,89 @@ const useStreamThreadRuntime = (options) => {
328
326
  }] }, runConfig === void 0 ? void 0 : { config: runConfig });
329
327
  },
330
328
  onReload: async (parentId, config) => {
331
- const stagedRun = getStagedRun(parentId);
332
- if (stagedRun) {
333
- if (config.sourceId && stagedMessagesRef.current.get(config.sourceId)?.transcriptStatus) removeStagedMessage(config.sourceId);
334
- const promotedIds = /* @__PURE__ */ new Set();
335
- for (const message of stagedRun.messages) {
336
- if (!message.id || stagedMessagesRef.current.get(message.id)?.transcriptStatus) continue;
337
- promotedIds.add(message.id);
338
- 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;
339
345
  }
340
- stagedBaseMessagesRef.current = null;
341
- if (stagedMessagesRef.current.size > 0) {
342
- const nextMessages = visibleMessagesRef.current.filter((message) => !message.id || !promotedIds.has(message.id));
343
- visibleMessagesRef.current = nextMessages;
344
- setStagedMessages(nextMessages);
345
- } else setStagedMessages(null);
346
- const runConfig = config.runConfig ?? stagedRun.runConfig;
347
- setActiveRunConfig(runConfig);
348
- await submitCarryingTranscripts(stagedRun.messages, () => stream.submit({ [messagesKey]: stagedRun.messages.map(toStagedMessageInput) }, runConfigToSubmitOptions(runConfig)));
349
- 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
+ });
350
364
  }
351
- const threadId = externalId;
352
- if (!threadId || parentId == null) return;
353
- const s = streamRef.current;
354
- const fork = planForkTranscripts(parentId);
355
- const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, config.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
356
- if (!checkpointId) return;
357
- dropTranscripts(fork.truncated);
358
- setActiveRunConfig(config.runConfig);
359
- await submitCarryingTranscripts(fork.transcripts, () => s.submit(fork.transcripts.length > 0 ? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) } : null, {
360
- forkFrom: checkpointId,
361
- ...runConfigToSubmitOptions(config.runConfig)
362
- }));
363
365
  },
364
366
  onEdit: async (message) => {
365
- if (!(message.startRun ?? message.role === "user")) {
366
- const truncated = truncateLangChainBaseMessages(threadMessagesRef.current, message.parentId);
367
- const stagedMessage = toStagedHumanMessage(message);
368
- stagedMessagesRef.current.set(stagedMessage.id, {
369
- message: stagedMessage,
370
- runConfig: message.runConfig,
371
- reconcileOnEcho: false,
372
- 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
373
407
  });
374
- stagedBaseMessagesRef.current = truncated;
375
- const nextMessages = [...truncated, stagedMessage];
376
- visibleMessagesRef.current = nextMessages;
377
- setStagedMessages(nextMessages);
378
- return;
379
408
  }
380
- const threadId = externalId;
381
- if (!threadId) return;
382
- const s = streamRef.current;
383
- const fork = planForkTranscripts(message.parentId);
384
- const checkpointId = await resolveForkCheckpoint(s.client, threadId, s.messages, fork.forkParentId, message.sourceId, s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(), messagesKey);
385
- if (!checkpointId) return;
386
- dropTranscripts(fork.truncated);
387
- const content = getMessageContent(message);
388
- setActiveRunConfig(message.runConfig);
389
- await submitCarryingTranscripts(fork.transcripts, () => s.submit({ [messagesKey]: [...fork.transcripts.map(toStagedMessageInput), {
390
- type: "human",
391
- content
392
- }] }, {
393
- forkFrom: checkpointId,
394
- ...runConfigToSubmitOptions(message.runConfig)
395
- }));
396
409
  },
397
410
  onCancel: unstable_allowCancellation !== false ? async () => {
411
+ threadController.dispatch({ type: "cancelFork" });
398
412
  activeRunConfigRef.current = void 0;
399
413
  await stream.stop();
400
414
  } : void 0
@@ -403,7 +417,7 @@ const useStreamThreadRuntime = (options) => {
403
417
  /**
404
418
  * Creates an assistant-ui runtime backed by LangChain's `useStream` hook.
405
419
  * Accepts the same options as `useStream` from `@langchain/react`, plus
406
- * `cloud` and `adapters`.
420
+ * `cloud`, `scopeId`, and `adapters`.
407
421
  *
408
422
  * @example
409
423
  * ```tsx
@@ -425,11 +439,12 @@ const useStreamThreadRuntime = (options) => {
425
439
  * ```
426
440
  */
427
441
  const useStreamRuntime = (rawOptions) => {
428
- const { cloud, unstable_threadListAdapter, create, delete: deleteFn, onThreadIdChange, ...options } = rawOptions;
442
+ const { cloud, scopeId, unstable_threadListAdapter, create, delete: deleteFn, onThreadIdChange, ...options } = rawOptions;
429
443
  const aui = useAui();
430
444
  const cloudAdapter = useCloudThreadListAdapter({
431
445
  sdk: LANGCHAIN_SDK,
432
446
  cloud,
447
+ scopeId,
433
448
  create: createCloudThreadListAdapterCreateFallback(create, aui.threadListItem),
434
449
  delete: deleteFn
435
450
  });
@@ -443,6 +458,6 @@ const useStreamRuntime = (rawOptions) => {
443
458
  });
444
459
  };
445
460
  //#endregion
446
- export { groupUIMessagesByParent, runConfigToSubmitOptions, useStreamRuntime };
461
+ export { getPendingToolCalls, groupUIMessagesByParent, runConfigToSubmitOptions, useStreamRuntime };
447
462
 
448
463
  //# sourceMappingURL=useStreamRuntime.js.map