@assistant-ui/ai-sdk 0.0.1
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 +21 -0
- package/README.md +32 -0
- package/dist/adapters/aiSDKFormatAdapter.d.ts +8 -0
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/aiSDKFormatAdapter.js +26 -0
- package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +6 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.js +37 -0
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
- package/dist/aiSDKExtras.d.ts +11 -0
- package/dist/aiSDKExtras.d.ts.map +1 -0
- package/dist/aiSDKExtras.js +7 -0
- package/dist/aiSDKExtras.js.map +1 -0
- package/dist/converters/convertMessage.d.ts +28 -0
- package/dist/converters/convertMessage.d.ts.map +1 -0
- package/dist/converters/convertMessage.js +249 -0
- package/dist/converters/convertMessage.js.map +1 -0
- package/dist/converters/modelContentEnvelope.d.ts +16 -0
- package/dist/converters/modelContentEnvelope.d.ts.map +1 -0
- package/dist/converters/modelContentEnvelope.js +22 -0
- package/dist/converters/modelContentEnvelope.js.map +1 -0
- package/dist/converters/toCreateMessage.d.ts +7 -0
- package/dist/converters/toCreateMessage.d.ts.map +1 -0
- package/dist/converters/toCreateMessage.js +58 -0
- package/dist/converters/toCreateMessage.js.map +1 -0
- package/dist/converters/toolOutputConversion.d.ts +29 -0
- package/dist/converters/toolOutputConversion.d.ts.map +1 -0
- package/dist/converters/toolOutputConversion.js +31 -0
- package/dist/converters/toolOutputConversion.js.map +1 -0
- package/dist/hooks.d.ts +19 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +20 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +13 -0
- package/dist/index.native.d.ts +12 -0
- package/dist/index.native.js +11 -0
- package/dist/model-context/injectInteractableContext.d.ts +37 -0
- package/dist/model-context/injectInteractableContext.d.ts.map +1 -0
- package/dist/model-context/injectInteractableContext.js +52 -0
- package/dist/model-context/injectInteractableContext.js.map +1 -0
- package/dist/model-context/injectQuoteContext.d.ts +27 -0
- package/dist/model-context/injectQuoteContext.d.ts.map +1 -0
- package/dist/model-context/injectQuoteContext.js +51 -0
- package/dist/model-context/injectQuoteContext.js.map +1 -0
- package/dist/runtime/AISDKChat.d.ts +16 -0
- package/dist/runtime/AISDKChat.d.ts.map +1 -0
- package/dist/runtime/AISDKChat.js +69 -0
- package/dist/runtime/AISDKChat.js.map +1 -0
- package/dist/runtime/AISDKThreads.d.ts +45 -0
- package/dist/runtime/AISDKThreads.d.ts.map +1 -0
- package/dist/runtime/AISDKThreads.js +203 -0
- package/dist/runtime/AISDKThreads.js.map +1 -0
- package/dist/runtime/useAISDKRuntime.d.ts +49 -0
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
- package/dist/runtime/useAISDKRuntime.js +266 -0
- package/dist/runtime/useAISDKRuntime.js.map +1 -0
- package/dist/runtime/useChatRuntime.d.ts +13 -0
- package/dist/runtime/useChatRuntime.d.ts.map +1 -0
- package/dist/runtime/useChatRuntime.js +52 -0
- package/dist/runtime/useChatRuntime.js.map +1 -0
- package/dist/runtime/useChatThread.d.ts +77 -0
- package/dist/runtime/useChatThread.d.ts.map +1 -0
- package/dist/runtime/useChatThread.js +134 -0
- package/dist/runtime/useChatThread.js.map +1 -0
- package/dist/runtime/useExternalHistory.d.ts +11 -0
- package/dist/runtime/useExternalHistory.d.ts.map +1 -0
- package/dist/runtime/useExternalHistory.js +278 -0
- package/dist/runtime/useExternalHistory.js.map +1 -0
- package/dist/runtime/useResourceCleanup.d.ts +5 -0
- package/dist/runtime/useResourceCleanup.d.ts.map +1 -0
- package/dist/runtime/useResourceCleanup.js +47 -0
- package/dist/runtime/useResourceCleanup.js.map +1 -0
- package/dist/runtime/useStreamingTiming.d.ts +15 -0
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
- package/dist/runtime/useStreamingTiming.js +38 -0
- package/dist/runtime/useStreamingTiming.js.map +1 -0
- package/dist/tools/frontendTools.d.ts +33 -0
- package/dist/tools/frontendTools.d.ts.map +1 -0
- package/dist/tools/frontendTools.js +33 -0
- package/dist/tools/frontendTools.js.map +1 -0
- package/dist/tools/generativeTools.d.ts +73 -0
- package/dist/tools/generativeTools.d.ts.map +1 -0
- package/dist/tools/generativeTools.js +245 -0
- package/dist/tools/generativeTools.js.map +1 -0
- package/dist/tools/mcp-stdio.node.d.ts +2 -0
- package/dist/tools/mcp-stdio.node.js +2 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +6 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.js +11 -0
- package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
- package/dist/transport/AssistantChatTransport.d.ts +23 -0
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
- package/dist/transport/AssistantChatTransport.js +134 -0
- package/dist/transport/AssistantChatTransport.js.map +1 -0
- package/dist/transport/resumable.d.ts +38 -0
- package/dist/transport/resumable.d.ts.map +1 -0
- package/dist/transport/resumable.js +113 -0
- package/dist/transport/resumable.js.map +1 -0
- package/dist/usage.d.ts +19 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +127 -0
- package/dist/usage.js.map +1 -0
- package/dist/utils/getVercelAIMessages.d.ts +7 -0
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
- package/dist/utils/getVercelAIMessages.js +9 -0
- package/dist/utils/getVercelAIMessages.js.map +1 -0
- package/dist/utils/sliceMessagesUntil.d.ts +6 -0
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
- package/dist/utils/sliceMessagesUntil.js +12 -0
- package/dist/utils/sliceMessagesUntil.js.map +1 -0
- package/package.json +100 -0
- package/src/__tests__/standalone.e2e.ts +59 -0
- package/src/adapters/aiSDKFormatAdapter.ts +41 -0
- package/src/adapters/vercelAttachmentAdapter.test.ts +29 -0
- package/src/adapters/vercelAttachmentAdapter.ts +36 -0
- package/src/aiSDKExtras.ts +11 -0
- package/src/converters/convertMessage.test.ts +1130 -0
- package/src/converters/convertMessage.ts +457 -0
- package/src/converters/modelContentEnvelope.ts +41 -0
- package/src/converters/toCreateMessage.test.ts +596 -0
- package/src/converters/toCreateMessage.ts +99 -0
- package/src/converters/toolOutputConversion.ts +26 -0
- package/src/hooks.ts +23 -0
- package/src/index.native.ts +21 -0
- package/src/index.ts +29 -0
- package/src/model-context/injectInteractableContext.test.ts +167 -0
- package/src/model-context/injectInteractableContext.ts +61 -0
- package/src/model-context/injectQuoteContext.test.ts +33 -0
- package/src/model-context/injectQuoteContext.ts +62 -0
- package/src/runtime/AISDKChat.integration.test.tsx +47 -0
- package/src/runtime/AISDKChat.react-compiler.test.ts +49 -0
- package/src/runtime/AISDKChat.test.ts +193 -0
- package/src/runtime/AISDKChat.ts +47 -0
- package/src/runtime/AISDKThreads.cloud.test.ts +136 -0
- package/src/runtime/AISDKThreads.test.ts +548 -0
- package/src/runtime/AISDKThreads.ts +223 -0
- package/src/runtime/__tests__/controlled-transport.ts +43 -0
- package/src/runtime/useAISDKRuntime.approval.test.tsx +69 -0
- package/src/runtime/useAISDKRuntime.test.ts +1021 -0
- package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
- package/src/runtime/useAISDKRuntime.ts +524 -0
- package/src/runtime/useChatOnTap.test.ts +58 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +67 -0
- package/src/runtime/useChatRuntime.test.ts +522 -0
- package/src/runtime/useChatRuntime.ts +49 -0
- package/src/runtime/useChatThread.ts +286 -0
- package/src/runtime/useExternalHistory.test.ts +1289 -0
- package/src/runtime/useExternalHistory.ts +435 -0
- package/src/runtime/useResourceCleanup.ts +32 -0
- package/src/runtime/useStreamingTiming.ts +68 -0
- package/src/tools/frontendTools.test.ts +205 -0
- package/src/tools/frontendTools.ts +83 -0
- package/src/tools/generativeTools.test.ts +785 -0
- package/src/tools/generativeTools.ts +470 -0
- package/src/tools/mcp-stdio.node.ts +1 -0
- package/src/tools/mcp-stdio.unsupported.ts +12 -0
- package/src/transport/AssistantChatTransport.test.ts +300 -0
- package/src/transport/AssistantChatTransport.ts +204 -0
- package/src/transport/resumable.test.ts +262 -0
- package/src/transport/resumable.ts +161 -0
- package/src/usage.test.ts +186 -0
- package/src/usage.ts +174 -0
- package/src/utils/getVercelAIMessages.ts +11 -0
- package/src/utils/sliceMessagesUntil.test.ts +55 -0
- package/src/utils/sliceMessagesUntil.ts +20 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { resource, useResource, withKey } from "@assistant-ui/tap";
|
|
4
|
+
import { useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { Chat, type UIMessage } from "@ai-sdk/react";
|
|
6
|
+
import type { ChatTransport } from "ai";
|
|
7
|
+
import type { AssistantCloud } from "assistant-cloud";
|
|
8
|
+
import {
|
|
9
|
+
InMemoryThreadList,
|
|
10
|
+
RemoteThreadList,
|
|
11
|
+
inMemoryThreadListTransformScopes,
|
|
12
|
+
} from "@assistant-ui/core/store";
|
|
13
|
+
import { ThreadClient } from "@assistant-ui/core/store/internal";
|
|
14
|
+
import { useCloudThreadListAdapter } from "@assistant-ui/core/react";
|
|
15
|
+
import {
|
|
16
|
+
attachTransformScopes,
|
|
17
|
+
useAssistantClientRef,
|
|
18
|
+
useAssistantScopeEffect,
|
|
19
|
+
} from "@assistant-ui/store/client";
|
|
20
|
+
import { useAui } from "@assistant-ui/store";
|
|
21
|
+
import { AssistantChatTransport } from "../transport/AssistantChatTransport";
|
|
22
|
+
import {
|
|
23
|
+
splitChatThreadOptions,
|
|
24
|
+
useChatThread,
|
|
25
|
+
type ChatThreadOptions,
|
|
26
|
+
} from "./useChatThread";
|
|
27
|
+
import { useResourceCleanup } from "./useResourceCleanup";
|
|
28
|
+
|
|
29
|
+
export type AISDKThreadsOptions<UI_MESSAGE extends UIMessage = UIMessage> =
|
|
30
|
+
Omit<ChatThreadOptions<UI_MESSAGE>, "id" | "transport" | "messages"> & {
|
|
31
|
+
/**
|
|
32
|
+
* The transport threads send through. A factory is invoked once per
|
|
33
|
+
* thread so each thread owns its instance. A plain
|
|
34
|
+
* `AssistantChatTransport` instance is cloned per thread (its
|
|
35
|
+
* assistant-ui wiring is per thread); any other transport instance is
|
|
36
|
+
* shared as-is. Defaults to one `AssistantChatTransport` per thread.
|
|
37
|
+
*/
|
|
38
|
+
transport?:
|
|
39
|
+
| ChatTransport<UI_MESSAGE>
|
|
40
|
+
| (() => ChatTransport<UI_MESSAGE>)
|
|
41
|
+
| undefined;
|
|
42
|
+
/**
|
|
43
|
+
* When set, the thread list is a `RemoteThreadList` backed by this
|
|
44
|
+
* assistant-cloud. Omit it to keep the in-memory list. The thread
|
|
45
|
+
* factory is keyed so cloud history reloads on a switch, and an
|
|
46
|
+
* in-flight run does not continue in the background.
|
|
47
|
+
*/
|
|
48
|
+
cloud?: AssistantCloud | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Controlled thread id for the cloud list. Ignored without `cloud`.
|
|
51
|
+
*/
|
|
52
|
+
threadId?: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Called with the settled remote id when the cloud list changes thread.
|
|
55
|
+
*/
|
|
56
|
+
onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type AISDKThreadChatOptions<UI_MESSAGE extends UIMessage = UIMessage> = Omit<
|
|
60
|
+
AISDKThreadsOptions<UI_MESSAGE>,
|
|
61
|
+
"cloud" | "threadId" | "onThreadIdChange"
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
type ChatEntry<UI_MESSAGE extends UIMessage> = {
|
|
65
|
+
chat: Chat<UI_MESSAGE>;
|
|
66
|
+
transport: ChatTransport<UI_MESSAGE>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const createChatEntry = <UI_MESSAGE extends UIMessage>(
|
|
70
|
+
threadId: string,
|
|
71
|
+
options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,
|
|
72
|
+
): ChatEntry<UI_MESSAGE> => {
|
|
73
|
+
const { chatInit } = splitChatThreadOptions(
|
|
74
|
+
options as ChatThreadOptions<UI_MESSAGE> | undefined,
|
|
75
|
+
);
|
|
76
|
+
const transport =
|
|
77
|
+
typeof options?.transport === "function"
|
|
78
|
+
? options.transport()
|
|
79
|
+
: options?.transport === undefined
|
|
80
|
+
? new AssistantChatTransport()
|
|
81
|
+
: options.transport instanceof AssistantChatTransport
|
|
82
|
+
? options.transport.__internal_clone()
|
|
83
|
+
: options.transport;
|
|
84
|
+
return {
|
|
85
|
+
chat: new Chat<UI_MESSAGE>({ ...chatInit, id: threadId, transport }),
|
|
86
|
+
transport,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const getOrCreateChatEntry = <UI_MESSAGE extends UIMessage>(
|
|
91
|
+
threadId: string,
|
|
92
|
+
options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,
|
|
93
|
+
chats: Map<string, ChatEntry<UI_MESSAGE>>,
|
|
94
|
+
): ChatEntry<UI_MESSAGE> => {
|
|
95
|
+
const existing = chats.get(threadId);
|
|
96
|
+
if (existing) return existing;
|
|
97
|
+
const created = createChatEntry(threadId, options);
|
|
98
|
+
chats.set(threadId, created);
|
|
99
|
+
return created;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const useAISDKChatThread = <UI_MESSAGE extends UIMessage = UIMessage>({
|
|
103
|
+
threadId,
|
|
104
|
+
options,
|
|
105
|
+
chats,
|
|
106
|
+
cloud,
|
|
107
|
+
}: {
|
|
108
|
+
threadId: string;
|
|
109
|
+
options: AISDKThreadChatOptions<UI_MESSAGE> | undefined;
|
|
110
|
+
chats: Map<string, ChatEntry<UI_MESSAGE>>;
|
|
111
|
+
cloud: boolean;
|
|
112
|
+
}) => {
|
|
113
|
+
const [owned] = useState(() =>
|
|
114
|
+
cloud ? createChatEntry(threadId, options) : undefined,
|
|
115
|
+
);
|
|
116
|
+
const { chat, transport } =
|
|
117
|
+
owned ?? getOrCreateChatEntry(threadId, options, chats);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (!cloud) return undefined;
|
|
121
|
+
return () => {
|
|
122
|
+
void chat.stop().catch(() => {});
|
|
123
|
+
};
|
|
124
|
+
}, [chat, cloud]);
|
|
125
|
+
|
|
126
|
+
const aui = useAui();
|
|
127
|
+
const fallbackItem = useMemo(
|
|
128
|
+
() => ({
|
|
129
|
+
initialize: async () => ({
|
|
130
|
+
remoteId: threadId,
|
|
131
|
+
externalId: undefined,
|
|
132
|
+
}),
|
|
133
|
+
}),
|
|
134
|
+
[threadId],
|
|
135
|
+
);
|
|
136
|
+
const runtime = useChatThread(
|
|
137
|
+
{ ...options, transport } as ChatThreadOptions<UI_MESSAGE>,
|
|
138
|
+
{
|
|
139
|
+
id: threadId,
|
|
140
|
+
isMainThread: true,
|
|
141
|
+
getThreadListItem: () =>
|
|
142
|
+
cloud
|
|
143
|
+
? aui.threadListItem.source
|
|
144
|
+
? aui.threadListItem
|
|
145
|
+
: undefined
|
|
146
|
+
: fallbackItem,
|
|
147
|
+
chat,
|
|
148
|
+
stopOnClientDestroy: cloud,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const clientRef = useAssistantClientRef();
|
|
153
|
+
useAssistantScopeEffect(
|
|
154
|
+
"modelContext",
|
|
155
|
+
() =>
|
|
156
|
+
runtime.registerModelContextProvider(clientRef.current!.modelContext()),
|
|
157
|
+
[runtime],
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return useResource(ThreadClient({ runtime: runtime.thread }));
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const AISDKChatThread = resource(useAISDKChatThread);
|
|
164
|
+
|
|
165
|
+
const useAISDKThreads = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
166
|
+
options?: AISDKThreadsOptions<UI_MESSAGE>,
|
|
167
|
+
) => {
|
|
168
|
+
const { cloud, threadId, onThreadIdChange, ...threadOptions } = options ?? {};
|
|
169
|
+
const [chats] = useState(() => new Map<string, ChatEntry<UI_MESSAGE>>());
|
|
170
|
+
const bindCloud = cloud !== undefined;
|
|
171
|
+
|
|
172
|
+
useResourceCleanup(true, () => {
|
|
173
|
+
for (const { chat } of chats.values()) {
|
|
174
|
+
void chat.stop().catch(() => {});
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const cloudAdapter = useCloudThreadListAdapter({ cloud });
|
|
179
|
+
const thread = (id: string) => {
|
|
180
|
+
const element = AISDKChatThread({
|
|
181
|
+
threadId: id,
|
|
182
|
+
options: threadOptions,
|
|
183
|
+
chats,
|
|
184
|
+
cloud: bindCloud,
|
|
185
|
+
});
|
|
186
|
+
return bindCloud ? withKey(id, element) : element;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return useResource(
|
|
190
|
+
bindCloud
|
|
191
|
+
? RemoteThreadList({
|
|
192
|
+
adapter: cloudAdapter,
|
|
193
|
+
thread,
|
|
194
|
+
threadId,
|
|
195
|
+
onThreadIdChange,
|
|
196
|
+
})
|
|
197
|
+
: InMemoryThreadList({
|
|
198
|
+
thread,
|
|
199
|
+
onDelete: (id) => {
|
|
200
|
+
void chats
|
|
201
|
+
.get(id)
|
|
202
|
+
?.chat.stop()
|
|
203
|
+
.catch(() => {});
|
|
204
|
+
chats.delete(id);
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* `AuiConfig` entry that runs one AI SDK chat per thread. Hosts the same
|
|
212
|
+
* per-thread orchestration as {@link AISDKChat} inside the client's own
|
|
213
|
+
* resource tree, so it works with any `AssistantClient` host, React or not.
|
|
214
|
+
* Without `cloud`, threads live in memory for the client's lifetime and keep
|
|
215
|
+
* their history across switches; each thread's chat id is its thread id.
|
|
216
|
+
* With `cloud`, the list is a `RemoteThreadList` and the factory is keyed so
|
|
217
|
+
* cloud history reloads on a switch. The store entry mounts only the visible
|
|
218
|
+
* thread, so a switch cancels an in-flight run. Model context is
|
|
219
|
+
* registered on the visible thread only.
|
|
220
|
+
*/
|
|
221
|
+
export const AISDKThreads = resource(useAISDKThreads);
|
|
222
|
+
|
|
223
|
+
attachTransformScopes(useAISDKThreads, inMemoryThreadListTransformScopes);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ChatTransport, UIMessage, UIMessageChunk } from "ai";
|
|
2
|
+
|
|
3
|
+
export const createControlledTransport = () => {
|
|
4
|
+
let controller!: ReadableStreamDefaultController<UIMessageChunk>;
|
|
5
|
+
const transport: ChatTransport<UIMessage> = {
|
|
6
|
+
sendMessages: async () =>
|
|
7
|
+
new ReadableStream<UIMessageChunk>({
|
|
8
|
+
start(c) {
|
|
9
|
+
controller = c;
|
|
10
|
+
},
|
|
11
|
+
}),
|
|
12
|
+
reconnectToStream: async () => null,
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
transport,
|
|
16
|
+
emit: (...chunks: UIMessageChunk[]) => {
|
|
17
|
+
for (const chunk of chunks) controller.enqueue(chunk);
|
|
18
|
+
},
|
|
19
|
+
close: () => controller.close(),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const createCancellableTransport = () => {
|
|
24
|
+
let cancelCount = 0;
|
|
25
|
+
let controller!: ReadableStreamDefaultController<UIMessageChunk>;
|
|
26
|
+
const transport: ChatTransport<UIMessage> = {
|
|
27
|
+
sendMessages: async () =>
|
|
28
|
+
new ReadableStream<UIMessageChunk>({
|
|
29
|
+
start(c) {
|
|
30
|
+
controller = c;
|
|
31
|
+
},
|
|
32
|
+
cancel() {
|
|
33
|
+
cancelCount++;
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
reconnectToStream: async () => null,
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
transport,
|
|
40
|
+
getCancelCount: () => cancelCount,
|
|
41
|
+
close: () => controller.close(),
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { renderHook } from "@testing-library/react";
|
|
4
|
+
import type { ExternalStoreAdapter } from "@assistant-ui/core";
|
|
5
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
const mocks = vi.hoisted(() => ({
|
|
8
|
+
adapter: undefined as ExternalStoreAdapter | undefined,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("@assistant-ui/core/react", async (importOriginal) => {
|
|
12
|
+
const original =
|
|
13
|
+
await importOriginal<typeof import("@assistant-ui/core/react")>();
|
|
14
|
+
return {
|
|
15
|
+
...original,
|
|
16
|
+
useExternalStoreRuntime: vi.fn((adapter: ExternalStoreAdapter) => {
|
|
17
|
+
mocks.adapter = adapter;
|
|
18
|
+
return {};
|
|
19
|
+
}),
|
|
20
|
+
useRuntimeAdapters: vi.fn(() => ({})),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
vi.mock("./useExternalHistory", async (importOriginal) => {
|
|
25
|
+
const original =
|
|
26
|
+
await importOriginal<typeof import("./useExternalHistory")>();
|
|
27
|
+
return {
|
|
28
|
+
...original,
|
|
29
|
+
useExternalHistory: vi.fn(() => ({
|
|
30
|
+
isLoading: false,
|
|
31
|
+
deleteMessage: vi.fn().mockResolvedValue(undefined),
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
import { useAISDKRuntime } from "./useAISDKRuntime";
|
|
37
|
+
|
|
38
|
+
describe("useAISDKRuntime tool approvals", () => {
|
|
39
|
+
it("forwards the AI SDK approval promise to the external-store adapter", () => {
|
|
40
|
+
const approvalPromise = Promise.resolve();
|
|
41
|
+
const addToolApprovalResponse = vi.fn(() => approvalPromise);
|
|
42
|
+
const chat = {
|
|
43
|
+
id: "chat-1",
|
|
44
|
+
status: "ready",
|
|
45
|
+
error: undefined,
|
|
46
|
+
messages: [],
|
|
47
|
+
setMessages: vi.fn(),
|
|
48
|
+
sendMessage: vi.fn(),
|
|
49
|
+
regenerate: vi.fn(),
|
|
50
|
+
addToolOutput: vi.fn(),
|
|
51
|
+
addToolApprovalResponse,
|
|
52
|
+
stop: vi.fn(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
renderHook(() => useAISDKRuntime(chat as never));
|
|
56
|
+
|
|
57
|
+
const result = mocks.adapter?.onRespondToToolApproval?.({
|
|
58
|
+
approvalId: "approval-1",
|
|
59
|
+
approved: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result).toBe(approvalPromise);
|
|
63
|
+
expect(addToolApprovalResponse).toHaveBeenCalledWith({
|
|
64
|
+
id: "approval-1",
|
|
65
|
+
approved: true,
|
|
66
|
+
options: { metadata: undefined },
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|