@convex-dev/agent 0.1.8-alpha.0 → 0.1.8-alpha.2

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.
Files changed (82) hide show
  1. package/README.md +2 -0
  2. package/dist/commonjs/client/index.d.ts +65 -51
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +70 -4
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/component/_generated/api.d.ts +235 -0
  7. package/dist/commonjs/component/messages.d.ts +361 -155
  8. package/dist/commonjs/component/messages.d.ts.map +1 -1
  9. package/dist/commonjs/component/messages.js +25 -0
  10. package/dist/commonjs/component/messages.js.map +1 -1
  11. package/dist/commonjs/component/schema.d.ts +670 -670
  12. package/dist/commonjs/component/streams.d.ts +65 -18
  13. package/dist/commonjs/component/streams.d.ts.map +1 -1
  14. package/dist/commonjs/component/streams.js +120 -2
  15. package/dist/commonjs/component/streams.js.map +1 -1
  16. package/dist/commonjs/component/threads.d.ts +6 -6
  17. package/dist/commonjs/component/threads.d.ts.map +1 -1
  18. package/dist/commonjs/component/threads.js +9 -0
  19. package/dist/commonjs/component/threads.js.map +1 -1
  20. package/dist/commonjs/component/users.d.ts.map +1 -1
  21. package/dist/commonjs/component/users.js +1 -0
  22. package/dist/commonjs/component/users.js.map +1 -1
  23. package/dist/commonjs/component/vector/index.d.ts +12 -12
  24. package/dist/commonjs/component/vector/index.d.ts.map +1 -1
  25. package/dist/commonjs/component/vector/index.js +4 -0
  26. package/dist/commonjs/component/vector/index.js.map +1 -1
  27. package/dist/commonjs/component/vector/tables.d.ts +6 -6
  28. package/dist/commonjs/react/index.d.ts.map +1 -1
  29. package/dist/commonjs/react/index.js +1 -1
  30. package/dist/commonjs/react/index.js.map +1 -1
  31. package/dist/commonjs/validators.d.ts +161 -161
  32. package/dist/commonjs.tsbuildinfo +1 -1
  33. package/dist/esm/client/index.d.ts +31 -17
  34. package/dist/esm/client/index.d.ts.map +1 -1
  35. package/dist/esm/client/index.js +70 -4
  36. package/dist/esm/client/index.js.map +1 -1
  37. package/dist/esm/component/_generated/api.d.ts +235 -0
  38. package/dist/esm/component/messages.d.ts +233 -27
  39. package/dist/esm/component/messages.d.ts.map +1 -1
  40. package/dist/esm/component/messages.js +25 -0
  41. package/dist/esm/component/messages.js.map +1 -1
  42. package/dist/esm/component/schema.d.ts +48 -48
  43. package/dist/esm/component/streams.d.ts +47 -0
  44. package/dist/esm/component/streams.d.ts.map +1 -1
  45. package/dist/esm/component/streams.js +120 -2
  46. package/dist/esm/component/streams.js.map +1 -1
  47. package/dist/esm/component/threads.d.ts +8 -8
  48. package/dist/esm/component/threads.d.ts.map +1 -1
  49. package/dist/esm/component/threads.js +9 -0
  50. package/dist/esm/component/threads.js.map +1 -1
  51. package/dist/esm/component/users.d.ts.map +1 -1
  52. package/dist/esm/component/users.js +1 -0
  53. package/dist/esm/component/users.js.map +1 -1
  54. package/dist/esm/component/vector/index.d.ts.map +1 -1
  55. package/dist/esm/component/vector/index.js +4 -0
  56. package/dist/esm/component/vector/index.js.map +1 -1
  57. package/dist/esm/react/index.d.ts.map +1 -1
  58. package/dist/esm/react/index.js +1 -1
  59. package/dist/esm/react/index.js.map +1 -1
  60. package/dist/esm/validators.d.ts +19 -19
  61. package/dist/esm.tsbuildinfo +1 -1
  62. package/package.json +4 -5
  63. package/src/client/index.ts +94 -4
  64. package/src/client/setup.test.ts +2 -2
  65. package/src/component/_generated/api.d.ts +235 -0
  66. package/src/component/messages.test.ts +140 -0
  67. package/src/component/messages.ts +30 -0
  68. package/src/component/streams.ts +148 -2
  69. package/src/component/threads.ts +8 -0
  70. package/src/component/users.ts +1 -0
  71. package/src/component/vector/index.ts +4 -0
  72. package/src/react/index.ts +1 -2
  73. package/dist/commonjs/react/usePaginatedQuery.d.ts +0 -241
  74. package/dist/commonjs/react/usePaginatedQuery.d.ts.map +0 -1
  75. package/dist/commonjs/react/usePaginatedQuery.js +0 -627
  76. package/dist/commonjs/react/usePaginatedQuery.js.map +0 -1
  77. package/dist/esm/react/usePaginatedQuery.d.ts +0 -241
  78. package/dist/esm/react/usePaginatedQuery.d.ts.map +0 -1
  79. package/dist/esm/react/usePaginatedQuery.js +0 -627
  80. package/dist/esm/react/usePaginatedQuery.js.map +0 -1
  81. package/react/package.json +0 -5
  82. package/src/react/usePaginatedQuery.ts +0 -901
