@convex-dev/agent 0.1.16 → 0.1.18-alpha.0
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 +1 -1
- package/dist/client/createTool.d.ts +5 -5
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +619 -0
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -0
- package/dist/client/definePlaygroundAPI.js +254 -0
- package/dist/client/definePlaygroundAPI.js.map +1 -0
- package/dist/client/files.js +1 -1
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +52 -10
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +49 -69
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/messages.d.ts +1 -1
- package/package.json +3 -2
- package/src/client/createTool.ts +14 -12
- package/src/client/definePlaygroundAPI.ts +336 -0
- package/src/client/files.ts +1 -1
- package/src/client/index.ts +143 -117
- package/src/client/types.ts +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import {
|
|
2
|
+
paginationOptsValidator,
|
|
3
|
+
queryGeneric,
|
|
4
|
+
mutationGeneric,
|
|
5
|
+
actionGeneric,
|
|
6
|
+
type GenericDataModel,
|
|
7
|
+
type GenericQueryCtx,
|
|
8
|
+
type ApiFromModules,
|
|
9
|
+
type GenericActionCtx,
|
|
10
|
+
} from "convex/server";
|
|
11
|
+
import {
|
|
12
|
+
vMessageDoc,
|
|
13
|
+
vThreadDoc,
|
|
14
|
+
vPaginationResult,
|
|
15
|
+
vMessage,
|
|
16
|
+
vContextOptions,
|
|
17
|
+
vStorageOptions,
|
|
18
|
+
type AgentComponent,
|
|
19
|
+
type Agent,
|
|
20
|
+
} from "@convex-dev/agent";
|
|
21
|
+
import type { ToolSet } from "ai";
|
|
22
|
+
import { v } from "convex/values";
|
|
23
|
+
|
|
24
|
+
export type PlaygroundAPI = ApiFromModules<{
|
|
25
|
+
playground: ReturnType<typeof definePlaygroundAPI>;
|
|
26
|
+
}>["playground"];
|
|
27
|
+
|
|
28
|
+
export type AgentsFn<DataModel extends GenericDataModel> = (
|
|
29
|
+
ctx: GenericActionCtx<DataModel> | GenericQueryCtx<DataModel>,
|
|
30
|
+
args: { userId: string | undefined; threadId: string | undefined },
|
|
31
|
+
) => Agent[] | Promise<Agent[]>;
|
|
32
|
+
|
|
33
|
+
// Playground API definition
|
|
34
|
+
export function definePlaygroundAPI<DataModel extends GenericDataModel>(
|
|
35
|
+
component: AgentComponent,
|
|
36
|
+
{
|
|
37
|
+
agents: agentsOrFn,
|
|
38
|
+
userNameLookup,
|
|
39
|
+
}: {
|
|
40
|
+
agents: Agent[] | AgentsFn<DataModel>;
|
|
41
|
+
userNameLookup?: (
|
|
42
|
+
ctx: GenericQueryCtx<DataModel>,
|
|
43
|
+
userId: string,
|
|
44
|
+
) => string | Promise<string>;
|
|
45
|
+
},
|
|
46
|
+
) {
|
|
47
|
+
function validateAgents(agents: Agent[]) {
|
|
48
|
+
for (const agent of agents) {
|
|
49
|
+
if (!agent.options.name) {
|
|
50
|
+
console.warn(
|
|
51
|
+
`Agent has no name (instructions: ${agent.options.instructions})`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function validateApiKey(ctx: RunQueryCtx, apiKey: string) {
|
|
58
|
+
await ctx.runQuery(component.apiKeys.validate, { apiKey });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isApiKeyValid = queryGeneric({
|
|
62
|
+
args: {
|
|
63
|
+
apiKey: v.string(),
|
|
64
|
+
},
|
|
65
|
+
handler: async (ctx, args) => {
|
|
66
|
+
try {
|
|
67
|
+
await validateApiKey(ctx, args.apiKey);
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
returns: v.boolean(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
async function getAgents(
|
|
77
|
+
ctx: GenericActionCtx<DataModel> | GenericQueryCtx<DataModel>,
|
|
78
|
+
args: { userId: string | undefined; threadId: string | undefined },
|
|
79
|
+
) {
|
|
80
|
+
const agents = Array.isArray(agentsOrFn)
|
|
81
|
+
? agentsOrFn
|
|
82
|
+
: await agentsOrFn(ctx, args);
|
|
83
|
+
validateAgents(agents);
|
|
84
|
+
return agents.map((agent, i) => ({
|
|
85
|
+
name: agent.options.name ?? `Agent ${i} (missing 'name')`,
|
|
86
|
+
agent,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// List all agents
|
|
91
|
+
const listAgents = queryGeneric({
|
|
92
|
+
args: {
|
|
93
|
+
apiKey: v.string(),
|
|
94
|
+
userId: v.optional(v.string()),
|
|
95
|
+
threadId: v.optional(v.string()),
|
|
96
|
+
},
|
|
97
|
+
handler: async (ctx, args) => {
|
|
98
|
+
const agents = await getAgents(ctx, {
|
|
99
|
+
userId: args.userId,
|
|
100
|
+
threadId: args.threadId,
|
|
101
|
+
});
|
|
102
|
+
await validateApiKey(ctx, args.apiKey);
|
|
103
|
+
return agents.map(({ name, agent }) => ({
|
|
104
|
+
name,
|
|
105
|
+
instructions: agent.options.instructions,
|
|
106
|
+
contextOptions: agent.options.contextOptions,
|
|
107
|
+
storageOptions: agent.options.storageOptions,
|
|
108
|
+
maxSteps: agent.options.maxSteps,
|
|
109
|
+
maxRetries: agent.options.maxRetries,
|
|
110
|
+
tools: agent.options.tools ? Object.keys(agent.options.tools) : [],
|
|
111
|
+
}));
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const listUsers = queryGeneric({
|
|
116
|
+
args: {
|
|
117
|
+
apiKey: v.string(),
|
|
118
|
+
paginationOpts: paginationOptsValidator,
|
|
119
|
+
},
|
|
120
|
+
handler: async (ctx, args) => {
|
|
121
|
+
await validateApiKey(ctx, args.apiKey);
|
|
122
|
+
const users = await ctx.runQuery(component.users.listUsersWithThreads, {
|
|
123
|
+
paginationOpts: args.paginationOpts,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
...users,
|
|
127
|
+
page: await Promise.all(
|
|
128
|
+
users.page.map(async (userId) => ({
|
|
129
|
+
_id: userId,
|
|
130
|
+
name: userNameLookup ? await userNameLookup(ctx, userId) : userId,
|
|
131
|
+
})),
|
|
132
|
+
),
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
returns: vPaginationResult(
|
|
136
|
+
v.object({
|
|
137
|
+
_id: v.string(),
|
|
138
|
+
name: v.string(),
|
|
139
|
+
}),
|
|
140
|
+
),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// List threads for a user (query)
|
|
144
|
+
const listThreads = queryGeneric({
|
|
145
|
+
args: {
|
|
146
|
+
apiKey: v.string(),
|
|
147
|
+
userId: v.optional(v.string()),
|
|
148
|
+
paginationOpts: paginationOptsValidator,
|
|
149
|
+
},
|
|
150
|
+
handler: async (ctx, args) => {
|
|
151
|
+
await validateApiKey(ctx, args.apiKey);
|
|
152
|
+
const results = await ctx.runQuery(
|
|
153
|
+
component.threads.listThreadsByUserId,
|
|
154
|
+
{
|
|
155
|
+
userId: args.userId,
|
|
156
|
+
paginationOpts: args.paginationOpts,
|
|
157
|
+
order: "desc",
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
return {
|
|
161
|
+
...results,
|
|
162
|
+
page: await Promise.all(
|
|
163
|
+
results.page.map(async (thread) => {
|
|
164
|
+
const {
|
|
165
|
+
page: [last],
|
|
166
|
+
} = await ctx.runQuery(component.messages.listMessagesByThreadId, {
|
|
167
|
+
threadId: thread._id,
|
|
168
|
+
order: "desc",
|
|
169
|
+
paginationOpts: {
|
|
170
|
+
numItems: 1,
|
|
171
|
+
cursor: null,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
...thread,
|
|
176
|
+
lastAgentName: last?.agentName,
|
|
177
|
+
latestMessage: last?.text,
|
|
178
|
+
lastMessageAt: last?._creationTime,
|
|
179
|
+
};
|
|
180
|
+
}),
|
|
181
|
+
),
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
returns: vPaginationResult(
|
|
185
|
+
v.object({
|
|
186
|
+
...vThreadDoc.fields,
|
|
187
|
+
lastAgentName: v.optional(v.string()),
|
|
188
|
+
latestMessage: v.optional(v.string()),
|
|
189
|
+
lastMessageAt: v.optional(v.number()),
|
|
190
|
+
}),
|
|
191
|
+
),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// List messages for a thread (query)
|
|
195
|
+
const listMessages = queryGeneric({
|
|
196
|
+
args: {
|
|
197
|
+
apiKey: v.string(),
|
|
198
|
+
threadId: v.string(),
|
|
199
|
+
paginationOpts: paginationOptsValidator,
|
|
200
|
+
},
|
|
201
|
+
handler: async (ctx, args) => {
|
|
202
|
+
await validateApiKey(ctx, args.apiKey);
|
|
203
|
+
return ctx.runQuery(component.messages.listMessagesByThreadId, {
|
|
204
|
+
threadId: args.threadId,
|
|
205
|
+
paginationOpts: args.paginationOpts,
|
|
206
|
+
order: "desc",
|
|
207
|
+
statuses: ["success", "failed", "pending"],
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
returns: vPaginationResult(vMessageDoc),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Create a thread (mutation)
|
|
214
|
+
const createThread = mutationGeneric({
|
|
215
|
+
args: {
|
|
216
|
+
apiKey: v.string(),
|
|
217
|
+
userId: v.string(),
|
|
218
|
+
title: v.optional(v.string()),
|
|
219
|
+
summary: v.optional(v.string()),
|
|
220
|
+
/** @deprecated Unused. */
|
|
221
|
+
agentName: v.optional(v.string()),
|
|
222
|
+
},
|
|
223
|
+
handler: async (ctx, args) => {
|
|
224
|
+
// if (args.agentName) {
|
|
225
|
+
// console.warn(
|
|
226
|
+
// "Upgrade to the latest version of @convex-dev/agent-playground"
|
|
227
|
+
// );
|
|
228
|
+
// }
|
|
229
|
+
await validateApiKey(ctx, args.apiKey);
|
|
230
|
+
const { _id } = await ctx.runMutation(component.threads.createThread, {
|
|
231
|
+
userId: args.userId,
|
|
232
|
+
title: args.title,
|
|
233
|
+
summary: args.summary,
|
|
234
|
+
});
|
|
235
|
+
return { threadId: _id };
|
|
236
|
+
},
|
|
237
|
+
returns: v.object({ threadId: v.string() }),
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Send a message (action)
|
|
241
|
+
const generateText = actionGeneric({
|
|
242
|
+
args: {
|
|
243
|
+
apiKey: v.string(),
|
|
244
|
+
agentName: v.string(),
|
|
245
|
+
userId: v.string(),
|
|
246
|
+
threadId: v.string(),
|
|
247
|
+
// Options for generateText
|
|
248
|
+
contextOptions: v.optional(vContextOptions),
|
|
249
|
+
storageOptions: v.optional(vStorageOptions),
|
|
250
|
+
// Args passed through to generateText
|
|
251
|
+
prompt: v.optional(v.string()),
|
|
252
|
+
messages: v.optional(v.array(vMessage)),
|
|
253
|
+
system: v.optional(v.string()),
|
|
254
|
+
},
|
|
255
|
+
handler: async (ctx: GenericActionCtx<DataModel>, args) => {
|
|
256
|
+
const {
|
|
257
|
+
apiKey,
|
|
258
|
+
agentName,
|
|
259
|
+
userId,
|
|
260
|
+
threadId,
|
|
261
|
+
contextOptions,
|
|
262
|
+
storageOptions,
|
|
263
|
+
system,
|
|
264
|
+
...rest
|
|
265
|
+
} = args;
|
|
266
|
+
await validateApiKey(ctx, apiKey);
|
|
267
|
+
const agents = await getAgents(ctx, {
|
|
268
|
+
userId: args.userId,
|
|
269
|
+
threadId: args.threadId,
|
|
270
|
+
});
|
|
271
|
+
const namedAgent = agents.find(({ name }) => name === agentName);
|
|
272
|
+
if (!namedAgent) throw new Error(`Unknown agent: ${agentName}`);
|
|
273
|
+
const { agent } = namedAgent;
|
|
274
|
+
const { thread } = await agent.continueThread(ctx, { threadId, userId });
|
|
275
|
+
const { messageId, text } = await thread.generateText(
|
|
276
|
+
{ ...rest, ...(system ? { system } : {}) },
|
|
277
|
+
{
|
|
278
|
+
contextOptions,
|
|
279
|
+
storageOptions,
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
return { messageId, text };
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Fetch prompt context (action)
|
|
287
|
+
const fetchPromptContext = actionGeneric({
|
|
288
|
+
args: {
|
|
289
|
+
apiKey: v.string(),
|
|
290
|
+
agentName: v.string(),
|
|
291
|
+
userId: v.optional(v.string()),
|
|
292
|
+
threadId: v.optional(v.string()),
|
|
293
|
+
messages: v.array(vMessage),
|
|
294
|
+
contextOptions: vContextOptions,
|
|
295
|
+
beforeMessageId: v.optional(v.string()),
|
|
296
|
+
},
|
|
297
|
+
handler: async (ctx, args) => {
|
|
298
|
+
await validateApiKey(ctx, args.apiKey);
|
|
299
|
+
const agents = await getAgents(ctx, {
|
|
300
|
+
userId: args.userId,
|
|
301
|
+
threadId: args.threadId,
|
|
302
|
+
});
|
|
303
|
+
const namedAgent = agents.find(({ name }) => name === args.agentName);
|
|
304
|
+
if (!namedAgent) throw new Error(`Unknown agent: ${args.agentName}`);
|
|
305
|
+
const { agent } = namedAgent;
|
|
306
|
+
const contextOptions = args.contextOptions;
|
|
307
|
+
if (args.beforeMessageId) {
|
|
308
|
+
contextOptions.recentMessages =
|
|
309
|
+
(contextOptions.recentMessages ?? 10) + 1;
|
|
310
|
+
}
|
|
311
|
+
const messages = await agent.fetchContextMessages(ctx, {
|
|
312
|
+
userId: args.userId,
|
|
313
|
+
threadId: args.threadId,
|
|
314
|
+
messages: args.messages,
|
|
315
|
+
contextOptions: args.contextOptions,
|
|
316
|
+
upToAndIncludingMessageId: args.beforeMessageId,
|
|
317
|
+
});
|
|
318
|
+
return messages.filter(
|
|
319
|
+
(m) => !args.beforeMessageId || m._id !== args.beforeMessageId,
|
|
320
|
+
);
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
isApiKeyValid,
|
|
326
|
+
listUsers,
|
|
327
|
+
listThreads,
|
|
328
|
+
listMessages,
|
|
329
|
+
listAgents,
|
|
330
|
+
createThread,
|
|
331
|
+
generateText,
|
|
332
|
+
fetchPromptContext,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
type RunQueryCtx = { runQuery: GenericQueryCtx<GenericDataModel>["runQuery"] };
|
package/src/client/files.ts
CHANGED
|
@@ -51,7 +51,7 @@ export async function storeFile(
|
|
|
51
51
|
sha256 ||
|
|
52
52
|
Array.from(
|
|
53
53
|
new Uint8Array(
|
|
54
|
-
await crypto.subtle.digest("SHA-256", await blob.arrayBuffer()),
|
|
54
|
+
await crypto.subtle.digest("SHA-256", await blob.slice().arrayBuffer()),
|
|
55
55
|
),
|
|
56
56
|
)
|
|
57
57
|
.map((b) => b.toString(16).padStart(2, "0"))
|