@convex-dev/agent 0.0.1-alpha.2 → 0.0.1-alpha.4
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 +91 -95
- package/dist/commonjs/client/index.d.ts +60 -60
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +80 -77
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/types.d.ts +1 -1
- package/dist/commonjs/client/types.d.ts.map +1 -1
- package/dist/commonjs/component/messages.d.ts +56 -56
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +128 -134
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +256 -256
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +18 -18
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/vector/index.d.ts +4 -4
- package/dist/commonjs/component/vector/index.d.ts.map +1 -1
- package/dist/commonjs/component/vector/index.js +12 -10
- package/dist/commonjs/component/vector/index.js.map +1 -1
- package/dist/commonjs/component/vector/tables.d.ts +3 -3
- package/dist/commonjs/component/vector/tables.js +6 -6
- package/dist/commonjs/component/vector/tables.js.map +1 -1
- package/dist/commonjs/validators.d.ts +5 -5
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +3 -3
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/index.d.ts +60 -60
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +80 -77
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/types.d.ts +1 -1
- package/dist/esm/client/types.d.ts.map +1 -1
- package/dist/esm/component/messages.d.ts +56 -56
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +128 -134
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +256 -256
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +18 -18
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/vector/index.d.ts +4 -4
- package/dist/esm/component/vector/index.d.ts.map +1 -1
- package/dist/esm/component/vector/index.js +12 -10
- package/dist/esm/component/vector/index.js.map +1 -1
- package/dist/esm/component/vector/tables.d.ts +3 -3
- package/dist/esm/component/vector/tables.js +6 -6
- package/dist/esm/component/vector/tables.js.map +1 -1
- package/dist/esm/validators.d.ts +5 -5
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +3 -3
- package/dist/esm/validators.js.map +1 -1
- package/package.json +2 -2
- package/src/client/index.ts +133 -127
- package/src/client/types.ts +1 -1
- package/src/component/_generated/api.d.ts +53 -53
- package/src/component/messages.ts +139 -145
- package/src/component/schema.ts +18 -18
- package/src/component/vector/index.ts +13 -11
- package/src/component/vector/tables.ts +6 -6
- package/src/validators.ts +6 -3
|
@@ -5,7 +5,7 @@ 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
|
+
vThreadStatus,
|
|
9
9
|
vMessageStatus,
|
|
10
10
|
vMessageWithFileAndId,
|
|
11
11
|
vSearchOptions,
|
|
@@ -30,27 +30,27 @@ import {
|
|
|
30
30
|
vVectorId,
|
|
31
31
|
} from "./vector/tables.js";
|
|
32
32
|
|
|
33
|
-
export const
|
|
34
|
-
args: {
|
|
33
|
+
export const getThread = query({
|
|
34
|
+
args: { threadId: v.id("threads") },
|
|
35
35
|
handler: async (ctx, args) => {
|
|
36
|
-
return ctx.db.get(args.
|
|
36
|
+
return ctx.db.get(args.threadId);
|
|
37
37
|
},
|
|
38
|
-
returns: v.union(v.doc("
|
|
38
|
+
returns: v.union(v.doc("threads"), v.null()),
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
export const
|
|
41
|
+
export const getThreadsByUserId = query({
|
|
42
42
|
args: {
|
|
43
43
|
userId: v.string(),
|
|
44
44
|
// Note: the other arguments cannot change from when the cursor was created.
|
|
45
45
|
cursor: v.optional(v.union(v.string(), v.null())),
|
|
46
46
|
limit: v.optional(v.number()),
|
|
47
47
|
offset: v.optional(v.number()),
|
|
48
|
-
statuses: v.optional(v.array(
|
|
48
|
+
statuses: v.optional(v.array(vThreadStatus)),
|
|
49
49
|
},
|
|
50
50
|
handler: async (ctx, args) => {
|
|
51
51
|
const streams = (args.statuses ?? ["active"]).map((status) =>
|
|
52
52
|
stream(ctx.db, schema)
|
|
53
|
-
.query("
|
|
53
|
+
.query("threads")
|
|
54
54
|
.withIndex("status_userId_order", (q) =>
|
|
55
55
|
q
|
|
56
56
|
.eq("status", status)
|
|
@@ -58,55 +58,55 @@ export const getChatsByUserId = query({
|
|
|
58
58
|
.gte("order", args.offset ?? 0)
|
|
59
59
|
)
|
|
60
60
|
);
|
|
61
|
-
const
|
|
61
|
+
const threads = await mergedStream(streams, ["order"]).paginate({
|
|
62
62
|
numItems: args.limit ?? 100,
|
|
63
63
|
cursor: args.cursor ?? null,
|
|
64
64
|
});
|
|
65
65
|
return {
|
|
66
|
-
|
|
67
|
-
continueCursor:
|
|
68
|
-
isDone:
|
|
66
|
+
threads: threads.page,
|
|
67
|
+
continueCursor: threads.continueCursor,
|
|
68
|
+
isDone: threads.isDone,
|
|
69
69
|
};
|
|
70
70
|
},
|
|
71
71
|
returns: v.object({
|
|
72
|
-
|
|
72
|
+
threads: v.array(v.doc("threads")),
|
|
73
73
|
continueCursor: v.string(),
|
|
74
74
|
isDone: v.boolean(),
|
|
75
75
|
}),
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
const
|
|
79
|
-
const statuses =
|
|
78
|
+
const vThread = schema.tables.threads.validator;
|
|
79
|
+
const statuses = vThread.fields.status.members.map((m) => m.value);
|
|
80
80
|
|
|
81
|
-
export const
|
|
82
|
-
args: omit(
|
|
81
|
+
export const createThread = mutation({
|
|
82
|
+
args: omit(vThread.fields, ["order", "status"]),
|
|
83
83
|
handler: async (ctx, args) => {
|
|
84
84
|
const streams = statuses.map((status) =>
|
|
85
85
|
stream(ctx.db, schema)
|
|
86
|
-
.query("
|
|
86
|
+
.query("threads")
|
|
87
87
|
.withIndex("status_userId_order", (q) =>
|
|
88
88
|
q.eq("status", status).eq("userId", args.userId)
|
|
89
89
|
)
|
|
90
90
|
.order("desc")
|
|
91
91
|
);
|
|
92
|
-
const
|
|
93
|
-
const order = (
|
|
94
|
-
const
|
|
92
|
+
const latestThread = await mergedStream(streams, ["order"]).first();
|
|
93
|
+
const order = (latestThread?.order ?? -1) + 1;
|
|
94
|
+
const threadId = await ctx.db.insert("threads", {
|
|
95
95
|
...args,
|
|
96
96
|
order,
|
|
97
97
|
status: "active",
|
|
98
98
|
});
|
|
99
|
-
return (await ctx.db.get(
|
|
99
|
+
return (await ctx.db.get(threadId))!;
|
|
100
100
|
},
|
|
101
|
-
returns: v.doc("
|
|
101
|
+
returns: v.doc("threads"),
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
export const
|
|
104
|
+
export const updateThread = mutation({
|
|
105
105
|
args: {
|
|
106
|
-
|
|
106
|
+
threadId: v.id("threads"),
|
|
107
107
|
patch: v.object(
|
|
108
108
|
partial(
|
|
109
|
-
pick(
|
|
109
|
+
pick(vThread.fields, [
|
|
110
110
|
"title",
|
|
111
111
|
"summary",
|
|
112
112
|
"defaultSystemPrompt",
|
|
@@ -116,43 +116,43 @@ export const updateChat = mutation({
|
|
|
116
116
|
),
|
|
117
117
|
},
|
|
118
118
|
handler: async (ctx, args) => {
|
|
119
|
-
const
|
|
120
|
-
assert(
|
|
121
|
-
await ctx.db.patch(args.
|
|
122
|
-
return (await ctx.db.get(args.
|
|
119
|
+
const thread = await ctx.db.get(args.threadId);
|
|
120
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
121
|
+
await ctx.db.patch(args.threadId, args.patch);
|
|
122
|
+
return (await ctx.db.get(args.threadId))!;
|
|
123
123
|
},
|
|
124
|
-
returns: v.doc("
|
|
124
|
+
returns: v.doc("threads"),
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
export const
|
|
128
|
-
args: {
|
|
127
|
+
export const archiveThread = mutation({
|
|
128
|
+
args: { threadId: v.id("threads") },
|
|
129
129
|
handler: async (ctx, args) => {
|
|
130
|
-
const
|
|
131
|
-
assert(
|
|
132
|
-
await ctx.db.patch(args.
|
|
133
|
-
return (await ctx.db.get(args.
|
|
130
|
+
const thread = await ctx.db.get(args.threadId);
|
|
131
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
132
|
+
await ctx.db.patch(args.threadId, { status: "archived" });
|
|
133
|
+
return (await ctx.db.get(args.threadId))!;
|
|
134
134
|
},
|
|
135
|
-
returns: v.doc("
|
|
135
|
+
returns: v.doc("threads"),
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
export const deleteAllForUserId = action({
|
|
139
139
|
args: { userId: v.string() },
|
|
140
140
|
handler: async (ctx, args) => {
|
|
141
141
|
let messagesCursor = null;
|
|
142
|
-
let
|
|
142
|
+
let threadsCursor = null;
|
|
143
143
|
let isDone = false;
|
|
144
144
|
while (!isDone) {
|
|
145
145
|
const result: {
|
|
146
146
|
messagesCursor: string;
|
|
147
|
-
|
|
147
|
+
threadsCursor: string | null;
|
|
148
148
|
isDone: boolean;
|
|
149
149
|
} = await ctx.runMutation(internal.messages._deletePageForUserId, {
|
|
150
150
|
userId: args.userId,
|
|
151
151
|
messagesCursor,
|
|
152
|
-
|
|
152
|
+
threadsCursor,
|
|
153
153
|
});
|
|
154
154
|
messagesCursor = result.messagesCursor;
|
|
155
|
-
|
|
155
|
+
threadsCursor = result.threadsCursor;
|
|
156
156
|
isDone = result.isDone;
|
|
157
157
|
}
|
|
158
158
|
},
|
|
@@ -167,7 +167,7 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
167
167
|
const isDone = await deleteAllFroUserIdAsyncHandler(ctx, {
|
|
168
168
|
userId: args.userId,
|
|
169
169
|
messagesCursor: null,
|
|
170
|
-
|
|
170
|
+
threadsCursor: null,
|
|
171
171
|
});
|
|
172
172
|
return isDone;
|
|
173
173
|
},
|
|
@@ -177,12 +177,12 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
177
177
|
const deleteAllArgs = {
|
|
178
178
|
userId: v.string(),
|
|
179
179
|
messagesCursor: nullable(v.string()),
|
|
180
|
-
|
|
180
|
+
threadsCursor: nullable(v.string()),
|
|
181
181
|
};
|
|
182
182
|
type DeleteAllArgs = ObjectType<typeof deleteAllArgs>;
|
|
183
183
|
const deleteAllReturns = {
|
|
184
184
|
messagesCursor: v.string(),
|
|
185
|
-
|
|
185
|
+
threadsCursor: nullable(v.string()),
|
|
186
186
|
isDone: v.boolean(),
|
|
187
187
|
};
|
|
188
188
|
type DeleteAllReturns = ObjectType<typeof deleteAllReturns>;
|
|
@@ -205,7 +205,7 @@ async function deleteAllFroUserIdAsyncHandler(
|
|
|
205
205
|
{
|
|
206
206
|
userId: args.userId,
|
|
207
207
|
messagesCursor: result.messagesCursor,
|
|
208
|
-
|
|
208
|
+
threadsCursor: result.threadsCursor,
|
|
209
209
|
}
|
|
210
210
|
);
|
|
211
211
|
}
|
|
@@ -223,20 +223,20 @@ async function deletePageForUserId(
|
|
|
223
223
|
): Promise<DeleteAllReturns> {
|
|
224
224
|
const streams = statuses.map((status) =>
|
|
225
225
|
stream(ctx.db, schema)
|
|
226
|
-
.query("
|
|
226
|
+
.query("threads")
|
|
227
227
|
.withIndex("status_userId_order", (q) =>
|
|
228
228
|
q.eq("status", status).eq("userId", args.userId)
|
|
229
229
|
)
|
|
230
230
|
.order("desc")
|
|
231
231
|
);
|
|
232
|
-
const
|
|
233
|
-
const messages = await
|
|
232
|
+
const threadStreams = mergedStream(streams, ["order"]);
|
|
233
|
+
const messages = await threadStreams
|
|
234
234
|
.flatMap(
|
|
235
235
|
async (c) =>
|
|
236
236
|
stream(ctx.db, schema)
|
|
237
237
|
.query("messages")
|
|
238
|
-
.withIndex("
|
|
239
|
-
q.eq("
|
|
238
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
239
|
+
q.eq("threadId", c._id).eq("status", "success")
|
|
240
240
|
),
|
|
241
241
|
["tool", "order", "stepOrder"]
|
|
242
242
|
)
|
|
@@ -246,20 +246,20 @@ async function deletePageForUserId(
|
|
|
246
246
|
});
|
|
247
247
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
248
248
|
if (messages.isDone) {
|
|
249
|
-
const
|
|
249
|
+
const threads = await threadStreams.paginate({
|
|
250
250
|
numItems: 100,
|
|
251
|
-
cursor: args.
|
|
251
|
+
cursor: args.threadsCursor ?? null,
|
|
252
252
|
});
|
|
253
|
-
await Promise.all(
|
|
253
|
+
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
254
254
|
return {
|
|
255
255
|
messagesCursor: messages.continueCursor,
|
|
256
|
-
|
|
257
|
-
isDone:
|
|
256
|
+
threadsCursor: threads.continueCursor,
|
|
257
|
+
isDone: threads.isDone,
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
260
|
return {
|
|
261
261
|
messagesCursor: messages.continueCursor,
|
|
262
|
-
|
|
262
|
+
threadsCursor: null,
|
|
263
263
|
isDone: messages.isDone,
|
|
264
264
|
};
|
|
265
265
|
}
|
|
@@ -274,66 +274,66 @@ async function deleteMessage(ctx: MutationCtx, messageDoc: Doc<"messages">) {
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
const
|
|
278
|
-
|
|
277
|
+
const deleteThreadArgs = {
|
|
278
|
+
threadId: v.id("threads"),
|
|
279
279
|
cursor: v.optional(v.string()),
|
|
280
280
|
limit: v.optional(v.number()),
|
|
281
281
|
};
|
|
282
|
-
type
|
|
283
|
-
const
|
|
282
|
+
type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
|
|
283
|
+
const deleteThreadReturns = {
|
|
284
284
|
cursor: v.string(),
|
|
285
285
|
isDone: v.boolean(),
|
|
286
286
|
};
|
|
287
|
-
type
|
|
287
|
+
type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
|
|
288
288
|
|
|
289
|
-
export const
|
|
290
|
-
args:
|
|
289
|
+
export const deleteAllForThreadIdSync = action({
|
|
290
|
+
args: deleteThreadArgs,
|
|
291
291
|
handler: async (ctx, args) => {
|
|
292
|
-
const result:
|
|
293
|
-
internal.messages.
|
|
294
|
-
{
|
|
292
|
+
const result: DeleteThreadReturns = await ctx.runMutation(
|
|
293
|
+
internal.messages._deletePageForThreadId,
|
|
294
|
+
{ threadId: args.threadId, cursor: args.cursor, limit: args.limit }
|
|
295
295
|
);
|
|
296
296
|
return result;
|
|
297
297
|
},
|
|
298
|
-
returns:
|
|
298
|
+
returns: deleteThreadReturns,
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
-
export const
|
|
302
|
-
args:
|
|
301
|
+
export const deleteAllForThreadIdAsync = mutation({
|
|
302
|
+
args: deleteThreadArgs,
|
|
303
303
|
handler: async (ctx, args) => {
|
|
304
|
-
const result = await
|
|
304
|
+
const result = await deletePageForThreadIdHandler(ctx, args);
|
|
305
305
|
if (!result.isDone) {
|
|
306
|
-
await ctx.scheduler.runAfter(0, api.messages.
|
|
307
|
-
|
|
306
|
+
await ctx.scheduler.runAfter(0, api.messages.deleteAllForThreadIdAsync, {
|
|
307
|
+
threadId: args.threadId,
|
|
308
308
|
cursor: result.cursor,
|
|
309
309
|
});
|
|
310
310
|
}
|
|
311
311
|
return result;
|
|
312
312
|
},
|
|
313
|
-
returns:
|
|
313
|
+
returns: deleteThreadReturns,
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
-
export const
|
|
317
|
-
args:
|
|
318
|
-
handler:
|
|
319
|
-
returns:
|
|
316
|
+
export const _deletePageForThreadId = internalMutation({
|
|
317
|
+
args: deleteThreadArgs,
|
|
318
|
+
handler: deletePageForThreadIdHandler,
|
|
319
|
+
returns: deleteThreadReturns,
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
-
async function
|
|
322
|
+
async function deletePageForThreadIdHandler(
|
|
323
323
|
ctx: MutationCtx,
|
|
324
|
-
args:
|
|
325
|
-
): Promise<
|
|
324
|
+
args: DeleteThreadArgs
|
|
325
|
+
): Promise<DeleteThreadReturns> {
|
|
326
326
|
const messages = await stream(ctx.db, schema)
|
|
327
327
|
.query("messages")
|
|
328
|
-
.withIndex("
|
|
329
|
-
q.eq("
|
|
328
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
329
|
+
q.eq("threadId", args.threadId).eq("status", "success")
|
|
330
330
|
)
|
|
331
331
|
.paginate({
|
|
332
332
|
numItems: args.limit ?? 100,
|
|
333
333
|
cursor: args.cursor ?? null,
|
|
334
334
|
});
|
|
335
335
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
336
|
-
await ctx.db.delete(args.
|
|
336
|
+
await ctx.db.delete(args.threadId);
|
|
337
337
|
return {
|
|
338
338
|
cursor: messages.continueCursor,
|
|
339
339
|
isDone: messages.isDone,
|
|
@@ -373,7 +373,7 @@ export const messageStatuses = vMessageDoc.fields.status.members.map(
|
|
|
373
373
|
|
|
374
374
|
const addMessagesArgs = {
|
|
375
375
|
userId: v.optional(v.string()),
|
|
376
|
-
|
|
376
|
+
threadId: v.optional(v.id("threads")),
|
|
377
377
|
stepId: v.optional(v.id("steps")),
|
|
378
378
|
parentMessageId: v.optional(v.id("messages")),
|
|
379
379
|
messages: v.array(vMessageWithFileAndId),
|
|
@@ -395,21 +395,21 @@ async function addMessagesHandler(
|
|
|
395
395
|
args: ObjectType<typeof addMessagesArgs>
|
|
396
396
|
) {
|
|
397
397
|
let userId = args.userId;
|
|
398
|
-
const
|
|
399
|
-
if (!userId && args.
|
|
400
|
-
const
|
|
401
|
-
assert(
|
|
402
|
-
userId =
|
|
398
|
+
const threadId = args.threadId;
|
|
399
|
+
if (!userId && args.threadId) {
|
|
400
|
+
const thread = await ctx.db.get(args.threadId);
|
|
401
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
402
|
+
userId = thread._id;
|
|
403
403
|
}
|
|
404
404
|
const { failPendingSteps, pending, messages, parentMessageId, ...rest } =
|
|
405
405
|
args;
|
|
406
406
|
const parent = parentMessageId && (await ctx.db.get(parentMessageId));
|
|
407
407
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
408
|
-
assert(args.
|
|
408
|
+
assert(args.threadId, "threadId is required to fail pending steps");
|
|
409
409
|
const pendingMessages = await ctx.db
|
|
410
410
|
.query("messages")
|
|
411
|
-
.withIndex("
|
|
412
|
-
q.eq("
|
|
411
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
412
|
+
q.eq("threadId", threadId).eq("status", "pending")
|
|
413
413
|
)
|
|
414
414
|
.collect();
|
|
415
415
|
await Promise.all(
|
|
@@ -418,13 +418,7 @@ async function addMessagesHandler(
|
|
|
418
418
|
)
|
|
419
419
|
);
|
|
420
420
|
}
|
|
421
|
-
|
|
422
|
-
const maxMessage = await getMaxMessage(ctx, chatId, userId);
|
|
423
|
-
// If the previous message isn't our parent, we make a new thread.
|
|
424
|
-
threadId =
|
|
425
|
-
parentMessageId && maxMessage?._id === parentMessageId
|
|
426
|
-
? maxMessage.threadId ?? parentMessageId
|
|
427
|
-
: parentMessageId;
|
|
421
|
+
const maxMessage = await getMaxMessage(ctx, threadId, userId);
|
|
428
422
|
let order = maxMessage?.order ?? -1;
|
|
429
423
|
const toReturn: Doc<"messages">[] = [];
|
|
430
424
|
if (messages.length > 0) {
|
|
@@ -436,7 +430,7 @@ async function addMessagesHandler(
|
|
|
436
430
|
const text = extractText(message);
|
|
437
431
|
const messageId = await ctx.db.insert("messages", {
|
|
438
432
|
...rest,
|
|
439
|
-
|
|
433
|
+
parentMessageId,
|
|
440
434
|
userId,
|
|
441
435
|
message,
|
|
442
436
|
id,
|
|
@@ -454,17 +448,17 @@ async function addMessagesHandler(
|
|
|
454
448
|
|
|
455
449
|
async function getMaxMessage(
|
|
456
450
|
ctx: QueryCtx,
|
|
457
|
-
|
|
451
|
+
threadId: Id<"threads"> | undefined,
|
|
458
452
|
userId: string | undefined
|
|
459
453
|
) {
|
|
460
|
-
assert(
|
|
461
|
-
if (
|
|
454
|
+
assert(threadId || userId, "One of threadId or userId is required");
|
|
455
|
+
if (threadId) {
|
|
462
456
|
return mergedStream(
|
|
463
457
|
["success" as const, "pending" as const].map((status) =>
|
|
464
458
|
stream(ctx.db, schema)
|
|
465
459
|
.query("messages")
|
|
466
|
-
.withIndex("
|
|
467
|
-
q.eq("
|
|
460
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
461
|
+
q.eq("threadId", threadId).eq("status", status).eq("tool", false)
|
|
468
462
|
)
|
|
469
463
|
.order("desc")
|
|
470
464
|
),
|
|
@@ -486,7 +480,7 @@ async function getMaxMessage(
|
|
|
486
480
|
}
|
|
487
481
|
|
|
488
482
|
const addStepsArgs = {
|
|
489
|
-
|
|
483
|
+
threadId: v.id("threads"),
|
|
490
484
|
messageId: v.id("messages"),
|
|
491
485
|
steps: v.array(vStepWithMessagesWithFileAndId),
|
|
492
486
|
failPendingSteps: v.optional(v.boolean()),
|
|
@@ -523,7 +517,7 @@ async function addStepsHandler(
|
|
|
523
517
|
let nextStepOrder = (steps.at(-1)?.stepOrder ?? -1) + 1;
|
|
524
518
|
for (const { step, messages } of args.steps) {
|
|
525
519
|
const stepId = await ctx.db.insert("steps", {
|
|
526
|
-
|
|
520
|
+
threadId: args.threadId,
|
|
527
521
|
parentMessageId: args.messageId,
|
|
528
522
|
order,
|
|
529
523
|
stepOrder: nextStepOrder,
|
|
@@ -531,7 +525,7 @@ async function addStepsHandler(
|
|
|
531
525
|
step,
|
|
532
526
|
});
|
|
533
527
|
await addMessagesHandler(ctx, {
|
|
534
|
-
|
|
528
|
+
threadId: args.threadId,
|
|
535
529
|
parentMessageId: args.messageId,
|
|
536
530
|
stepId,
|
|
537
531
|
messages,
|
|
@@ -596,9 +590,9 @@ async function commitMessageHandler(
|
|
|
596
590
|
[true, false].map((tool) =>
|
|
597
591
|
stream(ctx.db, schema)
|
|
598
592
|
.query("messages")
|
|
599
|
-
.withIndex("
|
|
593
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
600
594
|
q
|
|
601
|
-
.eq("
|
|
595
|
+
.eq("threadId", message.threadId)
|
|
602
596
|
.eq("status", "pending")
|
|
603
597
|
.eq("tool", tool)
|
|
604
598
|
.eq("order", order)
|
|
@@ -612,9 +606,9 @@ async function commitMessageHandler(
|
|
|
612
606
|
}
|
|
613
607
|
}
|
|
614
608
|
|
|
615
|
-
export const
|
|
609
|
+
export const getThreadMessages = query({
|
|
616
610
|
args: {
|
|
617
|
-
|
|
611
|
+
threadId: v.id("threads"),
|
|
618
612
|
isTool: v.optional(v.boolean()),
|
|
619
613
|
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
620
614
|
limit: v.optional(v.number()),
|
|
@@ -634,9 +628,9 @@ export const getChatMessages = query({
|
|
|
634
628
|
statuses.map((status) =>
|
|
635
629
|
stream(ctx.db, schema)
|
|
636
630
|
.query("messages")
|
|
637
|
-
.withIndex("
|
|
631
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => {
|
|
638
632
|
const qq = q
|
|
639
|
-
.eq("
|
|
633
|
+
.eq("threadId", args.threadId)
|
|
640
634
|
.eq("status", status)
|
|
641
635
|
.eq("tool", tool);
|
|
642
636
|
if (parent) {
|
|
@@ -670,19 +664,19 @@ export const getChatMessages = query({
|
|
|
670
664
|
export const searchMessages = action({
|
|
671
665
|
args: {
|
|
672
666
|
userId: v.optional(v.string()),
|
|
673
|
-
|
|
667
|
+
threadId: v.optional(v.id("threads")),
|
|
674
668
|
parentMessageId: v.optional(v.id("messages")),
|
|
675
669
|
...vSearchOptions.fields,
|
|
676
670
|
},
|
|
677
671
|
returns: v.array(v.doc("messages")),
|
|
678
672
|
handler: async (ctx, args): Promise<Doc<"messages">[]> => {
|
|
679
|
-
assert(args.userId || args.
|
|
673
|
+
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
680
674
|
const limit = args.limit;
|
|
681
675
|
let textSearchMessages: Doc<"messages">[] | undefined;
|
|
682
676
|
if (args.text) {
|
|
683
677
|
textSearchMessages = await ctx.runQuery(api.messages.textSearch, {
|
|
684
678
|
userId: args.userId,
|
|
685
|
-
|
|
679
|
+
threadId: args.threadId,
|
|
686
680
|
text: args.text,
|
|
687
681
|
limit,
|
|
688
682
|
});
|
|
@@ -699,8 +693,8 @@ export const searchMessages = action({
|
|
|
699
693
|
vector: args.vector,
|
|
700
694
|
filter: (q) =>
|
|
701
695
|
args.userId
|
|
702
|
-
? q.eq("model_kind_userId", [model, "
|
|
703
|
-
: q.eq("
|
|
696
|
+
? q.eq("model_kind_userId", [model, "thread", args.userId])
|
|
697
|
+
: q.eq("model_kind_threadId", [model, "thread", args.threadId!]),
|
|
704
698
|
limit,
|
|
705
699
|
})
|
|
706
700
|
).filter((v) => v._score > 0.5);
|
|
@@ -721,7 +715,7 @@ export const searchMessages = action({
|
|
|
721
715
|
internal.messages._fetchVectorMessages,
|
|
722
716
|
{
|
|
723
717
|
userId: args.userId,
|
|
724
|
-
|
|
718
|
+
threadId: args.threadId,
|
|
725
719
|
vectorIds,
|
|
726
720
|
textSearchMessages: textSearchMessages?.filter(
|
|
727
721
|
(m) => !vectorIds.includes(m.embeddingId!)
|
|
@@ -740,7 +734,7 @@ export const searchMessages = action({
|
|
|
740
734
|
export const _fetchVectorMessages = internalQuery({
|
|
741
735
|
args: {
|
|
742
736
|
userId: v.optional(v.string()),
|
|
743
|
-
|
|
737
|
+
threadId: v.optional(v.id("threads")),
|
|
744
738
|
vectorIds: v.array(vVectorId),
|
|
745
739
|
textSearchMessages: v.optional(v.array(v.doc("messages"))),
|
|
746
740
|
messageRange: v.object({ before: v.number(), after: v.number() }),
|
|
@@ -751,8 +745,8 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
751
745
|
handler: async (ctx, args): Promise<Doc<"messages">[]> => {
|
|
752
746
|
const parent =
|
|
753
747
|
args.parentMessageId && (await ctx.db.get(args.parentMessageId));
|
|
754
|
-
const { userId,
|
|
755
|
-
assert(userId ||
|
|
748
|
+
const { userId, threadId } = args;
|
|
749
|
+
assert(userId || threadId, "Specify userId or threadId to search");
|
|
756
750
|
let messages = (
|
|
757
751
|
await Promise.all(
|
|
758
752
|
args.vectorIds.map((embeddingId) =>
|
|
@@ -763,7 +757,7 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
763
757
|
userId
|
|
764
758
|
? q.eq("userId", userId)
|
|
765
759
|
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
766
|
-
q.eq("
|
|
760
|
+
q.eq("threadId", threadId as any)
|
|
767
761
|
)
|
|
768
762
|
.first()
|
|
769
763
|
)
|
|
@@ -777,12 +771,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
777
771
|
messages.sort((a, b) => a.order! - b.order!);
|
|
778
772
|
messages = messages.slice(0, args.limit);
|
|
779
773
|
// Fetch the surrounding messages
|
|
780
|
-
if (!
|
|
774
|
+
if (!threadId) {
|
|
781
775
|
return messages.sort((a, b) => a.order - b.order);
|
|
782
776
|
}
|
|
783
777
|
const included: Record<string, Set<number>> = {};
|
|
784
778
|
for (const m of messages) {
|
|
785
|
-
const searchId = m.
|
|
779
|
+
const searchId = m.threadId ?? m.userId!;
|
|
786
780
|
if (!included[searchId]) {
|
|
787
781
|
included[searchId] = new Set();
|
|
788
782
|
}
|
|
@@ -791,7 +785,7 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
791
785
|
const ranges: Record<string, Doc<"messages">[]> = {};
|
|
792
786
|
const { before, after } = args.messageRange;
|
|
793
787
|
for (const m of messages) {
|
|
794
|
-
const searchId = m.
|
|
788
|
+
const searchId = m.threadId ?? m.userId!;
|
|
795
789
|
const order = m.order!;
|
|
796
790
|
let earliest = order - before;
|
|
797
791
|
let latest = order + after;
|
|
@@ -809,12 +803,12 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
809
803
|
included[searchId].add(i);
|
|
810
804
|
}
|
|
811
805
|
if (earliest !== latest) {
|
|
812
|
-
if (m.
|
|
806
|
+
if (m.threadId) {
|
|
813
807
|
const surrounding = await ctx.db
|
|
814
808
|
.query("messages")
|
|
815
|
-
.withIndex("
|
|
809
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
816
810
|
q
|
|
817
|
-
.eq("
|
|
811
|
+
.eq("threadId", m.threadId)
|
|
818
812
|
.eq("status", "success")
|
|
819
813
|
.eq("tool", false)
|
|
820
814
|
.gt("order", earliest)
|
|
@@ -854,19 +848,19 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
854
848
|
// excluding duplicates in later ranges.
|
|
855
849
|
export const textSearch = query({
|
|
856
850
|
args: {
|
|
857
|
-
|
|
851
|
+
threadId: v.optional(v.id("threads")),
|
|
858
852
|
userId: v.optional(v.string()),
|
|
859
853
|
text: v.string(),
|
|
860
854
|
limit: v.number(),
|
|
861
855
|
},
|
|
862
856
|
handler: async (ctx, args) => {
|
|
863
|
-
assert(args.userId || args.
|
|
857
|
+
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
864
858
|
const messages = await ctx.db
|
|
865
859
|
.query("messages")
|
|
866
860
|
.withSearchIndex("text_search", (q) =>
|
|
867
861
|
args.userId
|
|
868
862
|
? q.search("text", args.text).eq("userId", args.userId)
|
|
869
|
-
: q.search("text", args.text).eq("
|
|
863
|
+
: q.search("text", args.text).eq("threadId", args.threadId!)
|
|
870
864
|
)
|
|
871
865
|
.take(args.limit);
|
|
872
866
|
return messages;
|
|
@@ -919,9 +913,9 @@ export const textSearch = query({
|
|
|
919
913
|
|
|
920
914
|
// const DEFAULT_MESSAGES_LIMIT = 40; // What pg & upstash do too.
|
|
921
915
|
|
|
922
|
-
// export const
|
|
916
|
+
// export const getThreadMessagesPage = query({
|
|
923
917
|
// args: {
|
|
924
|
-
//
|
|
918
|
+
// parentMessageId: v.string(),
|
|
925
919
|
// selectBy: v.optional(vSelectBy),
|
|
926
920
|
// // Unimplemented and as far I can tell no storage provider has either.
|
|
927
921
|
// // memoryConfig: v.optional(vMemoryConfig),
|
|
@@ -929,7 +923,7 @@ export const textSearch = query({
|
|
|
929
923
|
// handler: async (ctx, args): Promise<SerializedMessage[]> => {
|
|
930
924
|
// const messages = await ctx.db
|
|
931
925
|
// .query("messages")
|
|
932
|
-
// .withIndex("
|
|
926
|
+
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", args.parentMessageId))
|
|
933
927
|
// .order("desc")
|
|
934
928
|
// .take(args.selectBy?.last ? args.selectBy.last : DEFAULT_MESSAGES_LIMIT);
|
|
935
929
|
|
|
@@ -983,9 +977,9 @@ export const textSearch = query({
|
|
|
983
977
|
// ranges.map(async (range) => {
|
|
984
978
|
// return await ctx.db
|
|
985
979
|
// .query("messages")
|
|
986
|
-
// .withIndex("
|
|
980
|
+
// .withIndex("parentMessageId", (q) =>
|
|
987
981
|
// q
|
|
988
|
-
// .eq("
|
|
982
|
+
// .eq("parentMessageId", args.parentMessageId)
|
|
989
983
|
// .gte("threadOrder", range.start)
|
|
990
984
|
// .lte("threadOrder", range.end)
|
|
991
985
|
// )
|
|
@@ -1002,21 +996,21 @@ export const textSearch = query({
|
|
|
1002
996
|
// export const saveMessages = mutation({
|
|
1003
997
|
// args: { messages: v.array(vSerializedMessage) },
|
|
1004
998
|
// handler: async (ctx, args) => {
|
|
1005
|
-
// const
|
|
999
|
+
// const messagesByParentMessageId: Record<string, SerializedMessage[]> = {};
|
|
1006
1000
|
// for (const message of args.messages) {
|
|
1007
|
-
//
|
|
1008
|
-
// ...(
|
|
1001
|
+
// messagesByParentMessageId[message.parentMessageId] = [
|
|
1002
|
+
// ...(messagesByParentMessageId[message.parentMessageId] ?? []),
|
|
1009
1003
|
// message,
|
|
1010
1004
|
// ];
|
|
1011
1005
|
// }
|
|
1012
|
-
// for (const
|
|
1006
|
+
// for (const parentMessageId in messagesByParentMessageId) {
|
|
1013
1007
|
// const lastMessage = await ctx.db
|
|
1014
1008
|
// .query("messages")
|
|
1015
|
-
// .withIndex("
|
|
1009
|
+
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", parentMessageId))
|
|
1016
1010
|
// .order("desc")
|
|
1017
1011
|
// .first();
|
|
1018
1012
|
// let threadOrder = lastMessage?.threadOrder ?? 0;
|
|
1019
|
-
// for (const message of
|
|
1013
|
+
// for (const message of messagesByParentMessageId[parentMessageId]) {
|
|
1020
1014
|
// threadOrder++;
|
|
1021
1015
|
// await ctx.db.insert("messages", {
|
|
1022
1016
|
// ...message,
|