@@ -1177,6 +1177,241 @@ export type Mounts = {
1177
1177
  >;
1178
1178
  }>
1179
1179
  >;
1180
+ updateMessage: FunctionReference<
1181
+ "mutation",
1182
+ "public",
1183
+ {
1184
+ messageId: string;
1185
+ patch: {
1186
+ error?: string;
1187
+ message?:
1188
+ | {
1189
+ content:
1190
+ | string
1191
+ | Array<
1192
+ | {
1193
+ providerOptions?: Record<string, Record<string, any>>;
1194
+ text: string;
1195
+ type: "text";
1196
+ }
1197
+ | {
1198
+ image: string | ArrayBuffer;
1199
+ mimeType?: string;
1200
+ providerOptions?: Record<string, Record<string, any>>;
1201
+ type: "image";
1202
+ }
1203
+ | {
1204
+ data: string | ArrayBuffer;
1205
+ filename?: string;
1206
+ mimeType: string;
1207
+ providerOptions?: Record<string, Record<string, any>>;
1208
+ type: "file";
1209
+ }
1210
+ >;
1211
+ providerOptions?: Record<string, Record<string, any>>;
1212
+ role: "user";
1213
+ }
1214
+ | {
1215
+ content:
1216
+ | string
1217
+ | Array<
1218
+ | {
1219
+ providerOptions?: Record<string, Record<string, any>>;
1220
+ text: string;
1221
+ type: "text";
1222
+ }
1223
+ | {
1224
+ data: string | ArrayBuffer;
1225
+ filename?: string;
1226
+ mimeType: string;
1227
+ providerOptions?: Record<string, Record<string, any>>;
1228
+ type: "file";
1229
+ }
1230
+ | {
1231
+ providerOptions?: Record<string, Record<string, any>>;
1232
+ signature?: string;
1233
+ text: string;
1234
+ type: "reasoning";
1235
+ }
1236
+ | {
1237
+ data: string;
1238
+ providerOptions?: Record<string, Record<string, any>>;
1239
+ type: "redacted-reasoning";
1240
+ }
1241
+ | {
1242
+ args: any;
1243
+ providerOptions?: Record<string, Record<string, any>>;
1244
+ toolCallId: string;
1245
+ toolName: string;
1246
+ type: "tool-call";
1247
+ }
1248
+ >;
1249
+ providerOptions?: Record<string, Record<string, any>>;
1250
+ role: "assistant";
1251
+ }
1252
+ | {
1253
+ content: Array<{
1254
+ args?: any;
1255
+ experimental_content?: Array<
1256
+ | { text: string; type: "text" }
1257
+ | { data: string; mimeType?: string; type: "image" }
1258
+ >;
1259
+ isError?: boolean;
1260
+ providerOptions?: Record<string, Record<string, any>>;
1261
+ result: any;
1262
+ toolCallId: string;
1263
+ toolName: string;
1264
+ type: "tool-result";
1265
+ }>;
1266
+ providerOptions?: Record<string, Record<string, any>>;
1267
+ role: "tool";
1268
+ }
1269
+ | {
1270
+ content: string;
1271
+ providerOptions?: Record<string, Record<string, any>>;
1272
+ role: "system";
1273
+ };
1274
+ status?: "pending" | "success" | "failed";
1275
+ };
1276
+ },
1277
+ {
1278
+ _creationTime: number;
1279
+ _id: string;
1280
+ agentName?: string;
1281
+ embeddingId?: string;
1282
+ error?: string;
1283
+ fileIds?: Array<string>;
1284
+ finishReason?:
1285
+ | "stop"
1286
+ | "length"
1287
+ | "content-filter"
1288
+ | "tool-calls"
1289
+ | "error"
1290
+ | "other"
1291
+ | "unknown";
1292
+ id?: string;
1293
+ message?:
1294
+ | {
1295
+ content:
1296
+ | string
1297
+ | Array<
1298
+ | {
1299
+ providerOptions?: Record<string, Record<string, any>>;
1300
+ text: string;
1301
+ type: "text";
1302
+ }
1303
+ | {
1304
+ image: string | ArrayBuffer;
1305
+ mimeType?: string;
1306
+ providerOptions?: Record<string, Record<string, any>>;
1307
+ type: "image";
1308
+ }
1309
+ | {
1310
+ data: string | ArrayBuffer;
1311
+ filename?: string;
1312
+ mimeType: string;
1313
+ providerOptions?: Record<string, Record<string, any>>;
1314
+ type: "file";
1315
+ }
1316
+ >;
1317
+ providerOptions?: Record<string, Record<string, any>>;
1318
+ role: "user";
1319
+ }
1320
+ | {
1321
+ content:
1322
+ | string
1323
+ | Array<
1324
+ | {
1325
+ providerOptions?: Record<string, Record<string, any>>;
1326
+ text: string;
1327
+ type: "text";
1328
+ }
1329
+ | {
1330
+ data: string | ArrayBuffer;
1331
+ filename?: string;
1332
+ mimeType: string;
1333
+ providerOptions?: Record<string, Record<string, any>>;
1334
+ type: "file";
1335
+ }
1336
+ | {
1337
+ providerOptions?: Record<string, Record<string, any>>;
1338
+ signature?: string;
1339
+ text: string;
1340
+ type: "reasoning";
1341
+ }
1342
+ | {
1343
+ data: string;
1344
+ providerOptions?: Record<string, Record<string, any>>;
1345
+ type: "redacted-reasoning";
1346
+ }
1347
+ | {
1348
+ args: any;
1349
+ providerOptions?: Record<string, Record<string, any>>;
1350
+ toolCallId: string;
1351
+ toolName: string;
1352
+ type: "tool-call";
1353
+ }
1354
+ >;
1355
+ providerOptions?: Record<string, Record<string, any>>;
1356
+ role: "assistant";
1357
+ }
1358
+ | {
1359
+ content: Array<{
1360
+ args?: any;
1361
+ experimental_content?: Array<
1362
+ | { text: string; type: "text" }
1363
+ | { data: string; mimeType?: string; type: "image" }
1364
+ >;
1365
+ isError?: boolean;
1366
+ providerOptions?: Record<string, Record<string, any>>;
1367
+ result: any;
1368
+ toolCallId: string;
1369
+ toolName: string;
1370
+ type: "tool-result";
1371
+ }>;
1372
+ providerOptions?: Record<string, Record<string, any>>;
1373
+ role: "tool";
1374
+ }
1375
+ | {
1376
+ content: string;
1377
+ providerOptions?: Record<string, Record<string, any>>;
1378
+ role: "system";
1379
+ };
1380
+ model?: string;
1381
+ order: number;
1382
+ provider?: string;
1383
+ providerMetadata?: Record<string, Record<string, any>>;
1384
+ providerOptions?: Record<string, Record<string, any>>;
1385
+ reasoning?: string;
1386
+ reasoningDetails?: Array<
1387
+ | { signature?: string; text: string; type: "text" }
1388
+ | { data: string; type: "redacted" }
1389
+ >;
1390
+ sources?: Array<{
1391
+ id: string;
1392
+ providerOptions?: Record<string, Record<string, any>>;
1393
+ sourceType: "url";
1394
+ title?: string;
1395
+ url: string;
1396
+ }>;
1397
+ status: "pending" | "success" | "failed";
1398
+ stepOrder: number;
1399
+ text?: string;
1400
+ threadId: string;
1401
+ tool: boolean;
1402
+ usage?: {
1403
+ completionTokens: number;
1404
+ promptTokens: number;
1405
+ totalTokens: number;
1406
+ };
1407
+ userId?: string;
1408
+ warnings?: Array<
1409
+ | { details?: string; setting: string; type: "unsupported-setting" }
1410
+ | { details?: string; tool: any; type: "unsupported-tool" }
1411
+ | { message: string; type: "other" }
1412
+ >;
1413
+ }
1414
+ >;
1180
1415
  };
