@convex-dev/agent 0.0.1-alpha.4 → 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
|
@@ -3,11 +3,12 @@ import { paginator } from "convex-helpers/server/pagination";
|
|
|
3
3
|
import { mergedStream, stream } from "convex-helpers/server/stream";
|
|
4
4
|
import { nullable, partial } from "convex-helpers/validators";
|
|
5
5
|
import { DEFAULT_MESSAGE_RANGE, extractText, isTool } from "../shared.js";
|
|
6
|
-
import {
|
|
6
|
+
import { vEmbeddingsWithMetadata, vMessageStatus, vMessageWithFileAndId, vSearchOptions, vStepWithMessages, vThreadStatus, } from "../validators.js";
|
|
7
7
|
import { api, internal } from "./_generated/api.js";
|
|
8
8
|
import { action, internalMutation, internalQuery, mutation, query, } from "./_generated/server.js";
|
|
9
9
|
import { schema, v } from "./schema.js";
|
|
10
|
-
import {
|
|
10
|
+
import { insertVector, searchVectors } from "./vector/index.js";
|
|
11
|
+
import { VectorDimensions, vVectorId, } from "./vector/tables.js";
|
|
11
12
|
export const getThread = query({
|
|
12
13
|
args: { threadId: v.id("threads") },
|
|
13
14
|
handler: async (ctx, args) => {
|
|
@@ -22,16 +23,18 @@ export const getThreadsByUserId = query({
|
|
|
22
23
|
cursor: v.optional(v.union(v.string(), v.null())),
|
|
23
24
|
limit: v.optional(v.number()),
|
|
24
25
|
offset: v.optional(v.number()),
|
|
25
|
-
statuses: v.optional(
|
|
26
|
+
statuses: v.optional(vThreadStatus),
|
|
26
27
|
},
|
|
27
28
|
handler: async (ctx, args) => {
|
|
28
|
-
const
|
|
29
|
+
const status = args.statuses ?? "active";
|
|
30
|
+
const threads = await paginator(ctx.db, schema)
|
|
29
31
|
.query("threads")
|
|
30
|
-
.withIndex("
|
|
31
|
-
.eq("status", status)
|
|
32
|
+
.withIndex("userId_status_order", (q) => q
|
|
32
33
|
.eq("userId", args.userId)
|
|
33
|
-
.
|
|
34
|
-
|
|
34
|
+
.eq("status", status)
|
|
35
|
+
.gte("order", args.offset ?? 0))
|
|
36
|
+
.order("desc")
|
|
37
|
+
.paginate({
|
|
35
38
|
numItems: args.limit ?? 100,
|
|
36
39
|
cursor: args.cursor ?? null,
|
|
37
40
|
});
|
|
@@ -48,15 +51,14 @@ export const getThreadsByUserId = query({
|
|
|
48
51
|
}),
|
|
49
52
|
});
|
|
50
53
|
const vThread = schema.tables.threads.validator;
|
|
51
|
-
const statuses = vThread.fields.status.members.map((m) => m.value);
|
|
52
54
|
export const createThread = mutation({
|
|
53
55
|
args: omit(vThread.fields, ["order", "status"]),
|
|
54
56
|
handler: async (ctx, args) => {
|
|
55
|
-
const
|
|
57
|
+
const latestThread = await ctx.db
|
|
56
58
|
.query("threads")
|
|
57
|
-
.withIndex("
|
|
58
|
-
.order("desc")
|
|
59
|
-
|
|
59
|
+
.withIndex("userId_status_order", (q) => q.eq("userId", args.userId).eq("status", "active"))
|
|
60
|
+
.order("desc")
|
|
61
|
+
.first();
|
|
60
62
|
const order = (latestThread?.order ?? -1) + 1;
|
|
61
63
|
const threadId = await ctx.db.insert("threads", {
|
|
62
64
|
...args,
|
|
@@ -119,7 +121,7 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
119
121
|
userId: v.string(),
|
|
120
122
|
},
|
|
121
123
|
handler: async (ctx, args) => {
|
|
122
|
-
const isDone = await
|
|
124
|
+
const isDone = await deleteAllForUserIdAsyncHandler(ctx, {
|
|
123
125
|
userId: args.userId,
|
|
124
126
|
messagesCursor: null,
|
|
125
127
|
threadsCursor: null,
|
|
@@ -140,10 +142,10 @@ const deleteAllReturns = {
|
|
|
140
142
|
};
|
|
141
143
|
export const _deleteAllForUserIdAsync = internalMutation({
|
|
142
144
|
args: deleteAllArgs,
|
|
143
|
-
handler:
|
|
145
|
+
handler: deleteAllForUserIdAsyncHandler,
|
|
144
146
|
returns: v.boolean(),
|
|
145
147
|
});
|
|
146
|
-
async function
|
|
148
|
+
async function deleteAllForUserIdAsyncHandler(ctx, args) {
|
|
147
149
|
const result = await deletePageForUserId(ctx, args);
|
|
148
150
|
if (!result.isDone) {
|
|
149
151
|
await ctx.scheduler.runAfter(0, internal.messages._deleteAllForUserIdAsync, {
|
|
@@ -160,40 +162,35 @@ export const _deletePageForUserId = internalMutation({
|
|
|
160
162
|
returns: deleteAllReturns,
|
|
161
163
|
});
|
|
162
164
|
async function deletePageForUserId(ctx, args) {
|
|
163
|
-
const
|
|
165
|
+
const threads = await paginator(ctx.db, schema)
|
|
164
166
|
.query("threads")
|
|
165
|
-
.withIndex("
|
|
166
|
-
.order("desc")
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.
|
|
167
|
+
.withIndex("userId_status_order", (q) => q.eq("userId", args.userId))
|
|
168
|
+
.order("desc")
|
|
169
|
+
.paginate({
|
|
170
|
+
numItems: 100,
|
|
171
|
+
cursor: args.threadsCursor ?? null,
|
|
172
|
+
});
|
|
173
|
+
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
174
|
+
const messages = await paginator(ctx.db, schema)
|
|
170
175
|
.query("messages")
|
|
171
|
-
.withIndex("
|
|
176
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", args.userId))
|
|
177
|
+
.order("desc")
|
|
172
178
|
.paginate({
|
|
173
179
|
numItems: 100,
|
|
174
180
|
cursor: args.messagesCursor ?? null,
|
|
175
181
|
});
|
|
176
182
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
177
|
-
if (messages.isDone) {
|
|
178
|
-
const threads = await threadStreams.paginate({
|
|
179
|
-
numItems: 100,
|
|
180
|
-
cursor: args.threadsCursor ?? null,
|
|
181
|
-
});
|
|
182
|
-
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
183
|
-
return {
|
|
184
|
-
messagesCursor: messages.continueCursor,
|
|
185
|
-
threadsCursor: threads.continueCursor,
|
|
186
|
-
isDone: threads.isDone,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
183
|
return {
|
|
190
184
|
messagesCursor: messages.continueCursor,
|
|
191
|
-
threadsCursor:
|
|
185
|
+
threadsCursor: threads.continueCursor,
|
|
192
186
|
isDone: messages.isDone,
|
|
193
187
|
};
|
|
194
188
|
}
|
|
195
189
|
async function deleteMessage(ctx, messageDoc) {
|
|
196
190
|
await ctx.db.delete(messageDoc._id);
|
|
191
|
+
if (messageDoc.embeddingId) {
|
|
192
|
+
await ctx.db.delete(messageDoc.embeddingId);
|
|
193
|
+
}
|
|
197
194
|
if (messageDoc.fileId) {
|
|
198
195
|
const file = await ctx.db.get(messageDoc.fileId);
|
|
199
196
|
if (file) {
|
|
@@ -238,9 +235,9 @@ export const _deletePageForThreadId = internalMutation({
|
|
|
238
235
|
returns: deleteThreadReturns,
|
|
239
236
|
});
|
|
240
237
|
async function deletePageForThreadIdHandler(ctx, args) {
|
|
241
|
-
const messages = await
|
|
238
|
+
const messages = await paginator(ctx.db, schema)
|
|
242
239
|
.query("messages")
|
|
243
|
-
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", args.threadId)
|
|
240
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", args.threadId))
|
|
244
241
|
.paginate({
|
|
245
242
|
numItems: args.limit ?? 100,
|
|
246
243
|
cursor: args.cursor ?? null,
|
|
@@ -281,7 +278,7 @@ export const vMessageDoc = schema.tables.messages.validator;
|
|
|
281
278
|
export const messageStatuses = vMessageDoc.fields.status.members.map((m) => m.value);
|
|
282
279
|
const addMessagesArgs = {
|
|
283
280
|
userId: v.optional(v.string()),
|
|
284
|
-
threadId: v.
|
|
281
|
+
threadId: v.id("threads"),
|
|
285
282
|
stepId: v.optional(v.id("steps")),
|
|
286
283
|
parentMessageId: v.optional(v.id("messages")),
|
|
287
284
|
messages: v.array(vMessageWithFileAndId),
|
|
@@ -289,6 +286,7 @@ const addMessagesArgs = {
|
|
|
289
286
|
agentName: v.optional(v.string()),
|
|
290
287
|
pending: v.optional(v.boolean()),
|
|
291
288
|
failPendingSteps: v.optional(v.boolean()),
|
|
289
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
292
290
|
};
|
|
293
291
|
export const addMessages = mutation({
|
|
294
292
|
args: addMessagesArgs,
|
|
@@ -299,14 +297,15 @@ export const addMessages = mutation({
|
|
|
299
297
|
}),
|
|
300
298
|
});
|
|
301
299
|
async function addMessagesHandler(ctx, args) {
|
|
300
|
+
assert(!args.embeddings || args.embeddings.vectors.length === args.messages.length, "embeddings must have one vector per message");
|
|
302
301
|
let userId = args.userId;
|
|
303
302
|
const threadId = args.threadId;
|
|
304
303
|
if (!userId && args.threadId) {
|
|
305
304
|
const thread = await ctx.db.get(args.threadId);
|
|
306
305
|
assert(thread, `Thread ${args.threadId} not found`);
|
|
307
|
-
userId = thread.
|
|
306
|
+
userId = thread.userId;
|
|
308
307
|
}
|
|
309
|
-
const { failPendingSteps, pending, messages, parentMessageId, ...rest } = args;
|
|
308
|
+
const { failPendingSteps, pending, messages, parentMessageId, embeddings, ...rest } = args;
|
|
310
309
|
const parent = parentMessageId && (await ctx.db.get(parentMessageId));
|
|
311
310
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
312
311
|
assert(args.threadId, "threadId is required to fail pending steps");
|
|
@@ -320,7 +319,18 @@ async function addMessagesHandler(ctx, args) {
|
|
|
320
319
|
let order = maxMessage?.order ?? -1;
|
|
321
320
|
const toReturn = [];
|
|
322
321
|
if (messages.length > 0) {
|
|
323
|
-
for (const { message, fileId, id } of messages) {
|
|
322
|
+
for (const [i, { message, fileId, id }] of messages.entries()) {
|
|
323
|
+
const embedding = embeddings?.vectors[i] ?? undefined;
|
|
324
|
+
let embeddingId;
|
|
325
|
+
if (embeddings && embedding) {
|
|
326
|
+
embeddingId = await insertVector(ctx, embeddings.dimension, {
|
|
327
|
+
vector: embedding,
|
|
328
|
+
model: embeddings.model,
|
|
329
|
+
table: "messages",
|
|
330
|
+
userId,
|
|
331
|
+
threadId,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
324
334
|
const tool = isTool(message);
|
|
325
335
|
if (!tool) {
|
|
326
336
|
order++;
|
|
@@ -328,6 +338,7 @@ async function addMessagesHandler(ctx, args) {
|
|
|
328
338
|
const text = extractText(message);
|
|
329
339
|
const messageId = await ctx.db.insert("messages", {
|
|
330
340
|
...rest,
|
|
341
|
+
embeddingId,
|
|
331
342
|
parentMessageId,
|
|
332
343
|
userId,
|
|
333
344
|
message,
|
|
@@ -337,7 +348,13 @@ async function addMessagesHandler(ctx, args) {
|
|
|
337
348
|
text,
|
|
338
349
|
fileId,
|
|
339
350
|
status: pending ? "pending" : "success",
|
|
351
|
+
stepOrder: 0,
|
|
340
352
|
});
|
|
353
|
+
if (fileId) {
|
|
354
|
+
await ctx.db.patch(fileId, {
|
|
355
|
+
refcount: (await ctx.db.get(fileId)).refcount + 1,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
341
358
|
toReturn.push((await ctx.db.get(messageId)));
|
|
342
359
|
}
|
|
343
360
|
}
|
|
@@ -352,24 +369,39 @@ async function getMaxMessage(ctx, threadId, userId) {
|
|
|
352
369
|
.order("desc")), ["order", "stepOrder"]).first();
|
|
353
370
|
}
|
|
354
371
|
else {
|
|
355
|
-
|
|
372
|
+
// DO explicitly
|
|
373
|
+
const maxPending = await ctx.db
|
|
356
374
|
.query("messages")
|
|
357
|
-
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status",
|
|
358
|
-
.order("desc")
|
|
375
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status", "pending").eq("tool", false))
|
|
376
|
+
.order("desc")
|
|
377
|
+
.first();
|
|
378
|
+
const maxSuccess = await ctx.db
|
|
379
|
+
.query("messages")
|
|
380
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status", "success").eq("tool", false))
|
|
381
|
+
.order("desc")
|
|
382
|
+
.first();
|
|
383
|
+
return maxPending
|
|
384
|
+
? maxSuccess
|
|
385
|
+
? maxPending.order > maxSuccess.order
|
|
386
|
+
? maxPending
|
|
387
|
+
: maxSuccess
|
|
388
|
+
: maxPending
|
|
389
|
+
: maxSuccess ?? null;
|
|
359
390
|
}
|
|
360
391
|
}
|
|
361
|
-
const
|
|
392
|
+
const addStepArgs = {
|
|
362
393
|
threadId: v.id("threads"),
|
|
363
394
|
messageId: v.id("messages"),
|
|
364
|
-
|
|
395
|
+
step: vStepWithMessages,
|
|
365
396
|
failPendingSteps: v.optional(v.boolean()),
|
|
397
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
366
398
|
};
|
|
367
|
-
export const
|
|
368
|
-
args:
|
|
399
|
+
export const addStep = mutation({
|
|
400
|
+
args: addStepArgs,
|
|
369
401
|
returns: v.array(v.doc("steps")),
|
|
370
|
-
handler:
|
|
402
|
+
handler: addStepHandler,
|
|
371
403
|
});
|
|
372
|
-
async function
|
|
404
|
+
async function addStepHandler(ctx, args) {
|
|
373
405
|
const parentMessage = await ctx.db.get(args.messageId);
|
|
374
406
|
assert(parentMessage, `Message ${args.messageId} not found`);
|
|
375
407
|
const order = parentMessage.order;
|
|
@@ -386,35 +418,33 @@ async function addStepsHandler(ctx, args) {
|
|
|
386
418
|
await ctx.db.patch(step._id, { status: "failed" });
|
|
387
419
|
}
|
|
388
420
|
}
|
|
389
|
-
steps = steps.filter((s) => s.status
|
|
421
|
+
steps = steps.filter((s) => s.status !== "failed");
|
|
390
422
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
steps.push((await ctx.db.get(stepId)));
|
|
416
|
-
nextStepOrder++;
|
|
423
|
+
const { step, messages } = args.step;
|
|
424
|
+
const stepId = await ctx.db.insert("steps", {
|
|
425
|
+
threadId: args.threadId,
|
|
426
|
+
parentMessageId: args.messageId,
|
|
427
|
+
order,
|
|
428
|
+
stepOrder: (steps.at(-1)?.stepOrder ?? -1) + 1,
|
|
429
|
+
status: step.finishReason === "stop" ? "success" : "pending",
|
|
430
|
+
step,
|
|
431
|
+
});
|
|
432
|
+
await addMessagesHandler(ctx, {
|
|
433
|
+
threadId: args.threadId,
|
|
434
|
+
parentMessageId: args.messageId,
|
|
435
|
+
stepId,
|
|
436
|
+
messages,
|
|
437
|
+
model: parentMessage.model,
|
|
438
|
+
agentName: parentMessage.agentName,
|
|
439
|
+
pending: step.finishReason === "stop" ? false : true,
|
|
440
|
+
failPendingSteps: false,
|
|
441
|
+
embeddings: args.embeddings,
|
|
442
|
+
});
|
|
443
|
+
// We don't commit if the parent is still pending.
|
|
444
|
+
if (step.finishReason === "stop") {
|
|
445
|
+
await commitMessageHandler(ctx, { messageId: args.messageId });
|
|
417
446
|
}
|
|
447
|
+
steps.push((await ctx.db.get(stepId)));
|
|
418
448
|
return steps;
|
|
419
449
|
}
|
|
420
450
|
export const rollbackMessage = mutation({
|
|
@@ -426,6 +456,19 @@ export const rollbackMessage = mutation({
|
|
|
426
456
|
handler: async (ctx, { messageId, error }) => {
|
|
427
457
|
const message = await ctx.db.get(messageId);
|
|
428
458
|
assert(message, `Message ${messageId} not found`);
|
|
459
|
+
// TODO: do BFS to fail all associated messages, then steps
|
|
460
|
+
// with parentMessageId of those messages, etc.
|
|
461
|
+
const steps = await ctx.db
|
|
462
|
+
.query("steps")
|
|
463
|
+
.withIndex("parentMessageId_order_stepOrder", (q) =>
|
|
464
|
+
// TODO: fetch pending, and commit later
|
|
465
|
+
q.eq("parentMessageId", messageId))
|
|
466
|
+
.collect();
|
|
467
|
+
for (const step of steps) {
|
|
468
|
+
if (step.status === "pending") {
|
|
469
|
+
await ctx.db.patch(step._id, { status: "failed" });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
429
472
|
await ctx.db.patch(messageId, {
|
|
430
473
|
status: "failed",
|
|
431
474
|
text: error ?? message.text,
|
|
@@ -537,15 +580,14 @@ export const searchMessages = action({
|
|
|
537
580
|
if (!VectorDimensions.includes(dimension)) {
|
|
538
581
|
throw new Error(`Unsupported vector dimension: ${dimension}`);
|
|
539
582
|
}
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
: q.eq("model_kind_threadId", [model, "thread", args.threadId]),
|
|
583
|
+
const vectors = (await searchVectors(ctx, args.vector, {
|
|
584
|
+
dimension,
|
|
585
|
+
model: args.vectorModel ?? "unknown",
|
|
586
|
+
table: "messages",
|
|
587
|
+
userId: args.userId,
|
|
588
|
+
threadId: args.threadId,
|
|
547
589
|
limit,
|
|
548
|
-
})).filter((v) => v._score > 0
|
|
590
|
+
})).filter((v) => v._score > (args.vectorScoreThreshold ?? 0));
|
|
549
591
|
// Reciprocal rank fusion
|
|
550
592
|
const k = 10;
|
|
551
593
|
const textEmbeddingIds = textSearchMessages?.map((m) => m.embeddingId);
|
|
@@ -553,7 +595,7 @@ export const searchMessages = action({
|
|
|
553
595
|
.map((v, i) => ({
|
|
554
596
|
id: v._id,
|
|
555
597
|
score: 1 / (i + k) +
|
|
556
|
-
1 / (textEmbeddingIds?.indexOf(v._id) ?? Infinity + k),
|
|
598
|
+
1 / ((textEmbeddingIds?.indexOf(v._id) ?? Infinity) + k),
|
|
557
599
|
}))
|
|
558
600
|
.sort((a, b) => b.score - a.score);
|
|
559
601
|
const vectorIds = vectorScores.slice(0, limit).map((v) => v.id);
|
|
@@ -590,9 +632,10 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
590
632
|
.query("messages")
|
|
591
633
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
592
634
|
.filter((q) => userId
|
|
593
|
-
? q.eq("userId", userId)
|
|
594
|
-
:
|
|
595
|
-
|
|
635
|
+
? q.eq(q.field("userId"), userId)
|
|
636
|
+
: q.eq(q.field("threadId"), threadId))
|
|
637
|
+
// Don't include pending. Failed messages hopefully are deleted but may as well be safe.
|
|
638
|
+
.filter((q) => q.eq(q.field("status"), "success"))
|
|
596
639
|
.first()))).filter((m) => m !== undefined && m !== null && (!parent || m.order <= parent.order));
|
|
597
640
|
messages.push(...(args.textSearchMessages ?? []));
|
|
598
641
|
// TODO: prioritize more recent messages
|
|
@@ -638,8 +681,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
638
681
|
.eq("threadId", m.threadId)
|
|
639
682
|
.eq("status", "success")
|
|
640
683
|
.eq("tool", false)
|
|
641
|
-
.
|
|
642
|
-
.
|
|
684
|
+
.gte("order", earliest)
|
|
685
|
+
.lte("order", latest))
|
|
643
686
|
.collect();
|
|
644
687
|
if (!ranges[searchId]) {
|
|
645
688
|
ranges[searchId] = [];
|
|
@@ -653,8 +696,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
653
696
|
.eq("userId", m.userId)
|
|
654
697
|
.eq("status", "success")
|
|
655
698
|
.eq("tool", false)
|
|
656
|
-
.
|
|
657
|
-
.
|
|
699
|
+
.gte("order", earliest)
|
|
700
|
+
.lte("order", latest))
|
|
658
701
|
.collect();
|
|
659
702
|
if (!ranges[searchId]) {
|
|
660
703
|
ranges[searchId] = [];
|
|
@@ -663,9 +706,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
663
706
|
}
|
|
664
707
|
}
|
|
665
708
|
}
|
|
666
|
-
|
|
667
|
-
.
|
|
668
|
-
|
|
709
|
+
for (const r of Object.values(ranges).flat()) {
|
|
710
|
+
if (!messages.includes(r)) {
|
|
711
|
+
messages.push(r);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return messages.sort((a, b) => a.order - b.order);
|
|
669
715
|
},
|
|
670
716
|
});
|
|
671
717
|
// returns ranges of messages in order of text search relevance,
|
|
@@ -689,153 +735,4 @@ export const textSearch = query({
|
|
|
689
735
|
},
|
|
690
736
|
returns: v.array(v.doc("messages")),
|
|
691
737
|
});
|
|
692
|
-
// const vMemoryConfig = v.object({
|
|
693
|
-
// lastMessages: v.optional(v.union(v.number(), v.literal(false))),
|
|
694
|
-
// semanticRecall: v.optional(
|
|
695
|
-
// v.union(
|
|
696
|
-
// v.boolean(),
|
|
697
|
-
// v.object({
|
|
698
|
-
// topK: v.number(),
|
|
699
|
-
// messageRange: v.union(
|
|
700
|
-
// v.number(),
|
|
701
|
-
// v.object({ before: v.number(), after: v.number() }),
|
|
702
|
-
// ),
|
|
703
|
-
// }),
|
|
704
|
-
// ),
|
|
705
|
-
// ),
|
|
706
|
-
// workingMemory: v.optional(
|
|
707
|
-
// v.object({
|
|
708
|
-
// enabled: v.boolean(),
|
|
709
|
-
// template: v.optional(v.string()),
|
|
710
|
-
// use: v.optional(
|
|
711
|
-
// v.union(v.literal("text-stream"), v.literal("tool-call")),
|
|
712
|
-
// ),
|
|
713
|
-
// }),
|
|
714
|
-
// ),
|
|
715
|
-
// threads: v.optional(
|
|
716
|
-
// v.object({
|
|
717
|
-
// generateTitle: v.optional(v.boolean()),
|
|
718
|
-
// }),
|
|
719
|
-
// ),
|
|
720
|
-
// });
|
|
721
|
-
// const vSelectBy = v.object({
|
|
722
|
-
// vectorSearchString: v.optional(v.string()),
|
|
723
|
-
// last: v.optional(v.union(v.number(), v.literal(false))),
|
|
724
|
-
// include: v.optional(
|
|
725
|
-
// v.array(
|
|
726
|
-
// v.object({
|
|
727
|
-
// id: v.string(),
|
|
728
|
-
// withPreviousMessages: v.optional(v.number()),
|
|
729
|
-
// withNextMessages: v.optional(v.number()),
|
|
730
|
-
// })
|
|
731
|
-
// )
|
|
732
|
-
// ),
|
|
733
|
-
// });
|
|
734
|
-
// const DEFAULT_MESSAGES_LIMIT = 40; // What pg & upstash do too.
|
|
735
|
-
// export const getThreadMessagesPage = query({
|
|
736
|
-
// args: {
|
|
737
|
-
// parentMessageId: v.string(),
|
|
738
|
-
// selectBy: v.optional(vSelectBy),
|
|
739
|
-
// // Unimplemented and as far I can tell no storage provider has either.
|
|
740
|
-
// // memoryConfig: v.optional(vMemoryConfig),
|
|
741
|
-
// },
|
|
742
|
-
// handler: async (ctx, args): Promise<SerializedMessage[]> => {
|
|
743
|
-
// const messages = await ctx.db
|
|
744
|
-
// .query("messages")
|
|
745
|
-
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", args.parentMessageId))
|
|
746
|
-
// .order("desc")
|
|
747
|
-
// .take(args.selectBy?.last ? args.selectBy.last : DEFAULT_MESSAGES_LIMIT);
|
|
748
|
-
// const handled: boolean[] = [];
|
|
749
|
-
// const toFetch: number[] = [];
|
|
750
|
-
// for (const m of messages) {
|
|
751
|
-
// handled[m.threadOrder] = true;
|
|
752
|
-
// }
|
|
753
|
-
// await Promise.all(
|
|
754
|
-
// args.selectBy?.include?.map(async (range) => {
|
|
755
|
-
// const includeDoc = await ctx.db
|
|
756
|
-
// .query("messages")
|
|
757
|
-
// .withIndex("id", (q) => q.eq("id", range.id))
|
|
758
|
-
// .unique();
|
|
759
|
-
// if (!includeDoc) {
|
|
760
|
-
// console.warn(`Message ${range.id} not found`);
|
|
761
|
-
// return;
|
|
762
|
-
// }
|
|
763
|
-
// if (!range.withPreviousMessages && !range.withNextMessages) {
|
|
764
|
-
// messages.push(includeDoc);
|
|
765
|
-
// return;
|
|
766
|
-
// }
|
|
767
|
-
// const order = includeDoc.threadOrder;
|
|
768
|
-
// for (
|
|
769
|
-
// let i = order - (range.withPreviousMessages ?? 0);
|
|
770
|
-
// i < order + (range.withNextMessages ?? 0);
|
|
771
|
-
// i++
|
|
772
|
-
// ) {
|
|
773
|
-
// if (!handled[i]) {
|
|
774
|
-
// toFetch.push(i);
|
|
775
|
-
// handled[i] = true;
|
|
776
|
-
// }
|
|
777
|
-
// }
|
|
778
|
-
// }) ?? []
|
|
779
|
-
// );
|
|
780
|
-
// // sort and find unique numbers in toFetch
|
|
781
|
-
// const uniqueToFetch = [...new Set(toFetch)].sort();
|
|
782
|
-
// // find contiguous ranges in uniqueToFetch
|
|
783
|
-
// const ranges: { start: number; end: number }[] = [];
|
|
784
|
-
// for (let i = 0; i < uniqueToFetch.length; i++) {
|
|
785
|
-
// const start = uniqueToFetch[i];
|
|
786
|
-
// let end = start;
|
|
787
|
-
// while (i + 1 < uniqueToFetch.length && uniqueToFetch[i + 1] === end + 1) {
|
|
788
|
-
// end++;
|
|
789
|
-
// i++;
|
|
790
|
-
// }
|
|
791
|
-
// ranges.push({ start, end });
|
|
792
|
-
// }
|
|
793
|
-
// const fetched = (
|
|
794
|
-
// await Promise.all(
|
|
795
|
-
// ranges.map(async (range) => {
|
|
796
|
-
// return await ctx.db
|
|
797
|
-
// .query("messages")
|
|
798
|
-
// .withIndex("parentMessageId", (q) =>
|
|
799
|
-
// q
|
|
800
|
-
// .eq("parentMessageId", args.parentMessageId)
|
|
801
|
-
// .gte("threadOrder", range.start)
|
|
802
|
-
// .lte("threadOrder", range.end)
|
|
803
|
-
// )
|
|
804
|
-
// .collect();
|
|
805
|
-
// })
|
|
806
|
-
// )
|
|
807
|
-
// ).flat();
|
|
808
|
-
// messages.push(...fetched);
|
|
809
|
-
// return messages.map(messageToSerializedMastra);
|
|
810
|
-
// },
|
|
811
|
-
// returns: v.array(vSerializedMessage),
|
|
812
|
-
// });
|
|
813
|
-
// export const saveMessages = mutation({
|
|
814
|
-
// args: { messages: v.array(vSerializedMessage) },
|
|
815
|
-
// handler: async (ctx, args) => {
|
|
816
|
-
// const messagesByParentMessageId: Record<string, SerializedMessage[]> = {};
|
|
817
|
-
// for (const message of args.messages) {
|
|
818
|
-
// messagesByParentMessageId[message.parentMessageId] = [
|
|
819
|
-
// ...(messagesByParentMessageId[message.parentMessageId] ?? []),
|
|
820
|
-
// message,
|
|
821
|
-
// ];
|
|
822
|
-
// }
|
|
823
|
-
// for (const parentMessageId in messagesByParentMessageId) {
|
|
824
|
-
// const lastMessage = await ctx.db
|
|
825
|
-
// .query("messages")
|
|
826
|
-
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", parentMessageId))
|
|
827
|
-
// .order("desc")
|
|
828
|
-
// .first();
|
|
829
|
-
// let threadOrder = lastMessage?.threadOrder ?? 0;
|
|
830
|
-
// for (const message of messagesByParentMessageId[parentMessageId]) {
|
|
831
|
-
// threadOrder++;
|
|
832
|
-
// await ctx.db.insert("messages", {
|
|
833
|
-
// ...message,
|
|
834
|
-
// threadOrder,
|
|
835
|
-
// });
|
|
836
|
-
// }
|
|
837
|
-
// }
|
|
838
|
-
// },
|
|
839
|
-
// returns: v.null(),
|
|
840
|
-
// });
|
|
841
738
|
//# sourceMappingURL=messages.js.map
|