@convex-dev/agent 0.0.1-alpha.2 → 0.0.1-alpha.3
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
|
@@ -3,74 +3,74 @@ 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 { vThreadStatus, vMessageStatus, vMessageWithFileAndId, vSearchOptions, vStepWithMessagesWithFileAndId, } 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
10
|
import { getVectorTableName, VectorDimensions, vVectorId, } from "./vector/tables.js";
|
|
11
|
-
export const
|
|
12
|
-
args: {
|
|
11
|
+
export const getThread = query({
|
|
12
|
+
args: { threadId: v.id("threads") },
|
|
13
13
|
handler: async (ctx, args) => {
|
|
14
|
-
return ctx.db.get(args.
|
|
14
|
+
return ctx.db.get(args.threadId);
|
|
15
15
|
},
|
|
16
|
-
returns: v.union(v.doc("
|
|
16
|
+
returns: v.union(v.doc("threads"), v.null()),
|
|
17
17
|
});
|
|
18
|
-
export const
|
|
18
|
+
export const getThreadsByUserId = query({
|
|
19
19
|
args: {
|
|
20
20
|
userId: v.string(),
|
|
21
21
|
// Note: the other arguments cannot change from when the cursor was created.
|
|
22
22
|
cursor: v.optional(v.union(v.string(), v.null())),
|
|
23
23
|
limit: v.optional(v.number()),
|
|
24
24
|
offset: v.optional(v.number()),
|
|
25
|
-
statuses: v.optional(v.array(
|
|
25
|
+
statuses: v.optional(v.array(vThreadStatus)),
|
|
26
26
|
},
|
|
27
27
|
handler: async (ctx, args) => {
|
|
28
28
|
const streams = (args.statuses ?? ["active"]).map((status) => stream(ctx.db, schema)
|
|
29
|
-
.query("
|
|
29
|
+
.query("threads")
|
|
30
30
|
.withIndex("status_userId_order", (q) => q
|
|
31
31
|
.eq("status", status)
|
|
32
32
|
.eq("userId", args.userId)
|
|
33
33
|
.gte("order", args.offset ?? 0)));
|
|
34
|
-
const
|
|
34
|
+
const threads = await mergedStream(streams, ["order"]).paginate({
|
|
35
35
|
numItems: args.limit ?? 100,
|
|
36
36
|
cursor: args.cursor ?? null,
|
|
37
37
|
});
|
|
38
38
|
return {
|
|
39
|
-
|
|
40
|
-
continueCursor:
|
|
41
|
-
isDone:
|
|
39
|
+
threads: threads.page,
|
|
40
|
+
continueCursor: threads.continueCursor,
|
|
41
|
+
isDone: threads.isDone,
|
|
42
42
|
};
|
|
43
43
|
},
|
|
44
44
|
returns: v.object({
|
|
45
|
-
|
|
45
|
+
threads: v.array(v.doc("threads")),
|
|
46
46
|
continueCursor: v.string(),
|
|
47
47
|
isDone: v.boolean(),
|
|
48
48
|
}),
|
|
49
49
|
});
|
|
50
|
-
const
|
|
51
|
-
const statuses =
|
|
52
|
-
export const
|
|
53
|
-
args: omit(
|
|
50
|
+
const vThread = schema.tables.threads.validator;
|
|
51
|
+
const statuses = vThread.fields.status.members.map((m) => m.value);
|
|
52
|
+
export const createThread = mutation({
|
|
53
|
+
args: omit(vThread.fields, ["order", "status"]),
|
|
54
54
|
handler: async (ctx, args) => {
|
|
55
55
|
const streams = statuses.map((status) => stream(ctx.db, schema)
|
|
56
|
-
.query("
|
|
56
|
+
.query("threads")
|
|
57
57
|
.withIndex("status_userId_order", (q) => q.eq("status", status).eq("userId", args.userId))
|
|
58
58
|
.order("desc"));
|
|
59
|
-
const
|
|
60
|
-
const order = (
|
|
61
|
-
const
|
|
59
|
+
const latestThread = await mergedStream(streams, ["order"]).first();
|
|
60
|
+
const order = (latestThread?.order ?? -1) + 1;
|
|
61
|
+
const threadId = await ctx.db.insert("threads", {
|
|
62
62
|
...args,
|
|
63
63
|
order,
|
|
64
64
|
status: "active",
|
|
65
65
|
});
|
|
66
|
-
return (await ctx.db.get(
|
|
66
|
+
return (await ctx.db.get(threadId));
|
|
67
67
|
},
|
|
68
|
-
returns: v.doc("
|
|
68
|
+
returns: v.doc("threads"),
|
|
69
69
|
});
|
|
70
|
-
export const
|
|
70
|
+
export const updateThread = mutation({
|
|
71
71
|
args: {
|
|
72
|
-
|
|
73
|
-
patch: v.object(partial(pick(
|
|
72
|
+
threadId: v.id("threads"),
|
|
73
|
+
patch: v.object(partial(pick(vThread.fields, [
|
|
74
74
|
"title",
|
|
75
75
|
"summary",
|
|
76
76
|
"defaultSystemPrompt",
|
|
@@ -78,37 +78,37 @@ export const updateChat = mutation({
|
|
|
78
78
|
]))),
|
|
79
79
|
},
|
|
80
80
|
handler: async (ctx, args) => {
|
|
81
|
-
const
|
|
82
|
-
assert(
|
|
83
|
-
await ctx.db.patch(args.
|
|
84
|
-
return (await ctx.db.get(args.
|
|
81
|
+
const thread = await ctx.db.get(args.threadId);
|
|
82
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
83
|
+
await ctx.db.patch(args.threadId, args.patch);
|
|
84
|
+
return (await ctx.db.get(args.threadId));
|
|
85
85
|
},
|
|
86
|
-
returns: v.doc("
|
|
86
|
+
returns: v.doc("threads"),
|
|
87
87
|
});
|
|
88
|
-
export const
|
|
89
|
-
args: {
|
|
88
|
+
export const archiveThread = mutation({
|
|
89
|
+
args: { threadId: v.id("threads") },
|
|
90
90
|
handler: async (ctx, args) => {
|
|
91
|
-
const
|
|
92
|
-
assert(
|
|
93
|
-
await ctx.db.patch(args.
|
|
94
|
-
return (await ctx.db.get(args.
|
|
91
|
+
const thread = await ctx.db.get(args.threadId);
|
|
92
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
93
|
+
await ctx.db.patch(args.threadId, { status: "archived" });
|
|
94
|
+
return (await ctx.db.get(args.threadId));
|
|
95
95
|
},
|
|
96
|
-
returns: v.doc("
|
|
96
|
+
returns: v.doc("threads"),
|
|
97
97
|
});
|
|
98
98
|
export const deleteAllForUserId = action({
|
|
99
99
|
args: { userId: v.string() },
|
|
100
100
|
handler: async (ctx, args) => {
|
|
101
101
|
let messagesCursor = null;
|
|
102
|
-
let
|
|
102
|
+
let threadsCursor = null;
|
|
103
103
|
let isDone = false;
|
|
104
104
|
while (!isDone) {
|
|
105
105
|
const result = await ctx.runMutation(internal.messages._deletePageForUserId, {
|
|
106
106
|
userId: args.userId,
|
|
107
107
|
messagesCursor,
|
|
108
|
-
|
|
108
|
+
threadsCursor,
|
|
109
109
|
});
|
|
110
110
|
messagesCursor = result.messagesCursor;
|
|
111
|
-
|
|
111
|
+
threadsCursor = result.threadsCursor;
|
|
112
112
|
isDone = result.isDone;
|
|
113
113
|
}
|
|
114
114
|
},
|
|
@@ -122,7 +122,7 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
122
122
|
const isDone = await deleteAllFroUserIdAsyncHandler(ctx, {
|
|
123
123
|
userId: args.userId,
|
|
124
124
|
messagesCursor: null,
|
|
125
|
-
|
|
125
|
+
threadsCursor: null,
|
|
126
126
|
});
|
|
127
127
|
return isDone;
|
|
128
128
|
},
|
|
@@ -131,11 +131,11 @@ export const deleteAllForUserIdAsync = mutation({
|
|
|
131
131
|
const deleteAllArgs = {
|
|
132
132
|
userId: v.string(),
|
|
133
133
|
messagesCursor: nullable(v.string()),
|
|
134
|
-
|
|
134
|
+
threadsCursor: nullable(v.string()),
|
|
135
135
|
};
|
|
136
136
|
const deleteAllReturns = {
|
|
137
137
|
messagesCursor: v.string(),
|
|
138
|
-
|
|
138
|
+
threadsCursor: nullable(v.string()),
|
|
139
139
|
isDone: v.boolean(),
|
|
140
140
|
};
|
|
141
141
|
export const _deleteAllForUserIdAsync = internalMutation({
|
|
@@ -149,7 +149,7 @@ async function deleteAllFroUserIdAsyncHandler(ctx, args) {
|
|
|
149
149
|
await ctx.scheduler.runAfter(0, internal.messages._deleteAllForUserIdAsync, {
|
|
150
150
|
userId: args.userId,
|
|
151
151
|
messagesCursor: result.messagesCursor,
|
|
152
|
-
|
|
152
|
+
threadsCursor: result.threadsCursor,
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
return result.isDone;
|
|
@@ -161,34 +161,34 @@ export const _deletePageForUserId = internalMutation({
|
|
|
161
161
|
});
|
|
162
162
|
async function deletePageForUserId(ctx, args) {
|
|
163
163
|
const streams = statuses.map((status) => stream(ctx.db, schema)
|
|
164
|
-
.query("
|
|
164
|
+
.query("threads")
|
|
165
165
|
.withIndex("status_userId_order", (q) => q.eq("status", status).eq("userId", args.userId))
|
|
166
166
|
.order("desc"));
|
|
167
|
-
const
|
|
168
|
-
const messages = await
|
|
167
|
+
const threadStreams = mergedStream(streams, ["order"]);
|
|
168
|
+
const messages = await threadStreams
|
|
169
169
|
.flatMap(async (c) => stream(ctx.db, schema)
|
|
170
170
|
.query("messages")
|
|
171
|
-
.withIndex("
|
|
171
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", c._id).eq("status", "success")), ["tool", "order", "stepOrder"])
|
|
172
172
|
.paginate({
|
|
173
173
|
numItems: 100,
|
|
174
174
|
cursor: args.messagesCursor ?? null,
|
|
175
175
|
});
|
|
176
176
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
177
177
|
if (messages.isDone) {
|
|
178
|
-
const
|
|
178
|
+
const threads = await threadStreams.paginate({
|
|
179
179
|
numItems: 100,
|
|
180
|
-
cursor: args.
|
|
180
|
+
cursor: args.threadsCursor ?? null,
|
|
181
181
|
});
|
|
182
|
-
await Promise.all(
|
|
182
|
+
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
183
183
|
return {
|
|
184
184
|
messagesCursor: messages.continueCursor,
|
|
185
|
-
|
|
186
|
-
isDone:
|
|
185
|
+
threadsCursor: threads.continueCursor,
|
|
186
|
+
isDone: threads.isDone,
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
return {
|
|
190
190
|
messagesCursor: messages.continueCursor,
|
|
191
|
-
|
|
191
|
+
threadsCursor: null,
|
|
192
192
|
isDone: messages.isDone,
|
|
193
193
|
};
|
|
194
194
|
}
|
|
@@ -201,52 +201,52 @@ async function deleteMessage(ctx, messageDoc) {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
204
|
+
const deleteThreadArgs = {
|
|
205
|
+
threadId: v.id("threads"),
|
|
206
206
|
cursor: v.optional(v.string()),
|
|
207
207
|
limit: v.optional(v.number()),
|
|
208
208
|
};
|
|
209
|
-
const
|
|
209
|
+
const deleteThreadReturns = {
|
|
210
210
|
cursor: v.string(),
|
|
211
211
|
isDone: v.boolean(),
|
|
212
212
|
};
|
|
213
|
-
export const
|
|
214
|
-
args:
|
|
213
|
+
export const deleteAllForThreadIdSync = action({
|
|
214
|
+
args: deleteThreadArgs,
|
|
215
215
|
handler: async (ctx, args) => {
|
|
216
|
-
const result = await ctx.runMutation(internal.messages.
|
|
216
|
+
const result = await ctx.runMutation(internal.messages._deletePageForThreadId, { threadId: args.threadId, cursor: args.cursor, limit: args.limit });
|
|
217
217
|
return result;
|
|
218
218
|
},
|
|
219
|
-
returns:
|
|
219
|
+
returns: deleteThreadReturns,
|
|
220
220
|
});
|
|
221
|
-
export const
|
|
222
|
-
args:
|
|
221
|
+
export const deleteAllForThreadIdAsync = mutation({
|
|
222
|
+
args: deleteThreadArgs,
|
|
223
223
|
handler: async (ctx, args) => {
|
|
224
|
-
const result = await
|
|
224
|
+
const result = await deletePageForThreadIdHandler(ctx, args);
|
|
225
225
|
if (!result.isDone) {
|
|
226
|
-
await ctx.scheduler.runAfter(0, api.messages.
|
|
227
|
-
|
|
226
|
+
await ctx.scheduler.runAfter(0, api.messages.deleteAllForThreadIdAsync, {
|
|
227
|
+
threadId: args.threadId,
|
|
228
228
|
cursor: result.cursor,
|
|
229
229
|
});
|
|
230
230
|
}
|
|
231
231
|
return result;
|
|
232
232
|
},
|
|
233
|
-
returns:
|
|
233
|
+
returns: deleteThreadReturns,
|
|
234
234
|
});
|
|
235
|
-
export const
|
|
236
|
-
args:
|
|
237
|
-
handler:
|
|
238
|
-
returns:
|
|
235
|
+
export const _deletePageForThreadId = internalMutation({
|
|
236
|
+
args: deleteThreadArgs,
|
|
237
|
+
handler: deletePageForThreadIdHandler,
|
|
238
|
+
returns: deleteThreadReturns,
|
|
239
239
|
});
|
|
240
|
-
async function
|
|
240
|
+
async function deletePageForThreadIdHandler(ctx, args) {
|
|
241
241
|
const messages = await stream(ctx.db, schema)
|
|
242
242
|
.query("messages")
|
|
243
|
-
.withIndex("
|
|
243
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", args.threadId).eq("status", "success"))
|
|
244
244
|
.paginate({
|
|
245
245
|
numItems: args.limit ?? 100,
|
|
246
246
|
cursor: args.cursor ?? null,
|
|
247
247
|
});
|
|
248
248
|
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
249
|
-
await ctx.db.delete(args.
|
|
249
|
+
await ctx.db.delete(args.threadId);
|
|
250
250
|
return {
|
|
251
251
|
cursor: messages.continueCursor,
|
|
252
252
|
isDone: messages.isDone,
|
|
@@ -281,7 +281,7 @@ export const vMessageDoc = schema.tables.messages.validator;
|
|
|
281
281
|
export const messageStatuses = vMessageDoc.fields.status.members.map((m) => m.value);
|
|
282
282
|
const addMessagesArgs = {
|
|
283
283
|
userId: v.optional(v.string()),
|
|
284
|
-
|
|
284
|
+
threadId: v.optional(v.id("threads")),
|
|
285
285
|
stepId: v.optional(v.id("steps")),
|
|
286
286
|
parentMessageId: v.optional(v.id("messages")),
|
|
287
287
|
messages: v.array(vMessageWithFileAndId),
|
|
@@ -300,29 +300,23 @@ export const addMessages = mutation({
|
|
|
300
300
|
});
|
|
301
301
|
async function addMessagesHandler(ctx, args) {
|
|
302
302
|
let userId = args.userId;
|
|
303
|
-
const
|
|
304
|
-
if (!userId && args.
|
|
305
|
-
const
|
|
306
|
-
assert(
|
|
307
|
-
userId =
|
|
303
|
+
const threadId = args.threadId;
|
|
304
|
+
if (!userId && args.threadId) {
|
|
305
|
+
const thread = await ctx.db.get(args.threadId);
|
|
306
|
+
assert(thread, `Thread ${args.threadId} not found`);
|
|
307
|
+
userId = thread._id;
|
|
308
308
|
}
|
|
309
309
|
const { failPendingSteps, pending, messages, parentMessageId, ...rest } = args;
|
|
310
310
|
const parent = parentMessageId && (await ctx.db.get(parentMessageId));
|
|
311
311
|
if (failPendingSteps && parent?.status !== "pending") {
|
|
312
|
-
assert(args.
|
|
312
|
+
assert(args.threadId, "threadId is required to fail pending steps");
|
|
313
313
|
const pendingMessages = await ctx.db
|
|
314
314
|
.query("messages")
|
|
315
|
-
.withIndex("
|
|
315
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", threadId).eq("status", "pending"))
|
|
316
316
|
.collect();
|
|
317
317
|
await Promise.all(pendingMessages.map((m) => ctx.db.patch(m._id, { status: "failed", text: "Restarting" })));
|
|
318
318
|
}
|
|
319
|
-
|
|
320
|
-
const maxMessage = await getMaxMessage(ctx, chatId, userId);
|
|
321
|
-
// If the previous message isn't our parent, we make a new thread.
|
|
322
|
-
threadId =
|
|
323
|
-
parentMessageId && maxMessage?._id === parentMessageId
|
|
324
|
-
? maxMessage.threadId ?? parentMessageId
|
|
325
|
-
: parentMessageId;
|
|
319
|
+
const maxMessage = await getMaxMessage(ctx, threadId, userId);
|
|
326
320
|
let order = maxMessage?.order ?? -1;
|
|
327
321
|
const toReturn = [];
|
|
328
322
|
if (messages.length > 0) {
|
|
@@ -334,7 +328,7 @@ async function addMessagesHandler(ctx, args) {
|
|
|
334
328
|
const text = extractText(message);
|
|
335
329
|
const messageId = await ctx.db.insert("messages", {
|
|
336
330
|
...rest,
|
|
337
|
-
|
|
331
|
+
parentMessageId,
|
|
338
332
|
userId,
|
|
339
333
|
message,
|
|
340
334
|
id,
|
|
@@ -349,12 +343,12 @@ async function addMessagesHandler(ctx, args) {
|
|
|
349
343
|
}
|
|
350
344
|
return { messages: toReturn };
|
|
351
345
|
}
|
|
352
|
-
async function getMaxMessage(ctx,
|
|
353
|
-
assert(
|
|
354
|
-
if (
|
|
346
|
+
async function getMaxMessage(ctx, threadId, userId) {
|
|
347
|
+
assert(threadId || userId, "One of threadId or userId is required");
|
|
348
|
+
if (threadId) {
|
|
355
349
|
return mergedStream(["success", "pending"].map((status) => stream(ctx.db, schema)
|
|
356
350
|
.query("messages")
|
|
357
|
-
.withIndex("
|
|
351
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q.eq("threadId", threadId).eq("status", status).eq("tool", false))
|
|
358
352
|
.order("desc")), ["order", "stepOrder"]).first();
|
|
359
353
|
}
|
|
360
354
|
else {
|
|
@@ -365,7 +359,7 @@ async function getMaxMessage(ctx, chatId, userId) {
|
|
|
365
359
|
}
|
|
366
360
|
}
|
|
367
361
|
const addStepsArgs = {
|
|
368
|
-
|
|
362
|
+
threadId: v.id("threads"),
|
|
369
363
|
messageId: v.id("messages"),
|
|
370
364
|
steps: v.array(vStepWithMessagesWithFileAndId),
|
|
371
365
|
failPendingSteps: v.optional(v.boolean()),
|
|
@@ -397,7 +391,7 @@ async function addStepsHandler(ctx, args) {
|
|
|
397
391
|
let nextStepOrder = (steps.at(-1)?.stepOrder ?? -1) + 1;
|
|
398
392
|
for (const { step, messages } of args.steps) {
|
|
399
393
|
const stepId = await ctx.db.insert("steps", {
|
|
400
|
-
|
|
394
|
+
threadId: args.threadId,
|
|
401
395
|
parentMessageId: args.messageId,
|
|
402
396
|
order,
|
|
403
397
|
stepOrder: nextStepOrder,
|
|
@@ -405,7 +399,7 @@ async function addStepsHandler(ctx, args) {
|
|
|
405
399
|
step,
|
|
406
400
|
});
|
|
407
401
|
await addMessagesHandler(ctx, {
|
|
408
|
-
|
|
402
|
+
threadId: args.threadId,
|
|
409
403
|
parentMessageId: args.messageId,
|
|
410
404
|
stepId,
|
|
411
405
|
messages,
|
|
@@ -460,8 +454,8 @@ async function commitMessageHandler(ctx, { messageId }) {
|
|
|
460
454
|
const order = message.order;
|
|
461
455
|
const messages = await mergedStream([true, false].map((tool) => stream(ctx.db, schema)
|
|
462
456
|
.query("messages")
|
|
463
|
-
.withIndex("
|
|
464
|
-
.eq("
|
|
457
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q
|
|
458
|
+
.eq("threadId", message.threadId)
|
|
465
459
|
.eq("status", "pending")
|
|
466
460
|
.eq("tool", tool)
|
|
467
461
|
.eq("order", order))), ["order", "stepOrder"]).collect();
|
|
@@ -470,9 +464,9 @@ async function commitMessageHandler(ctx, { messageId }) {
|
|
|
470
464
|
// TODO: recursively commit steps & messages that might depend on this one.
|
|
471
465
|
}
|
|
472
466
|
}
|
|
473
|
-
export const
|
|
467
|
+
export const getThreadMessages = query({
|
|
474
468
|
args: {
|
|
475
|
-
|
|
469
|
+
threadId: v.id("threads"),
|
|
476
470
|
isTool: v.optional(v.boolean()),
|
|
477
471
|
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
478
472
|
limit: v.optional(v.number()),
|
|
@@ -488,9 +482,9 @@ export const getChatMessages = query({
|
|
|
488
482
|
const order = args.order ?? "desc";
|
|
489
483
|
const streams = toolOptions.flatMap((tool) => statuses.map((status) => stream(ctx.db, schema)
|
|
490
484
|
.query("messages")
|
|
491
|
-
.withIndex("
|
|
485
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => {
|
|
492
486
|
const qq = q
|
|
493
|
-
.eq("
|
|
487
|
+
.eq("threadId", args.threadId)
|
|
494
488
|
.eq("status", status)
|
|
495
489
|
.eq("tool", tool);
|
|
496
490
|
if (parent) {
|
|
@@ -521,19 +515,19 @@ export const getChatMessages = query({
|
|
|
521
515
|
export const searchMessages = action({
|
|
522
516
|
args: {
|
|
523
517
|
userId: v.optional(v.string()),
|
|
524
|
-
|
|
518
|
+
threadId: v.optional(v.id("threads")),
|
|
525
519
|
parentMessageId: v.optional(v.id("messages")),
|
|
526
520
|
...vSearchOptions.fields,
|
|
527
521
|
},
|
|
528
522
|
returns: v.array(v.doc("messages")),
|
|
529
523
|
handler: async (ctx, args) => {
|
|
530
|
-
assert(args.userId || args.
|
|
524
|
+
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
531
525
|
const limit = args.limit;
|
|
532
526
|
let textSearchMessages;
|
|
533
527
|
if (args.text) {
|
|
534
528
|
textSearchMessages = await ctx.runQuery(api.messages.textSearch, {
|
|
535
529
|
userId: args.userId,
|
|
536
|
-
|
|
530
|
+
threadId: args.threadId,
|
|
537
531
|
text: args.text,
|
|
538
532
|
limit,
|
|
539
533
|
});
|
|
@@ -548,8 +542,8 @@ export const searchMessages = action({
|
|
|
548
542
|
const vectors = (await ctx.vectorSearch(tableName, "vector", {
|
|
549
543
|
vector: args.vector,
|
|
550
544
|
filter: (q) => args.userId
|
|
551
|
-
? q.eq("model_kind_userId", [model, "
|
|
552
|
-
: q.eq("
|
|
545
|
+
? q.eq("model_kind_userId", [model, "thread", args.userId])
|
|
546
|
+
: q.eq("model_kind_threadId", [model, "thread", args.threadId]),
|
|
553
547
|
limit,
|
|
554
548
|
})).filter((v) => v._score > 0.5);
|
|
555
549
|
// Reciprocal rank fusion
|
|
@@ -565,7 +559,7 @@ export const searchMessages = action({
|
|
|
565
559
|
const vectorIds = vectorScores.slice(0, limit).map((v) => v.id);
|
|
566
560
|
const messages = await ctx.runQuery(internal.messages._fetchVectorMessages, {
|
|
567
561
|
userId: args.userId,
|
|
568
|
-
|
|
562
|
+
threadId: args.threadId,
|
|
569
563
|
vectorIds,
|
|
570
564
|
textSearchMessages: textSearchMessages?.filter((m) => !vectorIds.includes(m.embeddingId)),
|
|
571
565
|
messageRange: args.messageRange ?? DEFAULT_MESSAGE_RANGE,
|
|
@@ -580,7 +574,7 @@ export const searchMessages = action({
|
|
|
580
574
|
export const _fetchVectorMessages = internalQuery({
|
|
581
575
|
args: {
|
|
582
576
|
userId: v.optional(v.string()),
|
|
583
|
-
|
|
577
|
+
threadId: v.optional(v.id("threads")),
|
|
584
578
|
vectorIds: v.array(vVectorId),
|
|
585
579
|
textSearchMessages: v.optional(v.array(v.doc("messages"))),
|
|
586
580
|
messageRange: v.object({ before: v.number(), after: v.number() }),
|
|
@@ -590,27 +584,27 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
590
584
|
returns: v.array(v.doc("messages")),
|
|
591
585
|
handler: async (ctx, args) => {
|
|
592
586
|
const parent = args.parentMessageId && (await ctx.db.get(args.parentMessageId));
|
|
593
|
-
const { userId,
|
|
594
|
-
assert(userId ||
|
|
587
|
+
const { userId, threadId } = args;
|
|
588
|
+
assert(userId || threadId, "Specify userId or threadId to search");
|
|
595
589
|
let messages = (await Promise.all(args.vectorIds.map((embeddingId) => ctx.db
|
|
596
590
|
.query("messages")
|
|
597
591
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
598
592
|
.filter((q) => userId
|
|
599
593
|
? q.eq("userId", userId)
|
|
600
594
|
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
601
|
-
q.eq("
|
|
595
|
+
q.eq("threadId", threadId))
|
|
602
596
|
.first()))).filter((m) => m !== undefined && m !== null && (!parent || m.order <= parent.order));
|
|
603
597
|
messages.push(...(args.textSearchMessages ?? []));
|
|
604
598
|
// TODO: prioritize more recent messages
|
|
605
599
|
messages.sort((a, b) => a.order - b.order);
|
|
606
600
|
messages = messages.slice(0, args.limit);
|
|
607
601
|
// Fetch the surrounding messages
|
|
608
|
-
if (!
|
|
602
|
+
if (!threadId) {
|
|
609
603
|
return messages.sort((a, b) => a.order - b.order);
|
|
610
604
|
}
|
|
611
605
|
const included = {};
|
|
612
606
|
for (const m of messages) {
|
|
613
|
-
const searchId = m.
|
|
607
|
+
const searchId = m.threadId ?? m.userId;
|
|
614
608
|
if (!included[searchId]) {
|
|
615
609
|
included[searchId] = new Set();
|
|
616
610
|
}
|
|
@@ -619,7 +613,7 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
619
613
|
const ranges = {};
|
|
620
614
|
const { before, after } = args.messageRange;
|
|
621
615
|
for (const m of messages) {
|
|
622
|
-
const searchId = m.
|
|
616
|
+
const searchId = m.threadId ?? m.userId;
|
|
623
617
|
const order = m.order;
|
|
624
618
|
let earliest = order - before;
|
|
625
619
|
let latest = order + after;
|
|
@@ -637,11 +631,11 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
637
631
|
included[searchId].add(i);
|
|
638
632
|
}
|
|
639
633
|
if (earliest !== latest) {
|
|
640
|
-
if (m.
|
|
634
|
+
if (m.threadId) {
|
|
641
635
|
const surrounding = await ctx.db
|
|
642
636
|
.query("messages")
|
|
643
|
-
.withIndex("
|
|
644
|
-
.eq("
|
|
637
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) => q
|
|
638
|
+
.eq("threadId", m.threadId)
|
|
645
639
|
.eq("status", "success")
|
|
646
640
|
.eq("tool", false)
|
|
647
641
|
.gt("order", earliest)
|
|
@@ -678,18 +672,18 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
678
672
|
// excluding duplicates in later ranges.
|
|
679
673
|
export const textSearch = query({
|
|
680
674
|
args: {
|
|
681
|
-
|
|
675
|
+
threadId: v.optional(v.id("threads")),
|
|
682
676
|
userId: v.optional(v.string()),
|
|
683
677
|
text: v.string(),
|
|
684
678
|
limit: v.number(),
|
|
685
679
|
},
|
|
686
680
|
handler: async (ctx, args) => {
|
|
687
|
-
assert(args.userId || args.
|
|
681
|
+
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
688
682
|
const messages = await ctx.db
|
|
689
683
|
.query("messages")
|
|
690
684
|
.withSearchIndex("text_search", (q) => args.userId
|
|
691
685
|
? q.search("text", args.text).eq("userId", args.userId)
|
|
692
|
-
: q.search("text", args.text).eq("
|
|
686
|
+
: q.search("text", args.text).eq("threadId", args.threadId))
|
|
693
687
|
.take(args.limit);
|
|
694
688
|
return messages;
|
|
695
689
|
},
|
|
@@ -738,9 +732,9 @@ export const textSearch = query({
|
|
|
738
732
|
// ),
|
|
739
733
|
// });
|
|
740
734
|
// const DEFAULT_MESSAGES_LIMIT = 40; // What pg & upstash do too.
|
|
741
|
-
// export const
|
|
735
|
+
// export const getThreadMessagesPage = query({
|
|
742
736
|
// args: {
|
|
743
|
-
//
|
|
737
|
+
// parentMessageId: v.string(),
|
|
744
738
|
// selectBy: v.optional(vSelectBy),
|
|
745
739
|
// // Unimplemented and as far I can tell no storage provider has either.
|
|
746
740
|
// // memoryConfig: v.optional(vMemoryConfig),
|
|
@@ -748,7 +742,7 @@ export const textSearch = query({
|
|
|
748
742
|
// handler: async (ctx, args): Promise<SerializedMessage[]> => {
|
|
749
743
|
// const messages = await ctx.db
|
|
750
744
|
// .query("messages")
|
|
751
|
-
// .withIndex("
|
|
745
|
+
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", args.parentMessageId))
|
|
752
746
|
// .order("desc")
|
|
753
747
|
// .take(args.selectBy?.last ? args.selectBy.last : DEFAULT_MESSAGES_LIMIT);
|
|
754
748
|
// const handled: boolean[] = [];
|
|
@@ -801,9 +795,9 @@ export const textSearch = query({
|
|
|
801
795
|
// ranges.map(async (range) => {
|
|
802
796
|
// return await ctx.db
|
|
803
797
|
// .query("messages")
|
|
804
|
-
// .withIndex("
|
|
798
|
+
// .withIndex("parentMessageId", (q) =>
|
|
805
799
|
// q
|
|
806
|
-
// .eq("
|
|
800
|
+
// .eq("parentMessageId", args.parentMessageId)
|
|
807
801
|
// .gte("threadOrder", range.start)
|
|
808
802
|
// .lte("threadOrder", range.end)
|
|
809
803
|
// )
|
|
@@ -819,21 +813,21 @@ export const textSearch = query({
|
|
|
819
813
|
// export const saveMessages = mutation({
|
|
820
814
|
// args: { messages: v.array(vSerializedMessage) },
|
|
821
815
|
// handler: async (ctx, args) => {
|
|
822
|
-
// const
|
|
816
|
+
// const messagesByParentMessageId: Record<string, SerializedMessage[]> = {};
|
|
823
817
|
// for (const message of args.messages) {
|
|
824
|
-
//
|
|
825
|
-
// ...(
|
|
818
|
+
// messagesByParentMessageId[message.parentMessageId] = [
|
|
819
|
+
// ...(messagesByParentMessageId[message.parentMessageId] ?? []),
|
|
826
820
|
// message,
|
|
827
821
|
// ];
|
|
828
822
|
// }
|
|
829
|
-
// for (const
|
|
823
|
+
// for (const parentMessageId in messagesByParentMessageId) {
|
|
830
824
|
// const lastMessage = await ctx.db
|
|
831
825
|
// .query("messages")
|
|
832
|
-
// .withIndex("
|
|
826
|
+
// .withIndex("parentMessageId", (q) => q.eq("parentMessageId", parentMessageId))
|
|
833
827
|
// .order("desc")
|
|
834
828
|
// .first();
|
|
835
829
|
// let threadOrder = lastMessage?.threadOrder ?? 0;
|
|
836
|
-
// for (const message of
|
|
830
|
+
// for (const message of messagesByParentMessageId[parentMessageId]) {
|
|
837
831
|
// threadOrder++;
|
|
838
832
|
// await ctx.db.insert("messages", {
|
|
839
833
|
// ...message,
|