1181
1416
  streams: {
1182
1417
  addDelta: FunctionReference<
@@ -141,4 +141,144 @@ describe("agent", () => {
141
141
  stepOrder: 1,
142
142
  });
143
143
  });
144
+
145
+ test("updateMessage updates message content", async () => {
146
+ const t = convexTest(schema, modules);
147
+ const thread = await t.mutation(api.threads.createThread, {
148
+ userId: "test",
149
+ });
150
+ const { messages } = await t.mutation(api.messages.addMessages, {
151
+ threadId: thread._id as Id<"threads">,
152
+ messages: [{ message: { role: "user", content: "hello" } }],
153
+ });
154
+ const messageId = messages[0]._id as Id<"messages">;
155
+
156
+ const updatedMessage = await t.mutation(api.messages.updateMessage, {
157
+ messageId,
158
+ patch: {
159
+ message: { role: "user", content: "updated content" },
160
+ },
161
+ });
162
+
163
+ expect(updatedMessage.message).toEqual({
164
+ role: "user",
165
+ content: "updated content",
166
+ });
167
+ });
168
+
169
+ test("updateMessage updates message status", async () => {
170
+ const t = convexTest(schema, modules);
171
+ const thread = await t.mutation(api.threads.createThread, {
172
+ userId: "test",
173
+ });
174
+ const { messages } = await t.mutation(api.messages.addMessages, {
175
+ threadId: thread._id as Id<"threads">,
176
+ messages: [{ message: { role: "assistant", content: "hello" } }],
177
+ pending: true,
178
+ });
179
+ const messageId = messages[0]._id as Id<"messages">;
180
+
181
+ // Initial status should be pending
182
+ expect(messages[0].status).toBe("pending");
183
+
184
+ // Update to success
185
+ const updatedMessage = await t.mutation(api.messages.updateMessage, {
186
+ messageId,
187
+ patch: {
188
+ status: "success",
189
+ },
190
+ });
191
+
192
+ expect(updatedMessage.status).toBe("success");
193
+ });
194
+
195
+ test("updateMessage updates error field", async () => {
196
+ const t = convexTest(schema, modules);
197
+ const thread = await t.mutation(api.threads.createThread, {
198
+ userId: "test",
199
+ });
200
+ const { messages } = await t.mutation(api.messages.addMessages, {
201
+ threadId: thread._id as Id<"threads">,
202
+ messages: [{ message: { role: "assistant", content: "hello" } }],
203
+ pending: true,
204
+ });
205
+ const messageId = messages[0]._id as Id<"messages">;
206
+
207
+ const updatedMessage = await t.mutation(api.messages.updateMessage, {
208
+ messageId,
209
+ patch: {
210
+ status: "failed",
211
+ error: "Something went wrong",
212
+ },
213
+ });
214
+
215
+ expect(updatedMessage.status).toBe("failed");
216
+ expect(updatedMessage.error).toBe("Something went wrong");
217
+ });
218
+
219
+ test("updateMessage correctly updates tool messages", async () => {
220
+ const t = convexTest(schema, modules);
221
+ const thread = await t.mutation(api.threads.createThread, {
222
+ userId: "test",
223
+ });
224
+ const { messages } = await t.mutation(api.messages.addMessages, {
225
+ threadId: thread._id as Id<"threads">,
226
+ messages: [{
227
+ message: {
228
+ role: "assistant",
229
+ content: [
230
+ {
231
+ type: "tool-call",
232
+ args: { a: 1 },
233
+ toolCallId: "1",
234
+ toolName: "tool",
235
+ },
236
+ ],
237
+ },
238
+ }],
239
+ });
240
+ const messageId = messages[0]._id as Id<"messages">;
241
+
242
+ const updatedMessage = await t.mutation(api.messages.updateMessage, {
243
+ messageId,
244
+ patch: {
245
+ message: {
246
+ role: "assistant",
247
+ content: [
248
+ {
249
+ type: "tool-call",
250
+ args: { a: 2, b: 3 },
251
+ toolCallId: "1",
252
+ toolName: "tool",
253
+ },
254
+ ],
255
+ },
256
+ },
257
+ });
258
+
259
+ expect(updatedMessage.message).toEqual({
260
+ role: "assistant",
261
+ content: [
262
+ {
263
+ type: "tool-call",
264
+ args: { a: 2, b: 3 },
265
+ toolCallId: "1",
266
+ toolName: "tool",
267
+ },
268
+ ],
269
+ });
270
+ });
271
+
272
+ test("updateMessage throws error for non-existent message", async () => {
273
+ const t = convexTest(schema, modules);
274
+
275
+ await expect(
276
+ t.mutation(api.messages.updateMessage, {
277
+ messageId: "invalidId" as Id<"messages">,
278
+ patch: {
279
+ message: { role: "user", content: "test" },
280
+ },
281
+ })
282
+ ).rejects.toThrow();
283
+ });
144
284
  });
