@ulearning/ai-sdk-adapter 0.0.0-stage → 1.0.0-next.0
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 +29 -2
- package/dist/adapters/aiSDKFormatAdapter.d.ts +6 -0
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/aiSDKFormatAdapter.js +17 -0
- package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +3 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.js +36 -0
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
- package/dist/aiSDKExtras.d.ts +13 -0
- package/dist/aiSDKExtras.d.ts.map +1 -0
- package/dist/aiSDKExtras.js +6 -0
- package/dist/aiSDKExtras.js.map +1 -0
- package/dist/converters/convertMessage.d.ts +44 -0
- package/dist/converters/convertMessage.d.ts.map +1 -0
- package/dist/converters/convertMessage.js +465 -0
- package/dist/converters/convertMessage.js.map +1 -0
- package/dist/converters/toCreateMessage.d.ts +4 -0
- package/dist/converters/toCreateMessage.d.ts.map +1 -0
- package/dist/converters/toCreateMessage.js +65 -0
- package/dist/converters/toCreateMessage.js.map +1 -0
- package/dist/converters/toolApprovalAnswers.d.ts +3 -0
- package/dist/converters/toolApprovalAnswers.d.ts.map +1 -0
- package/dist/converters/toolApprovalAnswers.js +16 -0
- package/dist/converters/toolApprovalAnswers.js.map +1 -0
- package/dist/hooks.d.ts +17 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +19 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -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 +54 -0
- package/dist/model-context/injectInteractableContext.js.map +1 -0
- package/dist/model-context/injectQuoteContext.d.ts +25 -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 +13 -0
- package/dist/runtime/AISDKChat.d.ts.map +1 -0
- package/dist/runtime/AISDKChat.js +34 -0
- package/dist/runtime/AISDKChat.js.map +1 -0
- package/dist/runtime/AISDKThreads.d.ts +24 -0
- package/dist/runtime/AISDKThreads.d.ts.map +1 -0
- package/dist/runtime/AISDKThreads.js +92 -0
- package/dist/runtime/AISDKThreads.js.map +1 -0
- package/dist/runtime/DynamicChatTransport.d.ts +50 -0
- package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
- package/dist/runtime/DynamicChatTransport.js +153 -0
- package/dist/runtime/DynamicChatTransport.js.map +1 -0
- package/dist/runtime/getResumableAdapter.d.ts +5 -0
- package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
- package/dist/runtime/getResumableAdapter.js +11 -0
- package/dist/runtime/getResumableAdapter.js.map +1 -0
- package/dist/runtime/toolHistoryCodec.d.ts +20 -0
- package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
- package/dist/runtime/toolHistoryCodec.js +122 -0
- package/dist/runtime/toolHistoryCodec.js.map +1 -0
- package/dist/runtime/useAISDKRuntime.d.ts +95 -0
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
- package/dist/runtime/useAISDKRuntime.js +754 -0
- package/dist/runtime/useAISDKRuntime.js.map +1 -0
- package/dist/runtime/useChatRuntime.d.ts +17 -0
- package/dist/runtime/useChatRuntime.d.ts.map +1 -0
- package/dist/runtime/useChatRuntime.js +50 -0
- package/dist/runtime/useChatRuntime.js.map +1 -0
- package/dist/runtime/useChatSnapshot.d.ts +6 -0
- package/dist/runtime/useChatSnapshot.d.ts.map +1 -0
- package/dist/runtime/useChatSnapshot.js +62 -0
- package/dist/runtime/useChatSnapshot.js.map +1 -0
- package/dist/runtime/useChatThread.d.ts +121 -0
- package/dist/runtime/useChatThread.d.ts.map +1 -0
- package/dist/runtime/useChatThread.js +255 -0
- package/dist/runtime/useChatThread.js.map +1 -0
- package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
- package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
- package/dist/runtime/useDynamicChatTransport.js +26 -0
- package/dist/runtime/useDynamicChatTransport.js.map +1 -0
- package/dist/runtime/useExternalHistory.d.ts +12 -0
- package/dist/runtime/useExternalHistory.d.ts.map +1 -0
- package/dist/runtime/useExternalHistory.js +556 -0
- package/dist/runtime/useExternalHistory.js.map +1 -0
- package/dist/runtime/useMessageQueue.d.ts +14 -0
- package/dist/runtime/useMessageQueue.d.ts.map +1 -0
- package/dist/runtime/useMessageQueue.js +142 -0
- package/dist/runtime/useMessageQueue.js.map +1 -0
- package/dist/runtime/useResourceCleanup.d.ts +2 -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 +12 -0
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
- package/dist/runtime/useStreamingTiming.js +36 -0
- package/dist/runtime/useStreamingTiming.js.map +1 -0
- package/dist/tools/generativeTools.d.ts +71 -0
- package/dist/tools/generativeTools.d.ts.map +1 -0
- package/dist/tools/generativeTools.js +258 -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.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.node.js +1 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +3 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.js +10 -0
- package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
- package/dist/transport/AssistantChatTransport.d.ts +20 -0
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
- package/dist/transport/AssistantChatTransport.js +151 -0
- package/dist/transport/AssistantChatTransport.js.map +1 -0
- package/dist/transport/resumable.d.ts +36 -0
- package/dist/transport/resumable.d.ts.map +1 -0
- package/dist/transport/resumable.js +112 -0
- package/dist/transport/resumable.js.map +1 -0
- package/dist/usage.d.ts +24 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +121 -0
- package/dist/usage.js.map +1 -0
- package/dist/utils/getVercelAIMessages.d.ts +4 -0
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
- package/dist/utils/getVercelAIMessages.js +8 -0
- package/dist/utils/getVercelAIMessages.js.map +1 -0
- package/dist/utils/sliceMessagesUntil.d.ts +3 -0
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
- package/dist/utils/sliceMessagesUntil.js +10 -0
- package/dist/utils/sliceMessagesUntil.js.map +1 -0
- package/package.json +96 -3
- package/src/__tests__/standalone.e2e.ts +118 -0
- package/src/adapters/aiSDKFormatAdapter.test.ts +36 -0
- package/src/adapters/aiSDKFormatAdapter.ts +18 -0
- package/src/adapters/vercelAttachmentAdapter.test.ts +52 -0
- package/src/adapters/vercelAttachmentAdapter.ts +36 -0
- package/src/aiSDKExtras.ts +31 -0
- package/src/converters/convertMessage.metadata-key.test.tsx +182 -0
- package/src/converters/convertMessage.test.ts +2428 -0
- package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
- package/src/converters/convertMessage.ts +919 -0
- package/src/converters/toCreateMessage.test.ts +716 -0
- package/src/converters/toCreateMessage.ts +110 -0
- package/src/converters/toolApprovalAnswers.ts +27 -0
- package/src/hooks.test.ts +168 -0
- package/src/hooks.ts +26 -0
- package/src/index.ts +29 -0
- package/src/model-context/injectInteractableContext.test.ts +167 -0
- package/src/model-context/injectInteractableContext.ts +64 -0
- package/src/model-context/injectQuoteContext.test.ts +33 -0
- package/src/model-context/injectQuoteContext.ts +63 -0
- package/src/runtime/AISDKChat.artifacts.test.ts +48 -0
- package/src/runtime/AISDKChat.integration.test.tsx +148 -0
- package/src/runtime/AISDKChat.test.ts +241 -0
- package/src/runtime/AISDKChat.ts +43 -0
- package/src/runtime/AISDKThreads.test.ts +811 -0
- package/src/runtime/AISDKThreads.ts +194 -0
- package/src/runtime/DynamicChatTransport.test.ts +228 -0
- package/src/runtime/DynamicChatTransport.ts +281 -0
- package/src/runtime/__tests__/controlled-transport.ts +70 -0
- package/src/runtime/__tests__/tap.ts +80 -0
- package/src/runtime/__tests__/vue.ts +46 -0
- package/src/runtime/getResumableAdapter.ts +16 -0
- package/src/runtime/toolHistoryCodec.test.ts +187 -0
- package/src/runtime/toolHistoryCodec.ts +245 -0
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1956 -0
- package/src/runtime/useAISDKRuntime.approval.test.tsx +580 -0
- package/src/runtime/useAISDKRuntime.denied-tool.test.tsx +122 -0
- package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +302 -0
- package/src/runtime/useAISDKRuntime.metadata-cache.test.tsx +157 -0
- package/src/runtime/useAISDKRuntime.queue.test.tsx +707 -0
- package/src/runtime/useAISDKRuntime.reload.test.tsx +217 -0
- package/src/runtime/useAISDKRuntime.test.ts +2345 -0
- package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
- package/src/runtime/useAISDKRuntime.ts +1503 -0
- package/src/runtime/useAISDKRuntime.voice.test.tsx +319 -0
- package/src/runtime/useChatOnTap.test.ts +60 -0
- package/src/runtime/useChatRuntime.fast-refresh.test.tsx +83 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +661 -0
- package/src/runtime/useChatRuntime.local-storage.test.tsx +114 -0
- package/src/runtime/useChatRuntime.native.test.ts +147 -0
- package/src/runtime/useChatRuntime.run-telemetry.test.tsx +226 -0
- package/src/runtime/useChatRuntime.test.ts +619 -0
- package/src/runtime/useChatRuntime.ts +80 -0
- package/src/runtime/useChatSnapshot.test.ts +60 -0
- package/src/runtime/useChatSnapshot.ts +86 -0
- package/src/runtime/useChatThread.binding.test.tsx +140 -0
- package/src/runtime/useChatThread.test.ts +993 -0
- package/src/runtime/useChatThread.transport.test.tsx +149 -0
- package/src/runtime/useChatThread.ts +533 -0
- package/src/runtime/useDynamicChatTransport.ts +26 -0
- package/src/runtime/useExternalHistory.test.ts +4187 -0
- package/src/runtime/useExternalHistory.ts +902 -0
- package/src/runtime/useMessageQueue.effects.test.tsx +88 -0
- package/src/runtime/useMessageQueue.test.tsx +256 -0
- package/src/runtime/useMessageQueue.ts +189 -0
- package/src/runtime/useResourceCleanup.ts +58 -0
- package/src/runtime/useStreamingTiming.test.tsx +96 -0
- package/src/runtime/useStreamingTiming.ts +69 -0
- package/src/tools/generativeTools.test.ts +1058 -0
- package/src/tools/generativeTools.ts +491 -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 +631 -0
- package/src/transport/AssistantChatTransport.ts +236 -0
- package/src/transport/resumable.test.ts +262 -0
- package/src/transport/resumable.ts +162 -0
- package/src/usage.test.ts +204 -0
- package/src/usage.ts +179 -0
- package/src/utils/getVercelAIMessages.ts +11 -0
- package/src/utils/sliceMessagesUntil.test.ts +51 -0
- package/src/utils/sliceMessagesUntil.ts +16 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type AppendMessage } from "@ulearning/ai-core";
|
|
2
|
+
export declare const useMessageQueue: ({ enabled, isRunning, isSendDisabled, send, cancel, onError, }: {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
isRunning: boolean;
|
|
5
|
+
isSendDisabled: boolean;
|
|
6
|
+
send: (message: AppendMessage) => Promise<void>;
|
|
7
|
+
cancel: () => Promise<void>;
|
|
8
|
+
onError?: ((error: Error | undefined) => void) | undefined;
|
|
9
|
+
}) => {
|
|
10
|
+
adapter: import("@ulearning/ai-core").ExternalThreadQueueAdapter | undefined;
|
|
11
|
+
cancel: () => Promise<void>;
|
|
12
|
+
clear: () => void;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useMessageQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMessageQueue.d.ts","mappings":"AASA,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,oBAAoB;AAK3B,eAAO,MAAM,eAAe,GAAI,gEAO7B;IACD,OAAO,EAAE,OAAO;IAChB,SAAS,EAAE,OAAO;IAClB,cAAc,EAAE,OAAO;IACvB,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC;IAC/C,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS;CAC3D;;;;CA4JA","names":[],"ignoreList":[],"sources":["../../src/runtime/useMessageQueue.ts"],"sourcesContent":[null]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createMessageQueue } from "@ulearning/ai-core";
|
|
3
|
+
import { useCallback, useEffect, useInsertionEffect, useRef, useSyncExternalStore } from "@ulearning/ai-tap";
|
|
4
|
+
// src/runtime/useMessageQueue.ts
|
|
5
|
+
const subscribeNoop = () => () => {};
|
|
6
|
+
const getNoItems = () => void 0;
|
|
7
|
+
const useMessageQueue = ({ enabled, isRunning, isSendDisabled, send, cancel, onError }) => {
|
|
8
|
+
const sendRef = useRef(send);
|
|
9
|
+
const cancelRef = useRef(cancel);
|
|
10
|
+
const onErrorRef = useRef(onError);
|
|
11
|
+
const heldRef = useRef(!enabled || isSendDisabled);
|
|
12
|
+
const runningRef = useRef(isRunning);
|
|
13
|
+
useInsertionEffect(() => {
|
|
14
|
+
sendRef.current = send;
|
|
15
|
+
cancelRef.current = cancel;
|
|
16
|
+
onErrorRef.current = onError;
|
|
17
|
+
heldRef.current = !enabled || isSendDisabled;
|
|
18
|
+
runningRef.current = isRunning;
|
|
19
|
+
});
|
|
20
|
+
const cancelsRef = useRef(0);
|
|
21
|
+
const cancelRun = useCallback(() => {
|
|
22
|
+
cancelsRef.current++;
|
|
23
|
+
return cancelRef.current();
|
|
24
|
+
}, []);
|
|
25
|
+
const reportedRef = useRef({
|
|
26
|
+
controller: null,
|
|
27
|
+
busy: isRunning
|
|
28
|
+
});
|
|
29
|
+
const busyEdgesRef = useRef(0);
|
|
30
|
+
const idleWaitersRef = useRef([]);
|
|
31
|
+
const lastDispatchRef = useRef(Promise.resolve());
|
|
32
|
+
const mountedRef = useRef(true);
|
|
33
|
+
const generationRef = useRef(0);
|
|
34
|
+
const queueRef = useRef(null);
|
|
35
|
+
if (enabled && !queueRef.current) {
|
|
36
|
+
const controller = createMessageQueue({ run: (message) => {
|
|
37
|
+
const previous = lastDispatchRef.current;
|
|
38
|
+
const cancels = cancelsRef.current;
|
|
39
|
+
const generation = generationRef.current;
|
|
40
|
+
const busyEdgesAtDispatch = busyEdgesRef.current;
|
|
41
|
+
const dispatch = (async () => {
|
|
42
|
+
await previous;
|
|
43
|
+
let settledCancels = cancels;
|
|
44
|
+
while (true) {
|
|
45
|
+
if (!mountedRef.current || generation !== generationRef.current) {
|
|
46
|
+
if (mountedRef.current && (!runningRef.current || busyEdgesAtDispatch === busyEdgesRef.current)) controller.notifyIdle();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (heldRef.current || runningRef.current || reportedRef.current.busy) {
|
|
50
|
+
await new Promise((resolve) => {
|
|
51
|
+
idleWaitersRef.current.push(resolve);
|
|
52
|
+
});
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (settledCancels !== cancelsRef.current) {
|
|
56
|
+
settledCancels = cancelsRef.current;
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
const overtaken = settledCancels !== cancels;
|
|
63
|
+
const busyEdges = busyEdgesRef.current;
|
|
64
|
+
onErrorRef.current?.(void 0);
|
|
65
|
+
try {
|
|
66
|
+
if (overtaken) {
|
|
67
|
+
const error = /* @__PURE__ */ new Error("Queued send cancelled before dispatch");
|
|
68
|
+
error.name = "AbortError";
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
await sendRef.current(message);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (mountedRef.current && generation === generationRef.current) onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
if (busyEdgesRef.current === busyEdges && !runningRef.current) controller.notifyIdle();
|
|
77
|
+
})();
|
|
78
|
+
lastDispatchRef.current = dispatch.catch(() => {});
|
|
79
|
+
return dispatch;
|
|
80
|
+
} });
|
|
81
|
+
queueRef.current = controller;
|
|
82
|
+
}
|
|
83
|
+
const controller = queueRef.current;
|
|
84
|
+
useSyncExternalStore(controller?.subscribe ?? subscribeNoop, () => controller?.adapter.items, getNoItems);
|
|
85
|
+
useSyncExternalStore(controller?.subscribe ?? subscribeNoop, () => controller?.adapter.steerItems, getNoItems);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
mountedRef.current = true;
|
|
88
|
+
return () => {
|
|
89
|
+
mountedRef.current = false;
|
|
90
|
+
controller?.hold();
|
|
91
|
+
const waiters = idleWaitersRef.current;
|
|
92
|
+
idleWaitersRef.current = [];
|
|
93
|
+
for (const resolve of waiters) resolve();
|
|
94
|
+
};
|
|
95
|
+
}, [controller]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!controller) return;
|
|
98
|
+
if (!enabled || isSendDisabled) controller.hold();
|
|
99
|
+
else controller.release();
|
|
100
|
+
}, [
|
|
101
|
+
controller,
|
|
102
|
+
enabled,
|
|
103
|
+
isSendDisabled
|
|
104
|
+
]);
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const reported = reportedRef.current;
|
|
107
|
+
if (reported.controller !== controller || reported.busy !== isRunning) {
|
|
108
|
+
reportedRef.current = {
|
|
109
|
+
controller,
|
|
110
|
+
busy: isRunning
|
|
111
|
+
};
|
|
112
|
+
if (isRunning) {
|
|
113
|
+
busyEdgesRef.current++;
|
|
114
|
+
controller?.notifyBusy();
|
|
115
|
+
} else controller?.notifyIdle();
|
|
116
|
+
}
|
|
117
|
+
if (!enabled || isSendDisabled || isRunning) return;
|
|
118
|
+
const waiters = idleWaitersRef.current;
|
|
119
|
+
idleWaitersRef.current = [];
|
|
120
|
+
for (const resolve of waiters) resolve();
|
|
121
|
+
}, [
|
|
122
|
+
controller,
|
|
123
|
+
enabled,
|
|
124
|
+
isRunning,
|
|
125
|
+
isSendDisabled
|
|
126
|
+
]);
|
|
127
|
+
return {
|
|
128
|
+
adapter: enabled ? controller?.adapter : void 0,
|
|
129
|
+
cancel: cancelRun,
|
|
130
|
+
clear: () => {
|
|
131
|
+
onErrorRef.current?.(void 0);
|
|
132
|
+
generationRef.current++;
|
|
133
|
+
controller?.clear();
|
|
134
|
+
const waiters = idleWaitersRef.current;
|
|
135
|
+
idleWaitersRef.current = [];
|
|
136
|
+
for (const resolve of waiters) resolve();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
export { useMessageQueue };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=useMessageQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMessageQueue.js","names":[],"sources":["../../src/runtime/useMessageQueue.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useSyncExternalStore,\n} from \"@ulearning/ai-tap\";\nimport {\n createMessageQueue,\n type AppendMessage,\n type MessageQueueController,\n} from \"@ulearning/ai-core\";\n\nconst subscribeNoop = () => () => {};\nconst getNoItems = () => undefined;\n\nexport const useMessageQueue = ({\n enabled,\n isRunning,\n isSendDisabled,\n send,\n cancel,\n onError,\n}: {\n enabled: boolean;\n isRunning: boolean;\n isSendDisabled: boolean;\n send: (message: AppendMessage) => Promise<void>;\n cancel: () => Promise<void>;\n onError?: ((error: Error | undefined) => void) | undefined;\n}) => {\n const sendRef = useRef(send);\n const cancelRef = useRef(cancel);\n const onErrorRef = useRef(onError);\n const heldRef = useRef(!enabled || isSendDisabled);\n const runningRef = useRef(isRunning);\n useInsertionEffect(() => {\n sendRef.current = send;\n cancelRef.current = cancel;\n onErrorRef.current = onError;\n heldRef.current = !enabled || isSendDisabled;\n runningRef.current = isRunning;\n });\n\n const cancelsRef = useRef(0);\n const cancelRun = useCallback(() => {\n cancelsRef.current++;\n return cancelRef.current();\n }, []);\n\n const reportedRef = useRef<{\n controller: MessageQueueController | null;\n busy: boolean;\n }>({ controller: null, busy: isRunning });\n const busyEdgesRef = useRef(0);\n const idleWaitersRef = useRef<(() => void)[]>([]);\n const lastDispatchRef = useRef<Promise<void>>(Promise.resolve());\n const mountedRef = useRef(true);\n const generationRef = useRef(0);\n\n const queueRef = useRef<MessageQueueController | null>(null);\n if (enabled && !queueRef.current) {\n const controller = createMessageQueue({\n run: (message) => {\n const previous = lastDispatchRef.current;\n const cancels = cancelsRef.current;\n const generation = generationRef.current;\n const busyEdgesAtDispatch = busyEdgesRef.current;\n const dispatch = (async () => {\n await previous;\n let settledCancels = cancels;\n while (true) {\n if (!mountedRef.current || generation !== generationRef.current) {\n if (\n mountedRef.current &&\n (!runningRef.current ||\n busyEdgesAtDispatch === busyEdgesRef.current)\n ) {\n controller.notifyIdle();\n }\n return;\n }\n if (\n heldRef.current ||\n runningRef.current ||\n reportedRef.current.busy\n ) {\n await new Promise<void>((resolve) => {\n idleWaitersRef.current.push(resolve);\n });\n continue;\n }\n if (settledCancels !== cancelsRef.current) {\n settledCancels = cancelsRef.current;\n await new Promise((resolve) => setTimeout(resolve));\n continue;\n }\n break;\n }\n const overtaken = settledCancels !== cancels;\n const busyEdges = busyEdgesRef.current;\n onErrorRef.current?.(undefined);\n try {\n if (overtaken) {\n const error = new Error(\"Queued send cancelled before dispatch\");\n error.name = \"AbortError\";\n throw error;\n }\n await sendRef.current(message);\n } catch (error) {\n if (mountedRef.current && generation === generationRef.current) {\n onErrorRef.current?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n throw error;\n }\n if (busyEdgesRef.current === busyEdges && !runningRef.current) {\n controller.notifyIdle();\n }\n })();\n lastDispatchRef.current = dispatch.catch(() => {});\n return dispatch;\n },\n });\n queueRef.current = controller;\n }\n const controller = queueRef.current;\n\n useSyncExternalStore(\n controller?.subscribe ?? subscribeNoop,\n () => controller?.adapter.items,\n getNoItems,\n );\n useSyncExternalStore(\n controller?.subscribe ?? subscribeNoop,\n () => controller?.adapter.steerItems,\n getNoItems,\n );\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n controller?.hold();\n const waiters = idleWaitersRef.current;\n idleWaitersRef.current = [];\n for (const resolve of waiters) resolve();\n };\n }, [controller]);\n\n useEffect(() => {\n if (!controller) return;\n if (!enabled || isSendDisabled) controller.hold();\n else controller.release();\n }, [controller, enabled, isSendDisabled]);\n\n useEffect(() => {\n const reported = reportedRef.current;\n if (reported.controller !== controller || reported.busy !== isRunning) {\n reportedRef.current = { controller, busy: isRunning };\n if (isRunning) {\n busyEdgesRef.current++;\n controller?.notifyBusy();\n } else {\n controller?.notifyIdle();\n }\n }\n if (!enabled || isSendDisabled || isRunning) return;\n const waiters = idleWaitersRef.current;\n idleWaitersRef.current = [];\n for (const resolve of waiters) resolve();\n }, [controller, enabled, isRunning, isSendDisabled]);\n\n return {\n adapter: enabled ? controller?.adapter : undefined,\n cancel: cancelRun,\n clear: () => {\n onErrorRef.current?.(undefined);\n generationRef.current++;\n controller?.clear();\n const waiters = idleWaitersRef.current;\n idleWaitersRef.current = [];\n for (const resolve of waiters) resolve();\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResourceCleanup.d.ts","mappings":"AAGA,eAAO,MAAM,kBAAkB,GAC7B,SAAS,OAAO,EAChB,SAAS,MAAM,IAAI,EACnB,oBAAoB,WAAW,SAmDhC","names":[],"ignoreList":[],"sources":["../../src/runtime/useResourceCleanup.ts"],"sourcesContent":[null]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect, useRef } from "@ulearning/ai-tap";
|
|
2
|
+
import { useAssistantClientDestroySignal } from "@ulearning/ai-store/internal";
|
|
3
|
+
// src/runtime/useResourceCleanup.ts
|
|
4
|
+
const useResourceCleanup = (enabled, cleanup, hostDestroySignal) => {
|
|
5
|
+
const destroySignal = useAssistantClientDestroySignal();
|
|
6
|
+
const cleanupRef = useRef(cleanup);
|
|
7
|
+
const enabledRef = useRef(enabled);
|
|
8
|
+
const registrationRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
cleanupRef.current = cleanup;
|
|
11
|
+
enabledRef.current = enabled;
|
|
12
|
+
});
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!enabled || !destroySignal && !hostDestroySignal) return void 0;
|
|
15
|
+
const current = registrationRef.current;
|
|
16
|
+
if (current !== null && current.destroySignal === destroySignal && current.hostDestroySignal === hostDestroySignal) return;
|
|
17
|
+
current?.controller.abort();
|
|
18
|
+
const registration = new AbortController();
|
|
19
|
+
registrationRef.current = {
|
|
20
|
+
destroySignal,
|
|
21
|
+
hostDestroySignal,
|
|
22
|
+
controller: registration
|
|
23
|
+
};
|
|
24
|
+
const run = () => {
|
|
25
|
+
if (registration.signal.aborted) return;
|
|
26
|
+
registration.abort();
|
|
27
|
+
if (enabledRef.current) cleanupRef.current();
|
|
28
|
+
};
|
|
29
|
+
if (destroySignal?.aborted || hostDestroySignal?.aborted) {
|
|
30
|
+
run();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const options = {
|
|
34
|
+
once: true,
|
|
35
|
+
signal: registration.signal
|
|
36
|
+
};
|
|
37
|
+
destroySignal?.addEventListener("abort", run, options);
|
|
38
|
+
hostDestroySignal?.addEventListener("abort", run, options);
|
|
39
|
+
}, [
|
|
40
|
+
destroySignal,
|
|
41
|
+
enabled,
|
|
42
|
+
hostDestroySignal
|
|
43
|
+
]);
|
|
44
|
+
};
|
|
45
|
+
export { useResourceCleanup };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=useResourceCleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResourceCleanup.js","names":[],"sources":["../../src/runtime/useResourceCleanup.ts"],"sourcesContent":["import { useEffect, useRef } from \"@ulearning/ai-tap\";\nimport { useAssistantClientDestroySignal } from \"@ulearning/ai-store/internal\";\n\nexport const useResourceCleanup = (\n enabled: boolean,\n cleanup: () => void,\n hostDestroySignal?: AbortSignal,\n) => {\n const destroySignal = useAssistantClientDestroySignal();\n const cleanupRef = useRef(cleanup);\n const enabledRef = useRef(enabled);\n const registrationRef = useRef<{\n destroySignal: AbortSignal | undefined;\n hostDestroySignal: AbortSignal | undefined;\n controller: AbortController;\n } | null>(null);\n\n useEffect(() => {\n cleanupRef.current = cleanup;\n enabledRef.current = enabled;\n });\n\n useEffect(() => {\n if (!enabled || (!destroySignal && !hostDestroySignal)) return undefined;\n const current = registrationRef.current;\n if (\n current !== null &&\n current.destroySignal === destroySignal &&\n current.hostDestroySignal === hostDestroySignal\n ) {\n return undefined;\n }\n current?.controller.abort();\n\n const registration = new AbortController();\n registrationRef.current = {\n destroySignal,\n hostDestroySignal,\n controller: registration,\n };\n const run = () => {\n if (registration.signal.aborted) return;\n registration.abort();\n if (enabledRef.current) cleanupRef.current();\n };\n if (destroySignal?.aborted || hostDestroySignal?.aborted) {\n run();\n return undefined;\n }\n const options = { once: true, signal: registration.signal };\n destroySignal?.addEventListener(\"abort\", run, options);\n hostDestroySignal?.addEventListener(\"abort\", run, options);\n\n // The listeners must survive standalone soft unmounts so a later\n // permanent destroy still cleans up the retained resource state.\n return undefined;\n }, [destroySignal, enabled, hostDestroySignal]);\n};\n"],"mappings":";;;AAGA,MAAa,sBACX,SACA,SACA,sBACG;CACH,MAAM,gBAAgB,gCAAgC;CACtD,MAAM,aAAa,OAAO,OAAO;CACjC,MAAM,aAAa,OAAO,OAAO;CACjC,MAAM,kBAAkB,OAId,IAAI;CAEd,gBAAgB;EACd,WAAW,UAAU;EACrB,WAAW,UAAU;CACvB,CAAC;CAED,gBAAgB;EACd,IAAI,CAAC,WAAY,CAAC,iBAAiB,CAAC,mBAAoB,OAAO,KAAA;EAC/D,MAAM,UAAU,gBAAgB;EAChC,IACE,YAAY,QACZ,QAAQ,kBAAkB,iBAC1B,QAAQ,sBAAsB,mBAE9B;EAEF,SAAS,WAAW,MAAM;EAE1B,MAAM,eAAe,IAAI,gBAAgB;EACzC,gBAAgB,UAAU;GACxB;GACA;GACA,YAAY;EACd;EACA,MAAM,YAAY;GAChB,IAAI,aAAa,OAAO,SAAS;GACjC,aAAa,MAAM;GACnB,IAAI,WAAW,SAAS,WAAW,QAAQ;EAC7C;EACA,IAAI,eAAe,WAAW,mBAAmB,SAAS;GACxD,IAAI;GACJ;EACF;EACA,MAAM,UAAU;GAAE,MAAM;GAAM,QAAQ,aAAa;EAAO;EAC1D,eAAe,iBAAiB,SAAS,KAAK,OAAO;EACrD,mBAAmB,iBAAiB,SAAS,KAAK,OAAO;CAK3D,GAAG;EAAC;EAAe;EAAS;CAAiB,CAAC;AAChD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UIMessage } from "ai";
|
|
2
|
+
import type { MaybeRefOrGetter } from "vue";
|
|
3
|
+
import { type StreamingTimingAccessors } from "@ulearning/ai-core/vue";
|
|
4
|
+
export declare const aiSdkStreamingTimingAccessors: StreamingTimingAccessors<UIMessage>;
|
|
5
|
+
/**
|
|
6
|
+
* Tracks streaming timing for AI SDK messages client-side. Delegates to the
|
|
7
|
+
* shared `useStreamingTiming` primitive in `@ulearning/ai-core/vue`,
|
|
8
|
+
* adapted to the `UIMessage` shape. Timing is finalized when streaming ends
|
|
9
|
+
* and stored per message id.
|
|
10
|
+
*/
|
|
11
|
+
export declare const useStreamingTiming: (messages: MaybeRefOrGetter<readonly UIMessage[]>, isRunning: MaybeRefOrGetter<boolean>) => import("vue").ComputedRef<Record<string, import("@ulearning/ai-core").MessageTiming>>;
|
|
12
|
+
//# sourceMappingURL=useStreamingTiming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStreamingTiming.d.ts","mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI;AACnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK;AAE3C,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB;AAsC/B,eAAO,MAAM,6BAA6B,EAAE,wBAAwB,CAAC,SAAS,CAM3E;AAEH;;;;;;AAMA,eAAO,MAAM,kBAAkB,GAC7B,UAAU,gBAAgB,CAAC,SAAS,SAAS,EAAE,CAAC,EAChD,WAAW,gBAAgB,CAAC,OAAO,CAAC,0FAMnC","names":[],"ignoreList":[],"sources":["../../src/runtime/useStreamingTiming.ts"],"sourcesContent":[null]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isToolUIPart } from "ai";
|
|
3
|
+
import { useStreamingTiming as useStreamingTiming$1 } from "@ulearning/ai-core/vue";
|
|
4
|
+
// src/runtime/useStreamingTiming.ts
|
|
5
|
+
const findAssistant = (messages, messageId) => messages.find((m) => m.role === "assistant" && m.id === messageId);
|
|
6
|
+
const getTextLength = (messages, messageId) => {
|
|
7
|
+
const message = findAssistant(messages, messageId);
|
|
8
|
+
if (!message?.parts) return 0;
|
|
9
|
+
let len = 0;
|
|
10
|
+
for (const part of message.parts) if (part?.type === "text") len += part.text?.length ?? 0;
|
|
11
|
+
return len;
|
|
12
|
+
};
|
|
13
|
+
const getToolCallCount = (messages, messageId) => {
|
|
14
|
+
const message = findAssistant(messages, messageId);
|
|
15
|
+
if (!message?.parts) return 0;
|
|
16
|
+
let count = 0;
|
|
17
|
+
for (const part of message.parts) if (typeof part?.type === "string" && isToolUIPart(part)) count++;
|
|
18
|
+
return count;
|
|
19
|
+
};
|
|
20
|
+
const getAssistantMessageId = (messages) => messages.findLast((m) => m.role === "assistant")?.id;
|
|
21
|
+
const aiSdkStreamingTimingAccessors = {
|
|
22
|
+
getAssistantMessageId,
|
|
23
|
+
getTextLength,
|
|
24
|
+
getToolCallCount,
|
|
25
|
+
getMessageContent: (messages, id) => findAssistant(messages, id)?.parts
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Tracks streaming timing for AI SDK messages client-side. Delegates to the
|
|
29
|
+
* shared `useStreamingTiming` primitive in `@ulearning/ai-core/vue`,
|
|
30
|
+
* adapted to the `UIMessage` shape. Timing is finalized when streaming ends
|
|
31
|
+
* and stored per message id.
|
|
32
|
+
*/
|
|
33
|
+
const useStreamingTiming = (messages, isRunning) => useStreamingTiming$1(messages, isRunning, aiSdkStreamingTimingAccessors);
|
|
34
|
+
export { aiSdkStreamingTimingAccessors, useStreamingTiming };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=useStreamingTiming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStreamingTiming.js","names":["useStreamingTimingPrimitive"],"sources":["../../src/runtime/useStreamingTiming.ts"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage } from \"ai\";\nimport type { MaybeRefOrGetter } from \"vue\";\nimport { isToolUIPart } from \"ai\";\nimport {\n useStreamingTiming as useStreamingTimingPrimitive,\n type StreamingTimingAccessors,\n} from \"@ulearning/ai-core/vue\";\n\nconst findAssistant = (\n messages: readonly UIMessage[],\n messageId: string,\n): UIMessage | undefined =>\n messages.find((m) => m.role === \"assistant\" && m.id === messageId);\n\nconst getTextLength = (\n messages: readonly UIMessage[],\n messageId: string,\n): number => {\n const message = findAssistant(messages, messageId);\n if (!message?.parts) return 0;\n let len = 0;\n for (const part of message.parts) {\n if (part?.type === \"text\") len += part.text?.length ?? 0;\n }\n return len;\n};\n\nconst getToolCallCount = (\n messages: readonly UIMessage[],\n messageId: string,\n): number => {\n const message = findAssistant(messages, messageId);\n if (!message?.parts) return 0;\n let count = 0;\n for (const part of message.parts) {\n if (typeof part?.type === \"string\" && isToolUIPart(part)) count++;\n }\n return count;\n};\n\nconst getAssistantMessageId = (\n messages: readonly UIMessage[],\n): string | undefined => messages.findLast((m) => m.role === \"assistant\")?.id;\n\nexport const aiSdkStreamingTimingAccessors: StreamingTimingAccessors<UIMessage> =\n {\n getAssistantMessageId,\n getTextLength,\n getToolCallCount,\n getMessageContent: (messages, id) => findAssistant(messages, id)?.parts,\n };\n\n/**\n * Tracks streaming timing for AI SDK messages client-side. Delegates to the\n * shared `useStreamingTiming` primitive in `@ulearning/ai-core/vue`,\n * adapted to the `UIMessage` shape. Timing is finalized when streaming ends\n * and stored per message id.\n */\nexport const useStreamingTiming = (\n messages: MaybeRefOrGetter<readonly UIMessage[]>,\n isRunning: MaybeRefOrGetter<boolean>,\n) =>\n useStreamingTimingPrimitive(\n messages,\n isRunning,\n aiSdkStreamingTimingAccessors,\n );\n"],"mappings":";;;;AAUA,MAAM,iBACJ,UACA,cAEA,SAAS,MAAM,MAAM,EAAE,SAAS,eAAe,EAAE,OAAO,SAAS;AAEnE,MAAM,iBACJ,UACA,cACW;CACX,MAAM,UAAU,cAAc,UAAU,SAAS;CACjD,IAAI,CAAC,SAAS,OAAO,OAAO;CAC5B,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,QAAQ,OACzB,IAAI,MAAM,SAAS,QAAQ,OAAO,KAAK,MAAM,UAAU;CAEzD,OAAO;AACT;AAEA,MAAM,oBACJ,UACA,cACW;CACX,MAAM,UAAU,cAAc,UAAU,SAAS;CACjD,IAAI,CAAC,SAAS,OAAO,OAAO;CAC5B,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,QAAQ,OACzB,IAAI,OAAO,MAAM,SAAS,YAAY,aAAa,IAAI,GAAG;CAE5D,OAAO;AACT;AAEA,MAAM,yBACJ,aACuB,SAAS,UAAU,MAAM,EAAE,SAAS,WAAW,CAAC,EAAE;AAE3E,MAAa,gCACX;CACE;CACA;CACA;CACA,oBAAoB,UAAU,OAAO,cAAc,UAAU,EAAE,CAAC,EAAE;AACpE;;;;;;;AAQF,MAAa,sBACX,UACA,cAEAA,qBACE,UACA,WACA,6BACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type ToolSet } from "ai";
|
|
2
|
+
import type { Toolkit } from "@ulearning/ai-core/vue";
|
|
3
|
+
import { type FrontendTools } from "@ulearning/ai-stream/ai-sdk";
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated Options for the deprecated {@link generativeTools}. Use
|
|
6
|
+
* {@link AISDKToolkit} with {@link AISDKToolkitOptions} /
|
|
7
|
+
* {@link AISDKToolkitToolsOptions} instead.
|
|
8
|
+
*/
|
|
9
|
+
export interface GenerativeToolsOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The server build of a generative toolkit (schema + server `execute`). Typed
|
|
12
|
+
* as the canonical {@link Toolkit} so callers don't need to cast; the server
|
|
13
|
+
* build carries `execute`, recovered internally as {@link ToolkitDefinition}.
|
|
14
|
+
*/
|
|
15
|
+
toolkit: Toolkit;
|
|
16
|
+
/**
|
|
17
|
+
* Tools uploaded by the frontend (the request body's `tools`). Merged in
|
|
18
|
+
* alongside the `toolkit`; a server `execute` from `toolkit` takes precedence
|
|
19
|
+
* over an uploaded entry of the same name.
|
|
20
|
+
*/
|
|
21
|
+
frontendTools?: FrontendTools;
|
|
22
|
+
}
|
|
23
|
+
export type AISDKToolkitOptions = {
|
|
24
|
+
toolkit: Toolkit;
|
|
25
|
+
};
|
|
26
|
+
export type AISDKToolkitToolsOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Tools uploaded by the frontend request body.
|
|
29
|
+
*/
|
|
30
|
+
frontend?: FrontendTools;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Builds an AI SDK `ToolSet` for server-side use with `streamText` /
|
|
34
|
+
* `generateText` from a generative `toolkit` and the frontend-uploaded tools.
|
|
35
|
+
*
|
|
36
|
+
* Each toolkit tool's `execute` runs on the server. Pair this with the
|
|
37
|
+
* `"use generative"` compiler: import the toolkit in a server route (where it
|
|
38
|
+
* resolves to the server build — schema + `execute`, with `render` stripped) and
|
|
39
|
+
* pass it here. Tools without an `execute` are still exposed to the model but
|
|
40
|
+
* left for the client to fulfill. `frontendTools` lets the client contribute
|
|
41
|
+
* tools that aren't in the static toolkit.
|
|
42
|
+
*
|
|
43
|
+
* @deprecated Use {@link AISDKToolkit} instead:
|
|
44
|
+
* `new AISDKToolkit({ toolkit }).tools({ frontend })`. It is a strict superset
|
|
45
|
+
* (it also opens MCP server connections), so it replaces `generativeTools`
|
|
46
|
+
* everywhere. The `frontendTools` option is named `frontend` on `.tools()`, and
|
|
47
|
+
* `.tools()` is async. `generativeTools` will be removed in a future version.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // Define once at module scope so any MCP connections pool across requests.
|
|
52
|
+
* const aiToolkit = new AISDKToolkit({ toolkit: docsToolkit });
|
|
53
|
+
*
|
|
54
|
+
* // In your route handler:
|
|
55
|
+
* const { tools } = await req.json();
|
|
56
|
+
* streamText({
|
|
57
|
+
* abortSignal: req.signal,
|
|
58
|
+
* model,
|
|
59
|
+
* messages,
|
|
60
|
+
* tools: await aiToolkit.tools({ frontend: tools }),
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const generativeTools: (options: GenerativeToolsOptions) => ToolSet;
|
|
65
|
+
export declare class AISDKToolkit {
|
|
66
|
+
#private;
|
|
67
|
+
constructor(options: AISDKToolkitOptions);
|
|
68
|
+
tools(options?: AISDKToolkitToolsOptions): Promise<ToolSet>;
|
|
69
|
+
close(): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=generativeTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generativeTools.d.ts","mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,IAAI;AAU7C,OAAO,KAAK,EAEV,OAAO,EAER,MAAM,wBAAwB;AAC/B,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,6BAA6B;AAqF/E;;;;;AAKA,MAAM,WAAW,sBAAsB;IACrC;;;;;IAKA,OAAO,EAAE,OAAO;IAChB;;;;;IAKA,aAAa,CAAC,EAAE,aAAa;;AAG/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,OAAO;CACjB;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;IAGA,QAAQ,CAAC,EAAE,aAAa;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,OAUjE;AAED,qBAAa,YAAY;;gBAIX,OAAO,EAAE,mBAAmB;IAIlC,KAAK,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,OAAO,CAAC;IAsB/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC","names":[],"ignoreList":[],"sources":["../../src/tools/generativeTools.ts"],"sourcesContent":[null]}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { jsonSchema } from "ai";
|
|
2
|
+
import { toAISDKContent, toAISDKDefaultOutput, unwrapModelContentEnvelope } from "@ulearning/ai-stream/internal";
|
|
3
|
+
import { toJSONSchema } from "@ulearning/ai-stream";
|
|
4
|
+
import { frontendTools } from "@ulearning/ai-stream/ai-sdk";
|
|
5
|
+
import { createMCPClient } from "@ai-sdk/mcp";
|
|
6
|
+
import { Experimental_StdioMCPTransport } from "#mcp-stdio";
|
|
7
|
+
// src/tools/generativeTools.ts
|
|
8
|
+
const EMPTY_SCHEMA = {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {}
|
|
11
|
+
};
|
|
12
|
+
const humanNotSupported = () => {
|
|
13
|
+
throw new Error("`human()` is not available during server-side tool execution.");
|
|
14
|
+
};
|
|
15
|
+
const neverAbort = new AbortController().signal;
|
|
16
|
+
var MCPConnectionTimeoutError = class extends Error {};
|
|
17
|
+
const createMcpConnectionTimeoutError = (name, phase, timeoutMs) => new MCPConnectionTimeoutError(`MCP toolkit entry "${name}" timed out while ${phase} after ${timeoutMs}ms.`);
|
|
18
|
+
const withMcpConnectionTimeout = async (promise, options) => {
|
|
19
|
+
const timeoutMs = options.config.connectionTimeout;
|
|
20
|
+
if (timeoutMs === void 0) return await promise;
|
|
21
|
+
const remainingMs = timeoutMs - (Date.now() - options.startedAt);
|
|
22
|
+
const timeoutError = () => createMcpConnectionTimeoutError(options.name, options.phase, timeoutMs);
|
|
23
|
+
if (remainingMs <= 0) throw timeoutError();
|
|
24
|
+
let timeout;
|
|
25
|
+
try {
|
|
26
|
+
return await Promise.race([promise, new Promise((_, reject) => {
|
|
27
|
+
timeout = setTimeout(() => reject(timeoutError()), remainingMs);
|
|
28
|
+
})]);
|
|
29
|
+
} finally {
|
|
30
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const convertedParameterSchemas = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
const getOrConvertParameterSchema = (parameters) => {
|
|
35
|
+
const cached = convertedParameterSchemas.get(parameters);
|
|
36
|
+
if (cached) return cached;
|
|
37
|
+
const converted = toJSONSchema(parameters);
|
|
38
|
+
convertedParameterSchemas.set(parameters, converted);
|
|
39
|
+
return converted;
|
|
40
|
+
};
|
|
41
|
+
const parametersToInputSchema = (parameters) => {
|
|
42
|
+
if (!parameters) return jsonSchema(EMPTY_SCHEMA);
|
|
43
|
+
return jsonSchema(getOrConvertParameterSchema(parameters));
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Builds an AI SDK `ToolSet` for server-side use with `streamText` /
|
|
47
|
+
* `generateText` from a generative `toolkit` and the frontend-uploaded tools.
|
|
48
|
+
*
|
|
49
|
+
* Each toolkit tool's `execute` runs on the server. Pair this with the
|
|
50
|
+
* `"use generative"` compiler: import the toolkit in a server route (where it
|
|
51
|
+
* resolves to the server build — schema + `execute`, with `render` stripped) and
|
|
52
|
+
* pass it here. Tools without an `execute` are still exposed to the model but
|
|
53
|
+
* left for the client to fulfill. `frontendTools` lets the client contribute
|
|
54
|
+
* tools that aren't in the static toolkit.
|
|
55
|
+
*
|
|
56
|
+
* @deprecated Use {@link AISDKToolkit} instead:
|
|
57
|
+
* `new AISDKToolkit({ toolkit }).tools({ frontend })`. It is a strict superset
|
|
58
|
+
* (it also opens MCP server connections), so it replaces `generativeTools`
|
|
59
|
+
* everywhere. The `frontendTools` option is named `frontend` on `.tools()`, and
|
|
60
|
+
* `.tools()` is async. `generativeTools` will be removed in a future version.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* // Define once at module scope so any MCP connections pool across requests.
|
|
65
|
+
* const aiToolkit = new AISDKToolkit({ toolkit: docsToolkit });
|
|
66
|
+
*
|
|
67
|
+
* // In your route handler:
|
|
68
|
+
* const { tools } = await req.json();
|
|
69
|
+
* streamText({
|
|
70
|
+
* abortSignal: req.signal,
|
|
71
|
+
* model,
|
|
72
|
+
* messages,
|
|
73
|
+
* tools: await aiToolkit.tools({ frontend: tools }),
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
const generativeTools = (options) => {
|
|
78
|
+
assertNoMcpToolkitTools(options.toolkit);
|
|
79
|
+
return {
|
|
80
|
+
...options.frontendTools ? frontendTools(options.frontendTools) : {},
|
|
81
|
+
...toProviderToolSet(options.toolkit),
|
|
82
|
+
...toServerToolSet(options.toolkit)
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
var AISDKToolkit = class {
|
|
86
|
+
#toolkit;
|
|
87
|
+
#mcpClients = /* @__PURE__ */ new Map();
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.#toolkit = options.toolkit;
|
|
90
|
+
}
|
|
91
|
+
async tools(options = {}) {
|
|
92
|
+
const frontendToolSet = options.frontend ? frontendTools(options.frontend) : {};
|
|
93
|
+
const mcpToolSet = await this.#mcpTools();
|
|
94
|
+
const providerToolSet = toProviderToolSet(this.#toolkit);
|
|
95
|
+
const serverToolSet = toServerToolSet(this.#toolkit);
|
|
96
|
+
assertNoMcpToolNameCollisions(mcpToolSet, [
|
|
97
|
+
{
|
|
98
|
+
source: "frontend",
|
|
99
|
+
tools: frontendToolSet
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
source: "provider",
|
|
103
|
+
tools: providerToolSet
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
source: "toolkit",
|
|
107
|
+
tools: serverToolSet
|
|
108
|
+
}
|
|
109
|
+
]);
|
|
110
|
+
return {
|
|
111
|
+
...frontendToolSet,
|
|
112
|
+
...mcpToolSet.tools,
|
|
113
|
+
...providerToolSet,
|
|
114
|
+
...serverToolSet
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async close() {
|
|
118
|
+
const clientEntries = [...this.#mcpClients.entries()];
|
|
119
|
+
const clientNames = clientEntries.map(([name]) => name);
|
|
120
|
+
this.#mcpClients.clear();
|
|
121
|
+
const clientResults = await Promise.allSettled(clientEntries.map(([, clientPromise]) => clientPromise));
|
|
122
|
+
const clients = clientResults.flatMap((result, index) => result.status === "fulfilled" ? [[clientNames[index], result.value]] : []);
|
|
123
|
+
const closeResults = await Promise.allSettled(clients.map(([, client]) => client.close()));
|
|
124
|
+
const errors = [...clientResults.flatMap((result, index) => result.status === "rejected" ? [toMcpToolkitError(clientNames[index], "connect", result.reason)] : []), ...closeResults.flatMap((result, index) => result.status === "rejected" ? [toMcpToolkitError(clients[index][0], "close", result.reason)] : [])];
|
|
125
|
+
if (errors.length === 1) throw errors[0];
|
|
126
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to close one or more MCP clients");
|
|
127
|
+
}
|
|
128
|
+
async #mcpTools() {
|
|
129
|
+
const toolSets = await Promise.all(Object.entries(this.#toolkit).filter((entry) => isMcpToolkitTool(entry[1])).map(async ([name, tool]) => {
|
|
130
|
+
const startedAt = Date.now();
|
|
131
|
+
const clientPromise = this.#mcpClient(name, tool.server, startedAt);
|
|
132
|
+
const client = await clientPromise.catch((error) => {
|
|
133
|
+
if (error instanceof MCPConnectionTimeoutError) throw error;
|
|
134
|
+
throw toMcpToolkitError(name, "connect", error);
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
return [
|
|
138
|
+
name,
|
|
139
|
+
tool,
|
|
140
|
+
await withMcpConnectionTimeout(client.tools(), {
|
|
141
|
+
name,
|
|
142
|
+
config: tool.server,
|
|
143
|
+
phase: "listing tools",
|
|
144
|
+
startedAt
|
|
145
|
+
})
|
|
146
|
+
];
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error instanceof MCPConnectionTimeoutError) {
|
|
149
|
+
if (this.#mcpClients.get(name) === clientPromise) {
|
|
150
|
+
this.#mcpClients.delete(name);
|
|
151
|
+
client.close().catch(() => {});
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
throw toMcpToolkitError(name, "list tools", error);
|
|
156
|
+
}
|
|
157
|
+
}));
|
|
158
|
+
const tools = Object.create(null);
|
|
159
|
+
const toolSources = /* @__PURE__ */ new Map();
|
|
160
|
+
for (const [serverName, mcpTool, toolSet] of toolSets) for (const [toolName, tool] of Object.entries(toolSet)) {
|
|
161
|
+
if (isDisabledMcpTool(mcpTool.tools?.[toolName])) continue;
|
|
162
|
+
const exposedName = `${mcpTool.prefix ?? ""}${toolName}`;
|
|
163
|
+
const existingServerName = toolSources.get(exposedName);
|
|
164
|
+
if (existingServerName) throw new Error(`MCP tool name collision: "${exposedName}" is exposed by both "${existingServerName}" and "${serverName}". Rename one of the toolkit entries or expose distinct MCP tool names.`);
|
|
165
|
+
toolSources.set(exposedName, serverName);
|
|
166
|
+
tools[exposedName] = tool;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
tools,
|
|
170
|
+
sources: toolSources
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
#mcpClient(name, config, startedAt) {
|
|
174
|
+
const existing = this.#mcpClients.get(name);
|
|
175
|
+
if (existing) return existing;
|
|
176
|
+
const createPromise = createMCPClient(toMCPClientConfig(config));
|
|
177
|
+
let next;
|
|
178
|
+
next = withMcpConnectionTimeout(createPromise, {
|
|
179
|
+
name,
|
|
180
|
+
config,
|
|
181
|
+
phase: "connecting",
|
|
182
|
+
startedAt
|
|
183
|
+
}).catch((error) => {
|
|
184
|
+
if (this.#mcpClients.get(name) === next) this.#mcpClients.delete(name);
|
|
185
|
+
if (error instanceof MCPConnectionTimeoutError) createPromise.then((client) => client.close()).catch(() => {});
|
|
186
|
+
throw error;
|
|
187
|
+
});
|
|
188
|
+
this.#mcpClients.set(name, next);
|
|
189
|
+
return next;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const toMCPClientConfig = (config) => {
|
|
193
|
+
if (config.type === "stdio") return { transport: new Experimental_StdioMCPTransport({
|
|
194
|
+
command: config.command,
|
|
195
|
+
...config.args && { args: [...config.args] },
|
|
196
|
+
...config.env && { env: config.env },
|
|
197
|
+
...config.cwd && { cwd: config.cwd }
|
|
198
|
+
}) };
|
|
199
|
+
return { transport: {
|
|
200
|
+
type: config.type,
|
|
201
|
+
url: config.url,
|
|
202
|
+
...config.headers && { headers: config.headers },
|
|
203
|
+
...config.redirect && { redirect: config.redirect }
|
|
204
|
+
} };
|
|
205
|
+
};
|
|
206
|
+
const assertNoMcpToolNameCollisions = (mcp, toolSets) => {
|
|
207
|
+
for (const [toolName, serverName] of mcp.sources) for (const { source, tools } of toolSets) {
|
|
208
|
+
if (!Object.prototype.hasOwnProperty.call(tools, toolName)) continue;
|
|
209
|
+
throw new Error(`MCP tool "${toolName}" from "${serverName}" conflicts with ${source} tool "${toolName}". Rename one of the tools so each model-visible tool name is unique.`);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const isMcpToolkitTool = (tool) => tool.type === "mcp" && !tool.disabled;
|
|
213
|
+
const getErrorMessage = (error) => error instanceof Error ? error.message || error.name : String(error);
|
|
214
|
+
const toMcpToolkitError = (entryName, action, error) => {
|
|
215
|
+
return new Error(`MCP toolkit entry "${entryName}" failed to ${action}: ${getErrorMessage(error)}`, { cause: error });
|
|
216
|
+
};
|
|
217
|
+
const isDisabledMcpTool = (config) => config?.disabled === true;
|
|
218
|
+
const assertNoMcpToolkitTools = (toolkit) => {
|
|
219
|
+
const mcpToolName = Object.entries(toolkit).find(([, tool]) => isMcpToolkitTool(tool))?.[0];
|
|
220
|
+
if (!mcpToolName) return;
|
|
221
|
+
throw new Error(`MCP toolkit entry "${mcpToolName}" requires AISDKToolkit. Use new AISDKToolkit({ toolkit }).tools(...) instead of generativeTools(...).`);
|
|
222
|
+
};
|
|
223
|
+
const toAISDKToModelOutput = (toModelOutput) => async (options) => {
|
|
224
|
+
const { result, modelContent } = unwrapModelContentEnvelope(options.output);
|
|
225
|
+
if (modelContent !== void 0) return toAISDKContent(modelContent, { taggedFileData: true });
|
|
226
|
+
if (!toModelOutput) return toAISDKDefaultOutput(result);
|
|
227
|
+
const parts = await toModelOutput({
|
|
228
|
+
...options,
|
|
229
|
+
output: result
|
|
230
|
+
});
|
|
231
|
+
return toAISDKContent(parts, { taggedFileData: true });
|
|
232
|
+
};
|
|
233
|
+
const toServerToolSet = (toolkit) => Object.fromEntries(Object.entries(toolkit).filter(([, t]) => t.type !== "mcp" && t.type !== "provider" && !t.disabled).map(([name, t]) => {
|
|
234
|
+
const execute = t.execute;
|
|
235
|
+
return [name, {
|
|
236
|
+
...t.description !== void 0 && { description: t.description },
|
|
237
|
+
inputSchema: parametersToInputSchema(t.parameters),
|
|
238
|
+
toModelOutput: toAISDKToModelOutput(t.toModelOutput),
|
|
239
|
+
...t.providerOptions && { providerOptions: t.providerOptions },
|
|
240
|
+
...execute && { execute: (args, callOptions) => execute(args, {
|
|
241
|
+
toolCallId: callOptions.toolCallId,
|
|
242
|
+
abortSignal: callOptions.abortSignal ?? neverAbort,
|
|
243
|
+
human: humanNotSupported
|
|
244
|
+
}) }
|
|
245
|
+
}];
|
|
246
|
+
}));
|
|
247
|
+
const toProviderToolSet = (toolkit) => Object.fromEntries(Object.entries(toolkit).filter((entry) => isProviderToolkitTool(entry[1])).map(([name, t]) => [name, {
|
|
248
|
+
type: "provider",
|
|
249
|
+
id: t.providerId,
|
|
250
|
+
args: t.args,
|
|
251
|
+
...t.parameters && { inputSchema: parametersToInputSchema(t.parameters) },
|
|
252
|
+
...t.providerOptions && { providerOptions: t.providerOptions },
|
|
253
|
+
...t.supportsDeferredResults !== void 0 && { supportsDeferredResults: t.supportsDeferredResults }
|
|
254
|
+
}]));
|
|
255
|
+
const isProviderToolkitTool = (tool) => tool.type === "provider" && !tool.disabled;
|
|
256
|
+
export { AISDKToolkit, generativeTools };
|
|
257
|
+
|
|
258
|
+
//# sourceMappingURL=generativeTools.js.map
|