@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.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/README.md +32 -27
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +546 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +129 -27
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +66 -12
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/defaultComponent.d.ts +11 -0
- package/dist/client/defaultComponent.d.ts.map +1 -0
- package/dist/client/defaultComponent.js +7 -0
- package/dist/client/defaultComponent.js.map +1 -0
- package/dist/client/definePlaygroundAPI.d.ts +1323 -192
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +52 -28
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +20 -7
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +68 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +1056 -965
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +242 -748
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +461 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +106 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/mockModel.d.ts +42 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +175 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +58 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +346 -35
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +350 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +84 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +171 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streamText.d.ts +46 -0
- package/dist/client/streamText.d.ts.map +1 -0
- package/dist/client/streamText.js +93 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3705 -32
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +141 -59
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/threads.d.ts +46 -0
- package/dist/client/threads.d.ts.map +1 -0
- package/dist/client/threads.js +49 -0
- package/dist/client/threads.js.map +1 -0
- package/dist/client/types.d.ts +265 -128
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.d.ts +4 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +21 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +24 -2178
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js +10 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3119 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +4 -18
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +9 -5
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/files.d.ts +16 -10
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +2553 -342
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +387 -154
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +5697 -3584
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +18 -41
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +35 -335
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +114 -73
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +16 -16
- package/dist/component/users.d.ts +4 -4
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/component/vector/index.d.ts.map +1 -1
- package/dist/component/vector/index.js +1 -3
- package/dist/component/vector/index.js.map +1 -1
- package/dist/deltas.d.ts +43 -0
- package/dist/deltas.d.ts.map +1 -0
- package/dist/deltas.js +447 -0
- package/dist/deltas.js.map +1 -0
- package/dist/mapping.d.ts +20 -20
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +313 -96
- package/dist/mapping.js.map +1 -1
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/index.d.ts +5 -77
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -160
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts +36 -3
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +35 -9
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/types.d.ts +4 -18
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.d.ts +10 -0
- package/dist/react/useDeltaStreams.d.ts.map +1 -0
- package/dist/react/useDeltaStreams.js +101 -0
- package/dist/react/useDeltaStreams.js.map +1 -0
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +32 -15
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/react/useStreamingUIMessages.d.ts +22 -0
- package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
- package/dist/react/useStreamingUIMessages.js +92 -0
- package/dist/react/useStreamingUIMessages.js.map +1 -0
- package/dist/react/useThreadMessages.d.ts +104 -0
- package/dist/react/useThreadMessages.d.ts.map +1 -0
- package/dist/react/useThreadMessages.js +148 -0
- package/dist/react/useThreadMessages.js.map +1 -0
- package/dist/react/useUIMessages.d.ts +96 -0
- package/dist/react/useUIMessages.d.ts.map +1 -0
- package/dist/react/useUIMessages.js +108 -0
- package/dist/react/useUIMessages.js.map +1 -0
- package/dist/shared.d.ts +20 -4
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +45 -8
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +22981 -5666
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +245 -137
- package/dist/validators.js.map +1 -1
- package/package.json +98 -50
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +739 -0
- package/src/client/createTool.ts +293 -76
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +67 -31
- package/src/client/files.ts +100 -20
- package/src/client/index.test.ts +40 -85
- package/src/client/index.ts +520 -1290
- package/src/client/messages.ts +237 -0
- package/src/client/mockModel.ts +245 -0
- package/src/client/saveInputMessages.test.ts +583 -0
- package/src/client/saveInputMessages.ts +101 -0
- package/src/client/search.test.ts +1207 -0
- package/src/client/search.ts +577 -70
- package/src/client/start.ts +310 -0
- package/src/client/streamText.ts +163 -0
- package/src/client/streaming.test.ts +186 -0
- package/src/client/streaming.ts +219 -97
- package/src/client/threads.ts +83 -0
- package/src/client/types.ts +368 -219
- package/src/client/utils.ts +27 -0
- package/src/component/_generated/api.ts +64 -0
- package/src/component/_generated/component.ts +4913 -0
- package/src/component/_generated/{server.d.ts → server.ts} +33 -21
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +195 -51
- package/src/component/messages.ts +490 -201
- package/src/component/schema.ts +20 -46
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +184 -83
- package/src/component/users.test.ts +0 -1
- package/src/component/vector/index.ts +1 -3
- package/src/deltas.test.ts +626 -0
- package/src/deltas.ts +570 -0
- package/src/fromUIMessages.test.ts +497 -0
- package/src/mapping.test.ts +103 -6
- package/src/mapping.ts +422 -161
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/index.ts +10 -230
- package/src/react/optimisticallySendMessage.ts +55 -12
- package/src/react/types.ts +6 -39
- package/src/react/useDeltaStreams.ts +154 -0
- package/src/react/useSmoothText.ts +56 -36
- package/src/react/useStreamingUIMessages.ts +143 -0
- package/src/react/useThreadMessages.ts +262 -0
- package/src/react/useUIMessages.test.ts +255 -0
- package/src/react/useUIMessages.ts +195 -0
- package/src/shared.ts +88 -12
- package/src/test.ts +18 -0
- package/src/toUIMessages.test.ts +1269 -0
- package/src/validators.test.ts +18 -19
- package/src/validators.ts +325 -185
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- package/dist/client/listMessages.d.ts +0 -22
- package/dist/client/listMessages.d.ts.map +0 -1
- package/dist/client/listMessages.js +0 -25
- package/dist/client/listMessages.js.map +0 -1
- package/dist/package.json +0 -3
- package/dist/react/deltas.d.ts +0 -26
- package/dist/react/deltas.d.ts.map +0 -1
- package/dist/react/deltas.js +0 -384
- package/dist/react/deltas.js.map +0 -1
- package/dist/react/toUIMessages.d.ts +0 -15
- package/dist/react/toUIMessages.d.ts.map +0 -1
- package/dist/react/toUIMessages.js +0 -211
- package/dist/react/toUIMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
- package/src/component/_generated/api.d.ts +0 -2202
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/src/react/deltas.test.ts +0 -315
- package/src/react/deltas.ts +0 -478
- package/src/react/toUIMessages.test.ts +0 -420
- package/src/react/toUIMessages.ts +0 -253
- package/src/vitest.config.ts +0 -7
- /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
package/src/client/files.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AssistantContent,
|
|
3
|
+
FilePart,
|
|
4
|
+
ImagePart,
|
|
5
|
+
ModelMessage,
|
|
6
|
+
UserContent,
|
|
7
|
+
} from "ai";
|
|
2
8
|
import type { Id } from "../component/_generated/dataModel.js";
|
|
3
9
|
import type {
|
|
4
10
|
ActionCtx,
|
|
5
11
|
AgentComponent,
|
|
12
|
+
MutationCtx,
|
|
6
13
|
QueryCtx,
|
|
7
|
-
RunMutationCtx,
|
|
8
14
|
} from "./types.js";
|
|
15
|
+
import type { Message } from "../validators.js";
|
|
16
|
+
import { assert } from "convex-helpers";
|
|
17
|
+
import type { StorageReader } from "convex/server";
|
|
9
18
|
|
|
10
19
|
export const MAX_FILE_SIZE = 1024 * 64;
|
|
11
20
|
|
|
@@ -22,18 +31,17 @@ type File = {
|
|
|
22
31
|
* @param ctx A ctx object from an action.
|
|
23
32
|
* @param component The agent component.
|
|
24
33
|
* @param blob The blob to store.
|
|
25
|
-
* @param filename The filename to store.
|
|
26
|
-
* @param sha256 The sha256 hash of the file. If not provided, it will be
|
|
34
|
+
* @param args.filename The filename to store.
|
|
35
|
+
* @param args.sha256 The sha256 hash of the file. If not provided, it will be
|
|
27
36
|
* computed. However, to ensure no corruption during transfer, you can
|
|
28
37
|
* calculate this on the client to enforce integrity.
|
|
29
38
|
* @returns The URL, fileId, and storageId of the stored file.
|
|
30
39
|
*/
|
|
31
40
|
export async function storeFile(
|
|
32
|
-
ctx: ActionCtx |
|
|
41
|
+
ctx: ActionCtx | MutationCtx,
|
|
33
42
|
component: AgentComponent,
|
|
34
43
|
blob: Blob,
|
|
35
|
-
filename?: string,
|
|
36
|
-
sha256?: string,
|
|
44
|
+
{ filename, sha256 }: { filename?: string; sha256?: string } = {},
|
|
37
45
|
): Promise<{
|
|
38
46
|
file: File;
|
|
39
47
|
filePart: FilePart;
|
|
@@ -84,7 +92,7 @@ export async function storeFile(
|
|
|
84
92
|
storageId: newStorageId,
|
|
85
93
|
hash,
|
|
86
94
|
filename,
|
|
87
|
-
|
|
95
|
+
mediaType: blob.type,
|
|
88
96
|
});
|
|
89
97
|
const url = (await ctx.storage.getUrl(storageId as Id<"_storage">))!;
|
|
90
98
|
if (storageId !== newStorageId) {
|
|
@@ -108,7 +116,7 @@ export async function storeFile(
|
|
|
108
116
|
/**
|
|
109
117
|
* Get file metadata from the component.
|
|
110
118
|
* This also returns filePart (and imagePart if the file is an image),
|
|
111
|
-
* which are useful to construct a
|
|
119
|
+
* which are useful to construct a ModelMessage like
|
|
112
120
|
* ```ts
|
|
113
121
|
* const { filePart, imagePart } = await getFile(ctx, components.agent, fileId);
|
|
114
122
|
* const message: UserMessage = {
|
|
@@ -122,7 +130,7 @@ export async function storeFile(
|
|
|
122
130
|
* @returns The file metadata and content parts.
|
|
123
131
|
*/
|
|
124
132
|
export async function getFile(
|
|
125
|
-
ctx: ActionCtx | QueryCtx,
|
|
133
|
+
ctx: ActionCtx | (QueryCtx & { storage: StorageReader }),
|
|
126
134
|
component: AgentComponent,
|
|
127
135
|
fileId: string,
|
|
128
136
|
) {
|
|
@@ -134,8 +142,10 @@ export async function getFile(
|
|
|
134
142
|
if (!url) {
|
|
135
143
|
throw new Error(`File not found in storage: ${file.storageId}`);
|
|
136
144
|
}
|
|
145
|
+
// Support both mediaType (preferred) and mimeType (deprecated)
|
|
146
|
+
const mediaType = file.mediaType ?? file.mimeType ?? "";
|
|
137
147
|
return {
|
|
138
|
-
...getParts(url,
|
|
148
|
+
...getParts(url, mediaType, file.filename),
|
|
139
149
|
file: {
|
|
140
150
|
fileId,
|
|
141
151
|
url,
|
|
@@ -150,10 +160,7 @@ function getParts(
|
|
|
150
160
|
url: string,
|
|
151
161
|
mediaType: string,
|
|
152
162
|
filename: string | undefined,
|
|
153
|
-
): {
|
|
154
|
-
filePart: FilePart;
|
|
155
|
-
imagePart: ImagePart | undefined;
|
|
156
|
-
} {
|
|
163
|
+
): { filePart: FilePart; imagePart: ImagePart | undefined } {
|
|
157
164
|
const filePart: FilePart = {
|
|
158
165
|
type: "file",
|
|
159
166
|
data: new URL(url),
|
|
@@ -161,11 +168,84 @@ function getParts(
|
|
|
161
168
|
filename,
|
|
162
169
|
};
|
|
163
170
|
const imagePart: ImagePart | undefined = mediaType.startsWith("image/")
|
|
164
|
-
? {
|
|
165
|
-
type: "image",
|
|
166
|
-
image: new URL(url),
|
|
167
|
-
mediaType,
|
|
168
|
-
}
|
|
171
|
+
? { type: "image", image: new URL(url), mediaType }
|
|
169
172
|
: undefined;
|
|
170
173
|
return { filePart, imagePart };
|
|
171
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if a URL points to localhost
|
|
178
|
+
*/
|
|
179
|
+
function isLocalhostUrl(url: URL): boolean {
|
|
180
|
+
return (
|
|
181
|
+
url.hostname === "localhost" ||
|
|
182
|
+
url.hostname === "127.0.0.1" ||
|
|
183
|
+
url.hostname === "::1" ||
|
|
184
|
+
url.hostname === "0.0.0.0"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Download a file from a URL
|
|
190
|
+
*/
|
|
191
|
+
async function downloadFile(url: URL): Promise<ArrayBuffer> {
|
|
192
|
+
// Fetch the file
|
|
193
|
+
const response = await fetch(url);
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return await response.arrayBuffer();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Process messages to inline file and image URLs that point to localhost
|
|
203
|
+
* by converting them to base64. This solves the problem of LLMs not being
|
|
204
|
+
* able to access localhost URLs.
|
|
205
|
+
*/
|
|
206
|
+
export async function inlineMessagesFiles<T extends ModelMessage | Message>(
|
|
207
|
+
messages: T[],
|
|
208
|
+
): Promise<T[]> {
|
|
209
|
+
// Process each message to convert localhost URLs to base64
|
|
210
|
+
return Promise.all(
|
|
211
|
+
messages.map(async (message): Promise<T> => {
|
|
212
|
+
if (
|
|
213
|
+
(message.role !== "user" && message.role !== "assistant") ||
|
|
214
|
+
typeof message.content === "string" ||
|
|
215
|
+
!Array.isArray(message.content)
|
|
216
|
+
) {
|
|
217
|
+
return message;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const processedContent = await Promise.all(
|
|
221
|
+
message.content.map(async (part) => {
|
|
222
|
+
if (part.type === "image" && part.image instanceof URL) {
|
|
223
|
+
assert(
|
|
224
|
+
message.role === "user",
|
|
225
|
+
"Images can only be in user messages",
|
|
226
|
+
);
|
|
227
|
+
if (isLocalhostUrl(part.image)) {
|
|
228
|
+
const imageData = await downloadFile(part.image);
|
|
229
|
+
return { ...part, image: imageData } as ImagePart;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle file parts
|
|
234
|
+
if (part.type === "file" && part.data instanceof URL) {
|
|
235
|
+
if (isLocalhostUrl(part.data)) {
|
|
236
|
+
const fileData = await downloadFile(part.data);
|
|
237
|
+
return { ...part, data: fileData } as FilePart;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return part;
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
if (message.role === "user") {
|
|
245
|
+
return { ...message, content: processedContent as UserContent };
|
|
246
|
+
} else {
|
|
247
|
+
return { ...message, content: processedContent as AssistantContent };
|
|
248
|
+
}
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
}
|
package/src/client/index.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
Agent,
|
|
4
|
+
createThread,
|
|
4
5
|
filterOutOrphanedToolMessages,
|
|
5
6
|
type MessageDoc,
|
|
6
7
|
} from "./index.js";
|
|
@@ -19,14 +20,10 @@ import type {
|
|
|
19
20
|
} from "convex/server";
|
|
20
21
|
import { v } from "convex/values";
|
|
21
22
|
import { defineSchema } from "convex/server";
|
|
22
|
-
import {
|
|
23
|
-
import type {
|
|
24
|
-
LanguageModelV2,
|
|
25
|
-
LanguageModelV2StreamPart,
|
|
26
|
-
} from "@ai-sdk/provider";
|
|
27
|
-
import { simulateReadableStream, stepCountIs } from "ai";
|
|
23
|
+
import { stepCountIs } from "ai";
|
|
28
24
|
import { components, initConvexTest } from "./setup.test.js";
|
|
29
|
-
import { z } from "zod";
|
|
25
|
+
import { z } from "zod/v4";
|
|
26
|
+
import { mockModel } from "./mockModel.js";
|
|
30
27
|
|
|
31
28
|
const schema = defineSchema({});
|
|
32
29
|
type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|
|
@@ -35,11 +32,14 @@ const query = queryGeneric as QueryBuilder<DataModel, "public">;
|
|
|
35
32
|
const mutation = mutationGeneric as MutationBuilder<DataModel, "public">;
|
|
36
33
|
const action = actionGeneric as ActionBuilder<DataModel, "public">;
|
|
37
34
|
|
|
35
|
+
const TEST_TEXT = JSON.stringify({ hello: "world" });
|
|
36
|
+
|
|
38
37
|
const agent = new Agent(components.agent, {
|
|
39
38
|
name: "test",
|
|
40
39
|
instructions: "You are a test agent",
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
languageModel: mockModel({
|
|
41
|
+
content: [{ type: "text", text: TEST_TEXT }],
|
|
42
|
+
}),
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
export const testQuery = query({
|
|
@@ -47,31 +47,24 @@ export const testQuery = query({
|
|
|
47
47
|
handler: async (ctx, args) => {
|
|
48
48
|
return await agent.listMessages(ctx, {
|
|
49
49
|
threadId: args.threadId,
|
|
50
|
-
paginationOpts: {
|
|
51
|
-
cursor: null,
|
|
52
|
-
numItems: 10,
|
|
53
|
-
},
|
|
50
|
+
paginationOpts: { cursor: null, numItems: 10 },
|
|
54
51
|
excludeToolMessages: true,
|
|
55
52
|
statuses: ["success"],
|
|
56
53
|
});
|
|
57
54
|
},
|
|
58
55
|
});
|
|
59
56
|
|
|
60
|
-
export const
|
|
57
|
+
export const createThreadManually = mutation({
|
|
61
58
|
args: {},
|
|
62
59
|
handler: async (ctx) => {
|
|
63
|
-
const { threadId } = await agent.createThread(ctx, {
|
|
64
|
-
userId: "1",
|
|
65
|
-
});
|
|
60
|
+
const { threadId } = await agent.createThread(ctx, { userId: "1" });
|
|
66
61
|
return { threadId };
|
|
67
62
|
},
|
|
68
63
|
});
|
|
69
64
|
|
|
70
65
|
export const createThreadMutation = agent.createThreadMutation();
|
|
71
66
|
export const generateObjectAction = agent.asObjectAction({
|
|
72
|
-
schema: z.object({
|
|
73
|
-
prompt: z.any().describe("The prompt passed in"),
|
|
74
|
-
}),
|
|
67
|
+
schema: z.object({ hello: z.string().describe("A string for testing") }),
|
|
75
68
|
});
|
|
76
69
|
export const generateTextAction = agent.asTextAction({});
|
|
77
70
|
export const streamTextAction = agent.asTextAction({ stream: true });
|
|
@@ -80,9 +73,7 @@ export const saveMessageMutation = agent.asSaveMessagesMutation();
|
|
|
80
73
|
export const createAndGenerate = action({
|
|
81
74
|
args: {},
|
|
82
75
|
handler: async (ctx) => {
|
|
83
|
-
const { thread } = await agent.createThread(ctx, {
|
|
84
|
-
userId: "1",
|
|
85
|
-
});
|
|
76
|
+
const { thread } = await agent.createThread(ctx, { userId: "1" });
|
|
86
77
|
const result = await thread.generateText({
|
|
87
78
|
messages: [{ role: "user", content: "Hello" }],
|
|
88
79
|
});
|
|
@@ -162,7 +153,7 @@ export const fetchContextAction = action({
|
|
|
162
153
|
const testApi: ApiFromModules<{
|
|
163
154
|
fns: {
|
|
164
155
|
createAndGenerate: typeof createAndGenerate;
|
|
165
|
-
|
|
156
|
+
createThreadManually: typeof createThreadManually;
|
|
166
157
|
testQuery: typeof testQuery;
|
|
167
158
|
continueThreadAction: typeof continueThreadAction;
|
|
168
159
|
generateTextWithThread: typeof generateTextWithThread;
|
|
@@ -172,20 +163,19 @@ const testApi: ApiFromModules<{
|
|
|
172
163
|
generateObjectAction: typeof generateObjectAction;
|
|
173
164
|
saveMessageMutation: typeof saveMessageMutation;
|
|
174
165
|
};
|
|
175
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
166
|
}>["fns"] = anyApi["index.test"] as any;
|
|
177
167
|
|
|
178
168
|
describe("Agent thick client", () => {
|
|
179
169
|
test("should create a thread", async () => {
|
|
180
170
|
const t = initConvexTest(schema);
|
|
181
|
-
const result = await t.mutation(testApi.
|
|
171
|
+
const result = await t.mutation(testApi.createThreadManually, {});
|
|
182
172
|
expect(result.threadId).toBeTypeOf("string");
|
|
183
173
|
});
|
|
184
174
|
test("should create a thread and generate text", async () => {
|
|
185
175
|
const t = initConvexTest(schema);
|
|
186
176
|
const result = await t.action(testApi.createAndGenerate, {});
|
|
187
177
|
expect(result).toBeDefined();
|
|
188
|
-
expect(result).toMatch(
|
|
178
|
+
expect(result).toMatch(TEST_TEXT);
|
|
189
179
|
});
|
|
190
180
|
});
|
|
191
181
|
|
|
@@ -203,6 +193,7 @@ describe("filterOutOrphanedToolMessages", () => {
|
|
|
203
193
|
type: "tool-call",
|
|
204
194
|
toolCallId: "1",
|
|
205
195
|
toolName: "tool1",
|
|
196
|
+
input: { test: "test" },
|
|
206
197
|
args: { test: "test" },
|
|
207
198
|
},
|
|
208
199
|
],
|
|
@@ -236,10 +227,7 @@ describe("filterOutOrphanedToolMessages", () => {
|
|
|
236
227
|
order: 1,
|
|
237
228
|
stepOrder: 2,
|
|
238
229
|
tool: true,
|
|
239
|
-
message: {
|
|
240
|
-
role: "assistant",
|
|
241
|
-
content: [{ type: "text", text: "Hello" }],
|
|
242
|
-
},
|
|
230
|
+
message: { role: "assistant", content: [{ type: "text", text: "Hello" }] },
|
|
243
231
|
status: "success",
|
|
244
232
|
threadId: "1",
|
|
245
233
|
};
|
|
@@ -255,55 +243,24 @@ describe("filterOutOrphanedToolMessages", () => {
|
|
|
255
243
|
});
|
|
256
244
|
});
|
|
257
245
|
|
|
258
|
-
function mockModel(): LanguageModelV2 {
|
|
259
|
-
return new MockLanguageModelV2({
|
|
260
|
-
provider: "mock",
|
|
261
|
-
modelId: "mock",
|
|
262
|
-
// supportsStructuredOutputs: true,
|
|
263
|
-
doGenerate: async ({ prompt }) => ({
|
|
264
|
-
finishReason: "stop",
|
|
265
|
-
content: [{ type: "text", text: JSON.stringify({ prompt }) }],
|
|
266
|
-
warnings: [],
|
|
267
|
-
usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
|
|
268
|
-
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
269
|
-
text: JSON.stringify({ prompt }),
|
|
270
|
-
}),
|
|
271
|
-
doStream: async ({ prompt }) => ({
|
|
272
|
-
stream: simulateReadableStream({
|
|
273
|
-
chunkDelayInMs: 50,
|
|
274
|
-
initialDelayInMs: 100,
|
|
275
|
-
chunks: [
|
|
276
|
-
{
|
|
277
|
-
type: "text-delta",
|
|
278
|
-
textDelta: `This is a sample response to ${JSON.stringify(prompt)}`,
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
type: "finish",
|
|
282
|
-
finishReason: "stop",
|
|
283
|
-
usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
|
|
284
|
-
},
|
|
285
|
-
] as LanguageModelV2StreamPart[],
|
|
286
|
-
}),
|
|
287
|
-
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
288
|
-
}),
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
246
|
describe("Agent option variations and normal behavior", () => {
|
|
293
247
|
test("Agent can be constructed with minimal options", () => {
|
|
294
|
-
const a = new Agent(components.agent, {
|
|
248
|
+
const a = new Agent(components.agent, {
|
|
249
|
+
name: "minimal",
|
|
250
|
+
languageModel: mockModel(),
|
|
251
|
+
});
|
|
295
252
|
expect(a).toBeInstanceOf(Agent);
|
|
296
253
|
});
|
|
297
254
|
|
|
298
255
|
test("Agent can be constructed with all options", () => {
|
|
299
256
|
const a = new Agent(components.agent, {
|
|
300
257
|
name: "full",
|
|
301
|
-
|
|
258
|
+
languageModel: mockModel(),
|
|
302
259
|
instructions: "Test instructions",
|
|
303
260
|
contextOptions: { recentMessages: 5 },
|
|
304
261
|
storageOptions: { saveMessages: "all" },
|
|
305
262
|
stopWhen: stepCountIs(2),
|
|
306
|
-
maxRetries: 1,
|
|
263
|
+
callSettings: { maxRetries: 1 },
|
|
307
264
|
usageHandler: async () => {},
|
|
308
265
|
rawRequestResponseHandler: async () => {},
|
|
309
266
|
});
|
|
@@ -315,7 +272,7 @@ describe("Agent thread management", () => {
|
|
|
315
272
|
test("createThread returns threadId (mutation context)", async () => {
|
|
316
273
|
const t = initConvexTest(schema);
|
|
317
274
|
const threadId = await t.run(async (ctx) =>
|
|
318
|
-
|
|
275
|
+
createThread(ctx, components.agent, { userId: "2" }),
|
|
319
276
|
);
|
|
320
277
|
expect(threadId).toBeTypeOf("string");
|
|
321
278
|
});
|
|
@@ -323,7 +280,7 @@ describe("Agent thread management", () => {
|
|
|
323
280
|
test("continueThread returns thread object", async () => {
|
|
324
281
|
const t = initConvexTest(schema);
|
|
325
282
|
const threadId = await t.run(async (ctx) =>
|
|
326
|
-
|
|
283
|
+
createThread(ctx, components.agent, { userId: "3" }),
|
|
327
284
|
);
|
|
328
285
|
const result = await t.action(testApi.continueThreadAction, {
|
|
329
286
|
threadId,
|
|
@@ -337,7 +294,7 @@ describe("Agent message operations", () => {
|
|
|
337
294
|
test("saveMessage and saveMessages store messages", async () => {
|
|
338
295
|
const t = initConvexTest(schema);
|
|
339
296
|
const threadId = await t.run(async (ctx) =>
|
|
340
|
-
|
|
297
|
+
createThread(ctx, components.agent, { userId: "4" }),
|
|
341
298
|
);
|
|
342
299
|
const { messageId } = await t.run(async (ctx) =>
|
|
343
300
|
agent.saveMessage(ctx, {
|
|
@@ -348,7 +305,7 @@ describe("Agent message operations", () => {
|
|
|
348
305
|
);
|
|
349
306
|
expect(messageId).toBeTypeOf("string");
|
|
350
307
|
|
|
351
|
-
const {
|
|
308
|
+
const { messages } = await t.run(async (ctx) =>
|
|
352
309
|
agent.saveMessages(ctx, {
|
|
353
310
|
threadId,
|
|
354
311
|
userId: "4",
|
|
@@ -359,7 +316,7 @@ describe("Agent message operations", () => {
|
|
|
359
316
|
}),
|
|
360
317
|
);
|
|
361
318
|
expect(messages.length).toBe(2);
|
|
362
|
-
expect(
|
|
319
|
+
expect(messages[1]._id).toBeDefined();
|
|
363
320
|
});
|
|
364
321
|
});
|
|
365
322
|
|
|
@@ -367,7 +324,7 @@ describe("Agent text/object generation", () => {
|
|
|
367
324
|
test("generateText with custom context and storage options", async () => {
|
|
368
325
|
const t = initConvexTest(schema);
|
|
369
326
|
const threadId = await t.run(async (ctx) =>
|
|
370
|
-
|
|
327
|
+
createThread(ctx, components.agent, { userId: "5" }),
|
|
371
328
|
);
|
|
372
329
|
const result = await t.action(testApi.generateTextWithThread, {
|
|
373
330
|
threadId,
|
|
@@ -376,13 +333,13 @@ describe("Agent text/object generation", () => {
|
|
|
376
333
|
contextOptions: { recentMessages: 1 },
|
|
377
334
|
storageOptions: { saveMessages: "all" },
|
|
378
335
|
});
|
|
379
|
-
expect(result.text).
|
|
336
|
+
expect(result.text).toEqual(TEST_TEXT);
|
|
380
337
|
});
|
|
381
338
|
|
|
382
339
|
test("generateObject returns object", async () => {
|
|
383
340
|
const t = initConvexTest(schema);
|
|
384
341
|
const threadId = await t.run(async (ctx) =>
|
|
385
|
-
|
|
342
|
+
createThread(ctx, components.agent, { userId: "6" }),
|
|
386
343
|
);
|
|
387
344
|
const result = await t.action(testApi.generateObjectWithThread, {
|
|
388
345
|
threadId,
|
|
@@ -397,21 +354,21 @@ describe("Agent-generated mutations/actions/queries", () => {
|
|
|
397
354
|
test("createThreadMutation works via t.mutation", async () => {
|
|
398
355
|
const t = initConvexTest(schema);
|
|
399
356
|
// This test is for the registered mutation, not the agent method
|
|
400
|
-
const result = await t.mutation(testApi.
|
|
357
|
+
const result = await t.mutation(testApi.createThreadManually, {});
|
|
401
358
|
expect(result.threadId).toBeTypeOf("string");
|
|
402
359
|
});
|
|
403
360
|
|
|
404
361
|
test("asTextAction and asObjectAction work via t.action", async () => {
|
|
405
362
|
const t = initConvexTest(schema);
|
|
406
363
|
const threadId = await t.run(async (ctx) =>
|
|
407
|
-
|
|
364
|
+
createThread(ctx, components.agent, { userId: "8" }),
|
|
408
365
|
);
|
|
409
366
|
const textResult = await t.action(testApi.generateTextAction, {
|
|
410
367
|
userId: "8",
|
|
411
368
|
threadId,
|
|
412
369
|
messages: [{ role: "user", content: "Say hi" }],
|
|
413
370
|
});
|
|
414
|
-
expect(textResult.text).
|
|
371
|
+
expect(textResult.text).toEqual(TEST_TEXT);
|
|
415
372
|
|
|
416
373
|
const objResult = await t.action(testApi.generateObjectAction, {
|
|
417
374
|
userId: "8",
|
|
@@ -424,7 +381,7 @@ describe("Agent-generated mutations/actions/queries", () => {
|
|
|
424
381
|
test("asSaveMessagesMutation works via t.mutation", async () => {
|
|
425
382
|
const t = initConvexTest(schema);
|
|
426
383
|
const threadId = await t.run(async (ctx) =>
|
|
427
|
-
|
|
384
|
+
createThread(ctx, components.agent, { userId: "9" }),
|
|
428
385
|
);
|
|
429
386
|
const result = await t.mutation(testApi.saveMessageMutation, {
|
|
430
387
|
threadId,
|
|
@@ -435,8 +392,8 @@ describe("Agent-generated mutations/actions/queries", () => {
|
|
|
435
392
|
},
|
|
436
393
|
],
|
|
437
394
|
});
|
|
438
|
-
expect(result.
|
|
439
|
-
expect(result.
|
|
395
|
+
expect(result.messages.length).toBe(1);
|
|
396
|
+
expect(result.messages[0]._id).toBeDefined();
|
|
440
397
|
});
|
|
441
398
|
});
|
|
442
399
|
|
|
@@ -444,9 +401,7 @@ describe("Agent context and search options", () => {
|
|
|
444
401
|
test("fetchContextMessages returns context messages", async () => {
|
|
445
402
|
const t = initConvexTest(schema);
|
|
446
403
|
const threadId = await t.run(async (ctx) =>
|
|
447
|
-
agent
|
|
448
|
-
.createThread(ctx, { userId: "10" })
|
|
449
|
-
.then(({ threadId }) => threadId),
|
|
404
|
+
createThread(ctx, components.agent, { userId: "10" }),
|
|
450
405
|
);
|
|
451
406
|
await t.run(async (ctx) =>
|
|
452
407
|
agent.saveMessage(ctx, {
|