@convex-dev/agent 0.0.17-alpha.2 → 0.1.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.
Files changed (49) hide show
  1. package/README.md +26 -8
  2. package/dist/commonjs/client/index.d.ts +229 -756
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +135 -140
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/component/messages.d.ts +208 -896
  7. package/dist/commonjs/component/messages.d.ts.map +1 -1
  8. package/dist/commonjs/component/messages.js +117 -109
  9. package/dist/commonjs/component/messages.js.map +1 -1
  10. package/dist/commonjs/component/schema.d.ts +1278 -580
  11. package/dist/commonjs/component/schema.d.ts.map +1 -1
  12. package/dist/commonjs/component/schema.js +28 -21
  13. package/dist/commonjs/component/schema.js.map +1 -1
  14. package/dist/commonjs/component/users.d.ts +5 -2
  15. package/dist/commonjs/component/users.d.ts.map +1 -1
  16. package/dist/commonjs/component/users.js +57 -27
  17. package/dist/commonjs/component/users.js.map +1 -1
  18. package/dist/commonjs/validators.d.ts +44 -59
  19. package/dist/commonjs/validators.d.ts.map +1 -1
  20. package/dist/commonjs/validators.js +10 -12
  21. package/dist/commonjs/validators.js.map +1 -1
  22. package/dist/esm/client/index.d.ts +229 -756
  23. package/dist/esm/client/index.d.ts.map +1 -1
  24. package/dist/esm/client/index.js +135 -140
  25. package/dist/esm/client/index.js.map +1 -1
  26. package/dist/esm/component/messages.d.ts +208 -896
  27. package/dist/esm/component/messages.d.ts.map +1 -1
  28. package/dist/esm/component/messages.js +117 -109
  29. package/dist/esm/component/messages.js.map +1 -1
  30. package/dist/esm/component/schema.d.ts +1278 -580
  31. package/dist/esm/component/schema.d.ts.map +1 -1
  32. package/dist/esm/component/schema.js +28 -21
  33. package/dist/esm/component/schema.js.map +1 -1
  34. package/dist/esm/component/users.d.ts +5 -2
  35. package/dist/esm/component/users.d.ts.map +1 -1
  36. package/dist/esm/component/users.js +57 -27
  37. package/dist/esm/component/users.js.map +1 -1
  38. package/dist/esm/validators.d.ts +44 -59
  39. package/dist/esm/validators.d.ts.map +1 -1
  40. package/dist/esm/validators.js +10 -12
  41. package/dist/esm/validators.js.map +1 -1
  42. package/package.json +4 -2
  43. package/src/client/index.ts +215 -199
  44. package/src/component/_generated/api.d.ts +19 -94
  45. package/src/component/messages.test.ts +110 -3
  46. package/src/component/messages.ts +176 -154
  47. package/src/component/schema.ts +34 -21
  48. package/src/component/users.ts +57 -32
  49. package/src/validators.ts +12 -15
@@ -27,7 +27,7 @@ import {
27
27
  } from "ai";
28
28
  import { assert } from "convex-helpers";
29
29
  import { internalActionGeneric, internalMutationGeneric } from "convex/server";
30
- import { Infer, v } from "convex/values";
30
+ import { v } from "convex/values";
31
31
  import { z } from "zod";
32
32
  import { Mounts } from "../component/_generated/api.js";