@@ -265,6 +265,36 @@ export const commitMessage = mutation({
265
265
  returns: v.null(),
266
266
  handler: commitMessageHandler,
267
267
  });
268
+
269
+ export const updateMessage = mutation({
270
+ args: {
271
+ messageId: v.id("messages"),
272
+ patch: v.object({
273
+ message: v.optional(vMessageDoc.fields.message),
274
+ status: v.optional(vMessageStatus),
275
+ error: v.optional(v.string()),
276
+ }),
277
+ },
278
+ returns: vMessageDoc,
279
+ handler: async (ctx, args) => {
280
+ const message = await ctx.db.get(args.messageId);
281
+ assert(message, `Message ${args.messageId} not found`);
282
+
283
+ const patch: Partial<Doc<"messages">> = {
284
+ ...args.patch,
285
+ };
286
+
287
+ if (args.patch.message !== undefined) {
288
+ patch.message = args.patch.message;
289
+ patch.tool = isTool(args.patch.message);
290
+ patch.text = extractText(args.patch.message);
291
+ }
292
+
293
+ await ctx.db.patch(args.messageId, patch);
294
+ return publicMessage((await ctx.db.get(args.messageId))!);
295
+ },
296
+ });
297
+
268
298
  async function commitMessageHandler(
269
299
  ctx: MutationCtx,
270
300
  { messageId }: { messageId: Id<"messages"> }
@@ -5,15 +5,20 @@ import {
5
5
  vStreamDelta,
6
6
  vStreamMessage,
7
7
  } from "../validators.js";
8
- import { internal } from "./_generated/api.js";
9
- import type { Id } from "./_generated/dataModel.js";
8
+ import { api, internal } from "./_generated/api.js";
9
+ import type { Doc, Id } from "./_generated/dataModel.js";
10
10
  import {
11
11
  internalMutation,
12
12
  mutation,
13
13
  type MutationCtx,
14
14
  query,
15
+ action,
15
16
  } from "./_generated/server.js";
16
17
  import schema from "./schema.js";
18
+ import type { PaginationResult } from "convex/server";
19
+ import { paginator } from "convex-helpers/server/pagination";
20
+ import { stream } from "convex-helpers/server/stream";
21
+ import { mergedStream } from "convex-helpers/server/stream";
17
22
 
18
23
  const MAX_DELTAS_PER_REQUEST = 1000;
19
24
  const MAX_DELTAS_PER_STREAM = 100;
@@ -207,3 +212,144 @@ export const timeoutStream = internalMutation({
207
212
  });
208
213
  },
