@convex-dev/agent 0.0.16 → 0.0.17-alpha.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 +169 -98
- package/dist/commonjs/client/index.d.ts +806 -189
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +178 -28
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/apiKeys.d.ts +11 -0
- package/dist/commonjs/component/apiKeys.d.ts.map +1 -0
- package/dist/commonjs/component/apiKeys.js +69 -0
- package/dist/commonjs/component/apiKeys.js.map +1 -0
- package/dist/commonjs/component/files.d.ts +31 -0
- package/dist/commonjs/component/files.d.ts.map +1 -0
- package/dist/commonjs/component/files.js +61 -0
- package/dist/commonjs/component/files.js.map +1 -0
- package/dist/commonjs/component/messages.d.ts +720 -676
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +99 -287
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +1124 -1324
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +26 -14
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/threads.d.ts +95 -0
- package/dist/commonjs/component/threads.d.ts.map +1 -0
- package/dist/commonjs/component/threads.js +151 -0
- package/dist/commonjs/component/threads.js.map +1 -0
- package/dist/commonjs/component/users.d.ts +37 -0
- package/dist/commonjs/component/users.d.ts.map +1 -0
- package/dist/commonjs/component/users.js +118 -0
- package/dist/commonjs/component/users.js.map +1 -0
- package/dist/commonjs/component/vector/index.d.ts.map +1 -1
- package/dist/commonjs/component/vector/index.js +4 -6
- package/dist/commonjs/component/vector/index.js.map +1 -1
- package/dist/commonjs/component/vector/tables.d.ts +10 -10
- package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
- package/dist/commonjs/component/vector/tables.js.map +1 -1
- package/dist/commonjs/mapping.d.ts +7 -1
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js +39 -18
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/validators.d.ts +1669 -2340
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +35 -26
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/index.d.ts +806 -189
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +178 -28
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/apiKeys.d.ts +11 -0
- package/dist/esm/component/apiKeys.d.ts.map +1 -0
- package/dist/esm/component/apiKeys.js +69 -0
- package/dist/esm/component/apiKeys.js.map +1 -0
- package/dist/esm/component/files.d.ts +31 -0
- package/dist/esm/component/files.d.ts.map +1 -0
- package/dist/esm/component/files.js +61 -0
- package/dist/esm/component/files.js.map +1 -0
- package/dist/esm/component/messages.d.ts +720 -676
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +99 -287
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +1124 -1324
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +26 -14
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/threads.d.ts +95 -0
- package/dist/esm/component/threads.d.ts.map +1 -0
- package/dist/esm/component/threads.js +151 -0
- package/dist/esm/component/threads.js.map +1 -0
- package/dist/esm/component/users.d.ts +37 -0
- package/dist/esm/component/users.d.ts.map +1 -0
- package/dist/esm/component/users.js +118 -0
- package/dist/esm/component/users.js.map +1 -0
- package/dist/esm/component/vector/index.d.ts.map +1 -1
- package/dist/esm/component/vector/index.js +4 -6
- package/dist/esm/component/vector/index.js.map +1 -1
- package/dist/esm/component/vector/tables.d.ts +10 -10
- package/dist/esm/component/vector/tables.d.ts.map +1 -1
- package/dist/esm/component/vector/tables.js.map +1 -1
- package/dist/esm/mapping.d.ts +7 -1
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js +39 -18
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/validators.d.ts +1669 -2340
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +35 -26
- package/dist/esm/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +220 -59
- package/src/component/_generated/api.d.ts +601 -400
- package/src/component/apiKeys.ts +74 -0
- package/src/component/files.ts +72 -0
- package/src/component/messages.test.ts +23 -0
- package/src/component/messages.ts +127 -341
- package/src/component/schema.ts +32 -13
- package/src/component/threads.ts +184 -0
- package/src/component/users.ts +145 -0
- package/src/component/vector/index.ts +5 -6
- package/src/component/vector/tables.ts +15 -15
- package/src/mapping.ts +65 -32
- package/src/validators.ts +47 -28
package/src/component/schema.ts
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
vSource,
|
|
10
10
|
vLanguageModelV1CallWarning,
|
|
11
11
|
vFinishReason,
|
|
12
|
+
vProviderOptions,
|
|
13
|
+
vProviderMetadata,
|
|
14
|
+
vReasoningDetails,
|
|
15
|
+
vFile,
|
|
12
16
|
} from "../validators.js";
|
|
13
17
|
import { typedV } from "convex-helpers/validators";
|
|
14
18
|
import vectorTables, { vVectorId } from "./vector/tables.js";
|
|
@@ -29,35 +33,44 @@ export const schema = defineSchema({
|
|
|
29
33
|
id: v.optional(v.string()), // external id, e.g. from Vercel AI SDK
|
|
30
34
|
userId: v.optional(v.string()), // useful for future indexes (text search)
|
|
31
35
|
threadId: v.id("threads"),
|
|
36
|
+
// TODO: is this redunant with message at last step @ order - 1?
|
|
32
37
|
parentMessageId: v.optional(v.id("messages")),
|
|
33
38
|
stepId: v.optional(v.id("steps")),
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
// Repeats until a non-tool message.
|
|
40
|
+
order: v.number(),
|
|
41
|
+
stepOrder: v.number(),
|
|
42
|
+
embeddingId: v.optional(vVectorId),
|
|
36
43
|
error: v.optional(v.string()),
|
|
44
|
+
status: vMessageStatus,
|
|
45
|
+
|
|
46
|
+
// Context on how it was generated
|
|
47
|
+
agentName: v.optional(v.string()),
|
|
37
48
|
model: v.optional(v.string()),
|
|
38
49
|
provider: v.optional(v.string()),
|
|
50
|
+
providerOptions: v.optional(vProviderOptions), // Sent to model
|
|
51
|
+
|
|
52
|
+
// The result
|
|
53
|
+
message: v.optional(vMessage),
|
|
54
|
+
// Convenience fields extracted from the message
|
|
55
|
+
tool: v.boolean(), // either tool call (assistant) or tool result (tool)
|
|
39
56
|
text: v.optional(v.string()),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// Repeats until a non-tool message.
|
|
44
|
-
// Unset if it's not in a thread.
|
|
45
|
-
order: v.number(),
|
|
46
|
-
stepOrder: v.number(),
|
|
57
|
+
files: v.optional(v.array(vFile)),
|
|
58
|
+
|
|
59
|
+
// Result metadata
|
|
47
60
|
usage: v.optional(vUsage),
|
|
48
|
-
|
|
49
|
-
providerMetadata: v.optional(v.record(v.string(), v.any())),
|
|
61
|
+
providerMetadata: v.optional(vProviderMetadata), // Received from model
|
|
50
62
|
sources: v.optional(v.array(vSource)),
|
|
51
63
|
reasoning: v.optional(v.string()),
|
|
64
|
+
reasoningDetails: v.optional(vReasoningDetails),
|
|
52
65
|
warnings: v.optional(v.array(vLanguageModelV1CallWarning)),
|
|
53
|
-
|
|
54
|
-
status: vMessageStatus,
|
|
66
|
+
finishReason: v.optional(vFinishReason),
|
|
55
67
|
})
|
|
56
68
|
// Allows finding successful visible messages in order
|
|
57
69
|
// Also surface pending messages separately to e.g. stream
|
|
58
70
|
.index("threadId_status_tool_order_stepOrder", [
|
|
59
71
|
"threadId",
|
|
60
72
|
"status",
|
|
73
|
+
// TODO: we might not need this to be in the index..
|
|
61
74
|
"tool",
|
|
62
75
|
"order",
|
|
63
76
|
"stepOrder",
|
|
@@ -124,6 +137,12 @@ export const schema = defineSchema({
|
|
|
124
137
|
.index("hash", ["hash"])
|
|
125
138
|
.index("refcount", ["refcount"]),
|
|
126
139
|
...vectorTables,
|
|
140
|
+
// To authenticate playground usage
|
|
141
|
+
// Delete a key to invalidate it
|
|
142
|
+
// Provide a name to easily identify it / invalidate by name
|
|
143
|
+
apiKeys: defineTable({
|
|
144
|
+
name: v.optional(v.string()),
|
|
145
|
+
}).index("name", ["name"]),
|
|
127
146
|
});
|
|
128
147
|
|
|
129
148
|
export const vv = typedV(schema);
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { assert, omit, pick } from "convex-helpers";
|
|
2
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
3
|
+
import { partial } from "convex-helpers/validators";
|
|
4
|
+
import { ObjectType } from "convex/values";
|
|
5
|
+
import { paginationResultValidator } from "../validators.js";
|
|
6
|
+
import { api, internal } from "./_generated/api.js";
|
|
7
|
+
import { Doc } from "./_generated/dataModel.js";
|
|
8
|
+
import {
|
|
9
|
+
action,
|
|
10
|
+
internalMutation,
|
|
11
|
+
mutation,
|
|
12
|
+
MutationCtx,
|
|
13
|
+
query,
|
|
14
|
+
} from "./_generated/server.js";
|
|
15
|
+
import { schema, v } from "./schema.js";
|
|
16
|
+
import { paginationOptsValidator } from "convex/server";
|
|
17
|
+
import { deleteMessage } from "./messages.js";
|
|
18
|
+
import { ThreadDoc, vThreadDoc } from "../client/index.js";
|
|
19
|
+
|
|
20
|
+
function publicThreadOrNull(thread: Doc<"threads"> | null): ThreadDoc | null {
|
|
21
|
+
if (thread === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return publicThread(thread);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function publicThread(thread: Doc<"threads">): ThreadDoc {
|
|
28
|
+
return omit(thread, ["defaultSystemPrompt", "parentThreadIds", "order"]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const getThread = query({
|
|
32
|
+
args: { threadId: v.id("threads") },
|
|
33
|
+
handler: async (ctx, args) => {
|
|
34
|
+
return publicThreadOrNull(await ctx.db.get(args.threadId));
|
|
35
|
+
},
|
|
36
|
+
returns: v.union(vThreadDoc, v.null()),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const listThreadsByUserId = query({
|
|
40
|
+
args: {
|
|
41
|
+
userId: v.string(),
|
|
42
|
+
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
43
|
+
paginationOpts: v.optional(paginationOptsValidator),
|
|
44
|
+
},
|
|
45
|
+
handler: async (ctx, args) => {
|
|
46
|
+
const threads = await paginator(ctx.db, schema)
|
|
47
|
+
.query("threads")
|
|
48
|
+
.withIndex("userId", (q) => q.eq("userId", args.userId))
|
|
49
|
+
.order(args.order ?? "desc")
|
|
50
|
+
.paginate(args.paginationOpts ?? { cursor: null, numItems: 100 });
|
|
51
|
+
return {
|
|
52
|
+
...threads,
|
|
53
|
+
page: threads.page.map(publicThread),
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
returns: paginationResultValidator(vThreadDoc),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const vThread = schema.tables.threads.validator;
|
|
60
|
+
|
|
61
|
+
export const createThread = mutation({
|
|
62
|
+
args: omit(vThread.fields, ["order", "status"]),
|
|
63
|
+
handler: async (ctx, args) => {
|
|
64
|
+
const threadId = await ctx.db.insert("threads", {
|
|
65
|
+
...args,
|
|
66
|
+
status: "active",
|
|
67
|
+
});
|
|
68
|
+
return publicThread((await ctx.db.get(threadId))!);
|
|
69
|
+
},
|
|
70
|
+
returns: vThreadDoc,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const updateThread = mutation({
|
|
74
|
+
args: {
|
|
75
|
+
threadId: v.id("threads"),
|
|
76
|
+
patch: v.object(
|
|
77
|
+
partial(pick(vThread.fields, ["title", "summary", "status"]))
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
handler: async (ctx, 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 publicThread((await ctx.db.get(args.threadId))!);
|
|
85
|
+
},
|
|
86
|
+
returns: vThreadDoc,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// When we expose this, we need to also hide all the messages and steps
|
|
90
|
+
// export const archiveThread = mutation({
|
|
91
|
+
// args: { threadId: v.id("threads") },
|
|
92
|
+
// handler: async (ctx, args) => {
|
|
93
|
+
// const thread = await ctx.db.get(args.threadId);
|
|
94
|
+
// assert(thread, `Thread ${args.threadId} not found`);
|
|
95
|
+
// await ctx.db.patch(args.threadId, { status: "archived" });
|
|
96
|
+
// return publicThread((await ctx.db.get(args.threadId))!);
|
|
97
|
+
// },
|
|
98
|
+
// returns: vThreadDoc,
|
|
99
|
+
// });
|
|
100
|
+
|
|
101
|
+
// TODO: delete thread
|
|
102
|
+
|
|
103
|
+
const deleteThreadArgs = {
|
|
104
|
+
threadId: v.id("threads"),
|
|
105
|
+
cursor: v.optional(v.string()),
|
|
106
|
+
limit: v.optional(v.number()),
|
|
107
|
+
};
|
|
108
|
+
type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
|
|
109
|
+
const deleteThreadReturns = {
|
|
110
|
+
cursor: v.string(),
|
|
111
|
+
isDone: v.boolean(),
|
|
112
|
+
};
|
|
113
|
+
type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Use this to delete a thread and everything it contains.
|
|
117
|
+
* It will try to delete all pages synchronously.
|
|
118
|
+
* If it times out or fails, you'll have to run it again.
|
|
119
|
+
*/
|
|
120
|
+
export const deleteAllForThreadIdSync = action({
|
|
121
|
+
args: deleteThreadArgs,
|
|
122
|
+
handler: async (ctx, args) => {
|
|
123
|
+
let cursor = args.cursor;
|
|
124
|
+
while (true) {
|
|
125
|
+
const result = await ctx.runMutation(
|
|
126
|
+
internal.threads._deletePageForThreadId,
|
|
127
|
+
{ threadId: args.threadId, cursor, limit: args.limit }
|
|
128
|
+
);
|
|
129
|
+
if (result.isDone) {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
cursor = result.cursor;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
returns: v.null(),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const _deletePageForThreadId = internalMutation({
|
|
139
|
+
args: deleteThreadArgs,
|
|
140
|
+
handler: deletePageForThreadIdHandler,
|
|
141
|
+
returns: deleteThreadReturns,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Use this to delete a thread and everything it contains.
|
|
146
|
+
* It will continue deleting pages asynchronously.
|
|
147
|
+
*/
|
|
148
|
+
export const deleteAllForThreadIdAsync = mutation({
|
|
149
|
+
args: deleteThreadArgs,
|
|
150
|
+
handler: async (ctx, args) => {
|
|
151
|
+
const result = await deletePageForThreadIdHandler(ctx, args);
|
|
152
|
+
if (!result.isDone) {
|
|
153
|
+
await ctx.scheduler.runAfter(0, api.threads.deleteAllForThreadIdAsync, {
|
|
154
|
+
threadId: args.threadId,
|
|
155
|
+
cursor: result.cursor,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
},
|
|
160
|
+
returns: deleteThreadReturns,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
async function deletePageForThreadIdHandler(
|
|
164
|
+
ctx: MutationCtx,
|
|
165
|
+
args: DeleteThreadArgs
|
|
166
|
+
): Promise<DeleteThreadReturns> {
|
|
167
|
+
const messages = await paginator(ctx.db, schema)
|
|
168
|
+
.query("messages")
|
|
169
|
+
.withIndex("threadId_status_tool_order_stepOrder", (q) =>
|
|
170
|
+
q.eq("threadId", args.threadId)
|
|
171
|
+
)
|
|
172
|
+
.paginate({
|
|
173
|
+
numItems: args.limit ?? 100,
|
|
174
|
+
cursor: args.cursor ?? null,
|
|
175
|
+
});
|
|
176
|
+
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
177
|
+
if (messages.isDone) {
|
|
178
|
+
await ctx.db.delete(args.threadId);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
cursor: messages.continueCursor,
|
|
182
|
+
isDone: messages.isDone,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
2
|
+
import { nullable } from "convex-helpers/validators";
|
|
3
|
+
import { ObjectType } from "convex/values";
|
|
4
|
+
import { internal } from "./_generated/api.js";
|
|
5
|
+
import {
|
|
6
|
+
action,
|
|
7
|
+
internalMutation,
|
|
8
|
+
mutation,
|
|
9
|
+
MutationCtx,
|
|
10
|
+
query,
|
|
11
|
+
} from "./_generated/server.js";
|
|
12
|
+
import { schema, v } from "./schema.js";
|
|
13
|
+
import { deleteMessage } from "./messages.js";
|
|
14
|
+
import { paginationOptsValidator } from "convex/server";
|
|
15
|
+
import { stream } from "convex-helpers/server/stream";
|
|
16
|
+
import { paginationResultValidator } from "../validators.js";
|
|
17
|
+
|
|
18
|
+
// Note: it only searches for users with threads
|
|
19
|
+
export const listUsersWithThreads = query({
|
|
20
|
+
args: {
|
|
21
|
+
paginationOpts: paginationOptsValidator,
|
|
22
|
+
},
|
|
23
|
+
handler: async (ctx, args) => {
|
|
24
|
+
const results = await stream(ctx.db, schema)
|
|
25
|
+
.query("threads")
|
|
26
|
+
.withIndex("userId", (q) => q)
|
|
27
|
+
.filterWith(async (q) => !!q.userId)
|
|
28
|
+
.distinct(["userId"])
|
|
29
|
+
.paginate(args.paginationOpts);
|
|
30
|
+
return {
|
|
31
|
+
...results,
|
|
32
|
+
page: results.page.map((t) => t.userId).filter((t): t is string => !!t),
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
returns: paginationResultValidator(v.string()),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const deleteAllForUserId = action({
|
|
39
|
+
args: { userId: v.string() },
|
|
40
|
+
handler: async (ctx, args) => {
|
|
41
|
+
let messagesCursor = null;
|
|
42
|
+
let threadsCursor = null;
|
|
43
|
+
let isDone = false;
|
|
44
|
+
while (!isDone) {
|
|
45
|
+
const result: {
|
|
46
|
+
messagesCursor: string;
|
|
47
|
+
threadsCursor: string | null;
|
|
48
|
+
isDone: boolean;
|
|
49
|
+
} = await ctx.runMutation(internal.users._deletePageForUserId, {
|
|
50
|
+
userId: args.userId,
|
|
51
|
+
messagesCursor,
|
|
52
|
+
threadsCursor,
|
|
53
|
+
});
|
|
54
|
+
messagesCursor = result.messagesCursor;
|
|
55
|
+
threadsCursor = result.threadsCursor;
|
|
56
|
+
isDone = result.isDone;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
returns: v.null(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const deleteAllForUserIdAsync = mutation({
|
|
63
|
+
args: {
|
|
64
|
+
userId: v.string(),
|
|
65
|
+
},
|
|
66
|
+
handler: async (ctx, args) => {
|
|
67
|
+
const isDone = await deleteAllForUserIdAsyncHandler(ctx, {
|
|
68
|
+
userId: args.userId,
|
|
69
|
+
messagesCursor: null,
|
|
70
|
+
threadsCursor: null,
|
|
71
|
+
});
|
|
72
|
+
return isDone;
|
|
73
|
+
},
|
|
74
|
+
returns: v.boolean(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const deleteAllArgs = {
|
|
78
|
+
userId: v.string(),
|
|
79
|
+
messagesCursor: nullable(v.string()),
|
|
80
|
+
threadsCursor: nullable(v.string()),
|
|
81
|
+
};
|
|
82
|
+
type DeleteAllArgs = ObjectType<typeof deleteAllArgs>;
|
|
83
|
+
const deleteAllReturns = {
|
|
84
|
+
messagesCursor: v.string(),
|
|
85
|
+
threadsCursor: nullable(v.string()),
|
|
86
|
+
isDone: v.boolean(),
|
|
87
|
+
};
|
|
88
|
+
type DeleteAllReturns = ObjectType<typeof deleteAllReturns>;
|
|
89
|
+
|
|
90
|
+
export const _deleteAllForUserIdAsync = internalMutation({
|
|
91
|
+
args: deleteAllArgs,
|
|
92
|
+
handler: deleteAllForUserIdAsyncHandler,
|
|
93
|
+
returns: v.boolean(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
async function deleteAllForUserIdAsyncHandler(
|
|
97
|
+
ctx: MutationCtx,
|
|
98
|
+
args: DeleteAllArgs
|
|
99
|
+
): Promise<boolean> {
|
|
100
|
+
const result = await deletePageForUserId(ctx, args);
|
|
101
|
+
if (!result.isDone) {
|
|
102
|
+
await ctx.scheduler.runAfter(0, internal.users._deleteAllForUserIdAsync, {
|
|
103
|
+
userId: args.userId,
|
|
104
|
+
messagesCursor: result.messagesCursor,
|
|
105
|
+
threadsCursor: result.threadsCursor,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return result.isDone;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const _deletePageForUserId = internalMutation({
|
|
112
|
+
args: deleteAllArgs,
|
|
113
|
+
handler: deletePageForUserId,
|
|
114
|
+
returns: deleteAllReturns,
|
|
115
|
+
});
|
|
116
|
+
async function deletePageForUserId(
|
|
117
|
+
ctx: MutationCtx,
|
|
118
|
+
args: DeleteAllArgs
|
|
119
|
+
): Promise<DeleteAllReturns> {
|
|
120
|
+
const threads = await paginator(ctx.db, schema)
|
|
121
|
+
.query("threads")
|
|
122
|
+
.withIndex("userId", (q) => q.eq("userId", args.userId))
|
|
123
|
+
.order("desc")
|
|
124
|
+
.paginate({
|
|
125
|
+
numItems: 100,
|
|
126
|
+
cursor: args.threadsCursor ?? null,
|
|
127
|
+
});
|
|
128
|
+
await Promise.all(threads.page.map((c) => ctx.db.delete(c._id)));
|
|
129
|
+
const messages = await paginator(ctx.db, schema)
|
|
130
|
+
.query("messages")
|
|
131
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
132
|
+
q.eq("userId", args.userId)
|
|
133
|
+
)
|
|
134
|
+
.order("desc")
|
|
135
|
+
.paginate({
|
|
136
|
+
numItems: 100,
|
|
137
|
+
cursor: args.messagesCursor ?? null,
|
|
138
|
+
});
|
|
139
|
+
await Promise.all(messages.page.map((m) => deleteMessage(ctx, m)));
|
|
140
|
+
return {
|
|
141
|
+
messagesCursor: messages.continueCursor,
|
|
142
|
+
threadsCursor: threads.continueCursor,
|
|
143
|
+
isDone: messages.isDone,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -65,14 +65,13 @@ export const deleteBatchForThread = mutation({
|
|
|
65
65
|
handler: async (ctx, args) => {
|
|
66
66
|
const tableName = getVectorTableName(args.vectorDimension);
|
|
67
67
|
const vectors = await mergedStream(
|
|
68
|
-
["thread", "memory"].map((
|
|
68
|
+
["thread", "memory"].map((table) =>
|
|
69
69
|
stream(ctx.db, schema)
|
|
70
70
|
.query(tableName)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.eq("kind", kind)
|
|
71
|
+
.withIndex("model_table_threadId", (q) =>
|
|
72
|
+
q
|
|
73
|
+
.eq("model", args.model)
|
|
74
|
+
.eq("table", table)
|
|
76
75
|
.eq("threadId", args.threadId)
|
|
77
76
|
)
|
|
78
77
|
),
|
|
@@ -47,6 +47,21 @@ function table<D extends number>(dimensions: D): Table<D> {
|
|
|
47
47
|
.index("model_table_threadId", ["model", "table", "threadId"]);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
type Table<D extends number> = TableDefinition<
|
|
51
|
+
VObject<ObjectType<typeof embeddings>, typeof embeddings>,
|
|
52
|
+
{ model_table_threadId: ["model", "table", "threadId", "_creationTime"] },
|
|
53
|
+
GenericTableSearchIndexes,
|
|
54
|
+
VectorIndex<D>
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
type VectorIndex<D extends number> = {
|
|
58
|
+
vector: {
|
|
59
|
+
vectorField: "vector";
|
|
60
|
+
dimensions: D;
|
|
61
|
+
filterFields: "model_table_userId" | "model_table_threadId";
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
50
65
|
export type VectorSchema = SchemaDefinition<
|
|
51
66
|
{ [key in VectorTableName]: Table<128> },
|
|
52
67
|
true
|
|
@@ -80,21 +95,6 @@ export const vVectorId = v.union(
|
|
|
80
95
|
VId<(typeof VectorTableNames)[number]>[]
|
|
81
96
|
>;
|
|
82
97
|
|
|
83
|
-
type Table<D extends number> = TableDefinition<
|
|
84
|
-
VObject<ObjectType<typeof embeddings>, typeof embeddings>,
|
|
85
|
-
{ id: ["id"] },
|
|
86
|
-
GenericTableSearchIndexes,
|
|
87
|
-
VectorIndex<D>
|
|
88
|
-
>;
|
|
89
|
-
|
|
90
|
-
type VectorIndex<D extends number> = {
|
|
91
|
-
vector: {
|
|
92
|
-
vectorField: "vector";
|
|
93
|
-
dimensions: D;
|
|
94
|
-
filterFields: string;
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
|
|
98
98
|
export function getVectorTableName(dimension: VectorDimension) {
|
|
99
99
|
return `embeddings_${dimension}` as VectorTableName;
|
|
100
100
|
}
|
package/src/mapping.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type Message as AIMessage,
|
|
12
12
|
type UserContent,
|
|
13
13
|
} from "ai";
|
|
14
|
+
import type { FileUIPart } from "@ai-sdk/ui-utils";
|
|
14
15
|
import { assert } from "convex-helpers";
|
|
15
16
|
import {
|
|
16
17
|
MessageWithMetadata,
|
|
@@ -39,9 +40,11 @@ export type SerializedMessage = SerializeUrlsAndUint8Arrays<CoreMessage>;
|
|
|
39
40
|
export function serializeMessage(
|
|
40
41
|
messageWithId: CoreMessage & { id?: string }
|
|
41
42
|
): SerializedMessage {
|
|
42
|
-
const { id: _, ...message } = messageWithId;
|
|
43
|
+
const { id: _, experimental_providerMetadata, ...message } = messageWithId;
|
|
43
44
|
const content = message.content;
|
|
44
45
|
return {
|
|
46
|
+
// for backwards compatibility
|
|
47
|
+
providerOptions: experimental_providerMetadata,
|
|
45
48
|
...message,
|
|
46
49
|
content: serializeContent(content),
|
|
47
50
|
} as SerializedMessage;
|
|
@@ -90,6 +93,7 @@ export function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
90
93
|
provider: metadata.provider,
|
|
91
94
|
providerMetadata: step.providerMetadata,
|
|
92
95
|
reasoning: step.reasoning,
|
|
96
|
+
reasoningDetails: step.reasoningDetails,
|
|
93
97
|
usage: step.usage,
|
|
94
98
|
warnings: step.warnings,
|
|
95
99
|
finishReason: step.finishReason,
|
|
@@ -101,33 +105,41 @@ export function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
101
105
|
step.toolResults.length > 0
|
|
102
106
|
? step.response.messages.slice(-2)
|
|
103
107
|
: step.response.messages.slice(-1)
|
|
104
|
-
).map(
|
|
105
|
-
message:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
).map(
|
|
109
|
+
(message): MessageWithMetadata => ({
|
|
110
|
+
message: serializeMessage(message),
|
|
111
|
+
id: message.id,
|
|
112
|
+
...(message.role === "tool" ? toolFields : assistantFields),
|
|
113
|
+
text: step.text,
|
|
114
|
+
// fileId: message.fileId,
|
|
115
|
+
files: step.files.map((file) => ({
|
|
116
|
+
mimeType: file.mimeType,
|
|
117
|
+
data: serializeDataOrUrl(file.uint8Array ?? file.base64),
|
|
118
|
+
// TODO: if the file is big, store it and populate url, fileId
|
|
119
|
+
})),
|
|
120
|
+
})
|
|
121
|
+
);
|
|
110
122
|
return messages;
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
export function serializeObjectResult(
|
|
114
|
-
|
|
126
|
+
result: GenerateObjectResult<unknown>,
|
|
115
127
|
metadata: { model: string; provider: string }
|
|
116
128
|
): StepWithMessagesWithMetadata {
|
|
117
|
-
const text = JSON.stringify(
|
|
129
|
+
const text = JSON.stringify(result.object);
|
|
118
130
|
|
|
119
131
|
return {
|
|
120
132
|
messages: [
|
|
121
133
|
{
|
|
122
134
|
message: { role: "assistant" as const, content: text },
|
|
123
|
-
id:
|
|
135
|
+
id: result.response.id,
|
|
124
136
|
model: metadata.model,
|
|
125
137
|
provider: metadata.provider,
|
|
126
|
-
providerMetadata:
|
|
127
|
-
finishReason:
|
|
138
|
+
providerMetadata: result.providerMetadata,
|
|
139
|
+
finishReason: result.finishReason,
|
|
128
140
|
text,
|
|
129
|
-
usage:
|
|
130
|
-
warnings:
|
|
141
|
+
usage: result.usage,
|
|
142
|
+
warnings: result.warnings,
|
|
131
143
|
},
|
|
132
144
|
],
|
|
133
145
|
step: {
|
|
@@ -136,23 +148,22 @@ export function serializeObjectResult(
|
|
|
136
148
|
stepType: "initial",
|
|
137
149
|
toolCalls: [],
|
|
138
150
|
toolResults: [],
|
|
139
|
-
usage:
|
|
140
|
-
warnings:
|
|
141
|
-
finishReason:
|
|
142
|
-
|
|
151
|
+
usage: result.usage,
|
|
152
|
+
warnings: result.warnings,
|
|
153
|
+
finishReason: result.finishReason,
|
|
154
|
+
providerMetadata: result.providerMetadata,
|
|
155
|
+
request: result.request,
|
|
143
156
|
response: {
|
|
144
|
-
...
|
|
145
|
-
timestamp:
|
|
157
|
+
...result.response,
|
|
158
|
+
timestamp: result.response.timestamp.getTime(),
|
|
146
159
|
messages: [
|
|
147
160
|
serializeMessageWithId({
|
|
148
161
|
role: "assistant" as const,
|
|
149
162
|
content: text,
|
|
150
|
-
id:
|
|
163
|
+
id: result.response.id,
|
|
151
164
|
}),
|
|
152
165
|
],
|
|
153
166
|
},
|
|
154
|
-
providerMetadata: step.providerMetadata,
|
|
155
|
-
experimental_providerMetadata: step.experimental_providerMetadata,
|
|
156
167
|
},
|
|
157
168
|
};
|
|
158
169
|
}
|
|
@@ -161,16 +172,19 @@ export function serializeContent(content: Content): SerializedContent {
|
|
|
161
172
|
if (typeof content === "string") {
|
|
162
173
|
return content;
|
|
163
174
|
}
|
|
164
|
-
const serialized = content.map(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
const serialized = content.map(
|
|
176
|
+
({ experimental_providerMetadata, ...rest }) => {
|
|
177
|
+
const part = { providerOptions: experimental_providerMetadata, ...rest };
|
|
178
|
+
switch (part.type) {
|
|
179
|
+
case "image":
|
|
180
|
+
return { ...part, image: serializeDataOrUrl(part.image) };
|
|
181
|
+
case "file":
|
|
182
|
+
return { ...part, file: serializeDataOrUrl(part.data) };
|
|
183
|
+
default:
|
|
184
|
+
return part;
|
|
185
|
+
}
|
|
172
186
|
}
|
|
173
|
-
|
|
187
|
+
);
|
|
174
188
|
return serialized as SerializedContent;
|
|
175
189
|
}
|
|
176
190
|
|
|
@@ -222,6 +236,25 @@ function deserializeUrl(urlOrString: string | ArrayBuffer): URL | DataContent {
|
|
|
222
236
|
return urlOrString;
|
|
223
237
|
}
|
|
224
238
|
|
|
239
|
+
export function toUIFilePart(file: {
|
|
240
|
+
data?: ArrayBuffer | string;
|
|
241
|
+
url?: string;
|
|
242
|
+
mimeType: string;
|
|
243
|
+
}): FileUIPart {
|
|
244
|
+
return {
|
|
245
|
+
type: "file",
|
|
246
|
+
data:
|
|
247
|
+
file.data instanceof ArrayBuffer
|
|
248
|
+
? encodeBase64(file.data)
|
|
249
|
+
: file.url ?? file.data ?? "",
|
|
250
|
+
mimeType: file.mimeType,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function encodeBase64(data: ArrayBuffer): string {
|
|
255
|
+
return Buffer.from(data).toString("base64");
|
|
256
|
+
}
|
|
257
|
+
|
|
225
258
|
export function promptOrMessagesToCoreMessages(args: {
|
|
226
259
|
prompt?: string;
|
|
227
260
|
messages?: CoreMessage[] | AIMessageWithoutId[];
|