@convex-dev/agent 0.0.9 → 0.0.11-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.
@@ -60,7 +60,13 @@ import {
60
60
  vStorageOptions,
61
61
  vTextArgs,
62
62
  } from "../validators";
63
- import { RunActionCtx, RunMutationCtx, RunQueryCtx, UseApi } from "./types.js";
63
+ import {
64
+ MessageDoc,
65
+ RunActionCtx,
66
+ RunMutationCtx,
67
+ RunQueryCtx,
68
+ UseApi,
69
+ } from "./types.js";
64
70
 
65
71
  export { convexToZod, zodToConvex };
66
72
  export type { ThreadDoc, MessageDoc } from "./types.js";
@@ -72,11 +78,13 @@ export type { ThreadDoc, MessageDoc } from "./types.js";
72
78
  export type ContextOptions = {
73
79
  /**
74
80
  * Whether to include tool messages in the context.
81
+ * By default, tool calls and results are not included.
75
82
  */
76
83
  includeToolCalls?: boolean;
77
84
  /**
78
85
  * How many recent messages to include. These are added after the search
79
86
  * messages, and do not count against the search limit.
87
+ * Default: 100
80
88
  */
81
89
  recentMessages?: number;
82
90
  /**
@@ -84,21 +92,24 @@ export type ContextOptions = {
84
92
  */
85
93
  searchOptions?: {
86
94
  /**
87
- * The maximum number of messages to fetch.
95
+ * The maximum number of messages to fetch. Default is 10.
88
96
  */
89
97
  limit: number;
90
98
  /**
91
- * Whether to use text search to find messages.
99
+ * Whether to use text search to find messages. Default is false.
92
100
  */
93
101
  textSearch?: boolean;
94
102
  /**
95
- * Whether to use vector search to find messages.
103
+ * Whether to use vector search to find messages. Default is false.
104
+ * At least one of textSearch or vectorSearch must be true.
96
105
  */
97
106
  vectorSearch?: boolean;
98
107
  /**
108
+ * What messages around the search results to include.
109
+ * Default: { before: 2, after: 1 }
110
+ * (two before, and one after each message found in the search)
99
111
  * Note, this is after the limit is applied.
100
112
  * By default this will quadruple the number of messages fetched.
101
- * (two before, and one after each message found in the search)
102
113
  */
103
114
  messageRange?: { before: number; after: number };
104
115
  };
@@ -367,7 +378,7 @@ export class Agent<AgentTools extends ToolSet> {
367
378
  ): Promise<CoreMessage[]> {
368
379
  assert(args.userId || args.threadId, "Specify userId or threadId");
369
380
  // Fetch the latest messages from the thread
370
- const contextMessages: CoreMessage[] = [];
381
+ const contextMessages: MessageDoc[] = [];
371
382
  let included: Set<string> | undefined;
372
383
  const opts = this.mergedContextOptions(args);
373
384
  if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
@@ -385,16 +396,14 @@ export class Agent<AgentTools extends ToolSet> {
385
396
  );
386
397
  // TODO: track what messages we used for context
387
398
  included = new Set(searchMessages.map((m) => m._id));
388
- contextMessages.push(
389
- ...searchMessages.map((m) => deserializeMessage(m.message!))
390
- );
399
+ contextMessages.push(...searchMessages);
391
400
  }
392
401
  if (args.threadId && opts.recentMessages !== 0) {
393
402
  const { page } = await ctx.runQuery(
394
403
  this.component.messages.getThreadMessages,
395
404
  {
396
405
  threadId: args.threadId,
397
- isTool: args.includeToolCalls ?? false,
406
+ isTool: opts.includeToolCalls ? undefined : false,
398
407
  paginationOpts: {
399
408
  numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
400
409
  cursor: null,
@@ -404,13 +413,13 @@ export class Agent<AgentTools extends ToolSet> {
404
413
  statuses: ["success"],
405
414
  }
406
415
  );
407
- contextMessages.push(
408
- ...page
409
- .filter((m) => !included?.has(m._id))
410
- .map((m) => deserializeMessage(m.message!))
411
- );
416
+ contextMessages.push(...page.filter((m) => !included?.has(m._id)));
412
417
  }
413
- return contextMessages;
418
+ return contextMessages
419
+ .sort((a, b) =>
420
+ a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order
421
+ )
422
+ .map((m) => deserializeMessage(m.message!));
414
423
  }
415
424
 
416
425
  async getEmbeddings(messages: CoreMessage[]) {
@@ -1284,6 +1293,10 @@ type OurStreamObjectArgs<T> = StreamObjectArgs<T> &
1284
1293
  "onError" | "onFinish" | "abortSignal"
1285
1294
  >;
1286
1295
 
1296
+ type ThreadOutputMetadata = GenerationOutputMetadata & {
1297
+ messageId: string;
1298
+ };
1299
+
1287
1300
  interface Thread<AgentTools extends ToolSet> {
1288
1301
  /**
1289
1302
  * The target threadId, from the startThread or continueThread initializers.
@@ -1306,7 +1319,7 @@ interface Thread<AgentTools extends ToolSet> {
1306
1319
  Parameters<typeof generateText<TOOLS, OUTPUT, OUTPUT_PARTIAL>>[0]
1307
1320
  >
1308
1321
  ): Promise<
1309
- GenerateTextResult<TOOLS & AgentTools, OUTPUT> & GenerationOutputMetadata
1322
+ GenerateTextResult<TOOLS & AgentTools, OUTPUT> & ThreadOutputMetadata
1310
1323
  >;
1311
1324
 
1312
1325
  /**
@@ -1326,8 +1339,7 @@ interface Thread<AgentTools extends ToolSet> {
1326
1339
  Parameters<typeof streamText<TOOLS, OUTPUT, PARTIAL_OUTPUT>>[0]
1327
1340
  >
1328
1341
  ): Promise<
1329
- StreamTextResult<TOOLS & AgentTools, PARTIAL_OUTPUT> &
1330
- GenerationOutputMetadata
1342
+ StreamTextResult<TOOLS & AgentTools, PARTIAL_OUTPUT> & ThreadOutputMetadata
1331
1343
  >;
1332
1344
  /**
1333
1345
  * This behaves like {@link generateObject} from the "ai" package except that
@@ -1341,7 +1353,7 @@ interface Thread<AgentTools extends ToolSet> {
1341
1353
  */
1342
1354
  generateObject<T>(
1343
1355
  args: OurObjectArgs<T>
1344
- ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata>;
1356
+ ): Promise<GenerateObjectResult<T> & ThreadOutputMetadata>;
1345
1357
  /**
1346
1358
  * This behaves like {@link generateObject} from the "ai" package except that
1347
1359
  * it add context based on the userId and threadId and saves the input and
@@ -1354,7 +1366,7 @@ interface Thread<AgentTools extends ToolSet> {
1354
1366
  */
1355
1367
  generateObject(
1356
1368
  args: GenerateObjectNoSchemaOptions
1357
- ): Promise<GenerateObjectResult<JSONValue> & GenerationOutputMetadata>;
1369
+ ): Promise<GenerateObjectResult<JSONValue> & ThreadOutputMetadata>;
1358
1370
  /**
1359
1371
  * This behaves like {@link streamObject} from the "ai" package except that
1360
1372
  * it add context based on the userId and threadId and saves the input and
@@ -1368,6 +1380,6 @@ interface Thread<AgentTools extends ToolSet> {
1368
1380
  streamObject<T>(
1369
1381
  args: OurStreamObjectArgs<T>
1370
1382
  ): Promise<
1371
- StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
1383
+ StreamObjectResult<DeepPartial<T>, T, never> & ThreadOutputMetadata
1372
1384
  >;
1373
1385
  }
@@ -772,7 +772,10 @@ export const _fetchVectorMessages = internalQuery({
772
772
  )
773
773
  ).filter(
774
774
  (m): m is Doc<"messages"> =>
775
- m !== undefined && m !== null && (!parent || m.order <= parent.order)
775
+ m !== undefined &&
776
+ m !== null &&
777
+ !m.tool &&
778
+ (!parent || m.order <= parent.order)
776
779
  );
777
780
  messages.push(...(args.textSearchMessages ?? []));
778
781
  // TODO: prioritize more recent messages
@@ -873,6 +876,8 @@ export const textSearch = query({
873
876
  ? q.search("text", args.text).eq("userId", args.userId)
874
877
  : q.search("text", args.text).eq("threadId", args.threadId!)
875
878
  )
879
+ // Just in case tool messages slip through
880
+ .filter((q) => q.eq(q.field("tool"), false))
876
881
  .take(args.limit);
877
882
  return messages;
878
883
  },
package/src/mapping.ts CHANGED
@@ -86,8 +86,8 @@ export function serializeNewMessagesInStep<TOOLS extends ToolSet>(
86
86
  // ref: https://github.com/vercel/ai/blob/main/packages/ai/core/generate-text/to-response-messages.ts
87
87
  const messages: MessageWithFileAndId[] = (
88
88
  step.toolResults.length > 0
89
- ? step.response.messages.slice(0, -2)
90
- : step.response.messages.slice(0, -1)
89
+ ? step.response.messages.slice(-2)
90
+ : step.response.messages.slice(-1)
91
91
  ).map(serializeMessageWithId);
92
92
  return messages;
93
93
  }
@@ -193,14 +193,10 @@ function deserializeUrl(urlOrString: string | ArrayBuffer): URL | DataContent {
193
193
  }
194
194
 
195
195
  export function promptOrMessagesToCoreMessages(args: {
196
- system?: string;
197
196
  prompt?: string;
198
197
  messages?: CoreMessage[] | AIMessageWithoutId[];
199
198
  }): CoreMessage[] {
200
199
  const messages: CoreMessage[] = [];
201
- if (args.system) {
202
- messages.push({ role: "system", content: args.system });
203
- }
204
200
  assert(args.prompt || args.messages, "messages or prompt is required");
205
201
  if (args.messages) {
206
202
  if (