209
214
  });
215
+
216
+ async function deletePageForStreamId(
217
+ ctx: MutationCtx,
218
+ args: { streamId: Id<"streamingMessages">; cursor?: string }
219
+ ) {
220
+ const deltas = await ctx.db
221
+ .query("streamDeltas")
222
+ .withIndex("streamId_start_end", (q) => q.eq("streamId", args.streamId))
223
+ .paginate({
224
+ numItems: MAX_DELTAS_PER_REQUEST,
225
+ cursor: args.cursor ?? null,
226
+ });
227
+ await Promise.all(deltas.page.map((d) => ctx.db.delete(d._id)));
228
+ return deltas;
229
+ }
230
+
231
+ export async function deleteStreamsPageForThreadId(
232
+ ctx: MutationCtx,
233
+ args: { threadId: Id<"threads">; streamOrder?: number; deltaCursor?: string }
234
+ ) {
235
+ const allStreamMessages =
236
+ schema.tables.streamingMessages.validator.fields.state.members
237
+ .flatMap((state) => state.fields.kind.value)
238
+ .map((stateKind) =>
239
+ stream(ctx.db, schema)
240
+ .query("streamingMessages")
241
+ .withIndex("threadId_state_order_stepOrder", (q) =>
242
+ q
243
+ .eq("threadId", args.threadId)
244
+ .eq("state.kind", stateKind)
245
+ .gte("order", args.streamOrder ?? 0)
246
+ )
247
+ );
248
+ let deltaCursor = args.deltaCursor;
249
+ const streamMessage = await mergedStream(allStreamMessages, [
250
+ "threadId",
251
+ "state.kind",
252
+ "order",
253
+ "stepOrder",
254
+ ]).first();
255
+ if (!streamMessage) {
256
+ return {
257
+ isDone: true,
258
+ streamOrder: undefined,
259
+ deltaCursor: undefined,
260
+ };
261
+ }
262
+ const result = await deletePageForStreamId(ctx, {
263
+ streamId: streamMessage._id,
264
+ cursor: deltaCursor,
265
+ });
266
+ if (result.isDone) {
267
+ await ctx.db.delete(streamMessage._id);
268
+ deltaCursor = undefined;
269
+ }
270
+ return {
271
+ isDone: false,
272
+ streamOrder: streamMessage.order,
273
+ deltaCursor: result.continueCursor,
274
+ };
275
+ }
276
+
277
+ export const deleteStreamsPageForThreadIdMutation = internalMutation({
278
+ args: {
279
+ threadId: v.id("threads"),
280
+ streamOrder: v.optional(v.number()),
281
+ deltaCursor: v.optional(v.string()),
282
+ },
283
+ returns: v.object({
284
+ isDone: v.boolean(),
285
+ streamOrder: v.optional(v.number()),
286
+ deltaCursor: v.optional(v.string()),
287
+ }),
288
+ handler: deleteStreamsPageForThreadId,
289
+ });
290
+
291
+ export const deleteAllStreamsForThreadIdAsync = mutation({
292
+ args: {
293
+ threadId: v.id("threads"),
294
+ streamOrder: v.optional(v.number()),
295
+ deltaCursor: v.optional(v.string()),
296
+ },
297
+ returns: v.object({
298
+ isDone: v.boolean(),
299
+ streamOrder: v.optional(v.number()),
300
+ deltaCursor: v.optional(v.string()),
301
+ }),
302
+ handler: async (ctx, args) => {
303
+ const result = await deleteStreamsPageForThreadId(ctx, args);
304
+ if (!result.isDone) {
305
+ await ctx.scheduler.runAfter(
306
+ 0,
307
+ api.streams.deleteAllStreamsForThreadIdAsync,
308
+ {
309
+ threadId: args.threadId,
310
+ streamOrder: result.streamOrder,
311
+ deltaCursor: result.deltaCursor,
312
+ }
313
+ );
314
+ } else {
315
+ await ctx.db.delete(args.threadId);
316
+ }
317
+ return result;
318
+ },
319
+ });
320
+
321
+ export const deleteStreamSync = mutation({
322
+ args: { streamId: v.id("streamingMessages") },
323
+ returns: v.null(),
324
+ handler: async (ctx, args) => {
325
+ let deltas = await deletePageForStreamId(ctx, args);
326
+ while (!deltas.isDone) {
327
+ deltas = await deletePageForStreamId(ctx, {
328
+ ...args,
329
+ cursor: deltas.continueCursor,
330
+ });
331
+ }
332
+ await ctx.db.delete(args.streamId);
333
+ },
334
+ });
335
+
336
+ export const deleteAllStreamsForThreadIdSync = action({
337
+ args: { threadId: v.id("threads") },
338
+ returns: v.null(),
339
+ handler: async (ctx, args) => {
340
+ let result = await ctx.runMutation(
341
+ internal.streams.deleteStreamsPageForThreadIdMutation,
342
+ args
343
+ );
344
+ while (!result.isDone) {
345
+ result = await ctx.runMutation(
346
+ internal.streams.deleteStreamsPageForThreadIdMutation,
347
+ {
348
+ ...args,
349
+ streamOrder: result.streamOrder,
350
+ deltaCursor: result.deltaCursor,
351
+ }
352
+ );
353
+ }
354
+ },
355
+ });
@@ -131,6 +131,9 @@ export const deleteAllForThreadIdSync = action({
131
131
  }
132
132
  cursor = result.cursor;
133
133
  }
