@convex-dev/agent 0.0.1-alpha.3 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/commonjs/client/index.d.ts +12 -5
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +127 -44
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/messages.d.ts +114 -103
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +143 -246
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +808 -738
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +12 -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 +33 -21
- 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/validators.d.ts +101 -89
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +8 -1
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/index.d.ts +12 -5
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +127 -44
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/messages.d.ts +114 -103
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +143 -246
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +808 -738
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +12 -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 +33 -21
- 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/validators.d.ts +101 -89
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +8 -1
- package/dist/esm/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +149 -50
- package/src/component/_generated/api.d.ts +28 -16
- package/src/component/messages.ts +175 -289
- package/src/component/schema.ts +13 -3
- package/src/component/vector/index.ts +65 -26
- package/src/component/vector/tables.ts +38 -6
- package/src/validators.ts +11 -4
|
@@ -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,15 +381,25 @@ 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));
|
|
407
404
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
408
405
|
assert(args.threadId, "threadId is required to fail pending steps");
|
|
@@ -422,7 +419,18 @@ async function addMessagesHandler(
|
|
|
422
419
|
let order = maxMessage?.order ?? -1;
|
|
423
420
|
const toReturn: Doc<"messages">[] = [];
|
|
424
421
|
if (messages.length > 0) {
|
|
425
|
-
for (const { message, fileId, id } of messages) {
|
|
422
|
+
for (const [i, { message, fileId, id }] of messages.entries()) {
|
|
423
|
+
const embedding = embeddings?.vectors[i] ?? undefined;
|
|
424
|
+
let embeddingId: VectorTableId | undefined;
|
|
425
|
+
if (embeddings && embedding) {
|
|
426
|
+
embeddingId = await insertVector(ctx, embeddings.dimension, {
|
|
427
|
+
vector: embedding,
|
|
428
|
+
model: embeddings.model,
|
|
429
|
+
table: "messages",
|
|
430
|
+
userId,
|
|
431
|
+
threadId,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
426
434
|
const tool = isTool(message);
|
|
427
435
|
if (!tool) {
|
|
428
436
|
order++;
|
|
@@ -430,6 +438,7 @@ async function addMessagesHandler(
|
|
|
430
438
|
const text = extractText(message);
|
|
431
439
|
const messageId = await ctx.db.insert("messages", {
|
|
432
440
|
...rest,
|
|
441
|
+
embeddingId,
|
|
433
442
|
parentMessageId,
|
|
434
443
|
userId,
|
|
435
444
|
message,
|
|
@@ -439,7 +448,13 @@ async function addMessagesHandler(
|
|
|
439
448
|
text,
|
|
440
449
|
fileId,
|
|
441
450
|
status: pending ? "pending" : "success",
|
|
451
|
+
stepOrder: 0,
|
|
442
452
|
});
|
|
453
|
+
if (fileId) {
|
|
454
|
+
await ctx.db.patch(fileId, {
|
|
455
|
+
refcount: (await ctx.db.get(fileId))!.refcount + 1,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
443
458
|
toReturn.push((await ctx.db.get(messageId))!);
|
|
444
459
|
}
|
|
445
460
|
}
|
|
@@ -465,35 +480,47 @@ async function getMaxMessage(
|
|
|
465
480
|
["order", "stepOrder"]
|
|
466
481
|
).first();
|
|
467
482
|
} else {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
483
|
+
// DO explicitly
|
|
484
|
+
const maxPending = await ctx.db
|
|
485
|
+
.query("messages")
|
|
486
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
487
|
+
q.eq("userId", userId).eq("status", "pending").eq("tool", false)
|
|
488
|
+
)
|
|
489
|
+
.order("desc")
|
|
490
|
+
.first();
|
|
491
|
+
const maxSuccess = await ctx.db
|
|
492
|
+
.query("messages")
|
|
493
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
494
|
+
q.eq("userId", userId).eq("status", "success").eq("tool", false)
|
|
495
|
+
)
|
|
496
|
+
.order("desc")
|
|
497
|
+
.first();
|
|
498
|
+
return maxPending
|
|
499
|
+
? maxSuccess
|
|
500
|
+
? maxPending.order > maxSuccess.order
|
|
501
|
+
? maxPending
|
|
502
|
+
: maxSuccess
|
|
503
|
+
: maxPending
|
|
504
|
+
: maxSuccess ?? null;
|
|
479
505
|
}
|
|
480
506
|
}
|
|
481
507
|
|
|
482
|
-
const
|
|
508
|
+
const addStepArgs = {
|
|
483
509
|
threadId: v.id("threads"),
|
|
484
510
|
messageId: v.id("messages"),
|
|
485
|
-
|
|
511
|
+
step: vStepWithMessages,
|
|
486
512
|
failPendingSteps: v.optional(v.boolean()),
|
|
513
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
487
514
|
};
|
|
488
515
|
|
|
489
|
-
export const
|
|
490
|
-
args:
|
|
516
|
+
export const addStep = mutation({
|
|
517
|
+
args: addStepArgs,
|
|
491
518
|
returns: v.array(v.doc("steps")),
|
|
492
|
-
handler:
|
|
519
|
+
handler: addStepHandler,
|
|
493
520
|
});
|
|
494
|
-
async function
|
|
521
|
+
async function addStepHandler(
|
|
495
522
|
ctx: MutationCtx,
|
|
496
|
-
args: ObjectType<typeof
|
|
523
|
+
args: ObjectType<typeof addStepArgs>
|
|
497
524
|
) {
|
|
498
525
|
const parentMessage = await ctx.db.get(args.messageId);
|
|
499
526
|
assert(parentMessage, `Message ${args.messageId} not found`);
|
|
@@ -512,35 +539,33 @@ async function addStepsHandler(
|
|
|
512
539
|
await ctx.db.patch(step._id, { status: "failed" });
|
|
513
540
|
}
|
|
514
541
|
}
|
|
515
|
-
steps = steps.filter((s) => s.status
|
|
542
|
+
steps = steps.filter((s) => s.status !== "failed");
|
|
516
543
|
}
|
|
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++;
|
|
544
|
+
const { step, messages } = args.step;
|
|
545
|
+
const stepId = await ctx.db.insert("steps", {
|
|
546
|
+
threadId: args.threadId,
|
|
547
|
+
parentMessageId: args.messageId,
|
|
548
|
+
order,
|
|
549
|
+
stepOrder: (steps.at(-1)?.stepOrder ?? -1) + 1,
|
|
550
|
+
status: step.finishReason === "stop" ? "success" : "pending",
|
|
551
|
+
step,
|
|
552
|
+
});
|
|
553
|
+
await addMessagesHandler(ctx, {
|
|
554
|
+
threadId: args.threadId,
|
|
555
|
+
parentMessageId: args.messageId,
|
|
556
|
+
stepId,
|
|
557
|
+
messages,
|
|
558
|
+
model: parentMessage.model,
|
|
559
|
+
agentName: parentMessage.agentName,
|
|
560
|
+
pending: step.finishReason === "stop" ? false : true,
|
|
561
|
+
failPendingSteps: false,
|
|
562
|
+
embeddings: args.embeddings,
|
|
563
|
+
});
|
|
564
|
+
// We don't commit if the parent is still pending.
|
|
565
|
+
if (step.finishReason === "stop") {
|
|
566
|
+
await commitMessageHandler(ctx, { messageId: args.messageId });
|
|
543
567
|
}
|
|
568
|
+
steps.push((await ctx.db.get(stepId))!);
|
|
544
569
|
return steps;
|
|
545
570
|
}
|
|
546
571
|
|
|
@@ -553,6 +578,20 @@ export const rollbackMessage = mutation({
|
|
|
553
578
|
handler: async (ctx, { messageId, error }) => {
|
|
554
579
|
const message = await ctx.db.get(messageId);
|
|
555
580
|
assert(message, `Message ${messageId} not found`);
|
|
581
|
+
// TODO: do BFS to fail all associated messages, then steps
|
|
582
|
+
// with parentMessageId of those messages, etc.
|
|
583
|
+
const steps = await ctx.db
|
|
584
|
+
.query("steps")
|
|
585
|
+
.withIndex("parentMessageId_order_stepOrder", (q) =>
|
|
586
|
+
// TODO: fetch pending, and commit later
|
|
587
|
+
q.eq("parentMessageId", messageId)
|
|
588
|
+
)
|
|
589
|
+
.collect();
|
|
590
|
+
for (const step of steps) {
|
|
591
|
+
if (step.status === "pending") {
|
|
592
|
+
await ctx.db.patch(step._id, { status: "failed" });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
556
595
|
await ctx.db.patch(messageId, {
|
|
557
596
|
status: "failed",
|
|
558
597
|
text: error ?? message.text,
|
|
@@ -686,18 +725,16 @@ export const searchMessages = action({
|
|
|
686
725
|
if (!VectorDimensions.includes(dimension)) {
|
|
687
726
|
throw new Error(`Unsupported vector dimension: ${dimension}`);
|
|
688
727
|
}
|
|
689
|
-
const model = args.vectorModel ?? "unknown";
|
|
690
|
-
const tableName = getVectorTableName(dimension);
|
|
691
728
|
const vectors = (
|
|
692
|
-
await ctx
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
729
|
+
await searchVectors(ctx, args.vector, {
|
|
730
|
+
dimension,
|
|
731
|
+
model: args.vectorModel ?? "unknown",
|
|
732
|
+
table: "messages",
|
|
733
|
+
userId: args.userId,
|
|
734
|
+
threadId: args.threadId,
|
|
698
735
|
limit,
|
|
699
736
|
})
|
|
700
|
-
).filter((v) => v._score > 0
|
|
737
|
+
).filter((v) => v._score > (args.vectorScoreThreshold ?? 0));
|
|
701
738
|
// Reciprocal rank fusion
|
|
702
739
|
const k = 10;
|
|
703
740
|
const textEmbeddingIds = textSearchMessages?.map((m) => m.embeddingId);
|
|
@@ -706,11 +743,10 @@ export const searchMessages = action({
|
|
|
706
743
|
id: v._id,
|
|
707
744
|
score:
|
|
708
745
|
1 / (i + k) +
|
|
709
|
-
1 / (textEmbeddingIds?.indexOf(v._id) ?? Infinity + k),
|
|
746
|
+
1 / ((textEmbeddingIds?.indexOf(v._id) ?? Infinity) + k),
|
|
710
747
|
}))
|
|
711
748
|
.sort((a, b) => b.score - a.score);
|
|
712
749
|
const vectorIds = vectorScores.slice(0, limit).map((v) => v.id);
|
|
713
|
-
|
|
714
750
|
const messages: Doc<"messages">[] = await ctx.runQuery(
|
|
715
751
|
internal.messages._fetchVectorMessages,
|
|
716
752
|
{
|
|
@@ -755,10 +791,11 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
755
791
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
756
792
|
.filter((q) =>
|
|
757
793
|
userId
|
|
758
|
-
? q.eq("userId", userId)
|
|
759
|
-
:
|
|
760
|
-
q.eq("threadId", threadId as any)
|
|
794
|
+
? q.eq(q.field("userId"), userId)
|
|
795
|
+
: q.eq(q.field("threadId"), threadId)
|
|
761
796
|
)
|
|
797
|
+
// Don't include pending. Failed messages hopefully are deleted but may as well be safe.
|
|
798
|
+
.filter((q) => q.eq(q.field("status"), "success"))
|
|
762
799
|
.first()
|
|
763
800
|
)
|
|
764
801
|
)
|
|
@@ -811,8 +848,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
811
848
|
.eq("threadId", m.threadId)
|
|
812
849
|
.eq("status", "success")
|
|
813
850
|
.eq("tool", false)
|
|
814
|
-
.
|
|
815
|
-
.
|
|
851
|
+
.gte("order", earliest)
|
|
852
|
+
.lte("order", latest)
|
|
816
853
|
)
|
|
817
854
|
.collect();
|
|
818
855
|
if (!ranges[searchId]) {
|
|
@@ -827,8 +864,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
827
864
|
.eq("userId", m.userId!)
|
|
828
865
|
.eq("status", "success")
|
|
829
866
|
.eq("tool", false)
|
|
830
|
-
.
|
|
831
|
-
.
|
|
867
|
+
.gte("order", earliest)
|
|
868
|
+
.lte("order", latest)
|
|
832
869
|
)
|
|
833
870
|
.collect();
|
|
834
871
|
if (!ranges[searchId]) {
|
|
@@ -838,9 +875,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
838
875
|
}
|
|
839
876
|
}
|
|
840
877
|
}
|
|
841
|
-
|
|
842
|
-
.
|
|
843
|
-
|
|
878
|
+
for (const r of Object.values(ranges).flat()) {
|
|
879
|
+
if (!messages.includes(r)) {
|
|
880
|
+
messages.push(r);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return messages.sort((a, b) => a.order - b.order);
|
|
844
884
|
},
|
|
845
885
|
});
|
|
846
886
|
|
|
@@ -867,157 +907,3 @@ export const textSearch = query({
|
|
|
867
907
|
},
|
|
868
908
|
returns: v.array(v.doc("messages")),
|
|
869
909
|
});
|
|
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
|
-
// });
|