@convex-dev/agent 0.0.1-alpha.4 → 0.0.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.
- package/README.md +35 -7
- package/dist/commonjs/client/index.d.ts +800 -84
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +176 -130
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/messages.d.ts +124 -105
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +145 -247
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +822 -746
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +13 -3
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/vector/index.d.ts +21 -6
- package/dist/commonjs/component/vector/index.d.ts.map +1 -1
- package/dist/commonjs/component/vector/index.js +32 -20
- package/dist/commonjs/component/vector/index.js.map +1 -1
- package/dist/commonjs/component/vector/tables.d.ts +25 -5
- package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
- package/dist/commonjs/component/vector/tables.js +14 -6
- package/dist/commonjs/component/vector/tables.js.map +1 -1
- package/dist/commonjs/mapping.d.ts +3 -2
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js +45 -13
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/validators.d.ts +2817 -145
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +79 -7
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/index.d.ts +800 -84
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +176 -130
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/messages.d.ts +124 -105
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +145 -247
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +822 -746
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +13 -3
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/vector/index.d.ts +21 -6
- package/dist/esm/component/vector/index.d.ts.map +1 -1
- package/dist/esm/component/vector/index.js +32 -20
- package/dist/esm/component/vector/index.js.map +1 -1
- package/dist/esm/component/vector/tables.d.ts +25 -5
- package/dist/esm/component/vector/tables.d.ts.map +1 -1
- package/dist/esm/component/vector/tables.js +14 -6
- package/dist/esm/component/vector/tables.js.map +1 -1
- package/dist/esm/mapping.d.ts +3 -2
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js +45 -13
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/validators.d.ts +2817 -145
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +79 -7
- package/dist/esm/validators.js.map +1 -1
- package/package.json +7 -7
- package/src/client/index.ts +347 -170
- package/src/component/_generated/api.d.ts +33 -16
- package/src/component/messages.ts +177 -290
- package/src/component/schema.ts +14 -3
- package/src/component/vector/index.ts +59 -26
- package/src/component/vector/tables.ts +38 -6
- package/src/mapping.ts +60 -19
- package/src/validators.ts +112 -12
|
@@ -5,11 +5,12 @@ import { nullable, partial } from "convex-helpers/validators";
|
|
|
5
5
|
import { ObjectType } from "convex/values";
|
|
6
6
|
import { DEFAULT_MESSAGE_RANGE, extractText, isTool } from "../shared.js";
|
|
7
7
|
import {
|
|
8
|
-
|
|
8
|
+
vEmbeddingsWithMetadata,
|
|
9
9
|
vMessageStatus,
|
|
10
10
|
vMessageWithFileAndId,
|
|
11
11
|
vSearchOptions,
|
|
12
|
-
|
|
12
|
+
vStepWithMessages,
|
|
13
|
+
vThreadStatus,
|
|
13
14
|
} from "../validators.js";
|
|
14
15
|
import { api, internal } from "./_generated/api.js";
|
|
15
16
|
import { Doc, Id } from "./_generated/dataModel.js";
|
|
@@ -23,10 +24,11 @@ import {
|
|
|
23
24
|
QueryCtx,
|
|
24
25
|
} from "./_generated/server.js";
|
|
25
26
|
import { schema, v } from "./schema.js";
|
|
27
|
+
import { insertVector, searchVectors } from "./vector/index.js";
|
|
26
28
|
import {
|
|
27
|
-
getVectorTableName,
|
|
28
29
|
VectorDimension,
|
|
29
30
|
VectorDimensions,
|
|
31
|
+
VectorTableId,
|
|
30
32
|
vVectorId,
|
|
31
33
|
} from "./vector/tables.js";
|
|
32
34
|
|
|
@@ -45,23 +47,23 @@ export const getThreadsByUserId = query({
|
|
|
45
47
|
cursor: v.optional(v.union(v.string(), v.null())),
|
|
46
48
|
limit: v.optional(v.number()),
|
|
47
49
|
offset: v.optional(v.number()),
|
|
48
|
-
statuses: v.optional(
|
|
50
|
+
statuses: v.optional(vThreadStatus),
|
|
49
51
|
},
|
|
50
52
|
handler: async (ctx, args) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
const status = args.statuses ?? "active";
|
|
54
|
+
const threads = await paginator(ctx.db, schema)
|
|
55
|
+
.query("threads")
|
|
56
|
+
.withIndex("userId_status_order", (q) =>
|
|
57
|
+
q
|
|
58
|
+
.eq("userId", args.userId)
|
|
59
|
+
.eq("status", status)
|
|
60
|
+
.gte("order", args.offset ?? 0)
|
|
61
|
+
)
|
|
62
|
+
.order("desc")
|
|
63
|
+
.paginate({
|
|
64
|
+
numItems: args.limit ?? 100,
|
|
65
|
+
cursor: args.cursor ?? null,
|
|
66
|
+
});
|
|
65
67
|
return {
|
|
66
68
|
threads: threads.page,
|
|
67
69
|
continueCursor: threads.continueCursor,
|
|
@@ -76,20 +78,17 @@ export const getThreadsByUserId = query({
|
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
const vThread = schema.tables.threads.validator;
|
|
79
|
-
const statuses = vThread.fields.status.members.map((m) => m.value);
|
|
80
81
|
|
|
81
82
|
export const createThread = mutation({
|
|
82
83
|
args: omit(vThread.fields, ["order", "status"]),
|
|
83
84
|
handler: async (ctx, args) => {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
const latestThread = await mergedStream(streams, ["order"]).first();
|
|
85
|
+
const latestThread = await ctx.db
|
|
86
|
+
.query("threads")
|
|
87
|
+
.withIndex("userId_status_order", (q) =>
|
|
88
|
+
q.eq("userId", args.userId).eq("status", "active")
|
|
89
|
+
)
|
|
90
|
+
.order("desc")
|
|
91
|
+
.first();
|
|
93
92
|
const order = (latestThread?.order ?? -1) + 1;
|
|
94
93
|
const threadId = await ctx.db.insert("threads", {
|
|
95
94
|
...args,
|
|
@@ -164,7 +163,7 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
164
163
|
userId: v.string(),
|
|
165
164
|
},
|
|
166
165
|
handler: async (ctx, args) => {
|
|
167
|
-
const isDone = await
|
|
166
|
+
const isDone = await deleteAllForUserIdAsyncHandler(ctx, {
|
|
168
167
|
userId: args.userId,
|
|
169
168
|
messagesCursor: null,
|
|
170
169
|
threadsCursor: null,
|
|
@@ -189,11 +188,11 @@ type DeleteAllReturns = ObjectType<typeof deleteAllReturns>;
|
|
|
189
188
|
|
|
190
189
|
export const _deleteAllForUserIdAsync = internalMutation({
|
|
191
190
|
args: deleteAllArgs,
|
|
192
|
-
handler:
|
|
191
|
+
handler: deleteAllForUserIdAsyncHandler,
|
|
193
192
|
returns: v.boolean(),
|
|
194
193
|
});
|
|
195
194
|
|
|
196
|
-
async function
|
|
195
|
+
async function deleteAllForUserIdAsyncHandler(
|
|
197
196
|
ctx: MutationCtx,
|
|
198
197
|
args: DeleteAllArgs
|
|
199
198
|
): Promise<boolean> {
|
|
@@ -221,51 +220,38 @@ async function deletePageForUserId(
|
|
|
221
220
|
ctx: MutationCtx,
|
|
222
221
|
args: DeleteAllArgs
|
|
223
222
|
): Promise<DeleteAllReturns> {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const messages = await
|
|
234
|
-
.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
.query("messages")
|
|
238
|
-
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
239
|
-
q.eq("threadId", c._id).eq("status", "success")
|
|
240
|
-
),
|
|
241
|
-
["tool", "order", "stepOrder"]
|
|
223
|
+
const threads = await paginator(ctx.db, schema)
|
|
224
|
+
.query("threads")
|
|
225
|
+
.withIndex("userId_status_order", (q) => q.eq("userId", args.userId))
|
|
226
|
+
.order("desc")
|
|
227
|
+
.paginate({
|
|
228
|
+
numItems: 100,
|
|
229
|
+
cursor: args.threadsCursor ?? null,
|
|
230
|
+
});
|
|
231
|
+
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
232
|
+
const messages = await paginator(ctx.db, schema)
|
|
233
|
+
.query("messages")
|
|
234
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
235
|
+
q.eq("userId", args.userId)
|
|
242
236
|
)
|
|
237
|
+
.order("desc")
|
|
243
238
|
.paginate({
|
|
244
239
|
numItems: 100,
|
|
245
240
|
cursor: args.messagesCursor ?? null,
|
|
246
241
|
});
|
|
247
242
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
248
|
-
if (messages.isDone) {
|
|
249
|
-
const threads = await threadStreams.paginate({
|
|
250
|
-
numItems: 100,
|
|
251
|
-
cursor: args.threadsCursor ?? null,
|
|
252
|
-
});
|
|
253
|
-
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
254
|
-
return {
|
|
255
|
-
messagesCursor: messages.continueCursor,
|
|
256
|
-
threadsCursor: threads.continueCursor,
|
|
257
|
-
isDone: threads.isDone,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
243
|
return {
|
|
261
244
|
messagesCursor: messages.continueCursor,
|
|
262
|
-
threadsCursor:
|
|
245
|
+
threadsCursor: threads.continueCursor,
|
|
263
246
|
isDone: messages.isDone,
|
|
264
247
|
};
|
|
265
248
|
}
|
|
266
249
|
|
|
267
250
|
async function deleteMessage(ctx: MutationCtx, messageDoc: Doc<"messages">) {
|
|
268
251
|
await ctx.db.delete(messageDoc._id);
|
|
252
|
+
if (messageDoc.embeddingId) {
|
|
253
|
+
await ctx.db.delete(messageDoc.embeddingId);
|
|
254
|
+
}
|
|
269
255
|
if (messageDoc.fileId) {
|
|
270
256
|
const file = await ctx.db.get(messageDoc.fileId);
|
|
271
257
|
if (file) {
|
|
@@ -323,10 +309,10 @@ async function deletePageForThreadIdHandler(
|
|
|
323
309
|
ctx: MutationCtx,
|
|
324
310
|
args: DeleteThreadArgs
|
|
325
311
|
): Promise<DeleteThreadReturns> {
|
|
326
|
-
const messages = await
|
|
312
|
+
const messages = await paginator(ctx.db, schema)
|
|
327
313
|
.query("messages")
|
|
328
314
|
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
329
|
-
q.eq("threadId", args.threadId)
|
|
315
|
+
q.eq("threadId", args.threadId)
|
|
330
316
|
)
|
|
331
317
|
.paginate({
|
|
332
318
|
numItems: args.limit ?? 100,
|
|
@@ -373,7 +359,7 @@ export const messageStatuses = vMessageDoc.fields.status.members.map(
|
|
|
373
359
|
|
|
374
360
|
const addMessagesArgs = {
|
|
375
361
|
userId: v.optional(v.string()),
|
|
376
|
-
threadId: v.
|
|
362
|
+
threadId: v.id("threads"),
|
|
377
363
|
stepId: v.optional(v.id("steps")),
|
|
378
364
|
parentMessageId: v.optional(v.id("messages")),
|
|
379
365
|
messages: v.array(vMessageWithFileAndId),
|
|
@@ -381,6 +367,7 @@ const addMessagesArgs = {
|
|
|
381
367
|
agentName: v.optional(v.string()),
|
|
382
368
|
pending: v.optional(v.boolean()),
|
|
383
369
|
failPendingSteps: v.optional(v.boolean()),
|
|
370
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
384
371
|
};
|
|
385
372
|
export const addMessages = mutation({
|
|
386
373
|
args: addMessagesArgs,
|
|
@@ -394,16 +381,27 @@ async function addMessagesHandler(
|
|
|
394
381
|
ctx: MutationCtx,
|
|
395
382
|
args: ObjectType<typeof addMessagesArgs>
|
|
396
383
|
) {
|
|
384
|
+
assert(
|
|
385
|
+
!args.embeddings || args.embeddings.vectors.length === args.messages.length,
|
|
386
|
+
"embeddings must have one vector per message"
|
|
387
|
+
);
|
|
397
388
|
let userId = args.userId;
|
|
398
389
|
const threadId = args.threadId;
|
|
399
390
|
if (!userId && args.threadId) {
|
|
400
391
|
const thread = await ctx.db.get(args.threadId);
|
|
401
392
|
assert(thread, `Thread ${args.threadId} not found`);
|
|
402
|
-
userId = thread.
|
|
393
|
+
userId = thread.userId;
|
|
403
394
|
}
|
|
404
|
-
const {
|
|
405
|
-
|
|
395
|
+
const {
|
|
396
|
+
failPendingSteps,
|
|
397
|
+
pending,
|
|
398
|
+
messages,
|
|
399
|
+
parentMessageId,
|
|
400
|
+
embeddings,
|
|
401
|
+
...rest
|
|
402
|
+
} = args;
|
|
406
403
|
const parent = parentMessageId && (await ctx.db.get(parentMessageId));
|
|
404
|
+
// TODO: I think this is a bug - parent will be pending always?
|
|
407
405
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
408
406
|
assert(args.threadId, "threadId is required to fail pending steps");
|
|
409
407
|
const pendingMessages = await ctx.db
|
|
@@ -422,7 +420,18 @@ async function addMessagesHandler(
|
|
|
422
420
|
let order = maxMessage?.order ?? -1;
|
|
423
421
|
const toReturn: Doc<"messages">[] = [];
|
|
424
422
|
if (messages.length > 0) {
|
|
425
|
-
for (const { message, fileId, id } of messages) {
|
|
423
|
+
for (const [i, { message, fileId, id }] of messages.entries()) {
|
|
424
|
+
const embedding = embeddings?.vectors[i] ?? undefined;
|
|
425
|
+
let embeddingId: VectorTableId | undefined;
|
|
426
|
+
if (embeddings && embedding) {
|
|
427
|
+
embeddingId = await insertVector(ctx, embeddings.dimension, {
|
|
428
|
+
vector: embedding,
|
|
429
|
+
model: embeddings.model,
|
|
430
|
+
table: "messages",
|
|
431
|
+
userId,
|
|
432
|
+
threadId,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
426
435
|
const tool = isTool(message);
|
|
427
436
|
if (!tool) {
|
|
428
437
|
order++;
|
|
@@ -430,6 +439,7 @@ async function addMessagesHandler(
|
|
|
430
439
|
const text = extractText(message);
|
|
431
440
|
const messageId = await ctx.db.insert("messages", {
|
|
432
441
|
...rest,
|
|
442
|
+
embeddingId,
|
|
433
443
|
parentMessageId,
|
|
434
444
|
userId,
|
|
435
445
|
message,
|
|
@@ -439,7 +449,13 @@ async function addMessagesHandler(
|
|
|
439
449
|
text,
|
|
440
450
|
fileId,
|
|
441
451
|
status: pending ? "pending" : "success",
|
|
452
|
+
stepOrder: 0,
|
|
442
453
|
});
|
|
454
|
+
if (fileId) {
|
|
455
|
+
await ctx.db.patch(fileId, {
|
|
456
|
+
refcount: (await ctx.db.get(fileId))!.refcount + 1,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
443
459
|
toReturn.push((await ctx.db.get(messageId))!);
|
|
444
460
|
}
|
|
445
461
|
}
|
|
@@ -465,35 +481,47 @@ async function getMaxMessage(
|
|
|
465
481
|
["order", "stepOrder"]
|
|
466
482
|
).first();
|
|
467
483
|
} else {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
484
|
+
// DO explicitly
|
|
485
|
+
const maxPending = await ctx.db
|
|
486
|
+
.query("messages")
|
|
487
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
488
|
+
q.eq("userId", userId).eq("status", "pending").eq("tool", false)
|
|
489
|
+
)
|
|
490
|
+
.order("desc")
|
|
491
|
+
.first();
|
|
492
|
+
const maxSuccess = await ctx.db
|
|
493
|
+
.query("messages")
|
|
494
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
495
|
+
q.eq("userId", userId).eq("status", "success").eq("tool", false)
|
|
496
|
+
)
|
|
497
|
+
.order("desc")
|
|
498
|
+
.first();
|
|
499
|
+
return maxPending
|
|
500
|
+
? maxSuccess
|
|
501
|
+
? maxPending.order > maxSuccess.order
|
|
502
|
+
? maxPending
|
|
503
|
+
: maxSuccess
|
|
504
|
+
: maxPending
|
|
505
|
+
: maxSuccess ?? null;
|
|
479
506
|
}
|
|
480
507
|
}
|
|
481
508
|
|
|
482
|
-
const
|
|
509
|
+
const addStepArgs = {
|
|
483
510
|
threadId: v.id("threads"),
|
|
484
511
|
messageId: v.id("messages"),
|
|
485
|
-
|
|
512
|
+
step: vStepWithMessages,
|
|
486
513
|
failPendingSteps: v.optional(v.boolean()),
|
|
514
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
487
515
|
};
|
|
488
516
|
|
|
489
|
-
export const
|
|
490
|
-
args:
|
|
517
|
+
export const addStep = mutation({
|
|
518
|
+
args: addStepArgs,
|
|
491
519
|
returns: v.array(v.doc("steps")),
|
|
492
|
-
handler:
|
|
520
|
+
handler: addStepHandler,
|
|
493
521
|
});
|
|
494
|
-
async function
|
|
522
|
+
async function addStepHandler(
|
|
495
523
|
ctx: MutationCtx,
|
|
496
|
-
args: ObjectType<typeof
|
|
524
|
+
args: ObjectType<typeof addStepArgs>
|
|
497
525
|
) {
|
|
498
526
|
const parentMessage = await ctx.db.get(args.messageId);
|
|
499
527
|
assert(parentMessage, `Message ${args.messageId} not found`);
|
|
@@ -512,35 +540,33 @@ async function addStepsHandler(
|
|
|
512
540
|
await ctx.db.patch(step._id, { status: "failed" });
|
|
513
541
|
}
|
|
514
542
|
}
|
|
515
|
-
steps = steps.filter((s) => s.status
|
|
543
|
+
steps = steps.filter((s) => s.status !== "failed");
|
|
516
544
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
steps.push((await ctx.db.get(stepId))!);
|
|
542
|
-
nextStepOrder++;
|
|
545
|
+
const { step, messages } = args.step;
|
|
546
|
+
const stepId = await ctx.db.insert("steps", {
|
|
547
|
+
threadId: args.threadId,
|
|
548
|
+
parentMessageId: args.messageId,
|
|
549
|
+
order,
|
|
550
|
+
stepOrder: (steps.at(-1)?.stepOrder ?? -1) + 1,
|
|
551
|
+
status: step.finishReason === "stop" ? "success" : "pending",
|
|
552
|
+
step,
|
|
553
|
+
});
|
|
554
|
+
await addMessagesHandler(ctx, {
|
|
555
|
+
threadId: args.threadId,
|
|
556
|
+
parentMessageId: args.messageId,
|
|
557
|
+
stepId,
|
|
558
|
+
messages,
|
|
559
|
+
model: parentMessage.model,
|
|
560
|
+
agentName: parentMessage.agentName,
|
|
561
|
+
pending: step.finishReason === "stop" ? false : true,
|
|
562
|
+
failPendingSteps: false,
|
|
563
|
+
embeddings: args.embeddings,
|
|
564
|
+
});
|
|
565
|
+
// We don't commit if the parent is still pending.
|
|
566
|
+
if (step.finishReason === "stop") {
|
|
567
|
+
await commitMessageHandler(ctx, { messageId: args.messageId });
|
|
543
568
|
}
|
|
569
|
+
steps.push((await ctx.db.get(stepId))!);
|
|
544
570
|
return steps;
|
|
545
571
|
}
|
|
546
572
|
|
|
@@ -553,9 +579,23 @@ export const rollbackMessage = mutation({
|
|
|
553
579
|
handler: async (ctx, { messageId, error }) => {
|
|
554
580
|
const message = await ctx.db.get(messageId);
|
|
555
581
|
assert(message, `Message ${messageId} not found`);
|
|
582
|
+
// TODO: do BFS to fail all associated messages, then steps
|
|
583
|
+
// with parentMessageId of those messages, etc.
|
|
584
|
+
const steps = await ctx.db
|
|
585
|
+
.query("steps")
|
|
586
|
+
.withIndex("parentMessageId_order_stepOrder", (q) =>
|
|
587
|
+
// TODO: fetch pending, and commit later
|
|
588
|
+
q.eq("parentMessageId", messageId)
|
|
589
|
+
)
|
|
590
|
+
.collect();
|
|
591
|
+
for (const step of steps) {
|
|
592
|
+
if (step.status === "pending") {
|
|
593
|
+
await ctx.db.patch(step._id, { status: "failed" });
|
|
594
|
+
}
|
|
595
|
+
}
|
|
556
596
|
await ctx.db.patch(messageId, {
|
|
557
597
|
status: "failed",
|
|
558
|
-
|
|
598
|
+
error: error,
|
|
559
599
|
});
|
|
560
600
|
},
|
|
561
601
|
});
|
|
@@ -686,18 +726,16 @@ export const searchMessages = action({
|
|
|
686
726
|
if (!VectorDimensions.includes(dimension)) {
|
|
687
727
|
throw new Error(`Unsupported vector dimension: ${dimension}`);
|
|
688
728
|
}
|
|
689
|
-
const model = args.vectorModel ?? "unknown";
|
|
690
|
-
const tableName = getVectorTableName(dimension);
|
|
691
729
|
const vectors = (
|
|
692
|
-
await ctx
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
730
|
+
await searchVectors(ctx, args.vector, {
|
|
731
|
+
dimension,
|
|
732
|
+
model: args.vectorModel ?? "unknown",
|
|
733
|
+
table: "messages",
|
|
734
|
+
userId: args.userId,
|
|
735
|
+
threadId: args.threadId,
|
|
698
736
|
limit,
|
|
699
737
|
})
|
|
700
|
-
).filter((v) => v._score > 0
|
|
738
|
+
).filter((v) => v._score > (args.vectorScoreThreshold ?? 0));
|
|
701
739
|
// Reciprocal rank fusion
|
|
702
740
|
const k = 10;
|
|
703
741
|
const textEmbeddingIds = textSearchMessages?.map((m) => m.embeddingId);
|
|
@@ -706,11 +744,10 @@ export const searchMessages = action({
|
|
|
706
744
|
id: v._id,
|
|
707
745
|
score:
|
|
708
746
|
1 / (i + k) +
|
|
709
|
-
1 / (textEmbeddingIds?.indexOf(v._id) ?? Infinity + k),
|
|
747
|
+
1 / ((textEmbeddingIds?.indexOf(v._id) ?? Infinity) + k),
|
|
710
748
|
}))
|
|
711
749
|
.sort((a, b) => b.score - a.score);
|
|
712
750
|
const vectorIds = vectorScores.slice(0, limit).map((v) => v.id);
|
|
713
|
-
|
|
714
751
|
const messages: Doc<"messages">[] = await ctx.runQuery(
|
|
715
752
|
internal.messages._fetchVectorMessages,
|
|
716
753
|
{
|
|
@@ -755,10 +792,11 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
755
792
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
756
793
|
.filter((q) =>
|
|
757
794
|
userId
|
|
758
|
-
? q.eq("userId", userId)
|
|
759
|
-
:
|
|
760
|
-
q.eq("threadId", threadId as any)
|
|
795
|
+
? q.eq(q.field("userId"), userId)
|
|
796
|
+
: q.eq(q.field("threadId"), threadId)
|
|
761
797
|
)
|
|
798
|
+
// Don't include pending. Failed messages hopefully are deleted but may as well be safe.
|
|
799
|
+
.filter((q) => q.eq(q.field("status"), "success"))
|
|
762
800
|
.first()
|
|
763
801
|
)
|
|
764
802
|
)
|
|
@@ -811,8 +849,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
811
849
|
.eq("threadId", m.threadId)
|
|
812
850
|
.eq("status", "success")
|
|
813
851
|
.eq("tool", false)
|
|
814
|
-
.
|
|
815
|
-
.
|
|
852
|
+
.gte("order", earliest)
|
|
853
|
+
.lte("order", latest)
|
|
816
854
|
)
|
|
817
855
|
.collect();
|
|
818
856
|
if (!ranges[searchId]) {
|
|
@@ -827,8 +865,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
827
865
|
.eq("userId", m.userId!)
|
|
828
866
|
.eq("status", "success")
|
|
829
867
|
.eq("tool", false)
|
|
830
|
-
.
|
|
831
|
-
.
|
|
868
|
+
.gte("order", earliest)
|
|
869
|
+
.lte("order", latest)
|
|
832
870
|
)
|
|
833
871
|
.collect();
|
|
834
872
|
if (!ranges[searchId]) {
|
|
@@ -838,9 +876,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
838
876
|
}
|
|
839
877
|
}
|
|
840
878
|
}
|
|
841
|
-
|
|
842
|
-
.
|
|
843
|
-
|
|
879
|
+
for (const r of Object.values(ranges).flat()) {
|
|
880
|
+
if (!messages.includes(r)) {
|
|
881
|
+
messages.push(r);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return messages.sort((a, b) => a.order - b.order);
|
|
844
885
|
},
|
|
845
886
|
});
|
|
846
887
|
|
|
@@ -867,157 +908,3 @@ export const textSearch = query({
|
|
|
867
908
|
},
|
|
868
909
|
returns: v.array(v.doc("messages")),
|
|
869
910
|
});
|
|
870
|
-
|
|
871
|
-
// const vMemoryConfig = v.object({
|
|
872
|
-
// lastMessages: v.optional(v.union(v.number(), v.literal(false))),
|
|
873
|
-
// semanticRecall: v.optional(
|
|
874
|
-
// v.union(
|
|
875
|
-
// v.boolean(),
|
|
876
|
-
// v.object({
|
|
877
|
-
// topK: v.number(),
|
|
878
|
-
// messageRange: v.union(
|
|
879
|
-
// v.number(),
|
|
880
|
-
// v.object({ before: v.number(), after: v.number() }),
|
|
881
|
-
// ),
|
|
882
|
-
// }),
|
|
883
|
-
// ),
|
|
884
|
-
// ),
|
|
885
|
-
// workingMemory: v.optional(
|
|
886
|
-
// v.object({
|
|
887
|
-
// enabled: v.boolean(),
|
|
888
|
-
// template: v.optional(v.string()),
|
|
889
|
-
// use: v.optional(
|
|
890
|
-
// v.union(v.literal("text-stream"), v.literal("tool-call")),
|
|
891
|
-
// ),
|
|
892
|
-
// }),
|
|
893
|
-
// ),
|
|
894
|
-
// threads: v.optional(
|
|
895
|
-
// v.object({
|
|
896
|
-
// generateTitle: v.optional(v.boolean()),
|
|
897
|
-
// }),
|
|
898
|
-
// ),
|
|
899
|
-
// });
|
|
900
|
-
// const vSelectBy = v.object({
|
|
901
|
-
// vectorSearchString: v.optional(v.string()),
|
|
902
|
-
// last: v.optional(v.union(v.number(), v.literal(false))),
|
|
903
|
-
// include: v.optional(
|
|
904
|
-
// v.array(
|
|
905
|
-
// v.object({
|
|
906
|
-
// id: v.string(),
|
|
907
|
-
// withPreviousMessages: v.optional(v.number()),
|
|
908
|
-
// withNextMessages: v.optional(v.number()),
|
|
909
|
-
// })
|
|
910
|
-
// )
|
|
911
|
-
// ),
|
|
912
|
-
// });
|
|
913
|
-
|
|
914
|
-
// const DEFAULT_MESSAGES_LIMIT = 40; // What pg & upstash do too.
|
|
915
|
-
|
|
916
|
-
// export const getThreadMessagesPage = query({
|
|
917
|
-
// args: {
|
|
918
|
-
// parentMessageId: v.string(),
|
|
919
|
-
// selectBy: v.optional(vSelectBy),
|
|
920
|
-
// // Unimplemented and as far I can tell no storage provider has either.
|
|
921
|
-
// // memoryConfig: v.optional(vMemoryConfig),
|
|
922
|
-
// },
|
|
923
|
-
// handler: async (ctx, args): Promise<SerializedMessage[]> => {
|
|
924
|
-
// const messages = await ctx.db
|
|
925
|
-
// .query("messages")
|
|
926
|
-
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", args.parentMessageId))
|
|
927
|
-
// .order("desc")
|
|
928
|
-
// .take(args.selectBy?.last ? args.selectBy.last : DEFAULT_MESSAGES_LIMIT);
|
|
929
|
-
|
|
930
|
-
// const handled: boolean[] = [];
|
|
931
|
-
// const toFetch: number[] = [];
|
|
932
|
-
// for (const m of messages) {
|
|
933
|
-
// handled[m.threadOrder] = true;
|
|
934
|
-
// }
|
|
935
|
-
// await Promise.all(
|
|
936
|
-
// args.selectBy?.include?.map(async (range) => {
|
|
937
|
-
// const includeDoc = await ctx.db
|
|
938
|
-
// .query("messages")
|
|
939
|
-
// .withIndex("id", (q) => q.eq("id", range.id))
|
|
940
|
-
// .unique();
|
|
941
|
-
// if (!includeDoc) {
|
|
942
|
-
// console.warn(`Message ${range.id} not found`);
|
|
943
|
-
// return;
|
|
944
|
-
// }
|
|
945
|
-
// if (!range.withPreviousMessages && !range.withNextMessages) {
|
|
946
|
-
// messages.push(includeDoc);
|
|
947
|
-
// return;
|
|
948
|
-
// }
|
|
949
|
-
// const order = includeDoc.threadOrder;
|
|
950
|
-
// for (
|
|
951
|
-
// let i = order - (range.withPreviousMessages ?? 0);
|
|
952
|
-
// i < order + (range.withNextMessages ?? 0);
|
|
953
|
-
// i++
|
|
954
|
-
// ) {
|
|
955
|
-
// if (!handled[i]) {
|
|
956
|
-
// toFetch.push(i);
|
|
957
|
-
// handled[i] = true;
|
|
958
|
-
// }
|
|
959
|
-
// }
|
|
960
|
-
// }) ?? []
|
|
961
|
-
// );
|
|
962
|
-
// // sort and find unique numbers in toFetch
|
|
963
|
-
// const uniqueToFetch = [...new Set(toFetch)].sort();
|
|
964
|
-
// // find contiguous ranges in uniqueToFetch
|
|
965
|
-
// const ranges: { start: number; end: number }[] = [];
|
|
966
|
-
// for (let i = 0; i < uniqueToFetch.length; i++) {
|
|
967
|
-
// const start = uniqueToFetch[i];
|
|
968
|
-
// let end = start;
|
|
969
|
-
// while (i + 1 < uniqueToFetch.length && uniqueToFetch[i + 1] === end + 1) {
|
|
970
|
-
// end++;
|
|
971
|
-
// i++;
|
|
972
|
-
// }
|
|
973
|
-
// ranges.push({ start, end });
|
|
974
|
-
// }
|
|
975
|
-
// const fetched = (
|
|
976
|
-
// await Promise.all(
|
|
977
|
-
// ranges.map(async (range) => {
|
|
978
|
-
// return await ctx.db
|
|
979
|
-
// .query("messages")
|
|
980
|
-
// .withIndex("parentMessageId", (q) =>
|
|
981
|
-
// q
|
|
982
|
-
// .eq("parentMessageId", args.parentMessageId)
|
|
983
|
-
// .gte("threadOrder", range.start)
|
|
984
|
-
// .lte("threadOrder", range.end)
|
|
985
|
-
// )
|
|
986
|
-
// .collect();
|
|
987
|
-
// })
|
|
988
|
-
// )
|
|
989
|
-
// ).flat();
|
|
990
|
-
// messages.push(...fetched);
|
|
991
|
-
// return messages.map(messageToSerializedMastra);
|
|
992
|
-
// },
|
|
993
|
-
// returns: v.array(vSerializedMessage),
|
|
994
|
-
// });
|
|
995
|
-
|
|
996
|
-
// export const saveMessages = mutation({
|
|
997
|
-
// args: { messages: v.array(vSerializedMessage) },
|
|
998
|
-
// handler: async (ctx, args) => {
|
|
999
|
-
// const messagesByParentMessageId: Record<string, SerializedMessage[]> = {};
|
|
1000
|
-
// for (const message of args.messages) {
|
|
1001
|
-
// messagesByParentMessageId[message.parentMessageId] = [
|
|
1002
|
-
// ...(messagesByParentMessageId[message.parentMessageId] ?? []),
|
|
1003
|
-
// message,
|
|
1004
|
-
// ];
|
|
1005
|
-
// }
|
|
1006
|
-
// for (const parentMessageId in messagesByParentMessageId) {
|
|
1007
|
-
// const lastMessage = await ctx.db
|
|
1008
|
-
// .query("messages")
|
|
1009
|
-
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", parentMessageId))
|
|
1010
|
-
// .order("desc")
|
|
1011
|
-
// .first();
|
|
1012
|
-
// let threadOrder = lastMessage?.threadOrder ?? 0;
|
|
1013
|
-
// for (const message of messagesByParentMessageId[parentMessageId]) {
|
|
1014
|
-
// threadOrder++;
|
|
1015
|
-
// await ctx.db.insert("messages", {
|
|
1016
|
-
// ...message,
|
|
1017
|
-
// threadOrder,
|
|
1018
|
-
// });
|
|
1019
|
-
// }
|
|
1020
|
-
// }
|
|
1021
|
-
// },
|
|
1022
|
-
// returns: v.null(),
|
|
1023
|
-
// });
|