134
+ await ctx.runAction(api.streams.deleteAllStreamsForThreadIdSync, {
135
+ threadId: args.threadId,
136
+ });
134
137
  },
135
138
  returns: v.null(),
136
139
  });
@@ -154,6 +157,11 @@ export const deleteAllForThreadIdAsync = mutation({
154
157
  threadId: args.threadId,
155
158
  cursor: result.cursor,
156
159
  });
160
+ } else {
161
+ // Kick off the streams deletion
162
+ await ctx.scheduler.runAfter(0, api.threads.deleteAllForThreadIdSync, {
163
+ threadId: args.threadId,
164
+ });
157
165
  }
158
166
  return result;
159
167
  },
@@ -156,6 +156,7 @@ async function deletePageForUserId(
156
156
  });
157
157
  await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
158
158
  if (messages.isDone) {
159
+ // TODO: delete streams - maybe use workpool?
159
160
  await ctx.db.delete(threadInProgress);
160
161
  threadInProgress = null;
161
162
  messagesCursor = null;
@@ -172,6 +172,10 @@ export const updateBatch = mutation({
172
172
  model: v.string(),
173
173
  id: vVectorId,
174
174
  vector: v.array(v.number()),
175
+ // TODO: support changing the vector length by
176
+ // deleting from one table and inserting into another.
177
+ // However this requires updating all the messages that reference
178
+ // the vector.
175
179
  })
176
180
  ),
177
181
  },
@@ -2,11 +2,10 @@
2
2
  import type { ErrorMessage } from "convex-helpers";
3
3
  import {
4
4
  type PaginatedQueryArgs,
5
- // usePaginatedQuery,
6
5
  type UsePaginatedQueryResult,
7
6
  useQuery,
8
7
  } from "convex/react";
9
- import { usePaginatedQuery } from "./usePaginatedQuery.js";
8
+ import { usePaginatedQuery } from "convex-helpers/react";
10
9
  import type { FunctionArgs } from "convex/server";
11
10
  import { useMemo, useState } from "react";
12
11
  import type { MessageDoc } from "../client/index.js";