@assistant-ui/react-langchain 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/convertMessages.d.ts.map +1 -1
- package/dist/convertMessages.js +5 -1
- package/dist/convertMessages.js.map +1 -1
- package/dist/sdkIdentity.js +1 -1
- package/dist/useStreamRuntime.d.ts.map +1 -1
- package/dist/useStreamRuntime.js +1 -1
- package/dist/useStreamRuntime.js.map +1 -1
- package/dist/useSubagentTranscripts.d.ts.map +1 -1
- package/dist/useSubagentTranscripts.js +2 -1
- package/dist/useSubagentTranscripts.js.map +1 -1
- package/package.json +12 -9
- package/src/convertMessages.test.ts +84 -0
- package/src/convertMessages.ts +18 -10
- package/src/tests/settleOutsideAct.ts +15 -0
- package/src/useStreamRuntime.test.tsx +47 -7
- package/src/useStreamRuntime.ts +4 -1
- package/src/useStreamRuntime.voice.test.tsx +4 -3
- package/src/useSubagentTranscripts.fast-refresh.test.tsx +152 -0
- package/src/useSubagentTranscripts.test.tsx +2 -0
- package/src/useSubagentTranscripts.ts +2 -1
package/LICENSE
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessages.d.ts","sourceRoot":"","sources":["../src/convertMessages.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAS5E,OAAO,KAAK,EACV,oBAAoB,EAEpB,SAAS,EACV,mBAAgB;AAEjB,KAAK,iCAAiC,GACpC,2BAA2B,CAAC,QAAQ,GAAG;IACrC,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC/C,CAAC;AAcJ,eAAO,MAAM,cAAc,GAAI,SAAS,oBAAoB,KAAG,MAQ9D,CAAC;AA4DF,eAAO,MAAM,2BAA2B,GACtC,SAAS,oBAAoB,EAC7B,WAAU,iCAAsC,KAC/C,2BAA2B,CAAC,
|
|
1
|
+
{"version":3,"file":"convertMessages.d.ts","sourceRoot":"","sources":["../src/convertMessages.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAS5E,OAAO,KAAK,EACV,oBAAoB,EAEpB,SAAS,EACV,mBAAgB;AAEjB,KAAK,iCAAiC,GACpC,2BAA2B,CAAC,QAAQ,GAAG;IACrC,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC/C,CAAC;AAcJ,eAAO,MAAM,cAAc,GAAI,SAAS,oBAAoB,KAAG,MAQ9D,CAAC;AA4DF,eAAO,MAAM,2BAA2B,GACtC,SAAS,oBAAoB,EAC7B,WAAU,iCAAsC,KAC/C,2BAA2B,CAAC,OA4G9B,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,uBAAoB"}
|
package/dist/convertMessages.js
CHANGED
|
@@ -82,7 +82,11 @@ const convertLangChainBaseMessage = (message, metadata = {}) => {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
case "ai": {
|
|
85
|
-
const toolCallParts = message.tool_calls?.
|
|
85
|
+
const toolCallParts = message.tool_calls?.filter((tc) => {
|
|
86
|
+
if (typeof tc?.name === "string") return true;
|
|
87
|
+
warnOnceInDevelopment("Skipping a tool call without a name; its result is not shown either");
|
|
88
|
+
return false;
|
|
89
|
+
}).map((tc) => {
|
|
86
90
|
const { args, argsText } = toolCallArgs(tc.args);
|
|
87
91
|
return {
|
|
88
92
|
type: "tool-call",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessages.js","names":[],"sources":["../src/convertMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageTiming } from \"@assistant-ui/core\";\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport {\n convertLangChainContentBlock,\n getCustomMetadata,\n getMessageModality,\n uiMessageToDataPart,\n withAudioTranscript,\n} from \"./converter\";\nimport type {\n LangChainBaseMessage,\n LangChainContentBlock,\n UIMessage,\n} from \"./types\";\n\ntype LangChainMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n uiMessagesByParent?: Map<string, UIMessage[]>;\n messageTiming?: Record<string, MessageTiming>;\n };\n\nconst warnedMalformedMessages = new Set<string>();\nconst warnOnceInDevelopment = (message: string) => {\n if (\n typeof process === \"undefined\" ||\n process?.env?.NODE_ENV !== \"development\"\n )\n return;\n if (warnedMalformedMessages.has(message)) return;\n warnedMalformedMessages.add(message);\n console.warn(message);\n};\n\nexport const getMessageType = (message: LangChainBaseMessage): string => {\n if (typeof message._getType === \"function\") return message._getType();\n if (\"type\" in message)\n return (message as Record<string, unknown>).type as string;\n warnOnceInDevelopment(\n \"Cannot determine message type; rendering the message as system text\",\n );\n return \"unknown\";\n};\n\nconst contentBlocks = (content: unknown): readonly LangChainContentBlock[] => {\n if (content == null) return [];\n if (Array.isArray(content))\n return content.filter(\n (block) => typeof block === \"object\" && block !== null,\n );\n warnOnceInDevelopment(\n `Ignoring message content that is neither a string nor an array: ${typeof content}`,\n );\n return [];\n};\n\nconst normalizeToolCallArgs = (args: unknown): ReadonlyJSONObject => {\n if (typeof args !== \"object\" || args === null || Array.isArray(args)) {\n return {};\n }\n\n try {\n const prototype = Object.getPrototypeOf(args);\n return prototype === Object.prototype || prototype === null\n ? (args as ReadonlyJSONObject)\n : {};\n } catch {\n return {};\n }\n};\n\nconst toolCallArgs = (\n value: unknown,\n): { args: ReadonlyJSONObject; argsText: string } => {\n const args = normalizeToolCallArgs(value);\n try {\n const argsText = JSON.stringify(args);\n return typeof argsText === \"string\"\n ? { args, argsText }\n : { args: {}, argsText: \"{}\" };\n } catch {\n return { args: {}, argsText: \"{}\" };\n }\n};\n\nconst contentToParts = (content: unknown) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n\n return contentBlocks(content)\n .map(convertLangChainContentBlock)\n .filter((part) => part !== null && part !== undefined);\n};\n\nconst getStringContent = (content: unknown): string => {\n if (typeof content === \"string\") return content;\n return contentBlocks(content)\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n};\n\nexport const convertLangChainBaseMessage = (\n message: LangChainBaseMessage,\n metadata: LangChainMessageConverterMetadata = {},\n): useExternalMessageConverter.Message => {\n const type = getMessageType(message);\n\n switch (type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: getStringContent(message.content) }],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"human\": {\n const modality = getMessageModality(message.additional_kwargs);\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content),\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n ...(modality && { modality }),\n },\n };\n }\n\n case \"ai\": {\n const toolCallParts =\n message.tool_calls?.map((tc) => {\n
|
|
1
|
+
{"version":3,"file":"convertMessages.js","names":[],"sources":["../src/convertMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageTiming } from \"@assistant-ui/core\";\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport {\n convertLangChainContentBlock,\n getCustomMetadata,\n getMessageModality,\n uiMessageToDataPart,\n withAudioTranscript,\n} from \"./converter\";\nimport type {\n LangChainBaseMessage,\n LangChainContentBlock,\n UIMessage,\n} from \"./types\";\n\ntype LangChainMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n uiMessagesByParent?: Map<string, UIMessage[]>;\n messageTiming?: Record<string, MessageTiming>;\n };\n\nconst warnedMalformedMessages = new Set<string>();\nconst warnOnceInDevelopment = (message: string) => {\n if (\n typeof process === \"undefined\" ||\n process?.env?.NODE_ENV !== \"development\"\n )\n return;\n if (warnedMalformedMessages.has(message)) return;\n warnedMalformedMessages.add(message);\n console.warn(message);\n};\n\nexport const getMessageType = (message: LangChainBaseMessage): string => {\n if (typeof message._getType === \"function\") return message._getType();\n if (\"type\" in message)\n return (message as Record<string, unknown>).type as string;\n warnOnceInDevelopment(\n \"Cannot determine message type; rendering the message as system text\",\n );\n return \"unknown\";\n};\n\nconst contentBlocks = (content: unknown): readonly LangChainContentBlock[] => {\n if (content == null) return [];\n if (Array.isArray(content))\n return content.filter(\n (block) => typeof block === \"object\" && block !== null,\n );\n warnOnceInDevelopment(\n `Ignoring message content that is neither a string nor an array: ${typeof content}`,\n );\n return [];\n};\n\nconst normalizeToolCallArgs = (args: unknown): ReadonlyJSONObject => {\n if (typeof args !== \"object\" || args === null || Array.isArray(args)) {\n return {};\n }\n\n try {\n const prototype = Object.getPrototypeOf(args);\n return prototype === Object.prototype || prototype === null\n ? (args as ReadonlyJSONObject)\n : {};\n } catch {\n return {};\n }\n};\n\nconst toolCallArgs = (\n value: unknown,\n): { args: ReadonlyJSONObject; argsText: string } => {\n const args = normalizeToolCallArgs(value);\n try {\n const argsText = JSON.stringify(args);\n return typeof argsText === \"string\"\n ? { args, argsText }\n : { args: {}, argsText: \"{}\" };\n } catch {\n return { args: {}, argsText: \"{}\" };\n }\n};\n\nconst contentToParts = (content: unknown) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n\n return contentBlocks(content)\n .map(convertLangChainContentBlock)\n .filter((part) => part !== null && part !== undefined);\n};\n\nconst getStringContent = (content: unknown): string => {\n if (typeof content === \"string\") return content;\n return contentBlocks(content)\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n};\n\nexport const convertLangChainBaseMessage = (\n message: LangChainBaseMessage,\n metadata: LangChainMessageConverterMetadata = {},\n): useExternalMessageConverter.Message => {\n const type = getMessageType(message);\n\n switch (type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: getStringContent(message.content) }],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"human\": {\n const modality = getMessageModality(message.additional_kwargs);\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content),\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n ...(modality && { modality }),\n },\n };\n }\n\n case \"ai\": {\n const toolCallParts =\n message.tool_calls\n ?.filter((tc) => {\n if (typeof tc?.name === \"string\") return true;\n warnOnceInDevelopment(\n \"Skipping a tool call without a name; its result is not shown either\",\n );\n return false;\n })\n .map((tc) => {\n const { args, argsText } = toolCallArgs(tc.args);\n return {\n type: \"tool-call\" as const,\n toolCallId: tc.id,\n toolName: tc.name,\n args,\n argsText,\n };\n }) ?? [];\n\n const assistantStatus =\n typeof message.status === \"object\" ? message.status : undefined;\n\n const uiDataParts =\n (message.id\n ? metadata.uiMessagesByParent\n ?.get(message.id)\n ?.map(uiMessageToDataPart)\n : undefined) ?? [];\n\n const timing = metadata.messageTiming?.[message.id ?? \"\"];\n const modality = getMessageModality(message.additional_kwargs);\n\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...withAudioTranscript(\n contentToParts(message.content),\n message.additional_kwargs,\n ),\n ...toolCallParts,\n ...uiDataParts,\n ],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n ...(timing && { timing }),\n ...(modality && { modality }),\n },\n ...(assistantStatus && { status: assistantStatus }),\n };\n }\n\n case \"tool\":\n return {\n role: \"tool\",\n // `joinExternalMessages` only checks the name against the tool call\n // when it is non-null, so an empty name manufactures a mismatch.\n toolName: message.name || undefined,\n toolCallId: message.tool_call_id ?? \"\",\n result: message.content,\n artifact: message.artifact,\n isError: message.status === \"error\",\n };\n\n default:\n return {\n role: \"system\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text:\n typeof message.content === \"string\"\n ? message.content\n : (JSON.stringify(message.content) ?? \"\"),\n },\n ],\n };\n }\n};\n\nexport { getMessageContent } from \"./converter\";\n"],"mappings":"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"}
|
package/dist/sdkIdentity.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/useStreamRuntime.js
CHANGED
|
@@ -294,7 +294,7 @@ const useStreamThreadRuntime = (options) => {
|
|
|
294
294
|
const stagedMessageId = stageUserMessage(msg, true).id;
|
|
295
295
|
setActiveRunConfig(msg.runConfig);
|
|
296
296
|
const content = getMessageContent(msg);
|
|
297
|
-
const cancellations = autoCancelPendingToolCalls !== false ? getPendingToolCalls(streamRef.current.messages).map(createToolCallCancellationStub) : [];
|
|
297
|
+
const cancellations = autoCancelPendingToolCalls !== false ? getPendingToolCalls(streamRef.current.messages).filter((toolCall) => toolCall.id).map(createToolCallCancellationStub) : [];
|
|
298
298
|
try {
|
|
299
299
|
const { externalId } = await aui.threadListItem.initialize();
|
|
300
300
|
const transcripts = getUnsentTranscripts();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStreamRuntime.js","names":[],"sources":["../src/useStreamRuntime.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/store\" preserve=\"true\" />\n\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { AppendMessage, ToolExecutionStatus } from \"@assistant-ui/core\";\nimport {\n generateId,\n getExternalStoreMessages,\n pickExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport {\n createCloudThreadListAdapterCreateFallback,\n createToolCallCancellationStub,\n getThreadMessageText,\n scanPendingToolCalls,\n} from \"@assistant-ui/core/internal\";\nimport {\n useCloudThreadListAdapter,\n useExternalStoreRuntime,\n useExternalMessageConverter,\n useRemoteThreadListRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { STREAM_CONTROLLER, useChannel, useStream } from \"@langchain/react\";\nimport type {\n LangChainBaseMessage,\n LangChainToolCall,\n UIMessage,\n UseStreamRuntimeOptions,\n} from \"./types\";\nimport { getMessageModality, groupUIMessagesByParent } from \"./converter\";\nexport { groupUIMessagesByParent } from \"./converter\";\nimport {\n convertLangChainBaseMessage,\n getMessageContent,\n getMessageType,\n} from \"./convertMessages\";\nimport {\n attachSubagentTranscripts,\n createAttachMemo,\n} from \"./attachSubagentTranscripts\";\nimport { useSubagentTranscripts } from \"./useSubagentTranscripts\";\nimport {\n createUIFoldMemo,\n createUISnapshotMemo,\n foldUIUpdates,\n mergeUIMessages,\n reconcileUISnapshot,\n UI_CUSTOM_CHANNELS,\n} from \"./uiMessages\";\nimport { langChainExtras } from \"./runtimeExtras\";\nimport { resolveForkCheckpoint } from \"./resolveForkCheckpoint\";\nimport { useLangChainStreamingTiming } from \"./streamingTiming\";\nimport { LANGCHAIN_SDK } from \"./sdkIdentity\";\n\nexport const runConfigToSubmitOptions = (\n runConfig: AppendMessage[\"runConfig\"],\n) =>\n runConfig?.custom\n ? { config: { configurable: runConfig.custom } }\n : undefined;\n\ntype NormalizedRunConfigOptions = NonNullable<\n ReturnType<typeof runConfigToSubmitOptions>\n>;\n\nconst getPendingToolCalls = (\n messages: readonly LangChainBaseMessage[],\n): LangChainToolCall[] =>\n scanPendingToolCalls(\n messages,\n (message) => {\n const type = getMessageType(message);\n if (type === \"ai\") return { toolCalls: message.tool_calls ?? [] };\n if (type === \"tool\" && message.tool_call_id) {\n return { toolCallId: message.tool_call_id };\n }\n return undefined;\n },\n (toolCall) => toolCall.id,\n );\n\nconst toStagedHumanMessage = (\n msg: AppendMessage,\n id = generateId(),\n): LangChainBaseMessage & { id: string } => ({\n id,\n _getType: () => \"human\",\n content: getMessageContent(msg),\n});\n\nconst toStagedMessageInput = (message: LangChainBaseMessage) => ({\n id: message.id,\n type: getMessageType(message) === \"ai\" ? (\"ai\" as const) : (\"human\" as const),\n content: message.content,\n ...(message.additional_kwargs && {\n additional_kwargs: message.additional_kwargs,\n }),\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\nconst truncateLangChainBaseMessages = (\n threadMessages: readonly ThreadMessage[],\n parentId: string | null,\n): LangChainBaseMessage[] => {\n if (parentId === null) return [];\n const parentIndex = threadMessages.findIndex((m) => m.id === parentId);\n if (parentIndex === -1) return [];\n const truncated: LangChainBaseMessage[] = [];\n for (let i = 0; i <= parentIndex && i < threadMessages.length; i++) {\n truncated.push(\n ...getExternalStoreMessages<LangChainBaseMessage>(threadMessages[i]!),\n );\n }\n return truncated;\n};\n\ntype DistributiveOmit<T, K extends keyof any> = T extends unknown\n ? Omit<T, K>\n : never;\n\nconst useStreamThreadRuntime = (\n options: DistributiveOmit<\n UseStreamRuntimeOptions,\n \"cloud\" | \"unstable_threadListAdapter\" | \"create\" | \"delete\"\n >,\n) => {\n const { adapters, autoCancelPendingToolCalls, unstable_allowCancellation } =\n options;\n const aui = useAui();\n const messagesKey = options.messagesKey ?? \"messages\";\n const uiStateKey = options.uiStateKey ?? \"ui\";\n\n const externalId = useAuiState((s) => s.threadListItem.externalId) as\n | string\n | null;\n // Object.assign preserves the discriminated transport union; object spread\n // collapses its arms and no longer satisfies UseStreamOptions.\n const stream = useStream(\n Object.assign({}, options, { threadId: externalId }),\n );\n const [stagedMessages, setStagedMessages] = useState<\n LangChainBaseMessage[] | null\n >(null);\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n const effectiveIsRunning = stream.isLoading || hasExecutingTools;\n\n const [uiSnapshotMemo] = useState(createUISnapshotMemo);\n const uiStateValue = reconcileUISnapshot(\n stream.values[uiStateKey],\n uiSnapshotMemo,\n );\n\n const customEvents = useChannel(stream, UI_CUSTOM_CHANNELS);\n const [uiFoldMemo] = useState(createUIFoldMemo);\n const liveUiMessages = useMemo(\n () => foldUIUpdates(customEvents, uiFoldMemo),\n [customEvents, uiFoldMemo],\n );\n\n const mergedUiMessages = useMemo(\n () => mergeUIMessages(liveUiMessages, uiStateValue),\n [liveUiMessages, uiStateValue],\n );\n\n const uiMessagesByParent = useMemo(\n () => groupUIMessagesByParent<UIMessage>(mergedUiMessages),\n [mergedUiMessages],\n );\n\n const visibleMessages =\n stagedMessages ?? (stream.messages as LangChainBaseMessage[]);\n\n const messageTiming = useLangChainStreamingTiming(\n visibleMessages,\n effectiveIsRunning,\n );\n\n const subagentTranscripts = useSubagentTranscripts(\n stream,\n uiMessagesByParent,\n );\n\n const convertWithUI = useMemo<\n useExternalMessageConverter.Callback<LangChainBaseMessage>\n >(\n () => (message, metadata) =>\n convertLangChainBaseMessage(message, {\n ...metadata,\n uiMessagesByParent,\n messageTiming,\n }),\n [uiMessagesByParent, messageTiming],\n );\n\n const threadMessages = useExternalMessageConverter({\n callback: convertWithUI,\n messages: visibleMessages,\n isRunning: effectiveIsRunning,\n });\n const [memo] = useState(createAttachMemo);\n const messagesWithTranscripts = useMemo(\n () => attachSubagentTranscripts(threadMessages, subagentTranscripts, memo),\n [threadMessages, subagentTranscripts, memo],\n );\n\n const streamRef = useRef(stream);\n useInsertionEffect(() => {\n streamRef.current = stream;\n }, [stream]);\n\n const activeRunConfigRef = useRef<\n NormalizedRunConfigOptions[\"config\"] | undefined\n >(undefined);\n const runConfigByMessageIdRef = useRef(\n new Map<string, NormalizedRunConfigOptions[\"config\"] | undefined>(),\n );\n const activeThreadIdRef = useRef(externalId);\n const setActiveRunConfig = useCallback(\n (runConfig: AppendMessage[\"runConfig\"]) => {\n activeRunConfigRef.current = runConfigToSubmitOptions(runConfig)?.config;\n },\n [],\n );\n const withActiveRunConfig = useCallback(\n (submitOptions?: Record<string, unknown>) => {\n if (submitOptions && \"config\" in submitOptions) return submitOptions;\n if (activeRunConfigRef.current === undefined) return submitOptions;\n return { ...submitOptions, config: activeRunConfigRef.current };\n },\n [],\n );\n\n useEffect(() => {\n if (\n activeThreadIdRef.current !== null &&\n activeThreadIdRef.current !== externalId\n ) {\n activeRunConfigRef.current = undefined;\n runConfigByMessageIdRef.current.clear();\n }\n activeThreadIdRef.current = externalId;\n }, [externalId]);\n\n useEffect(() => {\n const messages = stream.messages as readonly LangChainBaseMessage[];\n const owned = runConfigByMessageIdRef.current;\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages.at(i);\n if (\n !message?.id ||\n getMessageType(message) !== \"ai\" ||\n !message.tool_calls?.length\n ) {\n continue;\n }\n if (owned.has(message.id)) return;\n break;\n }\n for (const message of messages) {\n if (\n !message.id ||\n getMessageType(message) !== \"ai\" ||\n !message.tool_calls?.length ||\n owned.has(message.id)\n ) {\n continue;\n }\n owned.set(message.id, activeRunConfigRef.current);\n }\n }, [stream.messages]);\n\n const visibleMessagesRef = useRef(visibleMessages);\n useInsertionEffect(() => {\n visibleMessagesRef.current = visibleMessages;\n }, [visibleMessages]);\n\n const threadMessagesRef = useRef(messagesWithTranscripts);\n useInsertionEffect(() => {\n threadMessagesRef.current = messagesWithTranscripts;\n }, [messagesWithTranscripts]);\n\n const stagedMessagesRef = useRef(\n new Map<\n string,\n {\n message: LangChainBaseMessage & { id: string };\n runConfig: AppendMessage[\"runConfig\"];\n reconcileOnEcho: boolean;\n baseMessageCount: number;\n transcriptStatus?: \"unsent\" | \"sent\";\n }\n >(),\n );\n const stagedBaseMessagesRef = useRef<LangChainBaseMessage[] | null>(null);\n useEffect(() => {\n if (stagedMessagesRef.current.size === 0) return;\n\n // Staged edits must keep their truncated base while stream updates arrive before promotion.\n const baseMessages =\n stagedBaseMessagesRef.current ??\n (stream.messages as LangChainBaseMessage[]);\n const remainingStagedMessages: LangChainBaseMessage[] = [];\n const matchedBaseMessageIndexes = new Set<number>();\n const visibleStagedIds = new Set(\n visibleMessagesRef.current.flatMap((m) => (m.id ? [m.id] : [])),\n );\n for (const [id, staged] of stagedMessagesRef.current) {\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) stagedMessagesRef.current.delete(id);\n else remainingStagedMessages.push(staged.message);\n }\n\n if (remainingStagedMessages.length === 0) {\n stagedBaseMessagesRef.current = null;\n visibleMessagesRef.current = baseMessages;\n // Reconciling against the upstream stream mutates the staged refs above,\n // which cannot happen during render.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setStagedMessages(null);\n return;\n }\n\n const nextMessages = [...baseMessages, ...remainingStagedMessages];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n }, [stream.messages]);\n\n const getStagedRun = (parentId: string | null) => {\n const parent = parentId\n ? stagedMessagesRef.current.get(parentId)\n : undefined;\n if (!parent || parent.transcriptStatus === \"sent\") return null;\n\n const staged: LangChainBaseMessage[] = [];\n for (const message of visibleMessagesRef.current) {\n const entry = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (entry && entry.transcriptStatus !== \"sent\") {\n staged.push(entry.message);\n }\n if (message.id === parentId) break;\n }\n\n return {\n messages: staged,\n runConfig: parent.runConfig,\n };\n };\n\n const appendVoiceTranscript = (message: ThreadMessage) => {\n const transcript = {\n id: message.id,\n _getType: () => (message.role === \"assistant\" ? \"ai\" : \"human\"),\n content: getThreadMessageText(message),\n ...(message.metadata.modality && {\n additional_kwargs: { modality: message.metadata.modality },\n }),\n };\n stagedMessagesRef.current.set(transcript.id, {\n message: transcript,\n runConfig: undefined,\n reconcileOnEcho: false,\n baseMessageCount: streamRef.current.messages.length,\n transcriptStatus: \"unsent\",\n });\n const nextMessages = [...visibleMessagesRef.current, transcript];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n };\n\n const getUnsentTranscripts = () =>\n visibleMessagesRef.current.filter(\n (message) =>\n message.id !== undefined &&\n stagedMessagesRef.current.get(message.id)?.transcriptStatus ===\n \"unsent\",\n );\n\n const setTranscriptStatus = (\n messages: readonly LangChainBaseMessage[],\n status: \"unsent\" | \"sent\",\n ) => {\n for (const message of messages) {\n const staged = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (staged?.transcriptStatus) staged.transcriptStatus = status;\n }\n };\n\n // Reserving before the submit keeps an overlapping submit from carrying the\n // same transcript; a failed submit hands it back to the next run.\n const submitCarryingTranscripts = async (\n transcripts: readonly LangChainBaseMessage[],\n submit: () => Promise<void>,\n ) => {\n setTranscriptStatus(transcripts, \"sent\");\n try {\n await submit();\n } catch (error) {\n setTranscriptStatus(transcripts, \"unsent\");\n throw error;\n }\n };\n\n const dropTranscripts = (messages: readonly LangChainBaseMessage[]) => {\n for (const message of messages) {\n if (\n message.id &&\n stagedMessagesRef.current.get(message.id)?.transcriptStatus\n )\n removeStagedMessage(message.id);\n }\n };\n\n const isTranscriptMessage = (message: LangChainBaseMessage) => {\n const staged = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (staged?.transcriptStatus === \"unsent\") return true;\n const type = getMessageType(message);\n return (\n (type === \"human\" || type === \"ai\") &&\n getMessageModality(message.additional_kwargs) !== undefined\n );\n };\n\n // A transcript reaches the graph in the same input as the message after it, so\n // no checkpoint ends at one; a fork starts before the trailing transcripts.\n const planForkTranscripts = (parentId: string | null) => {\n const visible = visibleMessagesRef.current;\n const parentIndex =\n parentId == null ? -1 : visible.findIndex((m) => m.id === parentId);\n if (parentId != null && parentIndex === -1)\n return { forkParentId: parentId, transcripts: [], truncated: [] };\n\n let start = parentIndex + 1;\n while (start > 0 && isTranscriptMessage(visible[start - 1]!)) start--;\n return {\n forkParentId: start > 0 ? (visible[start - 1]!.id ?? null) : null,\n transcripts: visible.slice(start, parentIndex + 1),\n truncated: visible.slice(parentIndex + 1),\n };\n };\n\n const stageUserMessage = (msg: AppendMessage, reconcileOnEcho = false) => {\n const stagedMessage = toStagedHumanMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n reconcileOnEcho,\n baseMessageCount: streamRef.current.messages.length,\n });\n const nextMessages = [...visibleMessagesRef.current, stagedMessage];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n return stagedMessage;\n };\n\n const removeStagedMessage = (id: string) => {\n if (!stagedMessagesRef.current.delete(id)) return;\n const nextMessages = visibleMessagesRef.current.filter(\n (message) => message.id !== id,\n );\n visibleMessagesRef.current = nextMessages;\n if (stagedMessagesRef.current.size === 0) {\n stagedBaseMessagesRef.current = null;\n setStagedMessages(null);\n } else {\n setStagedMessages(nextMessages);\n }\n };\n\n const extras = useMemo(\n () =>\n langChainExtras.provide({\n interrupt: stream.interrupt,\n interrupts: stream.interrupts,\n toolCalls: stream.toolCalls,\n subagents: stream.subagents,\n subgraphs: stream.subgraphs,\n stream,\n error: stream.error,\n submit: (values, submitOptions) => {\n const isResume = values == null || submitOptions?.command != null;\n return stream.submit(\n values,\n isResume ? withActiveRunConfig(submitOptions) : submitOptions,\n );\n },\n respond: (response, respondOptions) =>\n stream.respond(response, withActiveRunConfig(respondOptions)),\n respondAll: (responsesById, respondOptions) =>\n stream.respondAll(responsesById, withActiveRunConfig(respondOptions)),\n values: stream.values,\n messagesKey,\n }),\n [stream, messagesKey, withActiveRunConfig],\n );\n\n const runtime = useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n isRunning: stream.isLoading,\n isLoading: stream.isThreadLoading,\n messages: messagesWithTranscripts,\n adapters,\n extras,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: async (msg) => {\n if (!(msg.startRun ?? msg.role === \"user\")) {\n stageUserMessage(msg);\n return;\n }\n\n const stagedMessage = stageUserMessage(msg, true);\n const stagedMessageId = stagedMessage.id;\n setActiveRunConfig(msg.runConfig);\n const content = getMessageContent(msg);\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(\n streamRef.current.messages as readonly LangChainBaseMessage[],\n ).map(createToolCallCancellationStub)\n : [];\n // A null threadId is not a no-op for the SDK: it rebinds the controller\n // away from its self-created thread and forces a fresh one, so the\n // submit waits for initialization to produce an identity; core no\n // longer holds appends on that barrier.\n try {\n const { externalId } = await aui.threadListItem.initialize();\n const transcripts = getUnsentTranscripts();\n await submitCarryingTranscripts(transcripts, () =>\n streamRef.current.submit(\n {\n [messagesKey]: [\n ...cancellations,\n ...transcripts.map(toStagedMessageInput),\n {\n id: stagedMessageId,\n type: \"human\",\n content,\n },\n ],\n },\n {\n ...runConfigToSubmitOptions(msg.runConfig),\n ...(externalId != null ? { threadId: externalId } : {}),\n },\n ),\n );\n } catch (error) {\n removeStagedMessage(stagedMessageId);\n throw error;\n }\n },\n onVoiceTranscript: appendVoiceTranscript,\n onAddToolResult: async ({\n messageId,\n toolCallId,\n toolName,\n result,\n isError,\n artifact,\n }) => {\n const runConfig = runConfigByMessageIdRef.current.has(messageId)\n ? runConfigByMessageIdRef.current.get(messageId)\n : activeRunConfigRef.current;\n await stream.submit(\n {\n [messagesKey]: [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n ...(artifact !== undefined && { artifact }),\n status: isError ? \"error\" : \"success\",\n },\n ],\n },\n runConfig === undefined ? undefined : { config: runConfig },\n );\n },\n onReload: async (parentId, config) => {\n const stagedRun = getStagedRun(parentId);\n if (stagedRun) {\n if (\n config.sourceId &&\n stagedMessagesRef.current.get(config.sourceId)?.transcriptStatus\n )\n removeStagedMessage(config.sourceId);\n const promotedIds = new Set<string>();\n for (const message of stagedRun.messages) {\n if (\n !message.id ||\n stagedMessagesRef.current.get(message.id)?.transcriptStatus\n )\n continue;\n promotedIds.add(message.id);\n stagedMessagesRef.current.delete(message.id);\n }\n stagedBaseMessagesRef.current = null;\n if (stagedMessagesRef.current.size > 0) {\n const nextMessages = visibleMessagesRef.current.filter(\n (message) => !message.id || !promotedIds.has(message.id),\n );\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n } else {\n setStagedMessages(null);\n }\n const runConfig = config.runConfig ?? stagedRun.runConfig;\n setActiveRunConfig(runConfig);\n await submitCarryingTranscripts(stagedRun.messages, () =>\n stream.submit(\n {\n [messagesKey]: stagedRun.messages.map(toStagedMessageInput),\n },\n runConfigToSubmitOptions(runConfig),\n ),\n );\n return;\n }\n\n const threadId = externalId;\n if (!threadId || parentId == null) return;\n const s = streamRef.current;\n const fork = planForkTranscripts(parentId);\n const checkpointId = await resolveForkCheckpoint(\n s.client,\n threadId,\n s.messages as readonly LangChainBaseMessage[],\n fork.forkParentId,\n config.sourceId,\n s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),\n messagesKey,\n );\n if (!checkpointId) return;\n dropTranscripts(fork.truncated);\n setActiveRunConfig(config.runConfig);\n await submitCarryingTranscripts(fork.transcripts, () =>\n s.submit(\n fork.transcripts.length > 0\n ? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) }\n : null,\n {\n forkFrom: checkpointId,\n ...runConfigToSubmitOptions(config.runConfig),\n },\n ),\n );\n },\n onEdit: async (message) => {\n if (!(message.startRun ?? message.role === \"user\")) {\n const truncated = truncateLangChainBaseMessages(\n threadMessagesRef.current,\n message.parentId,\n );\n const stagedMessage = toStagedHumanMessage(message);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: message.runConfig,\n reconcileOnEcho: false,\n baseMessageCount: 0,\n });\n stagedBaseMessagesRef.current = truncated;\n const nextMessages = [...truncated, stagedMessage];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n return;\n }\n\n const threadId = externalId;\n if (!threadId) return;\n const s = streamRef.current;\n const fork = planForkTranscripts(message.parentId);\n const checkpointId = await resolveForkCheckpoint(\n s.client,\n threadId,\n s.messages as readonly LangChainBaseMessage[],\n fork.forkParentId,\n message.sourceId,\n s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),\n messagesKey,\n );\n if (!checkpointId) return;\n dropTranscripts(fork.truncated);\n const content = getMessageContent(message);\n setActiveRunConfig(message.runConfig);\n await submitCarryingTranscripts(fork.transcripts, () =>\n s.submit(\n {\n [messagesKey]: [\n ...fork.transcripts.map(toStagedMessageInput),\n { type: \"human\", content },\n ],\n },\n {\n forkFrom: checkpointId,\n ...runConfigToSubmitOptions(message.runConfig),\n },\n ),\n );\n },\n onCancel:\n unstable_allowCancellation !== false\n ? async () => {\n activeRunConfigRef.current = undefined;\n await stream.stop();\n }\n : undefined,\n });\n\n return runtime;\n};\n\n/**\n * Creates an assistant-ui runtime backed by LangChain's `useStream` hook.\n * Accepts the same options as `useStream` from `@langchain/react`, plus\n * `cloud` and `adapters`.\n *\n * @example\n * ```tsx\n * import { useStreamRuntime } from \"@assistant-ui/react-langchain\";\n * import { AssistantRuntimeProvider, Thread } from \"@assistant-ui/react\";\n *\n * function App() {\n * const runtime = useStreamRuntime({\n * assistantId: \"agent\",\n * apiUrl: \"http://localhost:2024\",\n * });\n *\n * return (\n * <AssistantRuntimeProvider runtime={runtime}>\n * <Thread />\n * </AssistantRuntimeProvider>\n * );\n * }\n * ```\n */\nexport const useStreamRuntime = (rawOptions: UseStreamRuntimeOptions) => {\n const {\n cloud,\n unstable_threadListAdapter,\n create,\n delete: deleteFn,\n onThreadIdChange,\n ...options\n } = rawOptions;\n\n const aui = useAui();\n const cloudAdapter = useCloudThreadListAdapter({\n sdk: LANGCHAIN_SDK,\n cloud,\n create: createCloudThreadListAdapterCreateFallback(\n create,\n aui.threadListItem,\n ),\n delete: deleteFn,\n });\n const adapter = unstable_threadListAdapter ?? cloudAdapter;\n\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useStreamThreadRuntime(options);\n },\n adapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useStreamRuntime.js","names":[],"sources":["../src/useStreamRuntime.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/store\" preserve=\"true\" />\n\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { AppendMessage, ToolExecutionStatus } from \"@assistant-ui/core\";\nimport {\n generateId,\n getExternalStoreMessages,\n pickExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport {\n createCloudThreadListAdapterCreateFallback,\n createToolCallCancellationStub,\n getThreadMessageText,\n scanPendingToolCalls,\n} from \"@assistant-ui/core/internal\";\nimport {\n useCloudThreadListAdapter,\n useExternalStoreRuntime,\n useExternalMessageConverter,\n useRemoteThreadListRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { STREAM_CONTROLLER, useChannel, useStream } from \"@langchain/react\";\nimport type {\n LangChainBaseMessage,\n LangChainToolCall,\n UIMessage,\n UseStreamRuntimeOptions,\n} from \"./types\";\nimport { getMessageModality, groupUIMessagesByParent } from \"./converter\";\nexport { groupUIMessagesByParent } from \"./converter\";\nimport {\n convertLangChainBaseMessage,\n getMessageContent,\n getMessageType,\n} from \"./convertMessages\";\nimport {\n attachSubagentTranscripts,\n createAttachMemo,\n} from \"./attachSubagentTranscripts\";\nimport { useSubagentTranscripts } from \"./useSubagentTranscripts\";\nimport {\n createUIFoldMemo,\n createUISnapshotMemo,\n foldUIUpdates,\n mergeUIMessages,\n reconcileUISnapshot,\n UI_CUSTOM_CHANNELS,\n} from \"./uiMessages\";\nimport { langChainExtras } from \"./runtimeExtras\";\nimport { resolveForkCheckpoint } from \"./resolveForkCheckpoint\";\nimport { useLangChainStreamingTiming } from \"./streamingTiming\";\nimport { LANGCHAIN_SDK } from \"./sdkIdentity\";\n\nexport const runConfigToSubmitOptions = (\n runConfig: AppendMessage[\"runConfig\"],\n) =>\n runConfig?.custom\n ? { config: { configurable: runConfig.custom } }\n : undefined;\n\ntype NormalizedRunConfigOptions = NonNullable<\n ReturnType<typeof runConfigToSubmitOptions>\n>;\n\nconst getPendingToolCalls = (\n messages: readonly LangChainBaseMessage[],\n): LangChainToolCall[] =>\n scanPendingToolCalls(\n messages,\n (message) => {\n const type = getMessageType(message);\n if (type === \"ai\") return { toolCalls: message.tool_calls ?? [] };\n if (type === \"tool\" && message.tool_call_id) {\n return { toolCallId: message.tool_call_id };\n }\n return undefined;\n },\n (toolCall) => toolCall.id,\n );\n\nconst toStagedHumanMessage = (\n msg: AppendMessage,\n id = generateId(),\n): LangChainBaseMessage & { id: string } => ({\n id,\n _getType: () => \"human\",\n content: getMessageContent(msg),\n});\n\nconst toStagedMessageInput = (message: LangChainBaseMessage) => ({\n id: message.id,\n type: getMessageType(message) === \"ai\" ? (\"ai\" as const) : (\"human\" as const),\n content: message.content,\n ...(message.additional_kwargs && {\n additional_kwargs: message.additional_kwargs,\n }),\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\nconst truncateLangChainBaseMessages = (\n threadMessages: readonly ThreadMessage[],\n parentId: string | null,\n): LangChainBaseMessage[] => {\n if (parentId === null) return [];\n const parentIndex = threadMessages.findIndex((m) => m.id === parentId);\n if (parentIndex === -1) return [];\n const truncated: LangChainBaseMessage[] = [];\n for (let i = 0; i <= parentIndex && i < threadMessages.length; i++) {\n truncated.push(\n ...getExternalStoreMessages<LangChainBaseMessage>(threadMessages[i]!),\n );\n }\n return truncated;\n};\n\ntype DistributiveOmit<T, K extends keyof any> = T extends unknown\n ? Omit<T, K>\n : never;\n\nconst useStreamThreadRuntime = (\n options: DistributiveOmit<\n UseStreamRuntimeOptions,\n \"cloud\" | \"unstable_threadListAdapter\" | \"create\" | \"delete\"\n >,\n) => {\n const { adapters, autoCancelPendingToolCalls, unstable_allowCancellation } =\n options;\n const aui = useAui();\n const messagesKey = options.messagesKey ?? \"messages\";\n const uiStateKey = options.uiStateKey ?? \"ui\";\n\n const externalId = useAuiState((s) => s.threadListItem.externalId) as\n | string\n | null;\n // Object.assign preserves the discriminated transport union; object spread\n // collapses its arms and no longer satisfies UseStreamOptions.\n const stream = useStream(\n Object.assign({}, options, { threadId: externalId }),\n );\n const [stagedMessages, setStagedMessages] = useState<\n LangChainBaseMessage[] | null\n >(null);\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n const effectiveIsRunning = stream.isLoading || hasExecutingTools;\n\n const [uiSnapshotMemo] = useState(createUISnapshotMemo);\n const uiStateValue = reconcileUISnapshot(\n stream.values[uiStateKey],\n uiSnapshotMemo,\n );\n\n const customEvents = useChannel(stream, UI_CUSTOM_CHANNELS);\n const [uiFoldMemo] = useState(createUIFoldMemo);\n const liveUiMessages = useMemo(\n () => foldUIUpdates(customEvents, uiFoldMemo),\n [customEvents, uiFoldMemo],\n );\n\n const mergedUiMessages = useMemo(\n () => mergeUIMessages(liveUiMessages, uiStateValue),\n [liveUiMessages, uiStateValue],\n );\n\n const uiMessagesByParent = useMemo(\n () => groupUIMessagesByParent<UIMessage>(mergedUiMessages),\n [mergedUiMessages],\n );\n\n const visibleMessages =\n stagedMessages ?? (stream.messages as LangChainBaseMessage[]);\n\n const messageTiming = useLangChainStreamingTiming(\n visibleMessages,\n effectiveIsRunning,\n );\n\n const subagentTranscripts = useSubagentTranscripts(\n stream,\n uiMessagesByParent,\n );\n\n const convertWithUI = useMemo<\n useExternalMessageConverter.Callback<LangChainBaseMessage>\n >(\n () => (message, metadata) =>\n convertLangChainBaseMessage(message, {\n ...metadata,\n uiMessagesByParent,\n messageTiming,\n }),\n [uiMessagesByParent, messageTiming],\n );\n\n const threadMessages = useExternalMessageConverter({\n callback: convertWithUI,\n messages: visibleMessages,\n isRunning: effectiveIsRunning,\n });\n const [memo] = useState(createAttachMemo);\n const messagesWithTranscripts = useMemo(\n () => attachSubagentTranscripts(threadMessages, subagentTranscripts, memo),\n [threadMessages, subagentTranscripts, memo],\n );\n\n const streamRef = useRef(stream);\n useInsertionEffect(() => {\n streamRef.current = stream;\n }, [stream]);\n\n const activeRunConfigRef = useRef<\n NormalizedRunConfigOptions[\"config\"] | undefined\n >(undefined);\n const runConfigByMessageIdRef = useRef(\n new Map<string, NormalizedRunConfigOptions[\"config\"] | undefined>(),\n );\n const activeThreadIdRef = useRef(externalId);\n const setActiveRunConfig = useCallback(\n (runConfig: AppendMessage[\"runConfig\"]) => {\n activeRunConfigRef.current = runConfigToSubmitOptions(runConfig)?.config;\n },\n [],\n );\n const withActiveRunConfig = useCallback(\n (submitOptions?: Record<string, unknown>) => {\n if (submitOptions && \"config\" in submitOptions) return submitOptions;\n if (activeRunConfigRef.current === undefined) return submitOptions;\n return { ...submitOptions, config: activeRunConfigRef.current };\n },\n [],\n );\n\n useEffect(() => {\n if (\n activeThreadIdRef.current !== null &&\n activeThreadIdRef.current !== externalId\n ) {\n activeRunConfigRef.current = undefined;\n runConfigByMessageIdRef.current.clear();\n }\n activeThreadIdRef.current = externalId;\n }, [externalId]);\n\n useEffect(() => {\n const messages = stream.messages as readonly LangChainBaseMessage[];\n const owned = runConfigByMessageIdRef.current;\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages.at(i);\n if (\n !message?.id ||\n getMessageType(message) !== \"ai\" ||\n !message.tool_calls?.length\n ) {\n continue;\n }\n if (owned.has(message.id)) return;\n break;\n }\n for (const message of messages) {\n if (\n !message.id ||\n getMessageType(message) !== \"ai\" ||\n !message.tool_calls?.length ||\n owned.has(message.id)\n ) {\n continue;\n }\n owned.set(message.id, activeRunConfigRef.current);\n }\n }, [stream.messages]);\n\n const visibleMessagesRef = useRef(visibleMessages);\n useInsertionEffect(() => {\n visibleMessagesRef.current = visibleMessages;\n }, [visibleMessages]);\n\n const threadMessagesRef = useRef(messagesWithTranscripts);\n useInsertionEffect(() => {\n threadMessagesRef.current = messagesWithTranscripts;\n }, [messagesWithTranscripts]);\n\n const stagedMessagesRef = useRef(\n new Map<\n string,\n {\n message: LangChainBaseMessage & { id: string };\n runConfig: AppendMessage[\"runConfig\"];\n reconcileOnEcho: boolean;\n baseMessageCount: number;\n transcriptStatus?: \"unsent\" | \"sent\";\n }\n >(),\n );\n const stagedBaseMessagesRef = useRef<LangChainBaseMessage[] | null>(null);\n useEffect(() => {\n if (stagedMessagesRef.current.size === 0) return;\n\n // Staged edits must keep their truncated base while stream updates arrive before promotion.\n const baseMessages =\n stagedBaseMessagesRef.current ??\n (stream.messages as LangChainBaseMessage[]);\n const remainingStagedMessages: LangChainBaseMessage[] = [];\n const matchedBaseMessageIndexes = new Set<number>();\n const visibleStagedIds = new Set(\n visibleMessagesRef.current.flatMap((m) => (m.id ? [m.id] : [])),\n );\n for (const [id, staged] of stagedMessagesRef.current) {\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) stagedMessagesRef.current.delete(id);\n else remainingStagedMessages.push(staged.message);\n }\n\n if (remainingStagedMessages.length === 0) {\n stagedBaseMessagesRef.current = null;\n visibleMessagesRef.current = baseMessages;\n // Reconciling against the upstream stream mutates the staged refs above,\n // which cannot happen during render.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setStagedMessages(null);\n return;\n }\n\n const nextMessages = [...baseMessages, ...remainingStagedMessages];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n }, [stream.messages]);\n\n const getStagedRun = (parentId: string | null) => {\n const parent = parentId\n ? stagedMessagesRef.current.get(parentId)\n : undefined;\n if (!parent || parent.transcriptStatus === \"sent\") return null;\n\n const staged: LangChainBaseMessage[] = [];\n for (const message of visibleMessagesRef.current) {\n const entry = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (entry && entry.transcriptStatus !== \"sent\") {\n staged.push(entry.message);\n }\n if (message.id === parentId) break;\n }\n\n return {\n messages: staged,\n runConfig: parent.runConfig,\n };\n };\n\n const appendVoiceTranscript = (message: ThreadMessage) => {\n const transcript = {\n id: message.id,\n _getType: () => (message.role === \"assistant\" ? \"ai\" : \"human\"),\n content: getThreadMessageText(message),\n ...(message.metadata.modality && {\n additional_kwargs: { modality: message.metadata.modality },\n }),\n };\n stagedMessagesRef.current.set(transcript.id, {\n message: transcript,\n runConfig: undefined,\n reconcileOnEcho: false,\n baseMessageCount: streamRef.current.messages.length,\n transcriptStatus: \"unsent\",\n });\n const nextMessages = [...visibleMessagesRef.current, transcript];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n };\n\n const getUnsentTranscripts = () =>\n visibleMessagesRef.current.filter(\n (message) =>\n message.id !== undefined &&\n stagedMessagesRef.current.get(message.id)?.transcriptStatus ===\n \"unsent\",\n );\n\n const setTranscriptStatus = (\n messages: readonly LangChainBaseMessage[],\n status: \"unsent\" | \"sent\",\n ) => {\n for (const message of messages) {\n const staged = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (staged?.transcriptStatus) staged.transcriptStatus = status;\n }\n };\n\n // Reserving before the submit keeps an overlapping submit from carrying the\n // same transcript; a failed submit hands it back to the next run.\n const submitCarryingTranscripts = async (\n transcripts: readonly LangChainBaseMessage[],\n submit: () => Promise<void>,\n ) => {\n setTranscriptStatus(transcripts, \"sent\");\n try {\n await submit();\n } catch (error) {\n setTranscriptStatus(transcripts, \"unsent\");\n throw error;\n }\n };\n\n const dropTranscripts = (messages: readonly LangChainBaseMessage[]) => {\n for (const message of messages) {\n if (\n message.id &&\n stagedMessagesRef.current.get(message.id)?.transcriptStatus\n )\n removeStagedMessage(message.id);\n }\n };\n\n const isTranscriptMessage = (message: LangChainBaseMessage) => {\n const staged = message.id\n ? stagedMessagesRef.current.get(message.id)\n : undefined;\n if (staged?.transcriptStatus === \"unsent\") return true;\n const type = getMessageType(message);\n return (\n (type === \"human\" || type === \"ai\") &&\n getMessageModality(message.additional_kwargs) !== undefined\n );\n };\n\n // A transcript reaches the graph in the same input as the message after it, so\n // no checkpoint ends at one; a fork starts before the trailing transcripts.\n const planForkTranscripts = (parentId: string | null) => {\n const visible = visibleMessagesRef.current;\n const parentIndex =\n parentId == null ? -1 : visible.findIndex((m) => m.id === parentId);\n if (parentId != null && parentIndex === -1)\n return { forkParentId: parentId, transcripts: [], truncated: [] };\n\n let start = parentIndex + 1;\n while (start > 0 && isTranscriptMessage(visible[start - 1]!)) start--;\n return {\n forkParentId: start > 0 ? (visible[start - 1]!.id ?? null) : null,\n transcripts: visible.slice(start, parentIndex + 1),\n truncated: visible.slice(parentIndex + 1),\n };\n };\n\n const stageUserMessage = (msg: AppendMessage, reconcileOnEcho = false) => {\n const stagedMessage = toStagedHumanMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n reconcileOnEcho,\n baseMessageCount: streamRef.current.messages.length,\n });\n const nextMessages = [...visibleMessagesRef.current, stagedMessage];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n return stagedMessage;\n };\n\n const removeStagedMessage = (id: string) => {\n if (!stagedMessagesRef.current.delete(id)) return;\n const nextMessages = visibleMessagesRef.current.filter(\n (message) => message.id !== id,\n );\n visibleMessagesRef.current = nextMessages;\n if (stagedMessagesRef.current.size === 0) {\n stagedBaseMessagesRef.current = null;\n setStagedMessages(null);\n } else {\n setStagedMessages(nextMessages);\n }\n };\n\n const extras = useMemo(\n () =>\n langChainExtras.provide({\n interrupt: stream.interrupt,\n interrupts: stream.interrupts,\n toolCalls: stream.toolCalls,\n subagents: stream.subagents,\n subgraphs: stream.subgraphs,\n stream,\n error: stream.error,\n submit: (values, submitOptions) => {\n const isResume = values == null || submitOptions?.command != null;\n return stream.submit(\n values,\n isResume ? withActiveRunConfig(submitOptions) : submitOptions,\n );\n },\n respond: (response, respondOptions) =>\n stream.respond(response, withActiveRunConfig(respondOptions)),\n respondAll: (responsesById, respondOptions) =>\n stream.respondAll(responsesById, withActiveRunConfig(respondOptions)),\n values: stream.values,\n messagesKey,\n }),\n [stream, messagesKey, withActiveRunConfig],\n );\n\n const runtime = useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n isRunning: stream.isLoading,\n isLoading: stream.isThreadLoading,\n messages: messagesWithTranscripts,\n adapters,\n extras,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: async (msg) => {\n if (!(msg.startRun ?? msg.role === \"user\")) {\n stageUserMessage(msg);\n return;\n }\n\n const stagedMessage = stageUserMessage(msg, true);\n const stagedMessageId = stagedMessage.id;\n setActiveRunConfig(msg.runConfig);\n const content = getMessageContent(msg);\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(\n streamRef.current.messages as readonly LangChainBaseMessage[],\n )\n // LangChain rejects a tool message without a tool_call_id.\n .filter((toolCall) => toolCall.id)\n .map(createToolCallCancellationStub)\n : [];\n // A null threadId is not a no-op for the SDK: it rebinds the controller\n // away from its self-created thread and forces a fresh one, so the\n // submit waits for initialization to produce an identity; core no\n // longer holds appends on that barrier.\n try {\n const { externalId } = await aui.threadListItem.initialize();\n const transcripts = getUnsentTranscripts();\n await submitCarryingTranscripts(transcripts, () =>\n streamRef.current.submit(\n {\n [messagesKey]: [\n ...cancellations,\n ...transcripts.map(toStagedMessageInput),\n {\n id: stagedMessageId,\n type: \"human\",\n content,\n },\n ],\n },\n {\n ...runConfigToSubmitOptions(msg.runConfig),\n ...(externalId != null ? { threadId: externalId } : {}),\n },\n ),\n );\n } catch (error) {\n removeStagedMessage(stagedMessageId);\n throw error;\n }\n },\n onVoiceTranscript: appendVoiceTranscript,\n onAddToolResult: async ({\n messageId,\n toolCallId,\n toolName,\n result,\n isError,\n artifact,\n }) => {\n const runConfig = runConfigByMessageIdRef.current.has(messageId)\n ? runConfigByMessageIdRef.current.get(messageId)\n : activeRunConfigRef.current;\n await stream.submit(\n {\n [messagesKey]: [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n ...(artifact !== undefined && { artifact }),\n status: isError ? \"error\" : \"success\",\n },\n ],\n },\n runConfig === undefined ? undefined : { config: runConfig },\n );\n },\n onReload: async (parentId, config) => {\n const stagedRun = getStagedRun(parentId);\n if (stagedRun) {\n if (\n config.sourceId &&\n stagedMessagesRef.current.get(config.sourceId)?.transcriptStatus\n )\n removeStagedMessage(config.sourceId);\n const promotedIds = new Set<string>();\n for (const message of stagedRun.messages) {\n if (\n !message.id ||\n stagedMessagesRef.current.get(message.id)?.transcriptStatus\n )\n continue;\n promotedIds.add(message.id);\n stagedMessagesRef.current.delete(message.id);\n }\n stagedBaseMessagesRef.current = null;\n if (stagedMessagesRef.current.size > 0) {\n const nextMessages = visibleMessagesRef.current.filter(\n (message) => !message.id || !promotedIds.has(message.id),\n );\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n } else {\n setStagedMessages(null);\n }\n const runConfig = config.runConfig ?? stagedRun.runConfig;\n setActiveRunConfig(runConfig);\n await submitCarryingTranscripts(stagedRun.messages, () =>\n stream.submit(\n {\n [messagesKey]: stagedRun.messages.map(toStagedMessageInput),\n },\n runConfigToSubmitOptions(runConfig),\n ),\n );\n return;\n }\n\n const threadId = externalId;\n if (!threadId || parentId == null) return;\n const s = streamRef.current;\n const fork = planForkTranscripts(parentId);\n const checkpointId = await resolveForkCheckpoint(\n s.client,\n threadId,\n s.messages as readonly LangChainBaseMessage[],\n fork.forkParentId,\n config.sourceId,\n s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),\n messagesKey,\n );\n if (!checkpointId) return;\n dropTranscripts(fork.truncated);\n setActiveRunConfig(config.runConfig);\n await submitCarryingTranscripts(fork.transcripts, () =>\n s.submit(\n fork.transcripts.length > 0\n ? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) }\n : null,\n {\n forkFrom: checkpointId,\n ...runConfigToSubmitOptions(config.runConfig),\n },\n ),\n );\n },\n onEdit: async (message) => {\n if (!(message.startRun ?? message.role === \"user\")) {\n const truncated = truncateLangChainBaseMessages(\n threadMessagesRef.current,\n message.parentId,\n );\n const stagedMessage = toStagedHumanMessage(message);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: message.runConfig,\n reconcileOnEcho: false,\n baseMessageCount: 0,\n });\n stagedBaseMessagesRef.current = truncated;\n const nextMessages = [...truncated, stagedMessage];\n visibleMessagesRef.current = nextMessages;\n setStagedMessages(nextMessages);\n return;\n }\n\n const threadId = externalId;\n if (!threadId) return;\n const s = streamRef.current;\n const fork = planForkTranscripts(message.parentId);\n const checkpointId = await resolveForkCheckpoint(\n s.client,\n threadId,\n s.messages as readonly LangChainBaseMessage[],\n fork.forkParentId,\n message.sourceId,\n s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),\n messagesKey,\n );\n if (!checkpointId) return;\n dropTranscripts(fork.truncated);\n const content = getMessageContent(message);\n setActiveRunConfig(message.runConfig);\n await submitCarryingTranscripts(fork.transcripts, () =>\n s.submit(\n {\n [messagesKey]: [\n ...fork.transcripts.map(toStagedMessageInput),\n { type: \"human\", content },\n ],\n },\n {\n forkFrom: checkpointId,\n ...runConfigToSubmitOptions(message.runConfig),\n },\n ),\n );\n },\n onCancel:\n unstable_allowCancellation !== false\n ? async () => {\n activeRunConfigRef.current = undefined;\n await stream.stop();\n }\n : undefined,\n });\n\n return runtime;\n};\n\n/**\n * Creates an assistant-ui runtime backed by LangChain's `useStream` hook.\n * Accepts the same options as `useStream` from `@langchain/react`, plus\n * `cloud` and `adapters`.\n *\n * @example\n * ```tsx\n * import { useStreamRuntime } from \"@assistant-ui/react-langchain\";\n * import { AssistantRuntimeProvider, Thread } from \"@assistant-ui/react\";\n *\n * function App() {\n * const runtime = useStreamRuntime({\n * assistantId: \"agent\",\n * apiUrl: \"http://localhost:2024\",\n * });\n *\n * return (\n * <AssistantRuntimeProvider runtime={runtime}>\n * <Thread />\n * </AssistantRuntimeProvider>\n * );\n * }\n * ```\n */\nexport const useStreamRuntime = (rawOptions: UseStreamRuntimeOptions) => {\n const {\n cloud,\n unstable_threadListAdapter,\n create,\n delete: deleteFn,\n onThreadIdChange,\n ...options\n } = rawOptions;\n\n const aui = useAui();\n const cloudAdapter = useCloudThreadListAdapter({\n sdk: LANGCHAIN_SDK,\n cloud,\n create: createCloudThreadListAdapterCreateFallback(\n create,\n aui.threadListItem,\n ),\n delete: deleteFn,\n });\n const adapter = unstable_threadListAdapter ?? cloudAdapter;\n\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useStreamThreadRuntime(options);\n },\n adapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSubagentTranscripts.d.ts","sourceRoot":"","sources":["../src/useSubagentTranscripts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSubagentTranscripts.d.ts","sourceRoot":"","sources":["../src/useSubagentTranscripts.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIrE,OAAO,EAIL,KAAK,kBAAkB,EACxB,uCAAoC;AAKrC,OAAO,KAAK,EAAwB,SAAS,EAAE,mBAAgB;AAS/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AA6crC,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,oBAAoB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,KAC3C,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAmBxC,CAAC"}
|
|
@@ -9,6 +9,7 @@ import { stepStreamingTiming } from "@assistant-ui/core";
|
|
|
9
9
|
import { convertExternalMessages, createExternalMessageConversionCache } from "@assistant-ui/core/react";
|
|
10
10
|
import { useEffect, useRef, useSyncExternalStore } from "react";
|
|
11
11
|
import { STREAM_CONTROLLER } from "@langchain/react";
|
|
12
|
+
import { useReplaySafeEffect } from "@assistant-ui/store/internal";
|
|
12
13
|
import { channelProjection } from "@langchain/langgraph-sdk/stream";
|
|
13
14
|
//#region src/useSubagentTranscripts.ts
|
|
14
15
|
const MAX_SUBAGENT_DEPTH = 16;
|
|
@@ -257,7 +258,7 @@ const useSubagentTranscripts = (stream, uiMessagesByParent) => {
|
|
|
257
258
|
stream.subagents,
|
|
258
259
|
uiMessagesByParent
|
|
259
260
|
]);
|
|
260
|
-
|
|
261
|
+
useReplaySafeEffect(() => () => source.dispose(), [source]);
|
|
261
262
|
return useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
262
263
|
};
|
|
263
264
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSubagentTranscripts.js","names":[],"sources":["../src/useSubagentTranscripts.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport {\n stepStreamingTiming,\n type MessageTiming,\n type StreamingTimingState,\n type ThreadMessage,\n type ToolCallTiming,\n} from \"@assistant-ui/core\";\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n type ExternalMessageConversionCache,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport { STREAM_CONTROLLER, type AnyStream } from \"@langchain/react\";\nimport type { BaseMessage } from \"@langchain/core/messages\";\nimport { channelProjection, type Event } from \"@langchain/langgraph-sdk/stream\";\nimport type { SubagentDiscoverySnapshot } from \"@langchain/react\";\nimport {\n attachSubagentTranscripts,\n type AttachMemo,\n createAttachMemo,\n type SubagentTranscript,\n} from \"./attachSubagentTranscripts\";\nimport { convertLangChainBaseMessage } from \"./convertMessages\";\nimport { groupUIMessagesByParent } from \"./converter\";\nimport { langChainStreamingTimingAccessors } from \"./streamingTiming\";\nimport { subagentMessagesProjection } from \"./subagentMessagesProjection\";\nimport type { LangChainBaseMessage, UIMessage } from \"./types\";\nimport {\n createUIFoldMemo,\n foldUIUpdates,\n mergeUIMessages,\n UI_CUSTOM_CHANNELS,\n type UIFoldMemo,\n} from \"./uiMessages\";\n\nexport const MAX_SUBAGENT_DEPTH = 16;\n\nconst ROOT_UI_CHANNEL_DEPTH = 1;\n\nconst TRANSCRIPT_METADATA = {};\n\nconst NO_UI_MESSAGES: readonly UIMessage[] = [];\n\nconst NO_MESSAGE_TIMING: Record<string, MessageTiming> = {};\n\ntype ProjectionStore<T> = {\n getSnapshot(): T;\n subscribe(listener: () => void): () => void;\n};\n\ntype ProjectionResource = {\n snapshot: SubagentDiscoverySnapshot;\n namespace: readonly string[];\n store: ProjectionStore<BaseMessage[]>;\n uiStore: ProjectionStore<readonly Event[]> | undefined;\n dispose: () => void;\n storeSnapshot: BaseMessage[] | undefined;\n status: SubagentDiscoverySnapshot[\"status\"] | undefined;\n uiFoldMemo: UIFoldMemo;\n localUiMessages: readonly UIMessage[];\n rootUiMessagesByParent: Map<string, UIMessage[]>;\n uiMessagesByParent: Map<string, UIMessage[]>;\n timingState: StreamingTimingState | null;\n messageTiming: Record<string, MessageTiming>;\n convertedMessageTiming: Record<string, MessageTiming> | undefined;\n convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;\n uiMessages: readonly UIMessage[];\n converted: readonly ThreadMessage[] | undefined;\n childTranscripts: ReadonlyMap<string, SubagentTranscript> | undefined;\n transcript: readonly ThreadMessage[] | undefined;\n timing: ToolCallTiming | undefined;\n entry: SubagentTranscript | undefined;\n memo: AttachMemo;\n cache: ExternalMessageConversionCache;\n};\n\ntype MeasuredTiming = {\n timing: ToolCallTiming | undefined;\n timingState: StreamingTimingState | null;\n messageTiming: Record<string, MessageTiming>;\n};\n\ntype NamespaceRequest = {\n id: string;\n attempts: number;\n pending: boolean;\n retryQueued: boolean;\n status: SubagentDiscoverySnapshot[\"status\"];\n};\n\ntype SubagentTranscriptSource = {\n resources: Map<string, ProjectionResource>;\n namespaceRequests: Map<string, NamespaceRequest>;\n snapshot: ReadonlyMap<string, SubagentTranscript>;\n listeners: Set<() => void>;\n controller: AnyStream[typeof STREAM_CONTROLLER] | undefined;\n uiMessagesByParent: Map<string, UIMessage[]>;\n subscribe(listener: () => void): () => void;\n getSnapshot(): ReadonlyMap<string, SubagentTranscript>;\n reconcile(\n controller: AnyStream[typeof STREAM_CONTROLLER],\n subagents: AnyStream[\"subagents\"],\n uiMessagesByParent: Map<string, UIMessage[]>,\n ): void;\n dispose(): void;\n};\n\nconst sameNamespace = (a: readonly string[], b: readonly string[]) =>\n a.length === b.length && a.every((segment, index) => segment === b[index]);\n\nconst needsNamespaceResolution = (snapshot: SubagentDiscoverySnapshot) =>\n snapshot.namespace.length === 1 &&\n snapshot.namespace[0] === `tools:${snapshot.id}`;\n\nconst requestSubagentNamespace = (\n source: SubagentTranscriptSource,\n controller: AnyStream[typeof STREAM_CONTROLLER],\n request: NamespaceRequest,\n) => {\n request.attempts += 1;\n request.pending = true;\n request.retryQueued = false;\n void controller\n .resolveSubagentNamespace(request.id)\n .catch(() => {})\n .finally(() => {\n if (\n source.controller !== controller ||\n source.namespaceRequests.get(request.id) !== request\n )\n return;\n\n request.pending = false;\n if (\n !request.retryQueued &&\n request.attempts === 1 &&\n request.status !== \"running\"\n ) {\n request.retryQueued = true;\n }\n if (!request.retryQueued || request.attempts >= 2) {\n request.retryQueued = false;\n return;\n }\n requestSubagentNamespace(source, controller, request);\n });\n};\n\nconst sameTranscriptEntries = (\n a: ReadonlyMap<string, SubagentTranscript> | undefined,\n b: ReadonlyMap<string, SubagentTranscript>,\n) =>\n a?.size === b.size &&\n [...b].every(([id, transcript]) => a.get(id) === transcript);\n\nconst collectUIMessages = (\n messages: readonly BaseMessage[],\n uiMessagesByParent: Map<string, UIMessage[]>,\n) => {\n const collected: UIMessage[] = [];\n if (uiMessagesByParent.size === 0) return collected;\n for (const message of messages) {\n const uiMessages = message.id && uiMessagesByParent.get(message.id);\n if (uiMessages) collected.push(...uiMessages);\n }\n return collected;\n};\n\nconst sameUIMessages = (a: readonly UIMessage[], b: readonly UIMessage[]) =>\n a.length === b.length && a.every((ui, index) => ui === b[index]);\n\nconst foldLocalUIMessages = (resource: ProjectionResource) => {\n const events = resource.uiStore?.getSnapshot();\n if (events === undefined) return NO_UI_MESSAGES;\n return foldUIUpdates(events, resource.uiFoldMemo);\n};\n\nconst mergeLocalUIMessages = (\n uiMessagesByParent: Map<string, UIMessage[]>,\n localUiMessages: readonly UIMessage[],\n) => {\n const merged = new Map(uiMessagesByParent);\n for (const [parentId, messages] of groupUIMessagesByParent<UIMessage>(\n localUiMessages,\n )) {\n merged.set(parentId, mergeUIMessages(messages, merged.get(parentId)));\n }\n return merged;\n};\n\nconst createConverter =\n (\n uiMessagesByParent: Map<string, UIMessage[]>,\n messageTiming: Record<string, MessageTiming>,\n ): useExternalMessageConverter.Callback<LangChainBaseMessage> =>\n (message, metadata) =>\n convertLangChainBaseMessage(message, {\n ...metadata,\n uiMessagesByParent,\n messageTiming,\n });\n\nconst updateTiming = (resource: ProjectionResource) => {\n if (resource.timing === undefined) return;\n const startedAt = resource.snapshot.startedAt.getTime();\n const completedAt = resource.snapshot.completedAt?.getTime();\n if (\n resource.timing.startedAt === startedAt &&\n resource.timing.completedAt === completedAt\n )\n return;\n resource.timing = {\n startedAt,\n ...(completedAt !== undefined && { completedAt }),\n };\n};\n\nconst createSubagentTranscriptSource = (): SubagentTranscriptSource => {\n const uiMessagesByParent = new Map<string, UIMessage[]>();\n const source: SubagentTranscriptSource = {\n resources: new Map(),\n namespaceRequests: new Map(),\n snapshot: new Map(),\n listeners: new Set(),\n controller: undefined,\n uiMessagesByParent,\n subscribe(listener) {\n source.listeners.add(listener);\n return () => source.listeners.delete(listener);\n },\n getSnapshot() {\n return source.snapshot;\n },\n reconcile(controller, subagents, uiMessagesByParent) {\n source.uiMessagesByParent = uiMessagesByParent;\n // A resource is rebuilt from scratch when its namespace resolves, which\n // routinely happens after the subagent finished. Its timing was measured\n // while the client watched the task run and cannot be measured again.\n const rebound = new Map<string, MeasuredTiming>();\n\n if (source.controller !== controller) {\n source.dispose();\n source.controller = controller;\n }\n\n for (const id of source.namespaceRequests.keys()) {\n if (!subagents.has(id)) source.namespaceRequests.delete(id);\n }\n\n for (const [id, resource] of source.resources) {\n const snapshot = subagents.get(id);\n if (\n !snapshot ||\n snapshot.depth > MAX_SUBAGENT_DEPTH ||\n !sameNamespace(resource.namespace, snapshot.namespace)\n ) {\n if (snapshot && snapshot.depth <= MAX_SUBAGENT_DEPTH)\n rebound.set(id, {\n timing: resource.timing,\n timingState: resource.timingState,\n messageTiming: resource.messageTiming,\n });\n resource.dispose();\n source.resources.delete(id);\n continue;\n }\n resource.snapshot = snapshot;\n }\n\n for (const snapshot of subagents.values()) {\n if (snapshot.depth > MAX_SUBAGENT_DEPTH) continue;\n if (!needsNamespaceResolution(snapshot)) {\n source.namespaceRequests.delete(snapshot.id);\n } else {\n const request = source.namespaceRequests.get(snapshot.id);\n if (!request) {\n const nextRequest: NamespaceRequest = {\n id: snapshot.id,\n attempts: 0,\n pending: false,\n retryQueued: false,\n status: snapshot.status,\n };\n source.namespaceRequests.set(snapshot.id, nextRequest);\n requestSubagentNamespace(source, controller, nextRequest);\n } else if (request.status !== snapshot.status) {\n request.status = snapshot.status;\n if (request.pending) {\n request.retryQueued = request.attempts < 2;\n } else if (request.attempts < 2) {\n requestSubagentNamespace(source, controller, request);\n }\n }\n }\n if (source.resources.has(snapshot.id)) continue;\n const acquired = controller.registry.acquire(\n subagentMessagesProjection(snapshot.namespace),\n );\n const acquiredUI =\n snapshot.namespace.length > ROOT_UI_CHANNEL_DEPTH\n ? controller.registry.acquire(\n channelProjection(UI_CUSTOM_CHANNELS, snapshot.namespace),\n )\n : undefined;\n const resource: ProjectionResource = {\n snapshot,\n namespace: snapshot.namespace,\n store: acquired.store,\n uiStore: acquiredUI?.store,\n dispose: () => {},\n storeSnapshot: undefined,\n status: undefined,\n uiFoldMemo: createUIFoldMemo(),\n localUiMessages: NO_UI_MESSAGES,\n rootUiMessagesByParent: source.uiMessagesByParent,\n uiMessagesByParent: source.uiMessagesByParent,\n timingState: rebound.get(snapshot.id)?.timingState ?? null,\n messageTiming:\n rebound.get(snapshot.id)?.messageTiming ?? NO_MESSAGE_TIMING,\n convertedMessageTiming: undefined,\n convert: createConverter(\n source.uiMessagesByParent,\n NO_MESSAGE_TIMING,\n ),\n uiMessages: [],\n converted: undefined,\n childTranscripts: undefined,\n transcript: undefined,\n timing: rebound.has(snapshot.id)\n ? rebound.get(snapshot.id)!.timing\n : snapshot.status === \"running\"\n ? { startedAt: snapshot.startedAt.getTime() }\n : undefined,\n entry: undefined,\n memo: createAttachMemo(),\n cache: createExternalMessageConversionCache(),\n };\n const unsubscribe = resource.store.subscribe(() => rebuild());\n const unsubscribeUI = resource.uiStore?.subscribe(() => rebuild());\n resource.dispose = () => {\n unsubscribe();\n unsubscribeUI?.();\n acquired.release();\n acquiredUI?.release();\n };\n source.resources.set(snapshot.id, resource);\n }\n\n rebuild();\n },\n dispose() {\n for (const resource of source.resources.values()) resource.dispose();\n source.resources.clear();\n source.namespaceRequests.clear();\n source.snapshot = new Map();\n for (const listener of source.listeners) listener();\n },\n };\n\n const rebuild = () => {\n const { uiMessagesByParent } = source;\n const resources = [...source.resources.values()];\n const childrenByParent = new Map<string, ProjectionResource[]>();\n\n for (const resource of resources) {\n const parentId = resource.snapshot.parentId;\n if (parentId == null) continue;\n const children = childrenByParent.get(parentId);\n if (children) children.push(resource);\n else childrenByParent.set(parentId, [resource]);\n }\n\n const transcripts = new Map<string, SubagentTranscript>();\n let changed = source.snapshot.size !== resources.length;\n const built = new Set<string>();\n\n const build = (resource: ProjectionResource, depth: number) => {\n if (built.has(resource.snapshot.id)) return;\n built.add(resource.snapshot.id);\n const children =\n depth < MAX_SUBAGENT_DEPTH\n ? (childrenByParent.get(resource.snapshot.id) ?? [])\n : [];\n for (const child of children) build(child, depth + 1);\n const childTranscripts = new Map(\n children.flatMap((child) =>\n child.entry ? [[child.snapshot.id, child.entry] as const] : [],\n ),\n );\n const storeSnapshot = resource.store.getSnapshot();\n const status = resource.snapshot.status;\n const stepped = stepStreamingTiming(\n resource.timingState,\n storeSnapshot as LangChainBaseMessage[],\n status === \"running\",\n langChainStreamingTimingAccessors,\n undefined,\n );\n resource.timingState = stepped.state;\n if (Object.keys(stepped.timings).length > 0) {\n resource.messageTiming = {\n ...resource.messageTiming,\n ...stepped.timings,\n };\n }\n const localUiMessages = foldLocalUIMessages(resource);\n if (\n resource.localUiMessages !== localUiMessages ||\n resource.rootUiMessagesByParent !== uiMessagesByParent ||\n resource.convertedMessageTiming !== resource.messageTiming\n ) {\n resource.localUiMessages = localUiMessages;\n resource.rootUiMessagesByParent = uiMessagesByParent;\n resource.uiMessagesByParent =\n localUiMessages.length === 0\n ? uiMessagesByParent\n : mergeLocalUIMessages(uiMessagesByParent, localUiMessages);\n resource.convert = createConverter(\n resource.uiMessagesByParent,\n resource.messageTiming,\n );\n }\n const uiMessages = collectUIMessages(\n storeSnapshot,\n resource.uiMessagesByParent,\n );\n\n const conversionChanged =\n resource.converted === undefined ||\n resource.storeSnapshot !== storeSnapshot ||\n resource.status !== status ||\n resource.convertedMessageTiming !== resource.messageTiming ||\n !sameUIMessages(resource.uiMessages, uiMessages);\n if (conversionChanged) {\n resource.converted = convertExternalMessages(\n storeSnapshot as LangChainBaseMessage[],\n resource.convert,\n status === \"running\",\n TRANSCRIPT_METADATA,\n resource.cache,\n );\n resource.storeSnapshot = storeSnapshot;\n resource.status = status;\n resource.uiMessages = uiMessages;\n resource.convertedMessageTiming = resource.messageTiming;\n }\n\n if (\n resource.transcript === undefined ||\n conversionChanged ||\n !sameTranscriptEntries(resource.childTranscripts, childTranscripts)\n ) {\n const transcript = attachSubagentTranscripts(\n resource.converted!,\n childTranscripts,\n resource.memo,\n );\n changed ||= resource.transcript !== transcript;\n resource.transcript = transcript;\n resource.childTranscripts = childTranscripts;\n }\n\n updateTiming(resource);\n if (\n resource.entry === undefined ||\n resource.entry.messages !== resource.transcript ||\n resource.entry.timing !== resource.timing\n ) {\n resource.entry = {\n messages: resource.transcript!,\n ...(resource.timing && { timing: resource.timing }),\n };\n changed = true;\n }\n\n if (!source.snapshot.has(resource.snapshot.id)) changed = true;\n transcripts.set(resource.snapshot.id, resource.entry);\n };\n\n for (const resource of resources) {\n if (\n resource.snapshot.parentId == null ||\n !source.resources.has(resource.snapshot.parentId)\n )\n build(resource, 1);\n }\n for (const resource of resources) build(resource, MAX_SUBAGENT_DEPTH);\n\n if (!changed) return;\n source.snapshot = transcripts;\n for (const listener of source.listeners) listener();\n };\n\n return source;\n};\n\nexport const useSubagentTranscripts = (\n stream: AnyStream,\n uiMessagesByParent: Map<string, UIMessage[]>,\n): ReadonlyMap<string, SubagentTranscript> => {\n const sourceRef = useRef<SubagentTranscriptSource | undefined>(undefined);\n if (!sourceRef.current) {\n sourceRef.current = createSubagentTranscriptSource();\n }\n const source = sourceRef.current;\n const controller = stream[STREAM_CONTROLLER];\n\n useEffect(() => {\n source.reconcile(controller, stream.subagents, uiMessagesByParent);\n }, [controller, source, stream.subagents, uiMessagesByParent]);\n\n useEffect(() => () => source.dispose(), [source]);\n\n return useSyncExternalStore(\n source.subscribe,\n source.getSnapshot,\n source.getSnapshot,\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSubagentTranscripts.js","names":[],"sources":["../src/useSubagentTranscripts.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport {\n stepStreamingTiming,\n type MessageTiming,\n type StreamingTimingState,\n type ThreadMessage,\n type ToolCallTiming,\n} from \"@assistant-ui/core\";\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n type ExternalMessageConversionCache,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport { useReplaySafeEffect } from \"@assistant-ui/store/internal\";\nimport { STREAM_CONTROLLER, type AnyStream } from \"@langchain/react\";\nimport type { BaseMessage } from \"@langchain/core/messages\";\nimport { channelProjection, type Event } from \"@langchain/langgraph-sdk/stream\";\nimport type { SubagentDiscoverySnapshot } from \"@langchain/react\";\nimport {\n attachSubagentTranscripts,\n type AttachMemo,\n createAttachMemo,\n type SubagentTranscript,\n} from \"./attachSubagentTranscripts\";\nimport { convertLangChainBaseMessage } from \"./convertMessages\";\nimport { groupUIMessagesByParent } from \"./converter\";\nimport { langChainStreamingTimingAccessors } from \"./streamingTiming\";\nimport { subagentMessagesProjection } from \"./subagentMessagesProjection\";\nimport type { LangChainBaseMessage, UIMessage } from \"./types\";\nimport {\n createUIFoldMemo,\n foldUIUpdates,\n mergeUIMessages,\n UI_CUSTOM_CHANNELS,\n type UIFoldMemo,\n} from \"./uiMessages\";\n\nexport const MAX_SUBAGENT_DEPTH = 16;\n\nconst ROOT_UI_CHANNEL_DEPTH = 1;\n\nconst TRANSCRIPT_METADATA = {};\n\nconst NO_UI_MESSAGES: readonly UIMessage[] = [];\n\nconst NO_MESSAGE_TIMING: Record<string, MessageTiming> = {};\n\ntype ProjectionStore<T> = {\n getSnapshot(): T;\n subscribe(listener: () => void): () => void;\n};\n\ntype ProjectionResource = {\n snapshot: SubagentDiscoverySnapshot;\n namespace: readonly string[];\n store: ProjectionStore<BaseMessage[]>;\n uiStore: ProjectionStore<readonly Event[]> | undefined;\n dispose: () => void;\n storeSnapshot: BaseMessage[] | undefined;\n status: SubagentDiscoverySnapshot[\"status\"] | undefined;\n uiFoldMemo: UIFoldMemo;\n localUiMessages: readonly UIMessage[];\n rootUiMessagesByParent: Map<string, UIMessage[]>;\n uiMessagesByParent: Map<string, UIMessage[]>;\n timingState: StreamingTimingState | null;\n messageTiming: Record<string, MessageTiming>;\n convertedMessageTiming: Record<string, MessageTiming> | undefined;\n convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;\n uiMessages: readonly UIMessage[];\n converted: readonly ThreadMessage[] | undefined;\n childTranscripts: ReadonlyMap<string, SubagentTranscript> | undefined;\n transcript: readonly ThreadMessage[] | undefined;\n timing: ToolCallTiming | undefined;\n entry: SubagentTranscript | undefined;\n memo: AttachMemo;\n cache: ExternalMessageConversionCache;\n};\n\ntype MeasuredTiming = {\n timing: ToolCallTiming | undefined;\n timingState: StreamingTimingState | null;\n messageTiming: Record<string, MessageTiming>;\n};\n\ntype NamespaceRequest = {\n id: string;\n attempts: number;\n pending: boolean;\n retryQueued: boolean;\n status: SubagentDiscoverySnapshot[\"status\"];\n};\n\ntype SubagentTranscriptSource = {\n resources: Map<string, ProjectionResource>;\n namespaceRequests: Map<string, NamespaceRequest>;\n snapshot: ReadonlyMap<string, SubagentTranscript>;\n listeners: Set<() => void>;\n controller: AnyStream[typeof STREAM_CONTROLLER] | undefined;\n uiMessagesByParent: Map<string, UIMessage[]>;\n subscribe(listener: () => void): () => void;\n getSnapshot(): ReadonlyMap<string, SubagentTranscript>;\n reconcile(\n controller: AnyStream[typeof STREAM_CONTROLLER],\n subagents: AnyStream[\"subagents\"],\n uiMessagesByParent: Map<string, UIMessage[]>,\n ): void;\n dispose(): void;\n};\n\nconst sameNamespace = (a: readonly string[], b: readonly string[]) =>\n a.length === b.length && a.every((segment, index) => segment === b[index]);\n\nconst needsNamespaceResolution = (snapshot: SubagentDiscoverySnapshot) =>\n snapshot.namespace.length === 1 &&\n snapshot.namespace[0] === `tools:${snapshot.id}`;\n\nconst requestSubagentNamespace = (\n source: SubagentTranscriptSource,\n controller: AnyStream[typeof STREAM_CONTROLLER],\n request: NamespaceRequest,\n) => {\n request.attempts += 1;\n request.pending = true;\n request.retryQueued = false;\n void controller\n .resolveSubagentNamespace(request.id)\n .catch(() => {})\n .finally(() => {\n if (\n source.controller !== controller ||\n source.namespaceRequests.get(request.id) !== request\n )\n return;\n\n request.pending = false;\n if (\n !request.retryQueued &&\n request.attempts === 1 &&\n request.status !== \"running\"\n ) {\n request.retryQueued = true;\n }\n if (!request.retryQueued || request.attempts >= 2) {\n request.retryQueued = false;\n return;\n }\n requestSubagentNamespace(source, controller, request);\n });\n};\n\nconst sameTranscriptEntries = (\n a: ReadonlyMap<string, SubagentTranscript> | undefined,\n b: ReadonlyMap<string, SubagentTranscript>,\n) =>\n a?.size === b.size &&\n [...b].every(([id, transcript]) => a.get(id) === transcript);\n\nconst collectUIMessages = (\n messages: readonly BaseMessage[],\n uiMessagesByParent: Map<string, UIMessage[]>,\n) => {\n const collected: UIMessage[] = [];\n if (uiMessagesByParent.size === 0) return collected;\n for (const message of messages) {\n const uiMessages = message.id && uiMessagesByParent.get(message.id);\n if (uiMessages) collected.push(...uiMessages);\n }\n return collected;\n};\n\nconst sameUIMessages = (a: readonly UIMessage[], b: readonly UIMessage[]) =>\n a.length === b.length && a.every((ui, index) => ui === b[index]);\n\nconst foldLocalUIMessages = (resource: ProjectionResource) => {\n const events = resource.uiStore?.getSnapshot();\n if (events === undefined) return NO_UI_MESSAGES;\n return foldUIUpdates(events, resource.uiFoldMemo);\n};\n\nconst mergeLocalUIMessages = (\n uiMessagesByParent: Map<string, UIMessage[]>,\n localUiMessages: readonly UIMessage[],\n) => {\n const merged = new Map(uiMessagesByParent);\n for (const [parentId, messages] of groupUIMessagesByParent<UIMessage>(\n localUiMessages,\n )) {\n merged.set(parentId, mergeUIMessages(messages, merged.get(parentId)));\n }\n return merged;\n};\n\nconst createConverter =\n (\n uiMessagesByParent: Map<string, UIMessage[]>,\n messageTiming: Record<string, MessageTiming>,\n ): useExternalMessageConverter.Callback<LangChainBaseMessage> =>\n (message, metadata) =>\n convertLangChainBaseMessage(message, {\n ...metadata,\n uiMessagesByParent,\n messageTiming,\n });\n\nconst updateTiming = (resource: ProjectionResource) => {\n if (resource.timing === undefined) return;\n const startedAt = resource.snapshot.startedAt.getTime();\n const completedAt = resource.snapshot.completedAt?.getTime();\n if (\n resource.timing.startedAt === startedAt &&\n resource.timing.completedAt === completedAt\n )\n return;\n resource.timing = {\n startedAt,\n ...(completedAt !== undefined && { completedAt }),\n };\n};\n\nconst createSubagentTranscriptSource = (): SubagentTranscriptSource => {\n const uiMessagesByParent = new Map<string, UIMessage[]>();\n const source: SubagentTranscriptSource = {\n resources: new Map(),\n namespaceRequests: new Map(),\n snapshot: new Map(),\n listeners: new Set(),\n controller: undefined,\n uiMessagesByParent,\n subscribe(listener) {\n source.listeners.add(listener);\n return () => source.listeners.delete(listener);\n },\n getSnapshot() {\n return source.snapshot;\n },\n reconcile(controller, subagents, uiMessagesByParent) {\n source.uiMessagesByParent = uiMessagesByParent;\n // A resource is rebuilt from scratch when its namespace resolves, which\n // routinely happens after the subagent finished. Its timing was measured\n // while the client watched the task run and cannot be measured again.\n const rebound = new Map<string, MeasuredTiming>();\n\n if (source.controller !== controller) {\n source.dispose();\n source.controller = controller;\n }\n\n for (const id of source.namespaceRequests.keys()) {\n if (!subagents.has(id)) source.namespaceRequests.delete(id);\n }\n\n for (const [id, resource] of source.resources) {\n const snapshot = subagents.get(id);\n if (\n !snapshot ||\n snapshot.depth > MAX_SUBAGENT_DEPTH ||\n !sameNamespace(resource.namespace, snapshot.namespace)\n ) {\n if (snapshot && snapshot.depth <= MAX_SUBAGENT_DEPTH)\n rebound.set(id, {\n timing: resource.timing,\n timingState: resource.timingState,\n messageTiming: resource.messageTiming,\n });\n resource.dispose();\n source.resources.delete(id);\n continue;\n }\n resource.snapshot = snapshot;\n }\n\n for (const snapshot of subagents.values()) {\n if (snapshot.depth > MAX_SUBAGENT_DEPTH) continue;\n if (!needsNamespaceResolution(snapshot)) {\n source.namespaceRequests.delete(snapshot.id);\n } else {\n const request = source.namespaceRequests.get(snapshot.id);\n if (!request) {\n const nextRequest: NamespaceRequest = {\n id: snapshot.id,\n attempts: 0,\n pending: false,\n retryQueued: false,\n status: snapshot.status,\n };\n source.namespaceRequests.set(snapshot.id, nextRequest);\n requestSubagentNamespace(source, controller, nextRequest);\n } else if (request.status !== snapshot.status) {\n request.status = snapshot.status;\n if (request.pending) {\n request.retryQueued = request.attempts < 2;\n } else if (request.attempts < 2) {\n requestSubagentNamespace(source, controller, request);\n }\n }\n }\n if (source.resources.has(snapshot.id)) continue;\n const acquired = controller.registry.acquire(\n subagentMessagesProjection(snapshot.namespace),\n );\n const acquiredUI =\n snapshot.namespace.length > ROOT_UI_CHANNEL_DEPTH\n ? controller.registry.acquire(\n channelProjection(UI_CUSTOM_CHANNELS, snapshot.namespace),\n )\n : undefined;\n const resource: ProjectionResource = {\n snapshot,\n namespace: snapshot.namespace,\n store: acquired.store,\n uiStore: acquiredUI?.store,\n dispose: () => {},\n storeSnapshot: undefined,\n status: undefined,\n uiFoldMemo: createUIFoldMemo(),\n localUiMessages: NO_UI_MESSAGES,\n rootUiMessagesByParent: source.uiMessagesByParent,\n uiMessagesByParent: source.uiMessagesByParent,\n timingState: rebound.get(snapshot.id)?.timingState ?? null,\n messageTiming:\n rebound.get(snapshot.id)?.messageTiming ?? NO_MESSAGE_TIMING,\n convertedMessageTiming: undefined,\n convert: createConverter(\n source.uiMessagesByParent,\n NO_MESSAGE_TIMING,\n ),\n uiMessages: [],\n converted: undefined,\n childTranscripts: undefined,\n transcript: undefined,\n timing: rebound.has(snapshot.id)\n ? rebound.get(snapshot.id)!.timing\n : snapshot.status === \"running\"\n ? { startedAt: snapshot.startedAt.getTime() }\n : undefined,\n entry: undefined,\n memo: createAttachMemo(),\n cache: createExternalMessageConversionCache(),\n };\n const unsubscribe = resource.store.subscribe(() => rebuild());\n const unsubscribeUI = resource.uiStore?.subscribe(() => rebuild());\n resource.dispose = () => {\n unsubscribe();\n unsubscribeUI?.();\n acquired.release();\n acquiredUI?.release();\n };\n source.resources.set(snapshot.id, resource);\n }\n\n rebuild();\n },\n dispose() {\n for (const resource of source.resources.values()) resource.dispose();\n source.resources.clear();\n source.namespaceRequests.clear();\n source.snapshot = new Map();\n for (const listener of source.listeners) listener();\n },\n };\n\n const rebuild = () => {\n const { uiMessagesByParent } = source;\n const resources = [...source.resources.values()];\n const childrenByParent = new Map<string, ProjectionResource[]>();\n\n for (const resource of resources) {\n const parentId = resource.snapshot.parentId;\n if (parentId == null) continue;\n const children = childrenByParent.get(parentId);\n if (children) children.push(resource);\n else childrenByParent.set(parentId, [resource]);\n }\n\n const transcripts = new Map<string, SubagentTranscript>();\n let changed = source.snapshot.size !== resources.length;\n const built = new Set<string>();\n\n const build = (resource: ProjectionResource, depth: number) => {\n if (built.has(resource.snapshot.id)) return;\n built.add(resource.snapshot.id);\n const children =\n depth < MAX_SUBAGENT_DEPTH\n ? (childrenByParent.get(resource.snapshot.id) ?? [])\n : [];\n for (const child of children) build(child, depth + 1);\n const childTranscripts = new Map(\n children.flatMap((child) =>\n child.entry ? [[child.snapshot.id, child.entry] as const] : [],\n ),\n );\n const storeSnapshot = resource.store.getSnapshot();\n const status = resource.snapshot.status;\n const stepped = stepStreamingTiming(\n resource.timingState,\n storeSnapshot as LangChainBaseMessage[],\n status === \"running\",\n langChainStreamingTimingAccessors,\n undefined,\n );\n resource.timingState = stepped.state;\n if (Object.keys(stepped.timings).length > 0) {\n resource.messageTiming = {\n ...resource.messageTiming,\n ...stepped.timings,\n };\n }\n const localUiMessages = foldLocalUIMessages(resource);\n if (\n resource.localUiMessages !== localUiMessages ||\n resource.rootUiMessagesByParent !== uiMessagesByParent ||\n resource.convertedMessageTiming !== resource.messageTiming\n ) {\n resource.localUiMessages = localUiMessages;\n resource.rootUiMessagesByParent = uiMessagesByParent;\n resource.uiMessagesByParent =\n localUiMessages.length === 0\n ? uiMessagesByParent\n : mergeLocalUIMessages(uiMessagesByParent, localUiMessages);\n resource.convert = createConverter(\n resource.uiMessagesByParent,\n resource.messageTiming,\n );\n }\n const uiMessages = collectUIMessages(\n storeSnapshot,\n resource.uiMessagesByParent,\n );\n\n const conversionChanged =\n resource.converted === undefined ||\n resource.storeSnapshot !== storeSnapshot ||\n resource.status !== status ||\n resource.convertedMessageTiming !== resource.messageTiming ||\n !sameUIMessages(resource.uiMessages, uiMessages);\n if (conversionChanged) {\n resource.converted = convertExternalMessages(\n storeSnapshot as LangChainBaseMessage[],\n resource.convert,\n status === \"running\",\n TRANSCRIPT_METADATA,\n resource.cache,\n );\n resource.storeSnapshot = storeSnapshot;\n resource.status = status;\n resource.uiMessages = uiMessages;\n resource.convertedMessageTiming = resource.messageTiming;\n }\n\n if (\n resource.transcript === undefined ||\n conversionChanged ||\n !sameTranscriptEntries(resource.childTranscripts, childTranscripts)\n ) {\n const transcript = attachSubagentTranscripts(\n resource.converted!,\n childTranscripts,\n resource.memo,\n );\n changed ||= resource.transcript !== transcript;\n resource.transcript = transcript;\n resource.childTranscripts = childTranscripts;\n }\n\n updateTiming(resource);\n if (\n resource.entry === undefined ||\n resource.entry.messages !== resource.transcript ||\n resource.entry.timing !== resource.timing\n ) {\n resource.entry = {\n messages: resource.transcript!,\n ...(resource.timing && { timing: resource.timing }),\n };\n changed = true;\n }\n\n if (!source.snapshot.has(resource.snapshot.id)) changed = true;\n transcripts.set(resource.snapshot.id, resource.entry);\n };\n\n for (const resource of resources) {\n if (\n resource.snapshot.parentId == null ||\n !source.resources.has(resource.snapshot.parentId)\n )\n build(resource, 1);\n }\n for (const resource of resources) build(resource, MAX_SUBAGENT_DEPTH);\n\n if (!changed) return;\n source.snapshot = transcripts;\n for (const listener of source.listeners) listener();\n };\n\n return source;\n};\n\nexport const useSubagentTranscripts = (\n stream: AnyStream,\n uiMessagesByParent: Map<string, UIMessage[]>,\n): ReadonlyMap<string, SubagentTranscript> => {\n const sourceRef = useRef<SubagentTranscriptSource | undefined>(undefined);\n if (!sourceRef.current) {\n sourceRef.current = createSubagentTranscriptSource();\n }\n const source = sourceRef.current;\n const controller = stream[STREAM_CONTROLLER];\n\n useEffect(() => {\n source.reconcile(controller, stream.subagents, uiMessagesByParent);\n }, [controller, source, stream.subagents, uiMessagesByParent]);\n\n useReplaySafeEffect(() => () => source.dispose(), [source]);\n\n return useSyncExternalStore(\n source.subscribe,\n source.getSnapshot,\n source.getSnapshot,\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-langchain",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"description": "LangChain useStream adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"langchain",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
],
|
|
41
41
|
"sideEffects": false,
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@assistant-ui/core": "^0.3.
|
|
44
|
-
"@assistant-ui/store": "^0.3.
|
|
43
|
+
"@assistant-ui/core": "^0.3.23",
|
|
44
|
+
"@assistant-ui/store": "^0.3.17",
|
|
45
45
|
"assistant-cloud": "*",
|
|
46
|
-
"assistant-stream": "^0.3.
|
|
46
|
+
"assistant-stream": "^0.3.47"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@langchain/core": "^1.0.0",
|
|
@@ -67,15 +67,16 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
71
|
-
"@
|
|
72
|
-
"@langchain/
|
|
73
|
-
"@langchain/
|
|
70
|
+
"@assistant-ui/x-buildutils": "0.0.30",
|
|
71
|
+
"@assistant-ui/x-react18": "0.0.0",
|
|
72
|
+
"@langchain/core": "^1.2.13",
|
|
73
|
+
"@langchain/langgraph-sdk": "^1.12.0",
|
|
74
|
+
"@langchain/react": "^1.2.0",
|
|
74
75
|
"@testing-library/react": "^16.3.3",
|
|
75
76
|
"@types/react": "^19.3.0",
|
|
76
77
|
"jsdom": "^30.0.1",
|
|
77
78
|
"react": "^19.3.0",
|
|
78
|
-
"vitest": "^5.0.
|
|
79
|
+
"vitest": "^5.0.3"
|
|
79
80
|
},
|
|
80
81
|
"publishConfig": {
|
|
81
82
|
"access": "public",
|
|
@@ -94,6 +95,8 @@
|
|
|
94
95
|
"build": "aui-build",
|
|
95
96
|
"test": "vitest run",
|
|
96
97
|
"typecheck": "tsc --noEmit",
|
|
98
|
+
"test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
|
|
99
|
+
"test:types:peer-react18": "aui-react18-types",
|
|
97
100
|
"test:watch": "vitest"
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -1182,6 +1182,70 @@ describe("convertLangChainBaseMessage malformed messages", () => {
|
|
|
1182
1182
|
}
|
|
1183
1183
|
});
|
|
1184
1184
|
|
|
1185
|
+
it("skips a null tool call entry and keeps the rest", () => {
|
|
1186
|
+
const result = convertLangChainBaseMessage(
|
|
1187
|
+
{
|
|
1188
|
+
...aiMessage([]),
|
|
1189
|
+
tool_calls: [null, { id: "call-1", name: "lookup", args: {} }],
|
|
1190
|
+
} as unknown as LangChainBaseMessage,
|
|
1191
|
+
{},
|
|
1192
|
+
);
|
|
1193
|
+
|
|
1194
|
+
expect(contentOf(result)).toEqual([
|
|
1195
|
+
{
|
|
1196
|
+
type: "tool-call",
|
|
1197
|
+
toolCallId: "call-1",
|
|
1198
|
+
toolName: "lookup",
|
|
1199
|
+
args: {},
|
|
1200
|
+
argsText: "{}",
|
|
1201
|
+
},
|
|
1202
|
+
]);
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
it("skips a tool call without a name so its named result does not throw", () => {
|
|
1206
|
+
const messages = convertExternalMessages(
|
|
1207
|
+
[
|
|
1208
|
+
{
|
|
1209
|
+
...aiMessage([]),
|
|
1210
|
+
tool_calls: [{ id: "call-1", args: {} }],
|
|
1211
|
+
} as unknown as LangChainBaseMessage,
|
|
1212
|
+
{
|
|
1213
|
+
_getType: () => "tool",
|
|
1214
|
+
id: "msg-tool",
|
|
1215
|
+
name: "search",
|
|
1216
|
+
tool_call_id: "call-1",
|
|
1217
|
+
content: "3 results",
|
|
1218
|
+
} as LangChainBaseMessage,
|
|
1219
|
+
],
|
|
1220
|
+
(message) => convertLangChainBaseMessage(message, {}),
|
|
1221
|
+
false,
|
|
1222
|
+
{},
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
expect(messages).toHaveLength(1);
|
|
1226
|
+
expect(messages[0]!.content).toEqual([]);
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
it("keeps a tool call whose name is empty", () => {
|
|
1230
|
+
const result = convertLangChainBaseMessage(
|
|
1231
|
+
{
|
|
1232
|
+
...aiMessage([]),
|
|
1233
|
+
tool_calls: [{ id: "call-1", name: "", args: {} }],
|
|
1234
|
+
},
|
|
1235
|
+
{},
|
|
1236
|
+
);
|
|
1237
|
+
|
|
1238
|
+
expect(contentOf(result)).toEqual([
|
|
1239
|
+
{
|
|
1240
|
+
type: "tool-call",
|
|
1241
|
+
toolCallId: "call-1",
|
|
1242
|
+
toolName: "",
|
|
1243
|
+
args: {},
|
|
1244
|
+
argsText: "{}",
|
|
1245
|
+
},
|
|
1246
|
+
]);
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1185
1249
|
it("converts a system message with null content to empty text", () => {
|
|
1186
1250
|
const result = convertLangChainBaseMessage(
|
|
1187
1251
|
{ _getType: () => "system", id: "msg-4", content: null },
|
|
@@ -1286,6 +1350,26 @@ describe("convertLangChainBaseMessage malformed messages", () => {
|
|
|
1286
1350
|
}
|
|
1287
1351
|
});
|
|
1288
1352
|
|
|
1353
|
+
it("warns once in development about a skipped tool call without a name", () => {
|
|
1354
|
+
vi.stubEnv("NODE_ENV", "development");
|
|
1355
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
1356
|
+
try {
|
|
1357
|
+
const message = {
|
|
1358
|
+
...aiMessage([]),
|
|
1359
|
+
tool_calls: [null, { id: "call-1", args: {} }],
|
|
1360
|
+
} as unknown as LangChainBaseMessage;
|
|
1361
|
+
convertLangChainBaseMessage(message, {});
|
|
1362
|
+
convertLangChainBaseMessage(message, {});
|
|
1363
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
1364
|
+
expect(warn).toHaveBeenCalledWith(
|
|
1365
|
+
"Skipping a tool call without a name; its result is not shown either",
|
|
1366
|
+
);
|
|
1367
|
+
} finally {
|
|
1368
|
+
warn.mockRestore();
|
|
1369
|
+
vi.unstubAllEnvs();
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1289
1373
|
it("warns once in development about non-array content", () => {
|
|
1290
1374
|
vi.stubEnv("NODE_ENV", "development");
|
|
1291
1375
|
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
package/src/convertMessages.ts
CHANGED
|
@@ -134,16 +134,24 @@ export const convertLangChainBaseMessage = (
|
|
|
134
134
|
|
|
135
135
|
case "ai": {
|
|
136
136
|
const toolCallParts =
|
|
137
|
-
message.tool_calls
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
137
|
+
message.tool_calls
|
|
138
|
+
?.filter((tc) => {
|
|
139
|
+
if (typeof tc?.name === "string") return true;
|
|
140
|
+
warnOnceInDevelopment(
|
|
141
|
+
"Skipping a tool call without a name; its result is not shown either",
|
|
142
|
+
);
|
|
143
|
+
return false;
|
|
144
|
+
})
|
|
145
|
+
.map((tc) => {
|
|
146
|
+
const { args, argsText } = toolCallArgs(tc.args);
|
|
147
|
+
return {
|
|
148
|
+
type: "tool-call" as const,
|
|
149
|
+
toolCallId: tc.id,
|
|
150
|
+
toolName: tc.name,
|
|
151
|
+
args,
|
|
152
|
+
argsText,
|
|
153
|
+
};
|
|
154
|
+
}) ?? [];
|
|
147
155
|
|
|
148
156
|
const assistantStatus =
|
|
149
157
|
typeof message.status === "object" ? message.status : undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { act, waitFor } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
// React 18's act holds renders until its callback settles, so a task that waits on a render starts inside act and settles outside it, where React can commit what the task waits on.
|
|
4
|
+
export async function settleOutsideAct<T>(task: () => Promise<T>): Promise<T> {
|
|
5
|
+
let pending!: Promise<T>;
|
|
6
|
+
await act(async () => {
|
|
7
|
+
pending = task();
|
|
8
|
+
pending.catch(() => {});
|
|
9
|
+
});
|
|
10
|
+
await waitFor(() => pending.then(settled, settled));
|
|
11
|
+
await act(async () => {});
|
|
12
|
+
return pending;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const settled = () => {};
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
RemoteThreadListAdapter,
|
|
10
10
|
} from "@assistant-ui/core";
|
|
11
11
|
import { useAui } from "@assistant-ui/store";
|
|
12
|
-
import type { LangChainBaseMessage } from "./types";
|
|
12
|
+
import type { LangChainBaseMessage, LangChainToolCall } from "./types";
|
|
13
13
|
import { startTransition, Suspense, type ReactNode } from "react";
|
|
14
14
|
import {
|
|
15
15
|
useLangChainRespond,
|
|
@@ -32,6 +32,7 @@ vi.mock("@langchain/react", () => ({
|
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
34
|
import { useStreamRuntime } from "./useStreamRuntime";
|
|
35
|
+
import { settleOutsideAct } from "./tests/settleOutsideAct";
|
|
35
36
|
|
|
36
37
|
type MockStream = {
|
|
37
38
|
messages: LangChainBaseMessage[];
|
|
@@ -194,18 +195,18 @@ describe("useStreamRuntime thread options", () => {
|
|
|
194
195
|
|
|
195
196
|
const view = render(<TestRuntime />);
|
|
196
197
|
|
|
197
|
-
await
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
await settleOutsideAct(() =>
|
|
199
|
+
capture.runtime!.threads.switchToThread("thread-a"),
|
|
200
|
+
);
|
|
200
201
|
|
|
201
202
|
const threadAOptions = mockUseStream.mock.calls
|
|
202
203
|
.map(([options]) => options as { threadId?: string | null })
|
|
203
204
|
.findLast((options) => options.threadId === "thread-a");
|
|
204
205
|
expect(threadAOptions).toBeDefined();
|
|
205
206
|
|
|
206
|
-
await
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
await settleOutsideAct(() =>
|
|
208
|
+
capture.runtime!.threads.switchToThread("thread-b"),
|
|
209
|
+
);
|
|
209
210
|
|
|
210
211
|
const threadBOptions = mockUseStream.mock.calls
|
|
211
212
|
.map(([options]) => options as { threadId?: string | null })
|
|
@@ -917,6 +918,45 @@ describe("useStreamRuntime staged messages", () => {
|
|
|
917
918
|
});
|
|
918
919
|
});
|
|
919
920
|
|
|
921
|
+
describe("useStreamRuntime pending tool call cancellation", () => {
|
|
922
|
+
it("cancels only the pending tool calls that carry an id", async () => {
|
|
923
|
+
const stream = createMockStream([
|
|
924
|
+
message("u1", "human", "look it up"),
|
|
925
|
+
{
|
|
926
|
+
id: "a1",
|
|
927
|
+
_getType: () => "ai",
|
|
928
|
+
content: "",
|
|
929
|
+
tool_calls: [
|
|
930
|
+
{ name: "lookup", args: {} } as LangChainToolCall,
|
|
931
|
+
{ id: "call-1", name: "search", args: {} },
|
|
932
|
+
],
|
|
933
|
+
},
|
|
934
|
+
]);
|
|
935
|
+
const { auiResult } = renderAui(stream);
|
|
936
|
+
|
|
937
|
+
await act(async () => {
|
|
938
|
+
await auiResult.current.thread.append({
|
|
939
|
+
role: "user",
|
|
940
|
+
content: [{ type: "text", text: "never mind" }],
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
expect(stream.submit).toHaveBeenCalledTimes(1);
|
|
945
|
+
expect(stream.submit.mock.calls[0]![0]).toEqual({
|
|
946
|
+
messages: [
|
|
947
|
+
{
|
|
948
|
+
type: "tool",
|
|
949
|
+
name: "search",
|
|
950
|
+
tool_call_id: "call-1",
|
|
951
|
+
content: JSON.stringify({ cancelled: true }),
|
|
952
|
+
status: "error",
|
|
953
|
+
},
|
|
954
|
+
{ id: expect.any(String), type: "human", content: "never mind" },
|
|
955
|
+
],
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
|
|
920
960
|
describe("useStreamRuntime committed refs", () => {
|
|
921
961
|
it("submits through the committed stream after an abandoned render", async () => {
|
|
922
962
|
const streamA = createMockStream();
|
package/src/useStreamRuntime.ts
CHANGED
|
@@ -573,7 +573,10 @@ const useStreamThreadRuntime = (
|
|
|
573
573
|
autoCancelPendingToolCalls !== false
|
|
574
574
|
? getPendingToolCalls(
|
|
575
575
|
streamRef.current.messages as readonly LangChainBaseMessage[],
|
|
576
|
-
)
|
|
576
|
+
)
|
|
577
|
+
// LangChain rejects a tool message without a tool_call_id.
|
|
578
|
+
.filter((toolCall) => toolCall.id)
|
|
579
|
+
.map(createToolCallCancellationStub)
|
|
577
580
|
: [];
|
|
578
581
|
// A null threadId is not a no-op for the SDK: it rebinds the controller
|
|
579
582
|
// away from its self-created thread and forces a fresh one, so the
|
|
@@ -13,6 +13,7 @@ import { getThreadMessageText } from "@assistant-ui/core/internal";
|
|
|
13
13
|
import { AssistantRuntimeProvider } from "@assistant-ui/core/react";
|
|
14
14
|
import type { LangChainBaseMessage } from "./types";
|
|
15
15
|
import { useStreamRuntime } from "./useStreamRuntime";
|
|
16
|
+
import { settleOutsideAct } from "./tests/settleOutsideAct";
|
|
16
17
|
|
|
17
18
|
const { mockUseStream, streamController } = vi.hoisted(() => ({
|
|
18
19
|
mockUseStream: vi.fn(),
|
|
@@ -477,9 +478,9 @@ const renderThreadRuntime = async (
|
|
|
477
478
|
{null}
|
|
478
479
|
</AssistantRuntimeProvider>,
|
|
479
480
|
);
|
|
480
|
-
await
|
|
481
|
-
|
|
482
|
-
|
|
481
|
+
await settleOutsideAct(() =>
|
|
482
|
+
rendered.result.current.threads.switchToThread("thread-a"),
|
|
483
|
+
);
|
|
483
484
|
await waitFor(() =>
|
|
484
485
|
expect(rendered.result.current.thread.getState().capabilities.voice).toBe(
|
|
485
486
|
true,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { act, useMemo } from "react";
|
|
4
|
+
import { afterAll, afterEach, expect, it, vi } from "vitest";
|
|
5
|
+
import type { UIMessage } from "./types";
|
|
6
|
+
|
|
7
|
+
type Family = { current: unknown };
|
|
8
|
+
type RendererInternals = {
|
|
9
|
+
setRefreshHandler: (resolve: (type: unknown) => Family | undefined) => void;
|
|
10
|
+
scheduleRefresh: (
|
|
11
|
+
root: unknown,
|
|
12
|
+
update: { staleFamilies: Set<Family>; updatedFamilies: Set<Family> },
|
|
13
|
+
) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const { streamController } = vi.hoisted(() => ({
|
|
17
|
+
streamController: Symbol("STREAM_CONTROLLER"),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("@langchain/react", async (importOriginal) => ({
|
|
21
|
+
...(await importOriginal<typeof import("@langchain/react")>()),
|
|
22
|
+
STREAM_CONTROLLER: streamController,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
let renderer: RendererInternals | undefined;
|
|
26
|
+
const fiberRoots = new Set<unknown>();
|
|
27
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
28
|
+
vi.stubGlobal("__REACT_DEVTOOLS_GLOBAL_HOOK__", {
|
|
29
|
+
supportsFiber: true,
|
|
30
|
+
inject: (internals: RendererInternals) => {
|
|
31
|
+
renderer = internals;
|
|
32
|
+
return 1;
|
|
33
|
+
},
|
|
34
|
+
onScheduleFiberRoot: () => {},
|
|
35
|
+
onCommitFiberRoot: (_id: number, root: unknown) => fiberRoots.add(root),
|
|
36
|
+
onCommitFiberUnmount: () => {},
|
|
37
|
+
});
|
|
38
|
+
const { cleanup, render, waitFor } = await import("@testing-library/react");
|
|
39
|
+
const { useSubagentTranscripts } = await import("./useSubagentTranscripts");
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
cleanup();
|
|
43
|
+
renderer?.setRefreshHandler(() => undefined);
|
|
44
|
+
fiberRoots.clear();
|
|
45
|
+
});
|
|
46
|
+
afterAll(() => vi.unstubAllGlobals());
|
|
47
|
+
|
|
48
|
+
it("keeps a live subagent projection and namespace request through Fast Refresh, then disposes on unmount", async () => {
|
|
49
|
+
const listeners = new Set<() => void>();
|
|
50
|
+
let messages = [
|
|
51
|
+
{ id: "message-one", _getType: () => "ai", content: "partial" },
|
|
52
|
+
];
|
|
53
|
+
const store = {
|
|
54
|
+
getSnapshot: () => messages,
|
|
55
|
+
subscribe: (listener: () => void) => {
|
|
56
|
+
listeners.add(listener);
|
|
57
|
+
return () => listeners.delete(listener);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const release = vi.fn();
|
|
61
|
+
const acquire = vi.fn(() => ({ store, release }));
|
|
62
|
+
let settleFirst!: () => void;
|
|
63
|
+
const pending = new Promise<void>((resolve) => {
|
|
64
|
+
settleFirst = resolve;
|
|
65
|
+
});
|
|
66
|
+
const resolveSubagentNamespace = vi
|
|
67
|
+
.fn<() => Promise<void>>()
|
|
68
|
+
.mockReturnValueOnce(pending)
|
|
69
|
+
.mockResolvedValue(undefined);
|
|
70
|
+
const snapshot = (status: "running" | "complete") => ({
|
|
71
|
+
id: "task-one",
|
|
72
|
+
namespace: ["tools:task-one"],
|
|
73
|
+
status,
|
|
74
|
+
parentId: null,
|
|
75
|
+
depth: 1,
|
|
76
|
+
startedAt: new Date(1_000),
|
|
77
|
+
completedAt: status === "complete" ? new Date(4_000) : null,
|
|
78
|
+
});
|
|
79
|
+
const stream = {
|
|
80
|
+
subagents: new Map([["task-one", snapshot("running")]]),
|
|
81
|
+
[streamController]: {
|
|
82
|
+
registry: { acquire },
|
|
83
|
+
resolveSubagentNamespace,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const uiMaps = new Set<Map<string, UIMessage[]>>();
|
|
87
|
+
let transcript: ReturnType<typeof useSubagentTranscripts> | undefined;
|
|
88
|
+
let rendered: string | undefined;
|
|
89
|
+
const host = (name: string) => () => {
|
|
90
|
+
const uiMessagesByParent = useMemo(
|
|
91
|
+
() => new Map<string, UIMessage[]>(),
|
|
92
|
+
[],
|
|
93
|
+
);
|
|
94
|
+
uiMaps.add(uiMessagesByParent);
|
|
95
|
+
rendered = name;
|
|
96
|
+
transcript = useSubagentTranscripts(stream as never, uiMessagesByParent);
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
const Before = host("before");
|
|
100
|
+
const After = host("after");
|
|
101
|
+
const view = render(<Before />);
|
|
102
|
+
|
|
103
|
+
await waitFor(() => expect(transcript?.has("task-one")).toBe(true));
|
|
104
|
+
expect(resolveSubagentNamespace).toHaveBeenCalledOnce();
|
|
105
|
+
expect(listeners.size).toBe(1);
|
|
106
|
+
await act(async () => {
|
|
107
|
+
messages = [
|
|
108
|
+
{ id: "message-one", _getType: () => "ai", content: "partial answer" },
|
|
109
|
+
];
|
|
110
|
+
for (const listener of listeners) listener();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const family: Family = { current: After };
|
|
114
|
+
renderer!.setRefreshHandler((type) =>
|
|
115
|
+
type === Before || type === After ? family : undefined,
|
|
116
|
+
);
|
|
117
|
+
await act(async () => {
|
|
118
|
+
for (const root of fiberRoots) {
|
|
119
|
+
renderer!.scheduleRefresh(root, {
|
|
120
|
+
staleFamilies: new Set(),
|
|
121
|
+
updatedFamilies: new Set([family]),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
await act(async () => {});
|
|
126
|
+
|
|
127
|
+
expect(rendered).toBe("after");
|
|
128
|
+
expect(uiMaps.size).toBeGreaterThan(1);
|
|
129
|
+
expect(release).not.toHaveBeenCalled();
|
|
130
|
+
expect(acquire).toHaveBeenCalledOnce();
|
|
131
|
+
expect(listeners.size).toBe(1);
|
|
132
|
+
expect(resolveSubagentNamespace).toHaveBeenCalledOnce();
|
|
133
|
+
|
|
134
|
+
stream.subagents = new Map([["task-one", snapshot("complete")]]);
|
|
135
|
+
view.rerender(<After />);
|
|
136
|
+
await waitFor(() =>
|
|
137
|
+
expect(
|
|
138
|
+
transcript?.get("task-one")?.messages[0]?.metadata?.timing,
|
|
139
|
+
).toMatchObject({
|
|
140
|
+
totalChunks: 2,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
await act(async () => settleFirst());
|
|
144
|
+
await waitFor(() =>
|
|
145
|
+
expect(resolveSubagentNamespace).toHaveBeenCalledTimes(2),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
view.unmount();
|
|
149
|
+
await act(async () => {});
|
|
150
|
+
expect(release).toHaveBeenCalledOnce();
|
|
151
|
+
expect(listeners.size).toBe(0);
|
|
152
|
+
});
|
|
@@ -185,6 +185,7 @@ describe("useSubagentTranscripts", () => {
|
|
|
185
185
|
expect(stream.acquire).toHaveBeenCalledTimes(2);
|
|
186
186
|
expect(stream.resolveSubagentNamespace).not.toHaveBeenCalled();
|
|
187
187
|
hook.unmount();
|
|
188
|
+
await act(async () => {});
|
|
188
189
|
expect(stream.releases.get("tools:one")).toHaveBeenCalledOnce();
|
|
189
190
|
expect(stream.releases.get("tools:two")).toHaveBeenCalledOnce();
|
|
190
191
|
});
|
|
@@ -773,6 +774,7 @@ describe("useSubagentTranscripts", () => {
|
|
|
773
774
|
expect(channelSpecs[0]?.namespace).toEqual(["tools:parent", "tools:child"]);
|
|
774
775
|
|
|
775
776
|
hook.unmount();
|
|
777
|
+
await act(async () => {});
|
|
776
778
|
expect(
|
|
777
779
|
stream.uiReleases.get("tools:parent/tools:child"),
|
|
778
780
|
).toHaveBeenCalledOnce();
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type ExternalMessageConversionCache,
|
|
15
15
|
type useExternalMessageConverter,
|
|
16
16
|
} from "@assistant-ui/core/react";
|
|
17
|
+
import { useReplaySafeEffect } from "@assistant-ui/store/internal";
|
|
17
18
|
import { STREAM_CONTROLLER, type AnyStream } from "@langchain/react";
|
|
18
19
|
import type { BaseMessage } from "@langchain/core/messages";
|
|
19
20
|
import { channelProjection, type Event } from "@langchain/langgraph-sdk/stream";
|
|
@@ -513,7 +514,7 @@ export const useSubagentTranscripts = (
|
|
|
513
514
|
source.reconcile(controller, stream.subagents, uiMessagesByParent);
|
|
514
515
|
}, [controller, source, stream.subagents, uiMessagesByParent]);
|
|
515
516
|
|
|
516
|
-
|
|
517
|
+
useReplaySafeEffect(() => () => source.dispose(), [source]);
|
|
517
518
|
|
|
518
519
|
return useSyncExternalStore(
|
|
519
520
|
source.subscribe,
|