@assistant-ui/ai-sdk 0.0.6 → 0.0.8
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/README.md +1 -1
- package/dist/adapters/aiSDKFormatAdapter.d.ts +2 -2
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +2 -5
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -1
- package/dist/adapters/vercelAttachmentAdapter.js +3 -3
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -1
- package/dist/aiSDKExtras.d.ts +6 -9
- package/dist/aiSDKExtras.d.ts.map +1 -1
- package/dist/converters/convertMessage.d.ts +34 -25
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +78 -8
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/converters/modelContentEnvelope.d.ts +10 -12
- package/dist/converters/modelContentEnvelope.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.d.ts +3 -6
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toolOutputConversion.d.ts +22 -25
- package/dist/converters/toolOutputConversion.d.ts.map +1 -1
- package/dist/hooks.d.ts +4 -7
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +17 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.native.d.ts +15 -12
- package/dist/index.native.d.ts.map +1 -0
- package/dist/model-context/injectInteractableContext.d.ts +3 -6
- package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +2 -5
- package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.d.ts +4 -7
- package/dist/runtime/AISDKChat.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.js +1 -1
- package/dist/runtime/AISDKThreads.d.ts +29 -32
- package/dist/runtime/AISDKThreads.d.ts.map +1 -1
- package/dist/runtime/AISDKThreads.js +26 -33
- package/dist/runtime/AISDKThreads.js.map +1 -1
- package/dist/runtime/sdkIdentity.d.ts +2 -5
- package/dist/runtime/sdkIdentity.d.ts.map +1 -1
- package/dist/runtime/sdkIdentity.js +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +72 -61
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +170 -56
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.d.ts +8 -11
- package/dist/runtime/useChatRuntime.d.ts.map +1 -1
- package/dist/runtime/useChatRuntime.js +14 -10
- package/dist/runtime/useChatRuntime.js.map +1 -1
- package/dist/runtime/useChatThread.d.ts +100 -81
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +33 -13
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts +8 -9
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +236 -58
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/runtime/useResourceCleanup.d.ts +1 -4
- package/dist/runtime/useResourceCleanup.d.ts.map +1 -1
- package/dist/runtime/useResourceCleanup.js +38 -14
- package/dist/runtime/useResourceCleanup.js.map +1 -1
- package/dist/runtime/useStreamingTiming.d.ts +5 -8
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -1
- package/dist/runtime/useStreamingTiming.js +1 -1
- package/dist/tools/frontendTools.d.ts +24 -27
- package/dist/tools/frontendTools.d.ts.map +1 -1
- package/dist/tools/generativeTools.d.ts +29 -32
- package/dist/tools/generativeTools.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.node.d.ts +2 -2
- package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +2 -5
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.d.ts +17 -20
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
- package/dist/transport/resumable.d.ts +29 -32
- package/dist/transport/resumable.d.ts.map +1 -1
- package/dist/usage.d.ts +12 -15
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/utils/getVercelAIMessages.d.ts +3 -6
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -1
- package/dist/utils/sliceMessagesUntil.d.ts +2 -5
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
- package/src/adapters/vercelAttachmentAdapter.ts +2 -2
- package/src/converters/convertMessage.test.ts +616 -3
- package/src/converters/convertMessage.ts +187 -12
- package/src/index.native.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/AISDKChat.test.ts +4 -5
- package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
- package/src/runtime/AISDKThreads.test.ts +88 -51
- package/src/runtime/AISDKThreads.ts +4 -13
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +338 -0
- package/src/runtime/useAISDKRuntime.approval.test.tsx +476 -4
- package/src/runtime/useAISDKRuntime.test.ts +282 -8
- package/src/runtime/useAISDKRuntime.ts +274 -46
- package/src/runtime/useAISDKRuntime.voice.test.tsx +319 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +56 -0
- package/src/runtime/useChatRuntime.test.ts +77 -5
- package/src/runtime/useChatRuntime.ts +6 -2
- package/src/runtime/useChatThread.transport.test.tsx +149 -0
- package/src/runtime/useChatThread.ts +77 -10
- package/src/runtime/useExternalHistory.test.ts +989 -3
- package/src/runtime/useExternalHistory.ts +390 -24
- package/src/runtime/useResourceCleanup.ts +41 -15
- package/src/tools/generativeTools.test.ts +7 -1
- package/src/usage.ts +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { useAISDKRuntime } from "./useAISDKRuntime.js";
|
|
3
3
|
import { AssistantChatTransport } from "../transport/AssistantChatTransport.js";
|
|
4
4
|
import { useResourceCleanup } from "./useResourceCleanup.js";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
|
|
6
5
|
import { pickExternalStoreSharedOptions } from "@assistant-ui/core";
|
|
7
|
-
import {
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
|
|
7
|
+
import { Chat, useChat } from "@ai-sdk/react";
|
|
8
8
|
//#region src/runtime/useChatThread.ts
|
|
9
9
|
const useDynamicChatTransport = (transport) => {
|
|
10
10
|
const transportRef = useRef(transport);
|
|
@@ -39,7 +39,7 @@ const getResumedStreamIds = (storage) => {
|
|
|
39
39
|
* same fields `useChat` would.
|
|
40
40
|
*/
|
|
41
41
|
const splitChatThreadOptions = (options) => {
|
|
42
|
-
const { adapters, transport, throttle, toCreateMessage, isDisabled: _isDisabled, isSendDisabled: _isSendDisabled, unstable_capabilities: _unstable_capabilities, suggestions: _suggestions, onResume, onResumeToolCall, onResumeError, joinStrategy, messageRepository, unstable_onBranchChange, ...chatInit } = options ?? {};
|
|
42
|
+
const { adapters, transport, throttle, toCreateMessage, isDisabled: _isDisabled, isSendDisabled: _isSendDisabled, unstable_capabilities: _unstable_capabilities, suggestions: _suggestions, onResume, onResumeToolCall, onRespondToToolApproval, onResumeError, joinStrategy, messageRepository, unstable_onBranchChange, ...chatInit } = options ?? {};
|
|
43
43
|
return {
|
|
44
44
|
adapters,
|
|
45
45
|
transport,
|
|
@@ -47,6 +47,7 @@ const splitChatThreadOptions = (options) => {
|
|
|
47
47
|
toCreateMessage,
|
|
48
48
|
onResume,
|
|
49
49
|
onResumeToolCall,
|
|
50
|
+
onRespondToToolApproval,
|
|
50
51
|
onResumeError,
|
|
51
52
|
joinStrategy,
|
|
52
53
|
messageRepository,
|
|
@@ -54,28 +55,47 @@ const splitChatThreadOptions = (options) => {
|
|
|
54
55
|
chatInit
|
|
55
56
|
};
|
|
56
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Constructs a `Chat` whose callbacks read the latest options through
|
|
60
|
+
* `callbacksRef`, the forwarding `useChat` applies only to a chat it
|
|
61
|
+
* constructs itself.
|
|
62
|
+
*/
|
|
63
|
+
const createChat = (init, callbacksRef) => new Chat({
|
|
64
|
+
...init,
|
|
65
|
+
onToolCall: (arg) => callbacksRef.current?.onToolCall?.(arg),
|
|
66
|
+
onData: (arg) => callbacksRef.current?.onData?.(arg),
|
|
67
|
+
onFinish: (arg) => callbacksRef.current?.onFinish?.(arg),
|
|
68
|
+
onError: (arg) => callbacksRef.current?.onError?.(arg),
|
|
69
|
+
sendAutomaticallyWhen: (arg) => callbacksRef.current?.sendAutomaticallyWhen?.(arg) ?? false
|
|
70
|
+
});
|
|
57
71
|
const useChatThread = (options, env) => {
|
|
58
|
-
const { adapters, transport: transportOptions, throttle, toCreateMessage, onResume, onResumeToolCall, onResumeError, joinStrategy, messageRepository, unstable_onBranchChange, chatInit: chatOptions } = splitChatThreadOptions(options);
|
|
59
|
-
const { id, isMainThread, getThreadListItem, stopOnClientDestroy = true, chat: externalChat, messageRepositoryInstance } = env;
|
|
60
|
-
const
|
|
61
|
-
const sourceTransport = transportOptions ?? defaultTransport;
|
|
72
|
+
const { adapters, transport: transportOptions, throttle, toCreateMessage, onResume, onResumeToolCall, onRespondToToolApproval, onResumeError, joinStrategy, messageRepository, unstable_onBranchChange, chatInit: chatOptions } = splitChatThreadOptions(options);
|
|
73
|
+
const { id, isMainThread, getThreadListItem, stopOnClientDestroy = true, hostDestroySignal, chat: externalChat, messageRepositoryInstance } = env;
|
|
74
|
+
const sourceTransport = useMemo(() => transportOptions === void 0 ? new AssistantChatTransport() : externalChat === void 0 && transportOptions instanceof AssistantChatTransport ? transportOptions.__internal_clone() : transportOptions, [transportOptions, externalChat]);
|
|
62
75
|
const transport = useDynamicChatTransport(sourceTransport);
|
|
63
|
-
const
|
|
76
|
+
const latestChatOptionsRef = useRef(chatOptions);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
latestChatOptionsRef.current = chatOptions;
|
|
79
|
+
});
|
|
80
|
+
const [ownedChat] = useState(() => externalChat ?? createChat({
|
|
64
81
|
...chatOptions,
|
|
65
82
|
id,
|
|
66
|
-
transport
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
transport
|
|
84
|
+
}, latestChatOptionsRef));
|
|
85
|
+
const chat = useChat({
|
|
86
|
+
chat: externalChat ?? ownedChat,
|
|
87
|
+
...throttle !== void 0 && { throttle }
|
|
69
88
|
});
|
|
70
89
|
useResourceCleanup(stopOnClientDestroy, () => {
|
|
71
90
|
chat.stop().catch(() => {});
|
|
72
|
-
});
|
|
91
|
+
}, hostDestroySignal);
|
|
73
92
|
const runtime = useAISDKRuntime(chat, {
|
|
74
93
|
adapters,
|
|
75
94
|
...pickExternalStoreSharedOptions(options ?? {}),
|
|
76
95
|
...toCreateMessage && { toCreateMessage },
|
|
77
96
|
...onResume && { onResume },
|
|
78
97
|
...onResumeToolCall && { onResumeToolCall },
|
|
98
|
+
...onRespondToToolApproval && { onRespondToToolApproval },
|
|
79
99
|
...joinStrategy && { joinStrategy },
|
|
80
100
|
...messageRepository && { messageRepository },
|
|
81
101
|
...messageRepositoryInstance && { unstable_messageRepositoryInstance: messageRepositoryInstance },
|
|
@@ -136,6 +156,6 @@ const useChatThread = (options, env) => {
|
|
|
136
156
|
return runtime;
|
|
137
157
|
};
|
|
138
158
|
//#endregion
|
|
139
|
-
export { splitChatThreadOptions, useChatThread };
|
|
159
|
+
export { createChat, splitChatThreadOptions, useChatThread };
|
|
140
160
|
|
|
141
161
|
//# sourceMappingURL=useChatThread.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatThread.js","names":["useChat","Chat","UIMessage","MessageRepository","pickExternalStoreSharedOptions","AssistantRuntime","ExternalStoreSharedOptions","useAISDKRuntime","AISDKRuntimeAdapter","CustomToCreateMessageFunction","ChatInit","ChatTransport","AssistantChatTransport","InitializableThreadListItem","AssistantChatResumableOptions","ResumableClientStorage","useCallback","useEffect","useMemo","useRef","useSyncExternalStore","useResourceCleanup","ChatThreadOptions","UI_MESSAGE","throttle","adapters","toCreateMessage","onResume","onResumeToolCall","onResumeError","error","joinStrategy","messageRepository","unstable_onBranchChange","ChatThreadEnvironment","id","isMainThread","getThreadListItem","stopOnClientDestroy","chat","messageRepositoryInstance","useDynamicChatTransport","transport","transportRef","current","dynamicTransport","Proxy","get","_","prop","res","bind","getResumableAdapter","candidate","undefined","call","getNoPendingStreamId","resumedStreamIdsByStorage","WeakMap","Set","getResumedStreamIds","storage","resumedStreamIds","set","splitChatThreadOptions","options","isDisabled","_isDisabled","isSendDisabled","_isSendDisabled","unstable_capabilities","_unstable_capabilities","suggestions","_suggestions","chatInit","useChatThread","env","transportOptions","chatOptions","externalChat","defaultTransport","sourceTransport","stop","catch","runtime","unstable_messageRepositoryInstance","setRuntime","__internal_setGetThreadListItem","subscribeToRuntime","callback","thread","subscribe","getHistoryLoadingSnapshot","getState","isLoading","isLoadingHistory","resumableStorage","subscribeToResumableStorage","getPendingStreamId","getStreamId","pendingStreamId","isChatRunning","status","onResumeErrorRef","has","add","resumeStream","err","console","warn","callbackError","clear"],"sources":["../../src/runtime/useChatThread.ts"],"sourcesContent":["\"use client\";\n\nimport { useChat, type Chat, type UIMessage } from \"@ai-sdk/react\";\nimport type { MessageRepository } from \"@assistant-ui/core/internal\";\nimport {\n pickExternalStoreSharedOptions,\n type AssistantRuntime,\n type ExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport {\n useAISDKRuntime,\n type AISDKRuntimeAdapter,\n type CustomToCreateMessageFunction,\n} from \"./useAISDKRuntime\";\nimport type { ChatInit, ChatTransport } from \"ai\";\nimport {\n AssistantChatTransport,\n type InitializableThreadListItem,\n} from \"../transport/AssistantChatTransport\";\nimport type {\n AssistantChatResumableOptions,\n ResumableClientStorage,\n} from \"../transport/resumable\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type ChatThreadOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> &\n ExternalStoreSharedOptions & {\n throttle?: number | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n toCreateMessage?: CustomToCreateMessageFunction;\n onResume?: AISDKRuntimeAdapter[\"onResume\"];\n onResumeToolCall?: AISDKRuntimeAdapter[\"onResumeToolCall\"];\n /**\n * Called when an automatic resumable stream reconnect fails. Use this to\n * surface a toast, report telemetry, or mark the thread as needing a\n * retry. The failed stream id is cleared after the callback unless a\n * newer id has replaced it.\n */\n onResumeError?: ((error: unknown) => void) | undefined;\n joinStrategy?: AISDKRuntimeAdapter[\"joinStrategy\"];\n messageRepository?: AISDKRuntimeAdapter<UI_MESSAGE>[\"messageRepository\"];\n unstable_onBranchChange?: AISDKRuntimeAdapter[\"unstable_onBranchChange\"];\n };\n\nexport type ChatThreadEnvironment<UI_MESSAGE extends UIMessage = UIMessage> = {\n id: string;\n isMainThread: boolean;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n stopOnClientDestroy?: boolean;\n /**\n * An externally owned chat instance. State lives on the instance, so it\n * survives the hosting resource unmounting; construction options are read\n * from the instance.\n */\n chat?: Chat<UI_MESSAGE> | undefined;\n /**\n * An externally owned per-thread message repository. Hosts that route\n * multiple threads through one mounting pass a distinct instance per\n * thread so histories and branches stay isolated.\n */\n messageRepositoryInstance?: MessageRepository | undefined;\n};\n\nconst useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): ChatTransport<UI_MESSAGE> => {\n const transportRef = useRef<ChatTransport<UI_MESSAGE>>(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n const dynamicTransport = useMemo(\n () =>\n new Proxy(transportRef.current, {\n get(_, prop) {\n const res =\n transportRef.current[prop as keyof ChatTransport<UI_MESSAGE>];\n return typeof res === \"function\"\n ? res.bind(transportRef.current)\n : res;\n },\n }),\n [],\n );\n return dynamicTransport;\n};\n\nconst getResumableAdapter = <UI_MESSAGE extends UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): AssistantChatResumableOptions | undefined => {\n if (transport instanceof AssistantChatTransport) {\n return transport.getResumableAdapter();\n }\n const candidate = (transport as { getResumableAdapter?: () => unknown })\n .getResumableAdapter;\n if (typeof candidate !== \"function\") return undefined;\n return candidate.call(transport) as AssistantChatResumableOptions | undefined;\n};\n\nconst getNoPendingStreamId = () => null;\n\nconst resumedStreamIdsByStorage = new WeakMap<\n ResumableClientStorage,\n Set<string>\n>();\n\nconst getResumedStreamIds = (storage: ResumableClientStorage | undefined) => {\n if (!storage) return new Set<string>();\n let resumedStreamIds = resumedStreamIdsByStorage.get(storage);\n if (!resumedStreamIds) {\n resumedStreamIds = new Set();\n resumedStreamIdsByStorage.set(storage, resumedStreamIds);\n }\n return resumedStreamIds;\n};\n\n/**\n * Splits the combined options into the assistant-ui side and the `ChatInit`\n * remainder the AI SDK consumes, so external `Chat` construction forwards the\n * same fields `useChat` would.\n */\nexport const splitChatThreadOptions = <UI_MESSAGE extends UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n) => {\n const {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n unstable_capabilities: _unstable_capabilities,\n suggestions: _suggestions,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n ...chatInit\n } = options ?? {};\n // peel guard: any shared key left in `chatInit` collapses this to `never`\n true satisfies keyof typeof chatInit &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n return {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit,\n };\n};\n\nexport const useChatThread = <UI_MESSAGE extends UIMessage = UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n env: ChatThreadEnvironment<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit: chatOptions,\n } = splitChatThreadOptions(options);\n\n const {\n id,\n isMainThread,\n getThreadListItem,\n stopOnClientDestroy = true,\n chat: externalChat,\n messageRepositoryInstance,\n } = env;\n\n const defaultTransport = useMemo(() => new AssistantChatTransport(), []);\n const sourceTransport = transportOptions ?? defaultTransport;\n const transport = useDynamicChatTransport(sourceTransport);\n\n const chat = useChat({\n ...chatOptions,\n id,\n transport,\n ...(throttle !== undefined && { throttle }),\n ...(externalChat !== undefined && { chat: externalChat }),\n });\n\n useResourceCleanup(stopOnClientDestroy, () => {\n void chat.stop().catch(() => {});\n });\n\n const runtime = useAISDKRuntime(chat, {\n adapters,\n ...pickExternalStoreSharedOptions(options ?? {}),\n ...(toCreateMessage && { toCreateMessage }),\n ...(onResume && { onResume }),\n ...(onResumeToolCall && { onResumeToolCall }),\n ...(joinStrategy && { joinStrategy }),\n ...(messageRepository && { messageRepository }),\n ...(messageRepositoryInstance && {\n unstable_messageRepositoryInstance: messageRepositoryInstance,\n }),\n ...(unstable_onBranchChange && { unstable_onBranchChange }),\n });\n\n if (sourceTransport instanceof AssistantChatTransport) {\n sourceTransport.setRuntime(runtime);\n sourceTransport.__internal_setGetThreadListItem(getThreadListItem);\n }\n\n const subscribeToRuntime = useCallback(\n (callback: () => void) => runtime.thread.subscribe(callback),\n [runtime],\n );\n const getHistoryLoadingSnapshot = useCallback(\n () => runtime.thread.getState().isLoading,\n [runtime],\n );\n const isLoadingHistory = useSyncExternalStore(\n subscribeToRuntime,\n getHistoryLoadingSnapshot,\n getHistoryLoadingSnapshot,\n );\n\n const resumableStorage = useMemo(\n () => getResumableAdapter(sourceTransport)?.storage,\n [sourceTransport],\n );\n const subscribeToResumableStorage = useCallback(\n (callback: () => void) =>\n isMainThread\n ? (resumableStorage?.subscribe?.(callback, id) ?? (() => {}))\n : () => {},\n [id, isMainThread, resumableStorage],\n );\n const getPendingStreamId = useCallback(\n () => (isMainThread ? (resumableStorage?.getStreamId(id) ?? null) : null),\n [id, isMainThread, resumableStorage],\n );\n const pendingStreamId = useSyncExternalStore(\n subscribeToResumableStorage,\n getPendingStreamId,\n getNoPendingStreamId,\n );\n const isChatRunning =\n chat.status === \"submitted\" || chat.status === \"streaming\";\n\n const resumedStreamIds = useMemo(\n () => getResumedStreamIds(resumableStorage),\n [resumableStorage],\n );\n const onResumeErrorRef = useRef(onResumeError);\n useEffect(() => {\n onResumeErrorRef.current = onResumeError;\n });\n useEffect(() => {\n if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) {\n return;\n }\n if (isChatRunning) {\n resumedStreamIds.add(pendingStreamId);\n return;\n }\n if (isLoadingHistory) return;\n resumedStreamIds.add(pendingStreamId);\n chat.resumeStream().catch((err: unknown) => {\n console.warn(\"[assistant-ui] resumable: resume failed\", err);\n try {\n onResumeErrorRef.current?.(err);\n } catch (callbackError) {\n console.error(\n \"[assistant-ui] resumable: onResumeError callback failed\",\n callbackError,\n );\n } finally {\n if (resumableStorage?.getStreamId(id) === pendingStreamId) {\n resumableStorage.clear(id);\n }\n }\n });\n }, [\n chat,\n id,\n isChatRunning,\n isLoadingHistory,\n pendingStreamId,\n resumableStorage,\n resumedStreamIds,\n ]);\n\n return runtime;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useChatThread.js","names":["Chat","useChat","UIMessage","MessageRepository","pickExternalStoreSharedOptions","AssistantRuntime","ExternalStoreSharedOptions","useAISDKRuntime","AISDKRuntimeAdapter","CustomToCreateMessageFunction","ChatInit","ChatTransport","AssistantChatTransport","InitializableThreadListItem","AssistantChatResumableOptions","ResumableClientStorage","useCallback","useEffect","useMemo","useRef","useState","useSyncExternalStore","useResourceCleanup","ChatThreadOptions","UI_MESSAGE","throttle","adapters","toCreateMessage","onResume","onResumeToolCall","onRespondToToolApproval","onResumeError","error","joinStrategy","messageRepository","unstable_onBranchChange","ChatThreadEnvironment","id","isMainThread","getThreadListItem","stopOnClientDestroy","hostDestroySignal","AbortSignal","chat","messageRepositoryInstance","useDynamicChatTransport","transport","transportRef","current","dynamicTransport","Proxy","get","_","prop","res","bind","getResumableAdapter","candidate","undefined","call","getNoPendingStreamId","resumedStreamIdsByStorage","WeakMap","Set","getResumedStreamIds","storage","resumedStreamIds","set","splitChatThreadOptions","options","isDisabled","_isDisabled","isSendDisabled","_isSendDisabled","unstable_capabilities","_unstable_capabilities","suggestions","_suggestions","chatInit","ChatCallbacks","Pick","createChat","init","callbacksRef","onToolCall","arg","onData","onFinish","onError","sendAutomaticallyWhen","useChatThread","env","transportOptions","chatOptions","externalChat","sourceTransport","__internal_clone","latestChatOptionsRef","ownedChat","stop","catch","runtime","unstable_messageRepositoryInstance","setRuntime","__internal_setGetThreadListItem","subscribeToRuntime","callback","thread","subscribe","getHistoryLoadingSnapshot","getState","isLoading","isLoadingHistory","resumableStorage","subscribeToResumableStorage","getPendingStreamId","getStreamId","pendingStreamId","isChatRunning","status","onResumeErrorRef","has","add","resumeStream","err","console","warn","callbackError","clear"],"sources":["../../src/runtime/useChatThread.ts"],"sourcesContent":["\"use client\";\n\nimport { Chat, useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { MessageRepository } from \"@assistant-ui/core/internal\";\nimport {\n pickExternalStoreSharedOptions,\n type AssistantRuntime,\n type ExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport {\n useAISDKRuntime,\n type AISDKRuntimeAdapter,\n type CustomToCreateMessageFunction,\n} from \"./useAISDKRuntime\";\nimport type { ChatInit, ChatTransport } from \"ai\";\nimport {\n AssistantChatTransport,\n type InitializableThreadListItem,\n} from \"../transport/AssistantChatTransport\";\nimport type {\n AssistantChatResumableOptions,\n ResumableClientStorage,\n} from \"../transport/resumable\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type ChatThreadOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> &\n ExternalStoreSharedOptions & {\n throttle?: number | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n toCreateMessage?: CustomToCreateMessageFunction;\n onResume?: AISDKRuntimeAdapter[\"onResume\"];\n onResumeToolCall?: AISDKRuntimeAdapter[\"onResumeToolCall\"];\n onRespondToToolApproval?: AISDKRuntimeAdapter[\"onRespondToToolApproval\"];\n /**\n * Called when an automatic resumable stream reconnect fails. Use this to\n * surface a toast, report telemetry, or mark the thread as needing a\n * retry. The failed stream id is cleared after the callback unless a\n * newer id has replaced it.\n */\n onResumeError?: ((error: unknown) => void) | undefined;\n joinStrategy?: AISDKRuntimeAdapter[\"joinStrategy\"];\n messageRepository?: AISDKRuntimeAdapter<UI_MESSAGE>[\"messageRepository\"];\n unstable_onBranchChange?: AISDKRuntimeAdapter[\"unstable_onBranchChange\"];\n };\n\nexport type ChatThreadEnvironment<UI_MESSAGE extends UIMessage = UIMessage> = {\n id: string;\n isMainThread: boolean;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n stopOnClientDestroy?: boolean;\n /**\n * Aborts when the React component hosting the runtime is deleted. A nested\n * runtime resolves the destroy signal of the provider above it, which\n * outlives the nested component, so this stops the chat on its own unmount.\n */\n hostDestroySignal?: AbortSignal | undefined;\n /**\n * An externally owned chat instance. State lives on the instance, so it\n * survives the hosting resource unmounting; construction options are read\n * from the instance.\n */\n chat?: Chat<UI_MESSAGE> | undefined;\n /**\n * An externally owned per-thread message repository. Hosts that route\n * multiple threads through one mounting pass a distinct instance per\n * thread so histories and branches stay isolated.\n */\n messageRepositoryInstance?: MessageRepository | undefined;\n};\n\nconst useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): ChatTransport<UI_MESSAGE> => {\n const transportRef = useRef<ChatTransport<UI_MESSAGE>>(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n const dynamicTransport = useMemo(\n () =>\n new Proxy(transportRef.current, {\n get(_, prop) {\n const res =\n transportRef.current[prop as keyof ChatTransport<UI_MESSAGE>];\n return typeof res === \"function\"\n ? res.bind(transportRef.current)\n : res;\n },\n }),\n [],\n );\n return dynamicTransport;\n};\n\nconst getResumableAdapter = <UI_MESSAGE extends UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): AssistantChatResumableOptions | undefined => {\n if (transport instanceof AssistantChatTransport) {\n return transport.getResumableAdapter();\n }\n const candidate = (transport as { getResumableAdapter?: () => unknown })\n .getResumableAdapter;\n if (typeof candidate !== \"function\") return undefined;\n return candidate.call(transport) as AssistantChatResumableOptions | undefined;\n};\n\nconst getNoPendingStreamId = () => null;\n\nconst resumedStreamIdsByStorage = new WeakMap<\n ResumableClientStorage,\n Set<string>\n>();\n\nconst getResumedStreamIds = (storage: ResumableClientStorage | undefined) => {\n if (!storage) return new Set<string>();\n let resumedStreamIds = resumedStreamIdsByStorage.get(storage);\n if (!resumedStreamIds) {\n resumedStreamIds = new Set();\n resumedStreamIdsByStorage.set(storage, resumedStreamIds);\n }\n return resumedStreamIds;\n};\n\n/**\n * Splits the combined options into the assistant-ui side and the `ChatInit`\n * remainder the AI SDK consumes, so external `Chat` construction forwards the\n * same fields `useChat` would.\n */\nexport const splitChatThreadOptions = <UI_MESSAGE extends UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n) => {\n const {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n unstable_capabilities: _unstable_capabilities,\n suggestions: _suggestions,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n ...chatInit\n } = options ?? {};\n // peel guard: any shared key left in `chatInit` collapses this to `never`\n true satisfies keyof typeof chatInit &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n return {\n adapters,\n transport,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit,\n };\n};\n\ntype ChatCallbacks<UI_MESSAGE extends UIMessage> = Pick<\n ChatInit<UI_MESSAGE>,\n \"onToolCall\" | \"onData\" | \"onFinish\" | \"onError\" | \"sendAutomaticallyWhen\"\n>;\n\n/**\n * Constructs a `Chat` whose callbacks read the latest options through\n * `callbacksRef`, the forwarding `useChat` applies only to a chat it\n * constructs itself.\n */\nexport const createChat = <UI_MESSAGE extends UIMessage>(\n init: ChatInit<UI_MESSAGE>,\n callbacksRef: { readonly current: ChatCallbacks<UI_MESSAGE> | undefined },\n): Chat<UI_MESSAGE> =>\n new Chat<UI_MESSAGE>({\n ...init,\n onToolCall: (arg) => callbacksRef.current?.onToolCall?.(arg),\n onData: (arg) => callbacksRef.current?.onData?.(arg),\n onFinish: (arg) => callbacksRef.current?.onFinish?.(arg),\n onError: (arg) => callbacksRef.current?.onError?.(arg),\n sendAutomaticallyWhen: (arg) =>\n callbacksRef.current?.sendAutomaticallyWhen?.(arg) ?? false,\n });\n\nexport const useChatThread = <UI_MESSAGE extends UIMessage = UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n env: ChatThreadEnvironment<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n throttle,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onRespondToToolApproval,\n onResumeError,\n joinStrategy,\n messageRepository,\n unstable_onBranchChange,\n chatInit: chatOptions,\n } = splitChatThreadOptions(options);\n\n const {\n id,\n isMainThread,\n getThreadListItem,\n stopOnClientDestroy = true,\n hostDestroySignal,\n chat: externalChat,\n messageRepositoryInstance,\n } = env;\n\n // Wiring below is per thread and mutated on the instance, so a transport\n // shared across simultaneously mounted threads is last-writer-wins. A\n // caller-owned chat is already bound to its own clone, so cloning again\n // here would wire a copy the chat never sends through.\n const sourceTransport = useMemo(\n () =>\n transportOptions === undefined\n ? new AssistantChatTransport()\n : externalChat === undefined &&\n transportOptions instanceof AssistantChatTransport\n ? transportOptions.__internal_clone()\n : transportOptions,\n [transportOptions, externalChat],\n );\n const transport = useDynamicChatTransport(sourceTransport);\n\n const latestChatOptionsRef = useRef(chatOptions);\n useEffect(() => {\n latestChatOptionsRef.current = chatOptions;\n });\n // `useChat` stops a chat it constructs whenever it unmounts, and a\n // resource's soft unmount runs that cleanup, so the thread owns its chat.\n const [ownedChat] = useState(\n () =>\n externalChat ??\n createChat({ ...chatOptions, id, transport }, latestChatOptionsRef),\n );\n\n const chat = useChat({\n chat: externalChat ?? ownedChat,\n ...(throttle !== undefined && { throttle }),\n });\n\n useResourceCleanup(\n stopOnClientDestroy,\n () => {\n void chat.stop().catch(() => {});\n },\n hostDestroySignal,\n );\n\n const runtime = useAISDKRuntime(chat, {\n adapters,\n ...pickExternalStoreSharedOptions(options ?? {}),\n ...(toCreateMessage && { toCreateMessage }),\n ...(onResume && { onResume }),\n ...(onResumeToolCall && { onResumeToolCall }),\n ...(onRespondToToolApproval && { onRespondToToolApproval }),\n ...(joinStrategy && { joinStrategy }),\n ...(messageRepository && { messageRepository }),\n ...(messageRepositoryInstance && {\n unstable_messageRepositoryInstance: messageRepositoryInstance,\n }),\n ...(unstable_onBranchChange && { unstable_onBranchChange }),\n });\n\n // Wire in render, not an effect: a send from a descendant's mount effect\n // runs before this hook's effect would (effects fire child-first), and must\n // see a wired transport. The clone is per thread, so a discarded render's\n // wiring is discarded with it and the committed render re-wires the same\n // instance.\n if (sourceTransport instanceof AssistantChatTransport) {\n sourceTransport.setRuntime(runtime);\n sourceTransport.__internal_setGetThreadListItem(getThreadListItem);\n }\n\n const subscribeToRuntime = useCallback(\n (callback: () => void) => runtime.thread.subscribe(callback),\n [runtime],\n );\n const getHistoryLoadingSnapshot = useCallback(\n () => runtime.thread.getState().isLoading,\n [runtime],\n );\n const isLoadingHistory = useSyncExternalStore(\n subscribeToRuntime,\n getHistoryLoadingSnapshot,\n getHistoryLoadingSnapshot,\n );\n\n const resumableStorage = useMemo(\n () => getResumableAdapter(sourceTransport)?.storage,\n [sourceTransport],\n );\n const subscribeToResumableStorage = useCallback(\n (callback: () => void) =>\n isMainThread\n ? (resumableStorage?.subscribe?.(callback, id) ?? (() => {}))\n : () => {},\n [id, isMainThread, resumableStorage],\n );\n const getPendingStreamId = useCallback(\n () => (isMainThread ? (resumableStorage?.getStreamId(id) ?? null) : null),\n [id, isMainThread, resumableStorage],\n );\n const pendingStreamId = useSyncExternalStore(\n subscribeToResumableStorage,\n getPendingStreamId,\n getNoPendingStreamId,\n );\n const isChatRunning =\n chat.status === \"submitted\" || chat.status === \"streaming\";\n\n const resumedStreamIds = useMemo(\n () => getResumedStreamIds(resumableStorage),\n [resumableStorage],\n );\n const onResumeErrorRef = useRef(onResumeError);\n useEffect(() => {\n onResumeErrorRef.current = onResumeError;\n });\n useEffect(() => {\n if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) {\n return;\n }\n if (isChatRunning) {\n resumedStreamIds.add(pendingStreamId);\n return;\n }\n if (isLoadingHistory) return;\n resumedStreamIds.add(pendingStreamId);\n chat.resumeStream().catch((err: unknown) => {\n console.warn(\"[assistant-ui] resumable: resume failed\", err);\n try {\n onResumeErrorRef.current?.(err);\n } catch (callbackError) {\n console.error(\n \"[assistant-ui] resumable: onResumeError callback failed\",\n callbackError,\n );\n } finally {\n if (resumableStorage?.getStreamId(id) === pendingStreamId) {\n resumableStorage.clear(id);\n }\n }\n });\n }, [\n chat,\n id,\n isChatRunning,\n isLoadingHistory,\n pendingStreamId,\n resumableStorage,\n resumedStreamIds,\n ]);\n\n return runtime;\n};\n"],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
declare const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import type { AssistantRuntime, ThreadHistoryAdapter, ThreadMessage, MessageFormatAdapter, MessageFormatRepository, ExportedMessageRepository, RespondToToolApprovalOptions, Unstable_ToolInteractionLog } from "@assistant-ui/core";
|
|
2
|
+
import { type RefObject } from "react";
|
|
3
|
+
export declare const toExportedMessageRepository: <TMessage>(toThreadMessages: (messages: TMessage[]) => ThreadMessage[], messages: MessageFormatRepository<TMessage>) => ExportedMessageRepository;
|
|
4
|
+
export declare const useExternalHistory: <TMessage>(runtimeRef: RefObject<AssistantRuntime>, historyAdapter: ThreadHistoryAdapter | undefined, toThreadMessages: (messages: TMessage[]) => ThreadMessage[], storageFormatAdapter: MessageFormatAdapter<TMessage, any>, onSetMessages: (messages: TMessage[]) => void, toolArtifacts?: Map<string, unknown>, onToolArtifactsRestored?: () => void, toolInteractions?: Map<string, Unstable_ToolInteractionLog>, onToolInteractionsRestored?: () => void, toolApprovalResponses?: Map<string, RespondToToolApprovalOptions>, onToolApprovalResponsesRestored?: () => void) => {
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
deleteMessage: (messageId: string) => Promise<void>;
|
|
7
|
+
persistToolInteractions: (messageId: string) => Promise<void>;
|
|
8
|
+
persistToolApprovalResponses: (messageId: string) => Promise<void>;
|
|
8
9
|
};
|
|
9
|
-
//#endregion
|
|
10
|
-
export { toExportedMessageRepository, useExternalHistory };
|
|
11
10
|
//# sourceMappingURL=useExternalHistory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExternalHistory.d.ts","
|
|
1
|
+
{"version":3,"file":"useExternalHistory.d.ts","sourceRoot":"","sources":["../../src/runtime/useExternalHistory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EAEpB,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,2BAA2B,EAC5B,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAIL,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAEf,eAAO,MAAM,2BAA2B,GAAI,QAAQ,EAClD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,UAAU,uBAAuB,CAAC,QAAQ,CAAC,KAC1C,yBAoBF,CAAC;AAwNF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,EACzC,YAAY,SAAS,CAAC,gBAAgB,CAAC,EACvC,gBAAgB,oBAAoB,GAAG,SAAS,EAChD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,sBAAsB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,EACzD,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,EAC7C,gBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,0BAA0B,MAAM,IAAI,EACpC,mBAAmB,GAAG,CAAC,MAAM,EAAE,2BAA2B,CAAC,EAC3D,6BAA6B,MAAM,IAAI,EACvC,wBAAwB,GAAG,CAAC,MAAM,EAAE,4BAA4B,CAAC,EACjE,kCAAkC,MAAM,IAAI;;+BA+dxB,MAAM;yCA3UZ,MAAM;8CAAN,MAAM;CAyXrB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { MessageRepository, readToolInteractionLog } from "@assistant-ui/core/internal";
|
|
3
3
|
import { getExternalStoreMessages } from "@assistant-ui/core";
|
|
4
|
-
import {
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
5
5
|
import { useAui } from "@assistant-ui/store";
|
|
6
6
|
//#region src/runtime/useExternalHistory.ts
|
|
7
7
|
const toExportedMessageRepository = (toThreadMessages, messages) => {
|
|
@@ -25,8 +25,107 @@ const toExportedMessageRepository = (toThreadMessages, messages) => {
|
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
27
|
const isAwaitingToolApproval = (message) => message.status?.type === "requires-action" && message.status.reason === "tool-calls";
|
|
28
|
+
const isTerminalMessage = (message) => message.status === void 0 || message.status.type === "complete" || message.status.type === "incomplete";
|
|
29
|
+
const TOOL_ARTIFACTS_METADATA_KEY = "__aui_toolArtifacts";
|
|
30
|
+
const TOOL_INTERACTIONS_METADATA_KEY = "__aui_toolInteractions";
|
|
31
|
+
const TOOL_APPROVAL_RESPONSES_METADATA_KEY = "__aui_toolApprovalResponses";
|
|
32
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
|
+
const collectToolArtifacts = (message, toolArtifacts) => {
|
|
34
|
+
if (!toolArtifacts) return void 0;
|
|
35
|
+
const entries = message.content.flatMap((part) => {
|
|
36
|
+
if (part.type !== "tool-call") return [];
|
|
37
|
+
const artifact = toolArtifacts.get(part.toolCallId);
|
|
38
|
+
return artifact === void 0 ? [] : [[part.toolCallId, artifact]];
|
|
39
|
+
});
|
|
40
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
41
|
+
};
|
|
42
|
+
const collectToolInteractions = (message, toolInteractions) => {
|
|
43
|
+
if (!toolInteractions) return void 0;
|
|
44
|
+
const entries = message.content.flatMap((part) => {
|
|
45
|
+
if (part.type !== "tool-call") return [];
|
|
46
|
+
const interactions = toolInteractions.get(part.toolCallId);
|
|
47
|
+
return interactions === void 0 ? [] : [[part.toolCallId, interactions]];
|
|
48
|
+
});
|
|
49
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
50
|
+
};
|
|
51
|
+
const collectToolApprovalResponses = (message, toolApprovalResponses) => {
|
|
52
|
+
if (!toolApprovalResponses) return void 0;
|
|
53
|
+
const entries = message.content.flatMap((part) => {
|
|
54
|
+
if (part.type !== "tool-call" || !part.approval) return [];
|
|
55
|
+
const response = toolApprovalResponses.get(part.approval.id);
|
|
56
|
+
if (!response) return [];
|
|
57
|
+
return [[part.approval.id, {
|
|
58
|
+
approved: response.approved,
|
|
59
|
+
...response.optionId != null && { optionId: response.optionId },
|
|
60
|
+
...response.text != null && { text: response.text },
|
|
61
|
+
...response.reason != null && { reason: response.reason }
|
|
62
|
+
}]];
|
|
63
|
+
});
|
|
64
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
65
|
+
};
|
|
66
|
+
const addToolData = (message, toolArtifacts, toolInteractions, toolApprovalResponses) => {
|
|
67
|
+
if (!toolArtifacts && !toolInteractions && !toolApprovalResponses || !isRecord(message) || !Array.isArray(message.parts)) return message;
|
|
68
|
+
const toolCallIds = message.parts.flatMap((part) => {
|
|
69
|
+
if (!isRecord(part) || typeof part.toolCallId !== "string") return [];
|
|
70
|
+
return [part.toolCallId];
|
|
71
|
+
});
|
|
72
|
+
const artifacts = toolArtifacts ? Object.fromEntries(toolCallIds.flatMap((toolCallId) => Object.hasOwn(toolArtifacts, toolCallId) ? [[toolCallId, toolArtifacts[toolCallId]]] : [])) : void 0;
|
|
73
|
+
const interactions = toolInteractions ? Object.fromEntries(toolCallIds.flatMap((toolCallId) => Object.hasOwn(toolInteractions, toolCallId) ? [[toolCallId, toolInteractions[toolCallId]]] : [])) : void 0;
|
|
74
|
+
const approvalIds = message.parts.flatMap((part) => {
|
|
75
|
+
if (!isRecord(part) || !isRecord(part.approval)) return [];
|
|
76
|
+
const approvalId = part.approval.id;
|
|
77
|
+
return typeof approvalId === "string" ? [approvalId] : [];
|
|
78
|
+
});
|
|
79
|
+
const approvalResponses = toolApprovalResponses ? Object.fromEntries(approvalIds.flatMap((approvalId) => Object.hasOwn(toolApprovalResponses, approvalId) ? [[approvalId, toolApprovalResponses[approvalId]]] : [])) : void 0;
|
|
80
|
+
const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;
|
|
81
|
+
const hasInteractions = !!interactions && Object.keys(interactions).length > 0;
|
|
82
|
+
const hasApprovalResponses = !!approvalResponses && Object.keys(approvalResponses).length > 0;
|
|
83
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses) return message;
|
|
84
|
+
const metadata = isRecord(message.metadata) ? message.metadata : {};
|
|
85
|
+
return {
|
|
86
|
+
...message,
|
|
87
|
+
metadata: {
|
|
88
|
+
...metadata,
|
|
89
|
+
...hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts },
|
|
90
|
+
...hasInteractions && { [TOOL_INTERACTIONS_METADATA_KEY]: interactions },
|
|
91
|
+
...hasApprovalResponses && { [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses }
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const restoreToolData = (message, toolArtifacts, toolInteractions, toolApprovalResponses) => {
|
|
96
|
+
if (!isRecord(message) || !isRecord(message.metadata)) return message;
|
|
97
|
+
const metadata = message.metadata;
|
|
98
|
+
const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);
|
|
99
|
+
const hasInteractions = Object.hasOwn(metadata, TOOL_INTERACTIONS_METADATA_KEY);
|
|
100
|
+
const hasApprovalResponses = Object.hasOwn(metadata, TOOL_APPROVAL_RESPONSES_METADATA_KEY);
|
|
101
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses) return message;
|
|
102
|
+
const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];
|
|
103
|
+
if (toolArtifacts && isRecord(artifacts)) for (const [toolCallId, artifact] of Object.entries(artifacts)) toolArtifacts.set(toolCallId, artifact);
|
|
104
|
+
const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];
|
|
105
|
+
if (toolInteractions && isRecord(interactions)) for (const [toolCallId, value] of Object.entries(interactions)) {
|
|
106
|
+
const log = readToolInteractionLog(value);
|
|
107
|
+
if (log) toolInteractions.set(toolCallId, log);
|
|
108
|
+
}
|
|
109
|
+
const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];
|
|
110
|
+
if (toolApprovalResponses && isRecord(approvalResponses)) for (const [approvalId, value] of Object.entries(approvalResponses)) {
|
|
111
|
+
if (!isRecord(value) || typeof value.approved !== "boolean") continue;
|
|
112
|
+
toolApprovalResponses.set(approvalId, {
|
|
113
|
+
approvalId,
|
|
114
|
+
approved: value.approved,
|
|
115
|
+
...typeof value.optionId === "string" && { optionId: value.optionId },
|
|
116
|
+
...typeof value.text === "string" && { text: value.text },
|
|
117
|
+
...typeof value.reason === "string" && { reason: value.reason }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const { [TOOL_ARTIFACTS_METADATA_KEY]: _, [TOOL_INTERACTIONS_METADATA_KEY]: __, [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___, ...restMetadata } = metadata;
|
|
121
|
+
const { metadata: _metadata, ...restMessage } = message;
|
|
122
|
+
return Object.keys(restMetadata).length === 0 ? restMessage : {
|
|
123
|
+
...restMessage,
|
|
124
|
+
metadata: restMetadata
|
|
125
|
+
};
|
|
126
|
+
};
|
|
28
127
|
const encodeContent = (storageFormatAdapter, item) => JSON.stringify(storageFormatAdapter.encode(item));
|
|
29
|
-
const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageFormatAdapter, onSetMessages) => {
|
|
128
|
+
const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageFormatAdapter, onSetMessages, toolArtifacts, onToolArtifactsRestored, toolInteractions, onToolInteractionsRestored, toolApprovalResponses, onToolApprovalResponsesRestored) => {
|
|
30
129
|
const loadedRef = useRef(false);
|
|
31
130
|
const [itemEpoch, setItemEpoch] = useState(0);
|
|
32
131
|
const aui = useAui();
|
|
@@ -50,12 +149,26 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
50
149
|
const loadHistory = async () => {
|
|
51
150
|
try {
|
|
52
151
|
const repo = await formatAdapter.load();
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
152
|
+
toolArtifacts?.clear();
|
|
153
|
+
toolInteractions?.clear();
|
|
154
|
+
toolApprovalResponses?.clear();
|
|
155
|
+
const restoredMessages = repo?.messages.map((item) => ({
|
|
156
|
+
...item,
|
|
157
|
+
message: restoreToolData(item.message, toolArtifacts, toolInteractions, toolApprovalResponses)
|
|
158
|
+
})) ?? [];
|
|
159
|
+
onToolArtifactsRestored?.();
|
|
160
|
+
onToolInteractionsRestored?.();
|
|
161
|
+
onToolApprovalResponsesRestored?.();
|
|
162
|
+
if (repo && restoredMessages.length > 0) {
|
|
163
|
+
const restoredRepo = {
|
|
164
|
+
...repo,
|
|
165
|
+
messages: restoredMessages
|
|
166
|
+
};
|
|
167
|
+
for (const [index, m] of repo.messages.entries()) persistedInnerMessages.current.set(storageFormatAdapter.getId(m.message), {
|
|
168
|
+
source: restoredMessages[index].message,
|
|
56
169
|
content: encodeContent(storageFormatAdapter, m)
|
|
57
170
|
});
|
|
58
|
-
const converted = toExportedMessageRepository(toThreadMessages,
|
|
171
|
+
const converted = toExportedMessageRepository(toThreadMessages, restoredRepo);
|
|
59
172
|
runtimeRef.current.thread.import(converted);
|
|
60
173
|
const tempRepo = new MessageRepository();
|
|
61
174
|
tempRepo.import(converted);
|
|
@@ -95,7 +208,13 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
95
208
|
optionalThreadListItem,
|
|
96
209
|
aui,
|
|
97
210
|
itemEpoch,
|
|
98
|
-
storageFormatAdapter
|
|
211
|
+
storageFormatAdapter,
|
|
212
|
+
toolArtifacts,
|
|
213
|
+
onToolArtifactsRestored,
|
|
214
|
+
toolInteractions,
|
|
215
|
+
onToolInteractionsRestored,
|
|
216
|
+
toolApprovalResponses,
|
|
217
|
+
onToolApprovalResponsesRestored
|
|
99
218
|
]);
|
|
100
219
|
const runStartRef = useRef(null);
|
|
101
220
|
const persistTimerRef = useRef(null);
|
|
@@ -103,9 +222,55 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
103
222
|
const stepBoundariesRef = useRef([]);
|
|
104
223
|
const wasRunningRef = useRef(false);
|
|
105
224
|
const toolCallCountRef = useRef(0);
|
|
225
|
+
const persistToolInteractions = useCallback((messageId) => {
|
|
226
|
+
const persistence = persistInFlightRef.current.then(async () => {
|
|
227
|
+
if (!formatAdapter?.update) return;
|
|
228
|
+
const messages_0 = runtimeRef.current.thread.getState().messages;
|
|
229
|
+
const message = messages_0.find((item_0) => item_0.id === messageId || getExternalStoreMessages(item_0).some((innerMessage) => storageFormatAdapter.getId(innerMessage) === messageId));
|
|
230
|
+
if (!message) return;
|
|
231
|
+
const previousMessages = messages_0.slice(0, messages_0.indexOf(message)).flatMap(getExternalStoreMessages);
|
|
232
|
+
let parentId = previousMessages.at(-1) ? storageFormatAdapter.getId(previousMessages.at(-1)) : null;
|
|
233
|
+
const storedToolArtifacts = collectToolArtifacts(message, toolArtifacts);
|
|
234
|
+
const storedToolInteractions = collectToolInteractions(message, toolInteractions);
|
|
235
|
+
const storedToolApprovalResponses = collectToolApprovalResponses(message, toolApprovalResponses);
|
|
236
|
+
for (const innerMessage_0 of getExternalStoreMessages(message)) {
|
|
237
|
+
const item_1 = {
|
|
238
|
+
parentId,
|
|
239
|
+
message: addToolData(innerMessage_0, storedToolArtifacts, storedToolInteractions, storedToolApprovalResponses)
|
|
240
|
+
};
|
|
241
|
+
const innerId = storageFormatAdapter.getId(item_1.message);
|
|
242
|
+
const persisted = persistedInnerMessages.current.get(innerId);
|
|
243
|
+
if (persisted) {
|
|
244
|
+
const content = encodeContent(storageFormatAdapter, item_1);
|
|
245
|
+
if (content === persisted.content) persisted.source = item_1.message;
|
|
246
|
+
else {
|
|
247
|
+
await formatAdapter.update(item_1, innerId);
|
|
248
|
+
persistedInnerMessages.current.set(innerId, {
|
|
249
|
+
source: item_1.message,
|
|
250
|
+
content
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
parentId = innerId;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
persistInFlightRef.current = persistence.catch(() => {});
|
|
258
|
+
return persistence.catch((error_0) => {
|
|
259
|
+
console.error("Failed to persist tool data:", error_0);
|
|
260
|
+
});
|
|
261
|
+
}, [
|
|
262
|
+
formatAdapter,
|
|
263
|
+
runtimeRef,
|
|
264
|
+
storageFormatAdapter,
|
|
265
|
+
toolArtifacts,
|
|
266
|
+
toolInteractions,
|
|
267
|
+
toolApprovalResponses
|
|
268
|
+
]);
|
|
106
269
|
useEffect(() => {
|
|
107
270
|
if (!formatAdapter) return;
|
|
108
271
|
const adapter = formatAdapter;
|
|
272
|
+
const idleScheduledInnerIds = /* @__PURE__ */ new Set();
|
|
273
|
+
const unpersistedInnerIds = (message_0) => isTerminalMessage(message_0) ? getExternalStoreMessages(message_0).map((innerMessage_1) => storageFormatAdapter.getId(innerMessage_1)).filter((innerId_0) => !persistedInnerMessages.current.has(innerId_0)) : [];
|
|
109
274
|
const unsubscribe = runtimeRef.current.thread.subscribe(() => {
|
|
110
275
|
const threadState_0 = runtimeRef.current.thread.getState();
|
|
111
276
|
const { isRunning } = threadState_0;
|
|
@@ -134,8 +299,13 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
134
299
|
}
|
|
135
300
|
return;
|
|
136
301
|
}
|
|
137
|
-
if (
|
|
138
|
-
|
|
302
|
+
if (wasRunning) {
|
|
303
|
+
if (runStartRef.current != null) stepBoundariesRef.current.push(Date.now() - runStartRef.current);
|
|
304
|
+
} else {
|
|
305
|
+
const pending = threadState_0.messages.flatMap(unpersistedInnerIds);
|
|
306
|
+
if (pending.every((innerId_1) => idleScheduledInnerIds.has(innerId_1))) return;
|
|
307
|
+
for (const innerId_2 of pending) idleScheduledInnerIds.add(innerId_2);
|
|
308
|
+
}
|
|
139
309
|
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
140
310
|
persistTimerRef.current = setTimeout(() => {
|
|
141
311
|
persistSettled(false);
|
|
@@ -180,58 +350,61 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
180
350
|
...stepTimestamps != null ? { stepTimestamps } : void 0
|
|
181
351
|
};
|
|
182
352
|
persistInFlightRef.current = persistInFlightRef.current.then(async () => {
|
|
183
|
-
const { messages:
|
|
353
|
+
const { messages: messages_1 } = latest;
|
|
184
354
|
let lastInnerMessageId = null;
|
|
185
355
|
const getLastInnerId = (msgs) => msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)) : null;
|
|
186
|
-
const toBatchItems = (msgs_0) => msgs_0.map((msg, idx) => ({
|
|
356
|
+
const toBatchItems = (msgs_0, toolArtifacts_0, toolInteractions_0, toolApprovalResponses_0) => msgs_0.map((msg, idx) => ({
|
|
187
357
|
parentId: idx === 0 ? lastInnerMessageId : storageFormatAdapter.getId(msgs_0[idx - 1]),
|
|
188
|
-
message: msg
|
|
358
|
+
message: addToolData(msg, toolArtifacts_0, toolInteractions_0, toolApprovalResponses_0)
|
|
189
359
|
}));
|
|
190
|
-
for (const
|
|
191
|
-
const innerMessages = getExternalStoreMessages(
|
|
192
|
-
const isTerminal =
|
|
193
|
-
const isAwaitingToolCalls = isAwaitingToolApproval(
|
|
360
|
+
for (const message_1 of messages_1) {
|
|
361
|
+
const innerMessages = getExternalStoreMessages(message_1);
|
|
362
|
+
const isTerminal = isTerminalMessage(message_1);
|
|
363
|
+
const isAwaitingToolCalls = isAwaitingToolApproval(message_1);
|
|
194
364
|
if (!(isTerminal || isAwaitingToolCalls && adapter.update !== void 0)) {
|
|
195
365
|
lastInnerMessageId = getLastInnerId(innerMessages) ?? lastInnerMessageId;
|
|
196
366
|
continue;
|
|
197
367
|
}
|
|
198
|
-
if (!historyIds.current.has(
|
|
199
|
-
historyIds.current.add(
|
|
200
|
-
deferredTelemetryIds.current.add(
|
|
368
|
+
if (!historyIds.current.has(message_1.id)) {
|
|
369
|
+
historyIds.current.add(message_1.id);
|
|
370
|
+
deferredTelemetryIds.current.add(message_1.id);
|
|
201
371
|
}
|
|
202
|
-
const batchItems = toBatchItems(innerMessages);
|
|
203
|
-
for (const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
if (!
|
|
207
|
-
await adapter.append(
|
|
208
|
-
persistedInnerMessages.current.set(
|
|
209
|
-
source:
|
|
210
|
-
content: encodeContent(storageFormatAdapter,
|
|
372
|
+
const batchItems = toBatchItems(innerMessages, collectToolArtifacts(message_1, toolArtifacts), collectToolInteractions(message_1, toolInteractions), collectToolApprovalResponses(message_1, toolApprovalResponses));
|
|
373
|
+
for (const item_2 of batchItems) {
|
|
374
|
+
const innerId_3 = storageFormatAdapter.getId(item_2.message);
|
|
375
|
+
const persisted_0 = persistedInnerMessages.current.get(innerId_3);
|
|
376
|
+
if (!persisted_0) {
|
|
377
|
+
await adapter.append(item_2);
|
|
378
|
+
persistedInnerMessages.current.set(innerId_3, {
|
|
379
|
+
source: item_2.message,
|
|
380
|
+
content: encodeContent(storageFormatAdapter, item_2)
|
|
211
381
|
});
|
|
212
|
-
} else if (
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
382
|
+
} else if (persisted_0.source !== item_2.message && durationMs !== void 0 && adapter.update) {
|
|
383
|
+
const content_0 = encodeContent(storageFormatAdapter, item_2);
|
|
384
|
+
if (content_0 === persisted_0.content) persisted_0.source = item_2.message;
|
|
215
385
|
else try {
|
|
216
|
-
await adapter.update(
|
|
217
|
-
persistedInnerMessages.current.set(
|
|
218
|
-
source:
|
|
219
|
-
content
|
|
386
|
+
await adapter.update(item_2, innerId_3);
|
|
387
|
+
persistedInnerMessages.current.set(innerId_3, {
|
|
388
|
+
source: item_2.message,
|
|
389
|
+
content: content_0
|
|
220
390
|
});
|
|
221
391
|
} catch {}
|
|
222
392
|
}
|
|
223
393
|
}
|
|
224
394
|
lastInnerMessageId = getLastInnerId(innerMessages) ?? lastInnerMessageId;
|
|
225
|
-
if (deferredTelemetryIds.current.has(
|
|
226
|
-
deferredTelemetryIds.current.delete(
|
|
227
|
-
adapter.reportTelemetry?.(batchItems, {
|
|
395
|
+
if (deferredTelemetryIds.current.has(message_1.id) && isTerminal) {
|
|
396
|
+
deferredTelemetryIds.current.delete(message_1.id);
|
|
397
|
+
adapter.reportTelemetry?.(batchItems.map((item_3, index_0) => ({
|
|
398
|
+
...item_3,
|
|
399
|
+
message: innerMessages[index_0]
|
|
400
|
+
})), {
|
|
228
401
|
...telemetryOptions,
|
|
229
|
-
message
|
|
402
|
+
message: message_1
|
|
230
403
|
});
|
|
231
404
|
}
|
|
232
405
|
}
|
|
233
|
-
}).catch((
|
|
234
|
-
console.error("Failed to persist message history:",
|
|
406
|
+
}).catch((error_1) => {
|
|
407
|
+
console.error("Failed to persist message history:", error_1);
|
|
235
408
|
});
|
|
236
409
|
}
|
|
237
410
|
return () => {
|
|
@@ -245,31 +418,34 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
245
418
|
}, [
|
|
246
419
|
formatAdapter,
|
|
247
420
|
storageFormatAdapter,
|
|
248
|
-
runtimeRef
|
|
421
|
+
runtimeRef,
|
|
422
|
+
toolArtifacts,
|
|
423
|
+
toolInteractions,
|
|
424
|
+
toolApprovalResponses
|
|
249
425
|
]);
|
|
250
426
|
return {
|
|
251
427
|
isLoading,
|
|
252
|
-
deleteMessage: useCallback(async (
|
|
428
|
+
deleteMessage: useCallback(async (messageId_0) => {
|
|
253
429
|
const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);
|
|
254
430
|
if (!deleteMessages) return;
|
|
255
|
-
const
|
|
256
|
-
const messageIndex =
|
|
431
|
+
const messages_2 = runtimeRef.current.thread.getState().messages;
|
|
432
|
+
const messageIndex = messages_2.findIndex((m_2) => m_2.id === messageId_0);
|
|
257
433
|
if (messageIndex === -1) return;
|
|
258
|
-
const previousInnerMessages =
|
|
259
|
-
let
|
|
260
|
-
const itemsToDelete = getExternalStoreMessages(
|
|
261
|
-
const
|
|
262
|
-
parentId,
|
|
263
|
-
message:
|
|
434
|
+
const previousInnerMessages = messages_2.slice(0, messageIndex).flatMap(getExternalStoreMessages);
|
|
435
|
+
let parentId_0 = previousInnerMessages.at(-1) ? storageFormatAdapter.getId(previousInnerMessages.at(-1)) : null;
|
|
436
|
+
const itemsToDelete = getExternalStoreMessages(messages_2[messageIndex]).map((message_2) => {
|
|
437
|
+
const item_4 = {
|
|
438
|
+
parentId: parentId_0,
|
|
439
|
+
message: message_2
|
|
264
440
|
};
|
|
265
|
-
|
|
266
|
-
return
|
|
441
|
+
parentId_0 = storageFormatAdapter.getId(message_2);
|
|
442
|
+
return item_4;
|
|
267
443
|
});
|
|
268
444
|
const deletion = persistInFlightRef.current.then(async () => {
|
|
269
445
|
await deleteMessages(itemsToDelete);
|
|
270
|
-
historyIds.current.delete(
|
|
271
|
-
deferredTelemetryIds.current.delete(
|
|
272
|
-
for (const
|
|
446
|
+
historyIds.current.delete(messageId_0);
|
|
447
|
+
deferredTelemetryIds.current.delete(messageId_0);
|
|
448
|
+
for (const item_5 of itemsToDelete) persistedInnerMessages.current.delete(storageFormatAdapter.getId(item_5.message));
|
|
273
449
|
});
|
|
274
450
|
persistInFlightRef.current = deletion.catch(() => {});
|
|
275
451
|
await deletion;
|
|
@@ -277,7 +453,9 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
277
453
|
formatAdapter,
|
|
278
454
|
runtimeRef,
|
|
279
455
|
storageFormatAdapter
|
|
280
|
-
])
|
|
456
|
+
]),
|
|
457
|
+
persistToolInteractions,
|
|
458
|
+
persistToolApprovalResponses: persistToolInteractions
|
|
281
459
|
};
|
|
282
460
|
};
|
|
283
461
|
//#endregion
|