@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
|
@@ -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,15 +297,17 @@ 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));
|
|
310
|
+
// TODO: I think this is a bug - parent will be pending always?
|
|
311
311
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
312
312
|
assert(args.threadId, "threadId is required to fail pending steps");
|
|
313
313
|
const pendingMessages = await ctx.db
|
|
@@ -320,7 +320,18 @@ async function addMessagesHandler(ctx, args) {
|
|
|
320
320
|
let order = maxMessage?.order ?? -1;
|
|
321
321
|
const toReturn = [];
|
|
322
322
|
if (messages.length > 0) {
|
|
323
|
-
for (const { message, fileId, id } of messages) {
|
|
323
|
+
for (const [i, { message, fileId, id }] of messages.entries()) {
|
|
324
|
+
const embedding = embeddings?.vectors[i] ?? undefined;
|
|
325
|
+
let embeddingId;
|
|
326
|
+
if (embeddings && embedding) {
|
|
327
|
+
embeddingId = await insertVector(ctx, embeddings.dimension, {
|
|
328
|
+
vector: embedding,
|
|
329
|
+
model: embeddings.model,
|
|
330
|
+
table: "messages",
|
|
331
|
+
userId,
|
|
332
|
+
threadId,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
324
335
|
const tool = isTool(message);
|
|
325
336
|
if (!tool) {
|
|
326
337
|
order++;
|
|
@@ -328,6 +339,7 @@ async function addMessagesHandler(ctx, args) {
|
|
|
328
339
|
const text = extractText(message);
|
|
329
340
|
const messageId = await ctx.db.insert("messages", {
|
|
330
341
|
...rest,
|
|
342
|
+
embeddingId,
|
|
331
343
|
parentMessageId,
|
|
332
344
|
userId,
|
|
333
345
|
message,
|
|
@@ -337,7 +349,13 @@ async function addMessagesHandler(ctx, args) {
|
|
|
337
349
|
text,
|
|
338
350
|
fileId,
|
|
339
351
|
status: pending ? "pending" : "success",
|
|
352
|
+
stepOrder: 0,
|
|
340
353
|
});
|
|
354
|
+
if (fileId) {
|
|
355
|
+
await ctx.db.patch(fileId, {
|
|
356
|
+
refcount: (await ctx.db.get(fileId)).refcount + 1,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
341
359
|
toReturn.push((await ctx.db.get(messageId)));
|
|
342
360
|
}
|
|
343
361
|
}
|
|
@@ -352,24 +370,39 @@ async function getMaxMessage(ctx, threadId, userId) {
|
|
|
352
370
|
.order("desc")), ["order", "stepOrder"]).first();
|
|
353
371
|
}
|
|
354
372
|
else {
|
|
355
|
-
|
|
373
|
+
// DO explicitly
|
|
374
|
+
const maxPending = await ctx.db
|
|
356
375
|
.query("messages")
|
|
357
|
-
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status",
|
|
358
|
-
.order("desc")
|
|
376
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status", "pending").eq("tool", false))
|
|
377
|
+
.order("desc")
|
|
378
|
+
.first();
|
|
379
|
+
const maxSuccess = await ctx.db
|
|
380
|
+
.query("messages")
|
|
381
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) => q.eq("userId", userId).eq("status", "success").eq("tool", false))
|
|
382
|
+
.order("desc")
|
|
383
|
+
.first();
|
|
384
|
+
return maxPending
|
|
385
|
+
? maxSuccess
|
|
386
|
+
? maxPending.order > maxSuccess.order
|
|
387
|
+
? maxPending
|
|
388
|
+
: maxSuccess
|
|
389
|
+
: maxPending
|
|
390
|
+
: maxSuccess ?? null;
|
|
359
391
|
}
|
|
360
392
|
}
|
|
361
|
-
const
|
|
393
|
+
const addStepArgs = {
|
|
362
394
|
threadId: v.id("threads"),
|
|
363
395
|
messageId: v.id("messages"),
|
|
364
|
-
|
|
396
|
+
step: vStepWithMessages,
|
|
365
397
|
failPendingSteps: v.optional(v.boolean()),
|
|
398
|
+
embeddings: v.optional(vEmbeddingsWithMetadata),
|
|
366
399
|
};
|
|
367
|
-
export const
|
|
368
|
-
args:
|
|
400
|
+
export const addStep = mutation({
|
|
401
|
+
args: addStepArgs,
|
|
369
402
|
returns: v.array(v.doc("steps")),
|
|
370
|
-
handler:
|
|
403
|
+
handler: addStepHandler,
|
|
371
404
|
});
|
|
372
|
-
async function
|
|
405
|
+
async function addStepHandler(ctx, args) {
|
|
373
406
|
const parentMessage = await ctx.db.get(args.messageId);
|
|
374
407
|
assert(parentMessage, `Message ${args.messageId} not found`);
|
|
375
408
|
const order = parentMessage.order;
|
|
@@ -386,35 +419,33 @@ async function addStepsHandler(ctx, args) {
|
|
|
386
419
|
await ctx.db.patch(step._id, { status: "failed" });
|
|
387
420
|
}
|
|
388
421
|
}
|
|
389
|
-
steps = steps.filter((s) => s.status
|
|
422
|
+
steps = steps.filter((s) => s.status !== "failed");
|
|
390
423
|
}
|
|
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++;
|
|
424
|
+
const { step, messages } = args.step;
|
|
425
|
+
const stepId = await ctx.db.insert("steps", {
|
|
426
|
+
threadId: args.threadId,
|
|
427
|
+
parentMessageId: args.messageId,
|
|
428
|
+
order,
|
|
429
|
+
stepOrder: (steps.at(-1)?.stepOrder ?? -1) + 1,
|
|
430
|
+
status: step.finishReason === "stop" ? "success" : "pending",
|
|
431
|
+
step,
|
|
432
|
+
});
|
|
433
|
+
await addMessagesHandler(ctx, {
|
|
434
|
+
threadId: args.threadId,
|
|
435
|
+
parentMessageId: args.messageId,
|
|
436
|
+
stepId,
|
|
437
|
+
messages,
|
|
438
|
+
model: parentMessage.model,
|
|
439
|
+
agentName: parentMessage.agentName,
|
|
440
|
+
pending: step.finishReason === "stop" ? false : true,
|
|
441
|
+
failPendingSteps: false,
|
|
442
|
+
embeddings: args.embeddings,
|
|
443
|
+
});
|
|
444
|
+
// We don't commit if the parent is still pending.
|
|
445
|
+
if (step.finishReason === "stop") {
|
|
446
|
+
await commitMessageHandler(ctx, { messageId: args.messageId });
|
|
417
447
|
}
|
|
448
|
+
steps.push((await ctx.db.get(stepId)));
|
|
418
449
|
return steps;
|
|
419
450
|
}
|
|
420
451
|
export const rollbackMessage = mutation({
|
|
@@ -426,9 +457,22 @@ export const rollbackMessage = mutation({
|
|
|
426
457
|
handler: async (ctx, { messageId, error }) => {
|
|
427
458
|
const message = await ctx.db.get(messageId);
|
|
428
459
|
assert(message, `Message ${messageId} not found`);
|
|
460
|
+
// TODO: do BFS to fail all associated messages, then steps
|
|
461
|
+
// with parentMessageId of those messages, etc.
|
|
462
|
+
const steps = await ctx.db
|
|
463
|
+
.query("steps")
|
|
464
|
+
.withIndex("parentMessageId_order_stepOrder", (q) =>
|
|
465
|
+
// TODO: fetch pending, and commit later
|
|
466
|
+
q.eq("parentMessageId", messageId))
|
|
467
|
+
.collect();
|
|
468
|
+
for (const step of steps) {
|
|
469
|
+
if (step.status === "pending") {
|
|
470
|
+
await ctx.db.patch(step._id, { status: "failed" });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
429
473
|
await ctx.db.patch(messageId, {
|
|
430
474
|
status: "failed",
|
|
431
|
-
|
|
475
|
+
error: error,
|
|
432
476
|
});
|
|
433
477
|
},
|
|
434
478
|
});
|
|
@@ -537,15 +581,14 @@ export const searchMessages = action({
|
|
|
537
581
|
if (!VectorDimensions.includes(dimension)) {
|
|
538
582
|
throw new Error(`Unsupported vector dimension: ${dimension}`);
|
|
539
583
|
}
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
: q.eq("model_kind_threadId", [model, "thread", args.threadId]),
|
|
584
|
+
const vectors = (await searchVectors(ctx, args.vector, {
|
|
585
|
+
dimension,
|
|
586
|
+
model: args.vectorModel ?? "unknown",
|
|
587
|
+
table: "messages",
|
|
588
|
+
userId: args.userId,
|
|
589
|
+
threadId: args.threadId,
|
|
547
590
|
limit,
|
|
548
|
-
})).filter((v) => v._score > 0
|
|
591
|
+
})).filter((v) => v._score > (args.vectorScoreThreshold ?? 0));
|
|
549
592
|
// Reciprocal rank fusion
|
|
550
593
|
const k = 10;
|
|
551
594
|
const textEmbeddingIds = textSearchMessages?.map((m) => m.embeddingId);
|
|
@@ -553,7 +596,7 @@ export const searchMessages = action({
|
|
|
553
596
|
.map((v, i) => ({
|
|
554
597
|
id: v._id,
|
|
555
598
|
score: 1 / (i + k) +
|
|
556
|
-
1 / (textEmbeddingIds?.indexOf(v._id) ?? Infinity + k),
|
|
599
|
+
1 / ((textEmbeddingIds?.indexOf(v._id) ?? Infinity) + k),
|
|
557
600
|
}))
|
|
558
601
|
.sort((a, b) => b.score - a.score);
|
|
559
602
|
const vectorIds = vectorScores.slice(0, limit).map((v) => v.id);
|
|
@@ -590,9 +633,10 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
590
633
|
.query("messages")
|
|
591
634
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
592
635
|
.filter((q) => userId
|
|
593
|
-
? q.eq("userId", userId)
|
|
594
|
-
:
|
|
595
|
-
|
|
636
|
+
? q.eq(q.field("userId"), userId)
|
|
637
|
+
: q.eq(q.field("threadId"), threadId))
|
|
638
|
+
// Don't include pending. Failed messages hopefully are deleted but may as well be safe.
|
|
639
|
+
.filter((q) => q.eq(q.field("status"), "success"))
|
|
596
640
|
.first()))).filter((m) => m !== undefined && m !== null && (!parent || m.order <= parent.order));
|
|
597
641
|
messages.push(...(args.textSearchMessages ?? []));
|
|
598
642
|
// TODO: prioritize more recent messages
|
|
@@ -638,8 +682,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
638
682
|
.eq("threadId", m.threadId)
|
|
639
683
|
.eq("status", "success")
|
|
640
684
|
.eq("tool", false)
|
|
641
|
-
.
|
|
642
|
-
.
|
|
685
|
+
.gte("order", earliest)
|
|
686
|
+
.lte("order", latest))
|
|
643
687
|
.collect();
|
|
644
688
|
if (!ranges[searchId]) {
|
|
645
689
|
ranges[searchId] = [];
|
|
@@ -653,8 +697,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
653
697
|
.eq("userId", m.userId)
|
|
654
698
|
.eq("status", "success")
|
|
655
699
|
.eq("tool", false)
|
|
656
|
-
.
|
|
657
|
-
.
|
|
700
|
+
.gte("order", earliest)
|
|
701
|
+
.lte("order", latest))
|
|
658
702
|
.collect();
|
|
659
703
|
if (!ranges[searchId]) {
|
|
660
704
|
ranges[searchId] = [];
|
|
@@ -663,9 +707,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
663
707
|
}
|
|
664
708
|
}
|
|
665
709
|
}
|
|
666
|
-
|
|
667
|
-
.
|
|
668
|
-
|
|
710
|
+
for (const r of Object.values(ranges).flat()) {
|
|
711
|
+
if (!messages.includes(r)) {
|
|
712
|
+
messages.push(r);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return messages.sort((a, b) => a.order - b.order);
|
|
669
716
|
},
|
|
670
717
|
});
|
|
671
718
|
// returns ranges of messages in order of text search relevance,
|
|
@@ -689,153 +736,4 @@ export const textSearch = query({
|
|
|
689
736
|
},
|
|
690
737
|
returns: v.array(v.doc("messages")),
|
|
691
738
|
});
|
|
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
739
|
//# sourceMappingURL=messages.js.map
|