33
33
  import {
@@ -57,10 +57,9 @@ import {
57
57
  type ProviderOptions,
58
58
  type SearchOptions,
59
59
  type Usage,
60
- vFileWithStringId,
60
+ vMessageWithMetadata,
61
61
  vSafeObjectArgs,
62
62
  vTextArgs,
63
- vThreadStatus,
64
63
  } from "../validators.js";
65
64
  import type {
66
65
  OpaqueIds,
@@ -69,12 +68,16 @@ import type {
69
68
  RunQueryCtx,
70
69
  UseApi,
71
70
  } from "./types.js";
72
- import schema from "../component/schema.js";
73
71
 
72
+ import type { MessageDoc, ThreadDoc } from "../component/schema.js";
73
+
74
+ export { vMessageDoc, vThreadDoc } from "../component/schema.js";
74
75
  export { extractText, isTool };
75
- export type { Usage, ProviderMetadata };
76
+ export type { Usage, ProviderMetadata, MessageDoc, ThreadDoc };
76
77
  export {
78
+ /** @deprecated Use vPaginationResult instead. */
77
79
  paginationResultValidator,
80
+ paginationResultValidator as vPaginationResult,
78
81
  vContextOptions,
79
82
  vUsage,
80
83
  vProviderMetadata,
@@ -261,11 +264,11 @@ export class Agent<AgentTools extends ToolSet> {
261
264
  */
262
265
  userId?: string;
263
266
  /**
264
- * The title of the thread. Not currently used.
267
+ * The title of the thread. Not currently used for anything.
265
268
  */
266
269
  title?: string;
267
270
  /**
268
- * The summary of the thread. Not currently used.
271
+ * The summary of the thread. Not currently used for anything.
269
272
  */
270
273
  summary?: string;
271
274
  /**
@@ -301,11 +304,11 @@ export class Agent<AgentTools extends ToolSet> {
301
304
  */
302
305
  userId?: string;
303
306
  /**
304
- * The title of the thread. Not currently used.
307
+ * The title of the thread. Not currently used for anything.
305
308
  */
306
309
  title?: string;
307
310
  /**
308
- * The summary of the thread. Not currently used.
311
+ * The summary of the thread. Not currently used for anything.
309
312
  */
310
313
  summary?: string;
311
314
  /**
@@ -418,37 +421,23 @@ export class Agent<AgentTools extends ToolSet> {
418
421
  threadId: string | undefined;
419
422
  messages: CoreMessage[];
420
423
  /**
421
- * If provided, it will search for messages before this message.
422
- * Note: if this is far in the past, the search results may be more
424
+ * If provided, it will search for messages up to and including this message.
425
+ * Note: if this is far in the past, text and vector search results may be more
423
426
  * limited, as it's post-filtering the results.
424
427
  */
425
- beforeMessageId?: string;
428
+ upToAndIncludingMessageId?: string;
426
429
  contextOptions: ContextOptions | undefined;
427
430
  }
428
431
  ): Promise<MessageDoc[]> {
429
432
  assert(args.userId || args.threadId, "Specify userId or threadId");
430
433
  // Fetch the latest messages from the thread
431
- const contextMessages: MessageDoc[] = [];
432
434
  let included: Set<string> | undefined;
433
435
  const opts = this.mergedContextOptions(args.contextOptions);
434
- if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
435
- if (!("runAction" in ctx)) {
436
- throw new Error("searchUserMessages only works in an action");
437
- }
438
- const searchMessages = await ctx.runAction(
439
- this.component.messages.searchMessages,
440
- {
441
- userId: opts?.searchOtherThreads ? args.userId : undefined,
442
- threadId: args.threadId,
443
- beforeMessageId: args.beforeMessageId,
444
- ...(await this.searchOptionsWithDefaults(opts, args.messages)),
445
- }
446
- );
447
- // TODO: track what messages we used for context
448
- included = new Set(searchMessages.map((m) => m._id));
449
- contextMessages.push(...searchMessages);
450
- }
451
- if (args.threadId && opts.recentMessages !== 0) {
436
+ const contextMessages: MessageDoc[] = [];
437
+ if (
438
+ args.threadId &&
439
+ (opts.recentMessages !== 0 || args.upToAndIncludingMessageId)
440
+ ) {
452
441
  const { page } = await ctx.runQuery(
453
442
  this.component.messages.listMessagesByThreadId,
454
443
  {
@@ -459,14 +448,39 @@ export class Agent<AgentTools extends ToolSet> {
459
448
  numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
460
449
  cursor: null,
461
450
  },
462
- beforeMessageId: args.beforeMessageId,
451
+ upToAndIncludingMessageId: args.upToAndIncludingMessageId,
463
452
  order: "desc",
464
453
  statuses: ["success"],
465
454
  }
466
455
  );
456
+ included = new Set(page.map((m) => m._id));
467
457
  contextMessages.push(
468
458
  // Reverse since we fetched in descending order
469
- ...page.filter((m) => !included?.has(m._id)).reverse()
459
+ ...page.reverse()
460
+ );
461
+ }
462
+ if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
463
+ const targetMessage = contextMessages.find(
464
+ (m) => m._id === args.upToAndIncludingMessageId
465
+ )?.message;
466
+ const messagesToSearch = targetMessage
467
+ ? [targetMessage, ...args.messages]
468
+ : args.messages;
469
+ if (!("runAction" in ctx)) {
470
+ throw new Error("searchUserMessages only works in an action");
471
+ }
472
+ const searchMessages = await ctx.runAction(
473
+ this.component.messages.searchMessages,
474
+ {
475
+ userId: opts?.searchOtherThreads ? args.userId : undefined,
476
+ threadId: args.threadId,
477
+ beforeMessageId: args.upToAndIncludingMessageId,
478
+ ...(await this.searchOptionsWithDefaults(opts, messagesToSearch)),
479
+ }
480
+ );
481
+ // TODO: track what messages we used for context
482
+ contextMessages.unshift(
483
+ ...searchMessages.filter((m) => !included?.has(m._id))
470
484
  );
471
485
  }
472
486
  // Ensure we don't include tool messages without a corresponding tool call
@@ -534,6 +548,14 @@ export class Agent<AgentTools extends ToolSet> {
534
548
  args: {
535
549
  threadId: string;
536
550
  userId?: string;
551
+ /**
552
+ * The message that these messages are in response to. They will be
553
+ * the same "order" as this message, at increasing stepOrder(s).
554
+ */
555
+ promptMessageId?: string;
556
+ /**
557
+ * The messages to save.
558
+ */
537
559
  messages: CoreMessageMaybeWithId[];
538
560
  /**
539
561
  * Metadata to save with the messages. Each element corresponds to the
@@ -561,14 +583,11 @@ export class Agent<AgentTools extends ToolSet> {
561
583
  threadId: args.threadId,
562
584
  userId: args.userId,
563
585
  agentName: this.options.name,
586
+ promptMessageId: args.promptMessageId,
587
+ embeddings,
564
588
  messages: args.messages.map(
565
589
  (m, i) =>
566
590
  ({
567
- embedding: embeddings?.vectors[i] && {
568
- model: embeddings.model,
569
- dimension: embeddings.dimension,
570
- vector: embeddings.vectors[i],
571
- },
572
591
  ...args.metadata?.[i],
573
592
  message: serializeMessage(m),
574
593
  }) as MessageWithMetadata
@@ -582,10 +601,40 @@ export class Agent<AgentTools extends ToolSet> {
582
601
  };
583
602
  }
584
603
 
604
+ /**
605
+ * Save messages to the thread.
606
+ * Useful as a step in Workflows, e.g.
607
+ * ```ts
608
+ * const saveMessages = agent.asSaveMessagesMutation();
609
+ *
610
+ * const myWorkflow = workflow.define()
611
+ * ```
612
+ * @returns A mutation that can be used to save messages to the thread.
613
+ */
614
+ asSaveMessagesMutation() {
615
+ return internalMutationGeneric({
616
+ args: {
617
+ threadId: v.string(),
618
+ userId: v.optional(v.string()),
619
+ promptMessageId: v.optional(v.string()),
620
+ messages: v.array(vMessageWithMetadata),
621
+ pending: v.optional(v.boolean()),
622
+ failPendingSteps: v.optional(v.boolean()),
623
+ },
624
+ handler: async (ctx, args) => {
625
+ return this.saveMessages(ctx, {
626
+ ...args,
627
+ messages: args.messages.map((m) => m.message),
628
+ metadata: args.messages.map(({ message: _, ...m }) => m),
629
+ });
630
+ },
631
+ });
632
+ }
633
+
585
634
  /**
586
635
  * Explicitly save a "step" created by the AI SDK.
587
636
  * @param ctx The ctx argument to a mutation or action.
588
- * @param args What to save
637
+ * @param args The Step generated by the AI SDK.
589
638
  */
590
639
  async saveStep<TOOLS extends ToolSet>(
591
640
  ctx: RunMutationCtx,
@@ -595,7 +644,7 @@ export class Agent<AgentTools extends ToolSet> {
595
644
  /**
596
645
  * The message this step is in response to.
597
646
  */
598
- parentMessageId: string;
647
+ promptMessageId: string;
599
648
  /**
600
649
  * The step to save, possibly including multiple tool calls.
601
650
  */
@@ -620,20 +669,11 @@ export class Agent<AgentTools extends ToolSet> {
620
669
  const embeddings = await this.generateEmbeddings(
621
670
  messages.map((m) => m.message)
622
671
  );
623
- if (embeddings) {
624
- const { model, dimension, vectors } = embeddings;
625
- for (let i = 0; i < messages.length; i++) {
626
- const vector = vectors[i];
627
- if (vector) {
628
- messages[i].embedding = { model, dimension, vector };
629
- }
630
- }
631
- }
632
672
  await ctx.runMutation(this.component.messages.addStep, {
633
673
  userId: args.userId,
634
674
  threadId: args.threadId,
635
- parentMessageId: args.parentMessageId,
636
- step: { step, messages },
675
+ promptMessageId: args.promptMessageId,
676
+ step: { step, messages, embeddings },
637
677
  failPendingSteps: false,
638
678
  });
639
679
  }
@@ -698,9 +738,7 @@ export class Agent<AgentTools extends ToolSet> {
698
738
  * set in the agent constructor.
699
739
  */
700
740
  usageHandler?: UsageHandler;
701
- /**
702
- * The tools to use for this thread. Overrides any tools passed in the agent constructor.
703
- */
741
+ /** @deprecated Pass `tools` in the next parameter instead. This is only intended to pass through thread-default tools. */
704
742
  tools?: ToolSet;
705
743
  },
706
744
  args: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>,
@@ -709,7 +747,7 @@ export class Agent<AgentTools extends ToolSet> {
709
747
  GenerateTextResult<TOOLS extends undefined ? AgentTools : TOOLS, OUTPUT> &
710
748
  GenerationOutputMetadata
711
749
  > {
712
- const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
750
+ const { args: aiArgs, messageId } = await this._saveMessagesAndFetchContext(
713
751
  ctx,
714
752
  args,
715
753
  { userId, threadId, ...options }
@@ -721,24 +759,20 @@ export class Agent<AgentTools extends ToolSet> {
721
759
  ) as TOOLS extends undefined ? AgentTools : TOOLS;
722
760
  const saveOutputMessages =
723
761
  options?.storageOptions?.saveOutputMessages ??
724
- args.saveOutputMessages ??
725
762
  this.options.storageOptions?.saveOutputMessages;
726
- const model = aiArgs.model ?? this.options.chat;
727
763
  const trackUsage = usageHandler ?? this.options.usageHandler;
728
764
  try {
729
765
  const result = (await generateText({
730
766
  // Can be overridden
731
767
  maxSteps: this.options.maxSteps,
732
- maxRetries: this.options.maxRetries,
733
768
  ...aiArgs,
734
- model,
735
769
  tools,
736
770
  onStepFinish: async (step) => {
737
771
  if (threadId && messageId && saveOutputMessages !== false) {
738
772
  await this.saveStep(ctx, {
739
773
  userId,
740
774
  threadId,
741
- parentMessageId: messageId,
775
+ promptMessageId: messageId,
742
776
  step,
743
777
  });
744
778
  }
@@ -747,8 +781,8 @@ export class Agent<AgentTools extends ToolSet> {
747
781
  userId,
748
782
  threadId,
749
783
  agentName: this.options.name,
750
- model: model.modelId,
751
- provider: model.provider,
784
+ model: aiArgs.model.modelId,
785
+ provider: aiArgs.model.provider,
752
786
  usage: step.usage,
753
787
  providerMetadata: step.providerMetadata,
754
788
  });
@@ -780,11 +814,6 @@ export class Agent<AgentTools extends ToolSet> {
780
814
  * resulting messages to the thread, if specified.
781
815
  * Use {@link continueThread} to get a version of this function already scoped
782
816
  * to a thread (and optionally userId).
783
- * @param ctx The context passed from the action function calling this.
784
- * @param { userId, threadId }: The user and thread to associate the message with
785
- * @param args The arguments to the streamText function, along with extra controls
786
- * for the {@link ContextOptions} and {@link StorageOptions}.
787
- * @returns The result of the streamText function.
788
817
  */
789
818
  async streamText<
790
819
  TOOLS extends ToolSet | undefined = undefined,
@@ -807,7 +836,14 @@ export class Agent<AgentTools extends ToolSet> {
807
836
  usageHandler?: UsageHandler;
808
837
  tools?: ToolSet;
809
838
  },
839
+ /**
840
+ * The arguments to the streamText function, similar to the ai `streamText` function.
841
+ */
810
842
  args: StreamingTextArgs<AgentTools, TOOLS, OUTPUT, PARTIAL_OUTPUT>,
843
+ /**
844
+ * The {@link ContextOptions} and {@link StorageOptions}
845
+ * options to use for fetching contextual messages and saving input/output messages.
846
+ */
811
847
  options?: Options
812
848
  ): Promise<
813
849
  StreamTextResult<
@@ -816,7 +852,7 @@ export class Agent<AgentTools extends ToolSet> {
816
852
  > &
817
853
  GenerationOutputMetadata
818
854
  > {
819
- const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
855
+ const { args: aiArgs, messageId } = await this._saveMessagesAndFetchContext(
820
856
  ctx,
821
857
  args,
822
858
  { userId, threadId, ...options }
@@ -828,16 +864,12 @@ export class Agent<AgentTools extends ToolSet> {
828
864
  ) as TOOLS extends undefined ? AgentTools : TOOLS;
829
865
  const saveOutputMessages =
830
866
  options?.storageOptions?.saveOutputMessages ??
831
- args.saveOutputMessages ??
832
867
  this.options.storageOptions?.saveOutputMessages;
833
- const model = aiArgs.model ?? this.options.chat;
834
868
  const trackUsage = usageHandler ?? this.options.usageHandler;
835
869
  const result = streamText({
836
870
  // Can be overridden
837
871
  maxSteps: this.options.maxSteps,
838
- maxRetries: this.options.maxRetries,
839
872
  ...aiArgs,
840
- model,
841
873
  tools,
842
874
  onChunk: async (chunk) => {
843
875
  // console.log("onChunk", chunk);
@@ -860,7 +892,7 @@ export class Agent<AgentTools extends ToolSet> {
860
892
  await this.saveStep(ctx, {
861
893
  userId,
862
894
  threadId,
863
- parentMessageId: messageId,
895
+ promptMessageId: messageId,
864
896
  step,
865
897
  });
866
898
  }
@@ -869,8 +901,8 @@ export class Agent<AgentTools extends ToolSet> {
869
901
  userId,
870
902
  threadId,
871
903
  agentName: this.options.name,
872
- model: model.modelId,
873
- provider: model.provider,
904
+ model: aiArgs.model.modelId,
905
+ provider: aiArgs.model.provider,
874
906
  usage: step.usage,
875
907
  providerMetadata: step.providerMetadata,
876
908
  });
@@ -886,12 +918,15 @@ export class Agent<AgentTools extends ToolSet> {
886
918
  return result;
887
919
  }
888
920
 
889
- async saveMessagesAndFetchContext<
921
+ async _saveMessagesAndFetchContext<
890
922
  T extends {
891
923
  id?: string;
892
924
  prompt?: string;
893
925
  messages?: CoreMessage[] | AIMessageWithoutId[];
894
926
  system?: string;
927
+ promptMessageId?: string;
928
+ model?: LanguageModelV1;
929
+ maxRetries?: number;
895
930
  },
896
931
  >(
897
932
  ctx: RunActionCtx | RunMutationCtx,
@@ -906,20 +941,33 @@ export class Agent<AgentTools extends ToolSet> {
906
941
  threadId: string | undefined;
907
942
  } & Options
908
943
  ): Promise<{
909
- args: T;
944
+ args: T & { model: LanguageModelV1 };
910
945
  messageId: string | undefined;
911
946
  }> {
912
- contextOptions ||= this.options.contextOptions ?? (args as ContextOptions);
913
- storageOptions ||= this.options.storageOptions ?? (args as StorageOptions);
914
- const messages = promptOrMessagesToCoreMessages(args);
947
+ contextOptions ||= this.options.contextOptions;
948
+ storageOptions ||= this.options.storageOptions;
949
+ // If only a messageId is provided, this will be empty.
950
+ const messages = args.promptMessageId
951
+ ? []
952
+ : promptOrMessagesToCoreMessages(args);
953
+ assert(
954
+ !args.promptMessageId || !(args.prompt || args.messages),
955
+ "you can't specify a prompt or message if you specify a promptMessageId"
956
+ );
957
+ // If only a messageId is provided, this will add that message to the end.
915
958
  const contextMessages = await this.fetchContextMessages(ctx, {
916
959
  userId,
917
960
  threadId,
961
+ upToAndIncludingMessageId: args.promptMessageId,
918
962
  messages,
919
963
  contextOptions,
920
964
  });
921
- let messageId: string | undefined;
922
- if (threadId && storageOptions?.saveAnyInputMessages !== false) {
965
+ let messageId = args.promptMessageId;
966
+ if (
967
+ threadId &&
968
+ messages.length &&
969
+ storageOptions?.saveAnyInputMessages !== false
970
+ ) {
923
971
  const saveAll = storageOptions?.saveAllInputMessages;
924
972
  const coreMessages = saveAll ? messages : messages.slice(-1);
925
973
  const saved = await this.saveMessages(ctx, {
@@ -932,16 +980,18 @@ export class Agent<AgentTools extends ToolSet> {
932
980
  });
933
981
  messageId = saved.lastMessageId;
934
982
  }
935
- const { prompt: _, ...rest } = args;
983
+ const { prompt: _, model, ...rest } = args;
936
984
  return {
937
985
  args: {
938
986
  ...rest,
987
+ maxRetries: args.maxRetries ?? this.options.maxRetries,
988
+ model: model ?? this.options.chat,
939
989
  system: args.system ?? this.options.instructions,
940
990
  messages: [
941
991
  ...contextMessages.map((m) => deserializeMessage(m.message!)),
942
992
  ...messages,
943
993
  ],
944
- } as T,
994
+ } as T & { model: LanguageModelV1 },
945
995
  messageId,
946
996
  };
947
997
  }
@@ -952,11 +1002,6 @@ export class Agent<AgentTools extends ToolSet> {
952
1002
  * resulting messages to the thread, if specified.
953
1003
  * Use {@link continueThread} to get a version of this function already scoped
954
1004
  * to a thread (and optionally userId).
955
- * @param ctx The context passed from the action function calling this.
956
- * @param { userId, threadId }: The user and thread to associate the message with
957
- * @param args The arguments to the generateObject function, along with extra controls
958
- * for the {@link ContextOptions} and {@link StorageOptions}.
959
- * @returns The result of the generateObject function.
960
1005
  */
961
1006
  async generateObject<T>(
962
1007
  ctx: RunActionCtx,
@@ -965,33 +1010,35 @@ export class Agent<AgentTools extends ToolSet> {
965
1010
  threadId,
966
1011
  usageHandler,
967
1012
  }: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
1013
+ /**
1014
+ * The arguments to the generateObject function, similar to the ai.generateObject function.
1015
+ */
968
1016
  args: OurObjectArgs<T>,
1017
+ /**
1018
+ * The {@link ContextOptions} and {@link StorageOptions}
1019
+ * options to use for fetching contextual messages and saving input/output messages.
1020
+ */
969
1021
  options?: Options
970
1022
  ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
971
- const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
1023
+ const { args: aiArgs, messageId } = await this._saveMessagesAndFetchContext(
972
1024
  ctx,
973
1025
  args,
974
1026
  { userId, threadId, ...options }
975
1027
  );
976
- const model = aiArgs.model ?? this.options.chat;
977
1028
  const trackUsage = usageHandler ?? this.options.usageHandler;
978
1029
  const saveOutputMessages =
979
1030
  options?.storageOptions?.saveOutputMessages ??
980
- args.saveOutputMessages ??
981
1031
  this.options.storageOptions?.saveOutputMessages;
982
1032
  try {
983
- const result = (await generateObject({
984
- // Can be overridden
985
- maxRetries: this.options.maxRetries,
986
- ...aiArgs,
987
- model,
1033
+ const result = (await generateObject(
988
1034
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
989
- } as any)) as GenerateObjectResult<T> & GenerationOutputMetadata;
1035
+ aiArgs as any
1036
+ )) as GenerateObjectResult<T> & GenerationOutputMetadata;
990
1037
 
991
1038
  if (threadId && messageId && saveOutputMessages !== false) {
992
1039
  await this.saveObject(ctx, {
993
1040
  threadId,
994
- parentMessageId: messageId,
1041
+ promptMessageId: messageId,
995
1042
  result,
996
1043
  userId,
997
1044
  });
@@ -1002,8 +1049,8 @@ export class Agent<AgentTools extends ToolSet> {
1002
1049
  userId,
1003
1050
  threadId,
1004
1051
  agentName: this.options.name,
1005
- model: model.modelId,
1006
- provider: model.provider,
1052
+ model: aiArgs.model.modelId,
1053
+ provider: aiArgs.model.provider,
1007
1054
  usage: result.usage,
1008
1055
  providerMetadata: result.providerMetadata,
1009
1056
  });
@@ -1021,16 +1068,11 @@ export class Agent<AgentTools extends ToolSet> {
1021
1068
  }
1022
1069
 
1023
1070
  /**
1024
- * This behaves like {@link streamObject} from the "ai" package except that
1071
+ * This behaves like `streamObject` from the "ai" package except that
1025
1072
  * it add context based on the userId and threadId and saves the input and
1026
1073
  * resulting messages to the thread, if specified.
1027
1074
  * Use {@link continueThread} to get a version of this function already scoped
1028
1075
  * to a thread (and optionally userId).
1029
- * @param ctx The context passed from the action function calling this.
1030
- * @param { userId, threadId }: The user and thread to associate the message with
1031
- * @param args The arguments to the streamObject function, along with extra controls
1032
- * for the {@link ContextOptions} and {@link StorageOptions}.
1033
- * @returns The result of the streamObject function.
1034
1076
  */
1035
1077
  async streamObject<T>(
1036
1078
  ctx: RunActionCtx,
@@ -1039,29 +1081,31 @@ export class Agent<AgentTools extends ToolSet> {
1039
1081
  threadId,
1040
1082
  usageHandler,
1041
1083
  }: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
1084
+ /**
1085
+ * The arguments to the streamObject function, similar to the ai `streamObject` function.
1086
+ */
1042
1087
  args: OurStreamObjectArgs<T>,
1088
+ /**
1089
+ * The {@link ContextOptions} and {@link StorageOptions}
1090
+ * options to use for fetching contextual messages and saving input/output messages.
1091
+ */
1043
1092
  options?: Options
1044
1093
  ): Promise<
1045
1094
  StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
1046
1095
  > {
1047
1096
  // TODO: unify all this shared code between all the generate* and stream* functions
1048
- const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
1097
+ const { args: aiArgs, messageId } = await this._saveMessagesAndFetchContext(
1049
1098
  ctx,
1050
1099
  args,
1051
1100
  { userId, threadId, ...options }
1052
1101
  );
1053
- const model = aiArgs.model ?? this.options.chat;
1054
1102
  const trackUsage = usageHandler ?? this.options.usageHandler;
1055
1103
  const saveOutputMessages =
1056
1104
  options?.storageOptions?.saveOutputMessages ??
1057
- args.saveOutputMessages ??
1058
1105
  this.options.storageOptions?.saveOutputMessages;
1059
1106
  const stream = streamObject<T>({
1060
- // Can be overridden
1061
- maxRetries: this.options.maxRetries,
1062
1107
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1063
1108
  ...(aiArgs as any),
1064
- model,
1065
1109
  onError: async (error) => {
1066
1110
  console.error("onError", error);
1067
1111
  return args.onError?.(error);
@@ -1071,7 +1115,7 @@ export class Agent<AgentTools extends ToolSet> {
1071
1115
  await this.saveObject(ctx, {
1072
1116
  userId,
1073
1117
  threadId,
1074
- parentMessageId: messageId,
1118
+ promptMessageId: messageId,
1075
1119
  result: {
1076
1120
  object: result.object,
1077
1121
  finishReason: "stop",
@@ -1092,8 +1136,8 @@ export class Agent<AgentTools extends ToolSet> {
1092
1136
  userId,
1093
1137
  threadId,
1094
1138
  agentName: this.options.name,
1095
- model: model.modelId,
1096
- provider: model.provider,
1139
+ model: aiArgs.model.modelId,
1140
+ provider: aiArgs.model.provider,
1097
1141
  usage: result.usage,
1098
1142
  providerMetadata: result.providerMetadata,
1099
1143
  });
@@ -1119,38 +1163,25 @@ export class Agent<AgentTools extends ToolSet> {
1119
1163
  args: {
1120
1164
  userId: string | undefined;
1121
1165
  threadId: string;
1122
- parentMessageId: string;
1166
+ promptMessageId: string;
1123
1167
  result: GenerateObjectResult<unknown>;
1124
1168
  metadata?: Omit<MessageWithMetadata, "message">;
1125
1169
  }
1126
1170
  ): Promise<void> {
1127
- const { step, messages: withoutEmbed } = serializeObjectResult(
1128
- args.result,
1129
- {
1130
- model: this.options.chat.modelId,
1131
- provider: this.options.chat.provider,
1132
- }
1171
+ const { step, messages } = serializeObjectResult(args.result, {
1172
+ model: this.options.chat.modelId,
1173
+ provider: this.options.chat.provider,
1174
+ });
1175
+ const embeddings = await this.generateEmbeddings(
1176
+ messages.map((m) => m.message)
1133
1177
  );
1134
- const embeddings = await this.generateEmbeddings([withoutEmbed[0].message]);
1135
- const messages = embeddings?.vectors[0]
1136
- ? [
1137
- {
1138
- ...withoutEmbed[0],
1139
- embedding: {
1140
- dimension: embeddings.dimension,
1141
- model: embeddings.model,
1142
- vector: embeddings.vectors[0],
1143
- },
1144
- },
1145
- ]
1146
- : withoutEmbed;
1147
1178
 
1148
1179
  await ctx.runMutation(this.component.messages.addStep, {
1149
1180
  userId: args.userId,
1150
1181
  threadId: args.threadId,
1151
- parentMessageId: args.parentMessageId,
1182
+ promptMessageId: args.promptMessageId,
1152
1183
  failPendingSteps: false,
1153
- step: { step, messages },
1184
+ step: { step, messages, embeddings },
1154
1185
  });
1155
1186
  }
1156
1187
 
@@ -1239,11 +1270,11 @@ export class Agent<AgentTools extends ToolSet> {
1239
1270
  * Create an action out of this agent so you can call it from workflows or other actions
1240
1271
  * without a wrapping function.
1241
1272
  * @param spec Configuration for the agent acting as an action, including
1242
- * {@link ContextOptions} and maxSteps.
1273
+ * {@link ContextOptions}, {@link StorageOptions}, and maxSteps.
1243
1274
  */
1244
1275
  asTextAction(spec?: {
1245
- contextOptions?: ContextOptions;
1246
1276
  maxSteps?: number;
1277
+ contextOptions?: ContextOptions;
1247
1278
  storageOptions?: StorageOptions;
1248
1279
  }) {
1249
1280
  const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
@@ -1308,7 +1339,7 @@ export class Agent<AgentTools extends ToolSet> {
1308
1339
  this.options.storageOptions,
1309
1340
  }
1310
1341
  );
1311
- return value.object as T;
1342
+ return { object: value.object as T };
1312
1343
  },
1313
1344
  });
1314
1345
  }
@@ -1341,7 +1372,6 @@ export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
1341
1372
  return result;
1342
1373
  }
1343
1374
 
1344
-
1345
1375
  export type ToolCtx = RunActionCtx & {
1346
1376
  userId?: string;
1347
1377
  threadId?: string;
@@ -1468,6 +1498,13 @@ type TextArgs<
1468
1498
  >[0],
1469
1499
  "toolChoice" | "tools" | "model"
1470
1500
  > & {
1501
+ /**
1502
+ * If provided, this message will be used as the "prompt" for the LLM call,
1503
+ * instead of the prompt or messages.
1504
+ * This is useful if you want to first save a user message, then use it as
1505
+ * the prompt for the LLM call in another call.
1506
+ */
1507
+ promptMessageId?: string;
1471
1508
  /**
1472
1509
  * The model to use for the tool calls. This will override the model specified
1473
1510
  * in the Agent constructor.
@@ -1483,8 +1520,7 @@ type TextArgs<
1483
1520
  * specified in the tools array. e.g. {toolName: "getWeather", type: "tool"}
1484
1521
  */
1485
1522
  toolChoice?: ToolChoice<TOOLS extends undefined ? AgentTools : TOOLS>;
1486
- } & ContextOptions & // DEPRECATED: pass them in the subsequent parameter instead
1487
- StorageOptions;
1523
+ };
1488
1524
 
1489
1525
  type StreamingTextArgs<
1490
1526
  AgentTools extends ToolSet,
@@ -1516,38 +1552,42 @@ type StreamingTextArgs<
1516
1552
  * specified in the tools array. e.g. {toolName: "getWeather", type: "tool"}
1517
1553
  */
1518
1554
  toolChoice?: ToolChoice<TOOLS extends undefined ? AgentTools : TOOLS>;
1519
- } & ContextOptions &
1520
- StorageOptions;
1555
+ };
1521
1556
 
1522
- type BaseGenerateObjectOptions = StorageOptions &
1523
- ContextOptions &
1524
- CallSettings & {
1525
- /**
1526
- * The model to use for the object generation. This will override the model
1527
- * specified in the Agent constructor.
1528
- */
1529
- model?: LanguageModelV1;
1530
- /**
1531
- * The system prompt to use for the object generation. This will override the
1532
- * system prompt specified in the Agent constructor.
1533
- */
1534
- system?: string;
1535
- /**
1536
- * The prompt to the LLM to use for the object generation.
1537
- * Specify this or messages, but not both.
1538
- */
1539
- prompt?: string;
1540
- /**
1541
- * The messages to use for the object generation.
1542
- * Note: recent messages are automatically added based on the thread it's
1543
- * associated with and your contextOptions.
1544
- */
1545
- messages?: CoreMessage[];
1546
- experimental_repairText?: RepairTextFunction;
1547
- experimental_telemetry?: TelemetrySettings;
1548
- providerOptions?: ProviderOptions;
1549
- experimental_providerMetadata?: ProviderMetadata;
1550
- };
1557
+ type BaseGenerateObjectOptions = CallSettings & {
1558
+ /**
1559
+ * The model to use for the object generation. This will override the model
1560
+ * specified in the Agent constructor.
1561
+ */
1562
+ model?: LanguageModelV1;
1563
+ /**
1564
+ * The system prompt to use for the object generation. This will override the
1565
+ * system prompt specified in the Agent constructor.
1566
+ */
1567
+ system?: string;
1568
+ /**
1569
+ * The prompt to the LLM to use for the object generation.
1570
+ * Specify this or messages, but not both.
1571
+ */
1572
+ prompt?: string;
1573
+ /**
1574
+ * The messages to use for the object generation.
1575
+ * Note: recent messages are automatically added based on the thread it's
1576
+ * associated with and your contextOptions.
1577
+ */
1578
+ messages?: CoreMessage[];
1579
+ /**
1580
+ * The message to use as the "prompt" for the object generation.
1581
+ * If this is provided, it will be used instead of the prompt or messages.
1582
+ * This is useful if you want to first save a user message, then use it as
1583
+ * the prompt for the object generation in another call.
1584
+ */
1585
+ promptMessageId?: string;
1586
+ experimental_repairText?: RepairTextFunction;
1587
+ experimental_telemetry?: TelemetrySettings;
1588
+ providerOptions?: ProviderOptions;
1589
+ experimental_providerMetadata?: ProviderMetadata;
1590
+ };
1551
1591
 
1552
1592
  type GenerateObjectObjectOptions<T extends Record<string, unknown>> =
1553
1593
  BaseGenerateObjectOptions & {
@@ -1724,30 +1764,6 @@ interface Thread<DefaultTools extends ToolSet> {
1724
1764
  >;
1725
1765
  }
1726
1766
 
1727
- export const vThreadDoc = v.object({
1728
- _id: v.string(),
1729
- _creationTime: v.number(),
1730
- userId: v.optional(v.string()), // Unset for anonymous
1731
- title: v.optional(v.string()),
1732
- summary: v.optional(v.string()),
1733
- status: vThreadStatus,
1734
- });
1735
- export type ThreadDoc = Infer<typeof vThreadDoc>;
1736
-
1737
- export const vMessageDoc = v.object({
1738
- _id: v.string(),
1739
- _creationTime: v.number(),
1740
- ...schema.tables.messages.validator.fields,
1741
- // Overwrite all the types that have a v.id validator
1742
- // Outside of the component, they are strings
1743
- threadId: v.string(),
1744
- parentMessageId: v.optional(v.string()),
1745
- stepId: v.optional(v.string()),
1746
- embeddingId: v.optional(v.string()),
1747
- files: v.optional(v.array(vFileWithStringId)),
1748
- });
1749
- export type MessageDoc = Infer<typeof vMessageDoc>;
1750
-
1751
1767
  type MessageWithMetadata = OpaqueIds<InnerMessageWithMetadata>;
1752
1768
 
1753
1769
  export function toUIMessages(messages: MessageDoc[]): UIMessage[] {