@convex-dev/agent 0.1.1 → 0.1.2-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.
Files changed (109) hide show
  1. package/README.md +9 -3
  2. package/dist/commonjs/client/createTool.d.ts +49 -0
  3. package/dist/commonjs/client/createTool.d.ts.map +1 -0
  4. package/dist/commonjs/client/createTool.js +45 -0
  5. package/dist/commonjs/client/createTool.js.map +1 -0
  6. package/dist/commonjs/client/index.d.ts +101 -322
  7. package/dist/commonjs/client/index.d.ts.map +1 -1
  8. package/dist/commonjs/client/index.js +142 -194
  9. package/dist/commonjs/client/index.js.map +1 -1
  10. package/dist/commonjs/client/streaming.d.ts +54 -0
  11. package/dist/commonjs/client/streaming.d.ts.map +1 -0
  12. package/dist/commonjs/client/streaming.js +121 -0
  13. package/dist/commonjs/client/streaming.js.map +1 -0
  14. package/dist/commonjs/client/types.d.ts +290 -2
  15. package/dist/commonjs/client/types.d.ts.map +1 -1
  16. package/dist/commonjs/component/messages.d.ts +104 -138
  17. package/dist/commonjs/component/messages.d.ts.map +1 -1
  18. package/dist/commonjs/component/messages.js +15 -14
  19. package/dist/commonjs/component/messages.js.map +1 -1
  20. package/dist/commonjs/component/schema.d.ts +873 -3
  21. package/dist/commonjs/component/schema.d.ts.map +1 -1
  22. package/dist/commonjs/component/schema.js +39 -1
  23. package/dist/commonjs/component/schema.js.map +1 -1
  24. package/dist/commonjs/component/streams.d.ts +191 -0
  25. package/dist/commonjs/component/streams.d.ts.map +1 -0
  26. package/dist/commonjs/component/streams.js +169 -0
  27. package/dist/commonjs/component/streams.js.map +1 -0
  28. package/dist/commonjs/component/threads.d.ts +1 -1
  29. package/dist/commonjs/component/threads.js +3 -3
  30. package/dist/commonjs/component/threads.js.map +1 -1
  31. package/dist/commonjs/component/users.js +2 -2
  32. package/dist/commonjs/component/users.js.map +1 -1
  33. package/dist/commonjs/mapping.d.ts.map +1 -1
  34. package/dist/commonjs/mapping.js +3 -1
  35. package/dist/commonjs/mapping.js.map +1 -1
  36. package/dist/commonjs/react/index.d.ts +99 -0
  37. package/dist/commonjs/react/index.d.ts.map +1 -1
  38. package/dist/commonjs/react/index.js +505 -1
  39. package/dist/commonjs/react/index.js.map +1 -1
  40. package/dist/commonjs/react/toUIMessages.d.ts +8 -0
  41. package/dist/commonjs/react/toUIMessages.d.ts.map +1 -0
  42. package/dist/commonjs/react/toUIMessages.js +134 -0
  43. package/dist/commonjs/react/toUIMessages.js.map +1 -0
  44. package/dist/commonjs/validators.d.ts +506 -4
  45. package/dist/commonjs/validators.d.ts.map +1 -1
  46. package/dist/commonjs/validators.js +49 -2
  47. package/dist/commonjs/validators.js.map +1 -1
  48. package/dist/esm/client/createTool.d.ts +49 -0
  49. package/dist/esm/client/createTool.d.ts.map +1 -0
  50. package/dist/esm/client/createTool.js +45 -0
  51. package/dist/esm/client/createTool.js.map +1 -0
  52. package/dist/esm/client/index.d.ts +101 -322
  53. package/dist/esm/client/index.d.ts.map +1 -1
  54. package/dist/esm/client/index.js +142 -194
  55. package/dist/esm/client/index.js.map +1 -1
  56. package/dist/esm/client/streaming.d.ts +54 -0
  57. package/dist/esm/client/streaming.d.ts.map +1 -0
  58. package/dist/esm/client/streaming.js +121 -0
  59. package/dist/esm/client/streaming.js.map +1 -0
  60. package/dist/esm/client/types.d.ts +290 -2
  61. package/dist/esm/client/types.d.ts.map +1 -1
  62. package/dist/esm/component/messages.d.ts +104 -138
  63. package/dist/esm/component/messages.d.ts.map +1 -1
  64. package/dist/esm/component/messages.js +15 -14
  65. package/dist/esm/component/messages.js.map +1 -1
  66. package/dist/esm/component/schema.d.ts +873 -3
  67. package/dist/esm/component/schema.d.ts.map +1 -1
  68. package/dist/esm/component/schema.js +39 -1
  69. package/dist/esm/component/schema.js.map +1 -1
  70. package/dist/esm/component/streams.d.ts +191 -0
  71. package/dist/esm/component/streams.d.ts.map +1 -0
  72. package/dist/esm/component/streams.js +169 -0
  73. package/dist/esm/component/streams.js.map +1 -0
  74. package/dist/esm/component/threads.d.ts +1 -1
  75. package/dist/esm/component/threads.js +3 -3
  76. package/dist/esm/component/threads.js.map +1 -1
  77. package/dist/esm/component/users.js +2 -2
  78. package/dist/esm/component/users.js.map +1 -1
  79. package/dist/esm/mapping.d.ts.map +1 -1
  80. package/dist/esm/mapping.js +3 -1
  81. package/dist/esm/mapping.js.map +1 -1
  82. package/dist/esm/react/index.d.ts +99 -0
  83. package/dist/esm/react/index.d.ts.map +1 -1
  84. package/dist/esm/react/index.js +505 -1
  85. package/dist/esm/react/index.js.map +1 -1
  86. package/dist/esm/react/toUIMessages.d.ts +8 -0
  87. package/dist/esm/react/toUIMessages.d.ts.map +1 -0
  88. package/dist/esm/react/toUIMessages.js +134 -0
  89. package/dist/esm/react/toUIMessages.js.map +1 -0
  90. package/dist/esm/validators.d.ts +506 -4
  91. package/dist/esm/validators.d.ts.map +1 -1
  92. package/dist/esm/validators.js +49 -2
  93. package/dist/esm/validators.js.map +1 -1
  94. package/package.json +5 -1
  95. package/src/client/createTool.ts +108 -0
  96. package/src/client/index.ts +270 -689
  97. package/src/client/streaming.ts +166 -0
  98. package/src/client/types.ts +430 -5
  99. package/src/component/_generated/api.d.ts +339 -188
  100. package/src/component/messages.ts +15 -14
  101. package/src/component/schema.ts +46 -0
  102. package/src/component/streams.ts +206 -0
  103. package/src/component/threads.ts +3 -3
  104. package/src/component/users.ts +2 -2
  105. package/src/mapping.ts +3 -1
  106. package/src/react/index.ts +687 -1
  107. package/src/react/toUIMessages.ts +153 -0
  108. package/src/validators.test.ts +18 -0
  109. package/src/validators.ts +72 -2
@@ -4,32 +4,20 @@ import type {
4
4
  DeepPartial,
5
5
  GenerateObjectResult,
6
6
  GenerateTextResult,
7
- JSONValue,
8
- RepairTextFunction,
9
- Schema,
10
7
  StepResult,
11
8
  StreamObjectResult,
12
9
  StreamTextResult,
13
- TelemetrySettings,
14
- Tool,
15
- ToolChoice,
16
- ToolExecutionOptions,
17
10
  ToolSet,
18
- UIMessage,
19
- } from "ai";
20
- import type { ToolInvocationUIPart } from "@ai-sdk/ui-utils";
21
- import {
22
- generateObject,
23
- generateText,
24
- streamObject,
25
- streamText,
26
- tool,
27
11
  } from "ai";
12
+ import { generateObject, generateText, streamObject, streamText } from "ai";
28
13
  import { assert } from "convex-helpers";
29
- import { internalActionGeneric, internalMutationGeneric } from "convex/server";
14
+ import {
15
+ internalActionGeneric,
16
+ internalMutationGeneric,
17
+ PaginationOptions,
18
+ PaginationResult,
19
+ } from "convex/server";
30
20
  import { v } from "convex/values";
31
- import { z } from "zod";
32
- import { Mounts } from "../component/_generated/api.js";
33
21
  import {
34
22
  validateVectorDimension,
35
23
  type VectorDimension,
@@ -42,7 +30,6 @@ import {
42
30
  serializeNewMessagesInStep,
43
31
  serializeObjectResult,
44
32
  serializeStep,
45
- toUIFilePart,
46
33
  } from "../mapping.js";
47
34
  import {
48
35
  DEFAULT_MESSAGE_RANGE,
@@ -51,132 +38,71 @@ import {
51
38
  isTool,
52
39
  } from "../shared.js";
53
40
  import {
54
- type CallSettings,
55
41
  type MessageWithMetadata as InnerMessageWithMetadata,
42
+ type MessageStatus,
56
43
  type ProviderMetadata,
57
- type ProviderOptions,
58
44
  type SearchOptions,
45
+ type StreamArgs,
59
46
  type Usage,
60
47
  vMessageWithMetadata,
61
48
  vSafeObjectArgs,
62
49
  vTextArgs,
63
50
  } from "../validators.js";
64
51
  import type {
52
+ AgentComponent,
53
+ ContextOptions,
54
+ GenerationOutputMetadata,
65
55
  OpaqueIds,
56
+ Options,
57
+ OurObjectArgs,
58
+ OurStreamObjectArgs,
66
59
  RunActionCtx,
67
60
  RunMutationCtx,
68
61
  RunQueryCtx,
69
- UseApi,
62
+ StorageOptions,
63
+ StreamingTextArgs,
64
+ SyncStreamsReturnValue,
65
+ TextArgs,
66
+ Thread,
67
+ UsageHandler,
70
68
  } from "./types.js";
71
69
 
72
70
  import type { MessageDoc, ThreadDoc } from "../component/schema.js";
71
+ import { createTool, wrapTools } from "./createTool.js";
72
+ import {
73
+ DeltaStreamer,
74
+ mergeTransforms,
75
+ StreamingOptions,
76
+ } from "./streaming.js";
73
77
 
74
78
  export { vMessageDoc, vThreadDoc } from "../component/schema.js";
75
- export { extractText, isTool };
76
- export type { Usage, ProviderMetadata, MessageDoc, ThreadDoc };
77
79
  export {
78
- /** @deprecated Use vPaginationResult instead. */
79
- paginationResultValidator,
80
- paginationResultValidator as vPaginationResult,
80
+ vAssistantMessage,
81
81
  vContextOptions,
82
- vUsage,
82
+ vMessage,
83
+ vPaginationResult,
83
84
  vProviderMetadata,
84
- vUserMessage,
85
- vAssistantMessage,
86
- vToolMessage,
87
85
  vStorageOptions,
86
+ vStreamArgs,
88
87
  vSystemMessage,
89
- vMessage,
88
+ vToolMessage,
89
+ vUsage,
90
+ vUserMessage,
90
91
  } from "../validators.js";
91
-
92
- /**
93
- * Options to configure what messages are fetched as context,
94
- * automatically with thread.generateText, or directly via search.
95
- */
96
- export type ContextOptions = {
97
- /** @deprecated Use excludeToolMessages instead. */
98
- includeToolCalls?: boolean;
99
- /**
100
- * Whether to include tool messages in the context.
101
- * By default, tool calls and results are not included.
102
- */
103
- excludeToolMessages?: boolean;
104
- /**
105
- * How many recent messages to include. These are added after the search
106
- * messages, and do not count against the search limit.
107
- * Default: 100
108
- */
109
- recentMessages?: number;
110
- /**
111
- * Options for searching messages.
112
- */
113
- searchOptions?: {
114
- /**
115
- * The maximum number of messages to fetch. Default is 10.
116
- */
117
- limit: number;
118
- /**
119
- * Whether to use text search to find messages. Default is false.
120
- */
121
- textSearch?: boolean;
122
- /**
123
- * Whether to use vector search to find messages. Default is false.
124
- * At least one of textSearch or vectorSearch must be true.
125
- */
126
- vectorSearch?: boolean;
127
- /**
128
- * What messages around the search results to include.
129
- * Default: { before: 2, after: 1 }
130
- * (two before, and one after each message found in the search)
131
- * Note, this is after the limit is applied.
132
- * By default this will quadruple the number of messages fetched.
133
- */
134
- messageRange?: { before: number; after: number };
135
- };
136
- /**
137
- * Whether to search across other threads for relevant messages.
138
- * By default, only the current thread is searched.
139
- */
140
- searchOtherThreads?: boolean;
92
+ export { createTool, extractText, isTool };
93
+ export type {
94
+ AgentComponent,
95
+ ContextOptions,
96
+ MessageDoc,
97
+ ProviderMetadata,
98
+ StorageOptions,
99
+ SyncStreamsReturnValue,
100
+ Thread,
101
+ ThreadDoc,
102
+ Usage,
103
+ UsageHandler,
141
104
  };
142
105
 
143
- /**
144
- * Options to configure the automatic saving of messages
145
- * when generating text / objects in a thread.
146
- */
147
- export type StorageOptions = {
148
- /**
149
- * Defaults to false, allowing you to pass in arbitrary context that will
150
- * be in addition to automatically fetched content.
151
- * Pass true to have all input messages saved to the thread history.
152
- */
153
- saveAllInputMessages?: boolean;
154
- /** Defaults to true, saving the prompt, or last message passed to generateText. */
155
- saveAnyInputMessages?: boolean;
156
- /** Defaults to true. Whether to save messages generated while chatting. */
157
- saveOutputMessages?: boolean;
158
- };
159
-
160
- export type GenerationOutputMetadata = { messageId?: string };
161
-
162
- type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };
163
-
164
- export type UsageHandler = (
165
- ctx: RunActionCtx,
166
- args: {
167
- userId: string | undefined;
168
- threadId: string | undefined;
169
- agentName: string | undefined;
170
- usage: Usage;
171
- // Often has more information, like cached token usage in the case of openai.
172
- providerMetadata: ProviderMetadata | undefined;
173
- model: string;
174
- provider: string;
175
- }
176
- ) => void | Promise<void>;
177
-
178
- export type AgentComponent = UseApi<Mounts>;
179
-
180
106
  export class Agent<AgentTools extends ToolSet> {
181
107
  constructor(
182
108
  public component: AgentComponent,
@@ -406,6 +332,72 @@ export class Agent<AgentTools extends ToolSet> {
406
332
  };
407
333
  }
408
334
 
335
+ /**
336
+ * List messages from a thread.
337
+ * @param ctx A ctx object from a query, mutation, or action.
338
+ * @param args.threadId The thread to list messages from.
339
+ * @param args.paginationOpts Pagination options (e.g. via usePaginatedQuery).
340
+ * @param args.excludeToolMessages Whether to exclude tool messages.
341
+ * False by default.
342
+ * @param args.statuses What statuses to include. All by default.
343
+ * @returns The MessageDoc's in a format compatible with usePaginatedQuery.
344
+ */
345
+ async listMessages(
346
+ ctx: RunQueryCtx,
347
+ args: {
348
+ threadId: string;
349
+ paginationOpts: PaginationOptions;
350
+ excludeToolMessages?: boolean;
351
+ statuses?: MessageStatus[];
352
+ }
353
+ ): Promise<PaginationResult<MessageDoc>> {
354
+ if (args.paginationOpts.numItems === 0) {
355
+ return {
356
+ page: [],
357
+ isDone: true,
358
+ continueCursor: args.paginationOpts.cursor ?? "",
359
+ };
360
+ }
361
+ return ctx.runQuery(this.component.messages.listMessagesByThreadId, {
362
+ order: "desc",
363
+ ...args,
364
+ });
365
+ }
366
+
367
+ /**
368
+ * A function that handles fetching stream deltas, used with the React hooks
369
+ * `useThreadMessages` or `useStreamingThreadMessages`.
370
+ * @param ctx A ctx object from a query, mutation, or action.
371
+ * @param args.threadId The thread to sync streams for.
372
+ * @param args.streamArgs The stream arguments with per-stream cursors.
373
+ * @returns The deltas for each stream from their existing cursor.
374
+ */
375
+ async syncStreams(
376
+ ctx: RunQueryCtx,
377
+ args: {
378
+ threadId: string;
379
+ streamArgs: StreamArgs | undefined;
380
+ }
381
+ ): Promise<SyncStreamsReturnValue | undefined> {
382
+ if (!args.streamArgs) return undefined;
383
+ if (args.streamArgs.kind === "list") {
384
+ return {
385
+ kind: "list",
386
+ messages: await ctx.runQuery(this.component.streams.list, {
387
+ threadId: args.threadId,
388
+ }),
389
+ };
390
+ } else {
391
+ return {
392
+ kind: "deltas",
393
+ deltas: await ctx.runQuery(this.component.streams.listDeltas, {
394
+ threadId: args.threadId,
395
+ cursors: args.streamArgs.cursors,
396
+ }),
397
+ };
398
+ }
399
+ }
400
+
409
401
  /**
410
402
  * Fetch the context messages for a thread.
411
403
  * @param ctx Either a query, mutation, or action ctx.
@@ -432,7 +424,7 @@ export class Agent<AgentTools extends ToolSet> {
432
424
  assert(args.userId || args.threadId, "Specify userId or threadId");
433
425
  // Fetch the latest messages from the thread
434
426
  let included: Set<string> | undefined;
435
- const opts = this.mergedContextOptions(args.contextOptions);
427
+ const opts = this._mergedContextOptions(args.contextOptions);
436
428
  const contextMessages: MessageDoc[] = [];
437
429
  if (
438
430
  args.threadId &&
@@ -475,7 +467,7 @@ export class Agent<AgentTools extends ToolSet> {
475
467
  userId: opts?.searchOtherThreads ? args.userId : undefined,
476
468
  threadId: args.threadId,
477
469
  beforeMessageId: args.upToAndIncludingMessageId,
478
- ...(await this.searchOptionsWithDefaults(opts, messagesToSearch)),
470
+ ...(await this._searchOptionsWithDefaults(opts, messagesToSearch)),
479
471
  }
480
472
  );
481
473
  // TODO: track what messages we used for context
@@ -537,6 +529,35 @@ export class Agent<AgentTools extends ToolSet> {
537
529
  return embeddings;
538
530
  }
539
531
 
532
+ async saveMessage(
533
+ ctx: RunMutationCtx,
534
+ args: {
535
+ threadId: string;
536
+ userId?: string;
537
+ metadata?: Omit<MessageWithMetadata, "message">;
538
+ } & (
539
+ | {
540
+ prompt?: undefined;
541
+ message: CoreMessage;
542
+ }
543
+ | {
544
+ prompt: string;
545
+ message?: undefined;
546
+ }
547
+ )
548
+ ) {
549
+ const { lastMessageId } = await this.saveMessages(ctx, {
550
+ threadId: args.threadId,
551
+ userId: args.userId,
552
+ messages:
553
+ args.prompt !== undefined
554
+ ? [{ role: "user", content: args.prompt }]
555
+ : [args.message],
556
+ metadata: args.metadata ? [args.metadata] : undefined,
557
+ });
558
+ return { messageId: lastMessageId };
559
+ }
560
+
540
561
  /**
541
562
  * Explicitly save messages associated with the thread (& user if provided)
542
563
  * @param ctx The ctx parameter to a mutation or action.
@@ -576,7 +597,7 @@ export class Agent<AgentTools extends ToolSet> {
576
597
  }
577
598
  ): Promise<{
578
599
  lastMessageId: string;
579
- messageIds: string[];
600
+ messages: MessageDoc[];
580
601
  }> {
581
602
  const embeddings = await this.generateEmbeddings(args.messages);
582
603
  const result = await ctx.runMutation(this.component.messages.addMessages, {
@@ -597,7 +618,7 @@ export class Agent<AgentTools extends ToolSet> {
597
618
  });
598
619
  return {
599
620
  lastMessageId: result.messages.at(-1)!._id,
600
- messageIds: result.messages.map((m) => m._id),
621
+ messages: result.messages,
601
622
  };
602
623
  }
603
624
 
@@ -622,11 +643,15 @@ export class Agent<AgentTools extends ToolSet> {
622
643
  failPendingSteps: v.optional(v.boolean()),
623
644
  },
624
645
  handler: async (ctx, args) => {
625
- return this.saveMessages(ctx, {
646
+ const { lastMessageId, messages } = await this.saveMessages(ctx, {
626
647
  ...args,
627
648
  messages: args.messages.map((m) => m.message),
628
649
  metadata: args.messages.map(({ message: _, ...m }) => m),
629
650
  });
651
+ return {
652
+ lastMessageId,
653
+ messageIds: messages.map((m) => m._id),
654
+ };
630
655
  },
631
656
  });
632
657
  }
@@ -660,7 +685,7 @@ export class Agent<AgentTools extends ToolSet> {
660
685
  */
661
686
  provider?: string;
662
687
  }
663
- ): Promise<void> {
688
+ ): Promise<MessageDoc[]> {
664
689
  const step = serializeStep(args.step as StepResult<ToolSet>);
665
690
  const messages = serializeNewMessagesInStep(args.step, {
666
691
  provider: args.provider ?? this.options.chat.provider,
@@ -669,13 +694,14 @@ export class Agent<AgentTools extends ToolSet> {
669
694
  const embeddings = await this.generateEmbeddings(
670
695
  messages.map((m) => m.message)
671
696
  );
672
- await ctx.runMutation(this.component.messages.addStep, {
697
+ const saved = await ctx.runMutation(this.component.messages.addStep, {
673
698
  userId: args.userId,
674
699
  threadId: args.threadId,
675
700
  promptMessageId: args.promptMessageId,
676
701
  step: { step, messages, embeddings },
677
702
  failPendingSteps: false,
678
703
  });
704
+ return saved;
679
705
  }
680
706
 
681
707
  /**
@@ -752,7 +778,7 @@ export class Agent<AgentTools extends ToolSet> {
752
778
  args,
753
779
  { userId, threadId, ...options }
754
780
  );
755
- const toolCtx = { ...ctx, userId, threadId, messageId };
781
+ const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
756
782
  const tools = wrapTools(
757
783
  toolCtx,
758
784
  args.tools ?? threadTools ?? this.options.tools
@@ -844,7 +870,19 @@ export class Agent<AgentTools extends ToolSet> {
844
870
  * The {@link ContextOptions} and {@link StorageOptions}
845
871
  * options to use for fetching contextual messages and saving input/output messages.
846
872
  */
847
- options?: Options
873
+ options?: Options & {
874
+ /**
875
+ * Whether to save incremental data (deltas) from streaming responses.
876
+ * Defaults to false.
877
+ * If false, it will not save any deltas to the database.
878
+ * If true, it will save deltas with {@link DEFAULT_STREAMING_OPTIONS}.
879
+ *
880
+ * Regardless of this option, when streaming you are able to use this
881
+ * `streamText` function as you would with the "ai" package's version:
882
+ * iterating over the text, streaming it over HTTP, etc.
883
+ */
884
+ saveStreamDeltas?: boolean | StreamingOptions;
885
+ }
848
886
  ): Promise<
849
887
  StreamTextResult<
850
888
  TOOLS extends undefined ? AgentTools : TOOLS,
@@ -852,28 +890,50 @@ export class Agent<AgentTools extends ToolSet> {
852
890
  > &
853
891
  GenerationOutputMetadata
854
892
  > {
855
- const { args: aiArgs, messageId } = await this._saveMessagesAndFetchContext(
856
- ctx,
857
- args,
858
- { userId, threadId, ...options }
859
- );
860
- const toolCtx = { ...ctx, userId, threadId, messageId };
893
+ const context = await this._saveMessagesAndFetchContext(ctx, args, {
894
+ userId,
895
+ threadId,
896
+ ...options,
897
+ });
898
+ const { args: aiArgs, messageId, order, stepOrder } = context;
899
+ const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
861
900
  const tools = wrapTools(
862
901
  toolCtx,
863
902
  args.tools ?? threadTools ?? this.options.tools
864
903
  ) as TOOLS extends undefined ? AgentTools : TOOLS;
865
- const saveOutputMessages =
866
- options?.storageOptions?.saveOutputMessages ??
867
- this.options.storageOptions?.saveOutputMessages;
904
+ const storageOptions = {
905
+ ...this.options.storageOptions,
906
+ ...options?.storageOptions,
907
+ };
908
+ const saveOutputMessages = storageOptions.saveOutputMessages;
868
909
  const trackUsage = usageHandler ?? this.options.usageHandler;
910
+ const streamer =
911
+ threadId && options?.saveStreamDeltas
912
+ ? new DeltaStreamer(this.component, ctx, options.saveStreamDeltas, {
913
+ threadId,
914
+ userId,
915
+ agentName: this.options.name,
916
+ model: aiArgs.model.modelId,
917
+ provider: aiArgs.model.provider,
918
+ providerOptions: aiArgs.providerOptions,
919
+ order,
920
+ stepOrder,
921
+ })
922
+ : undefined;
923
+
869
924
  const result = streamText({
870
925
  // Can be overridden
871
926
  maxSteps: this.options.maxSteps,
872
927
  ...aiArgs,
873
928
  tools,
874
- onChunk: async (chunk) => {
929
+ experimental_transform: mergeTransforms(
930
+ options?.saveStreamDeltas,
931
+ args.experimental_transform
932
+ ),
933
+ onChunk: async (event) => {
934
+ await streamer?.addParts([event.chunk]);
875
935
  // console.log("onChunk", chunk);
876
- return args.onChunk?.(chunk);
936
+ return args.onChunk?.(event);
877
937
  },
878
938
  onError: async (error) => {
879
939
  console.error("onError", error);
@@ -889,12 +949,13 @@ export class Agent<AgentTools extends ToolSet> {
889
949
  // console.log("onStepFinish", step);
890
950
  // TODO: compare delta to the output. internally drop the deltas when committing
891
951
  if (threadId && messageId) {
892
- await this.saveStep(ctx, {
952
+ const saved = await this.saveStep(ctx, {
893
953
  userId,
894
954
  threadId,
895
955
  promptMessageId: messageId,
896
956
  step,
897
957
  });
958
+ await streamer?.finish(saved);
898
959
  }
899
960
  if (trackUsage && step.usage) {
900
961
  await trackUsage(ctx, {
@@ -943,6 +1004,8 @@ export class Agent<AgentTools extends ToolSet> {
943
1004
  ): Promise<{
944
1005
  args: T & { model: LanguageModelV1 };
945
1006
  messageId: string | undefined;
1007
+ order: number | undefined;
1008
+ stepOrder: number | undefined;
946
1009
  }> {
947
1010
  contextOptions ||= this.options.contextOptions;
948
1011
  storageOptions ||= this.options.storageOptions;
@@ -963,6 +1026,12 @@ export class Agent<AgentTools extends ToolSet> {
963
1026
  contextOptions,
964
1027
  });
965
1028
  let messageId = args.promptMessageId;
1029
+ let order = args.promptMessageId
1030
+ ? contextMessages.at(-1)?.order
1031
+ : undefined;
1032
+ let stepOrder = args.promptMessageId
1033
+ ? contextMessages.at(-1)?.stepOrder
1034
+ : undefined;
966
1035
  if (
967
1036
  threadId &&
968
1037
  messages.length &&
@@ -979,6 +1048,8 @@ export class Agent<AgentTools extends ToolSet> {
979
1048
  failPendingSteps: true,
980
1049
  });
981
1050
  messageId = saved.lastMessageId;
1051
+ order = saved.messages.at(-1)?.order;
1052
+ stepOrder = saved.messages.at(-1)?.stepOrder;
982
1053
  }
983
1054
  const { prompt: _, model, ...rest } = args;
984
1055
  return {
@@ -993,6 +1064,8 @@ export class Agent<AgentTools extends ToolSet> {
993
1064
  ],
994
1065
  } as T & { model: LanguageModelV1 },
995
1066
  messageId,
1067
+ order,
1068
+ stepOrder,
996
1069
  };
997
1070
  }
998
1071
 
@@ -1185,7 +1258,7 @@ export class Agent<AgentTools extends ToolSet> {
1185
1258
  });
1186
1259
  }
1187
1260
 
1188
- mergedContextOptions(opts: ContextOptions | undefined): ContextOptions {
1261
+ _mergedContextOptions(opts: ContextOptions | undefined): ContextOptions {
1189
1262
  const searchOptions = {
1190
1263
  ...this.options.contextOptions?.searchOptions,
1191
1264
  ...opts?.searchOptions,
@@ -1199,7 +1272,7 @@ export class Agent<AgentTools extends ToolSet> {
1199
1272
  };
1200
1273
  }
1201
1274
 
1202
- async searchOptionsWithDefaults(
1275
+ async _searchOptionsWithDefaults(
1203
1276
  contextOptions: ContextOptions,
1204
1277
  messages: CoreMessage[]
1205
1278
  ): Promise<SearchOptions> {
@@ -1273,31 +1346,68 @@ export class Agent<AgentTools extends ToolSet> {
1273
1346
  * {@link ContextOptions}, {@link StorageOptions}, and maxSteps.
1274
1347
  */
1275
1348
  asTextAction(spec?: {
1349
+ /**
1350
+ * The maximum number of steps to take in this action.
1351
+ * Defaults to the {@link Agent.maxSteps} option.
1352
+ */
1276
1353
  maxSteps?: number;
1354
+ /**
1355
+ * The {@link ContextOptions} to use for fetching contextual messages and
1356
+ * saving input/output messages.
1357
+ * Defaults to the {@link Agent.contextOptions} option.
1358
+ */
1277
1359
  contextOptions?: ContextOptions;
1360
+ /**
1361
+ * The {@link StorageOptions} to use for saving input/output messages.
1362
+ * Defaults to the {@link Agent.storageOptions} option.
1363
+ */
1278
1364
  storageOptions?: StorageOptions;
1365
+ /**
1366
+ * Whether to stream the text.
1367
+ * If false, it will generate the text in a single call. (default)
1368
+ * If true or {@link StreamingOptions}, it will stream the text from the LLM
1369
+ * and save the chunks to the database with the options you specify, or the
1370
+ * defaults if you pass true.
1371
+ */
1372
+ stream?: boolean | StreamingOptions;
1279
1373
  }) {
1280
1374
  const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
1281
1375
  return internalActionGeneric({
1282
1376
  args: vTextArgs,
1283
1377
  handler: async (ctx, args) => {
1284
1378
  const { contextOptions, storageOptions, ...rest } = args;
1285
- const value = await this.generateText(
1286
- ctx,
1287
- { userId: args.userId, threadId: args.threadId },
1288
- { maxSteps, ...rest },
1289
- {
1290
- contextOptions:
1291
- contextOptions ??
1292
- spec?.contextOptions ??
1293
- this.options.contextOptions,
1294
- storageOptions:
1295
- storageOptions ??
1296
- spec?.storageOptions ??
1297
- this.options.storageOptions,
1298
- }
1299
- );
1300
- return value.text;
1379
+ const stream =
1380
+ args.stream === true ? spec?.stream || true : spec?.stream ?? false;
1381
+ const targetArgs = { userId: args.userId, threadId: args.threadId };
1382
+ const llmArgs = { maxSteps, ...rest };
1383
+ const opts = {
1384
+ contextOptions:
1385
+ contextOptions ??
1386
+ spec?.contextOptions ??
1387
+ this.options.contextOptions,
1388
+ storageOptions:
1389
+ storageOptions ??
1390
+ spec?.storageOptions ??
1391
+ this.options.storageOptions,
1392
+ saveStreamDeltas: stream,
1393
+ };
1394
+ if (stream) {
1395
+ const result = await this.streamText(ctx, targetArgs, llmArgs, opts);
1396
+ await result.consumeStream();
1397
+ return {
1398
+ text: await result.text,
1399
+ finishReason: await result.finishReason,
1400
+ messageId: result.messageId,
1401
+ };
1402
+ } else {
1403
+ const { text, messageId, finishReason } = await this.generateText(
1404
+ ctx,
1405
+ targetArgs,
1406
+ llmArgs,
1407
+ opts
1408
+ );
1409
+ return { text, messageId, finishReason };
1410
+ }
1301
1411
  },
1302
1412
  });
1303
1413
  }
@@ -1372,534 +1482,5 @@ export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
1372
1482
  return result;
1373
1483
  }
1374
1484
 
1375
- export type ToolCtx = RunActionCtx & {
1376
- userId?: string;
1377
- threadId?: string;
1378
- messageId?: string;
1379
- };
1380
-
1381
- // Vendoring in from "ai" package since it wasn't exported
1382
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1383
- type ToolParameters = z.ZodTypeAny | Schema<any>;
1384
- type inferParameters<PARAMETERS extends ToolParameters> =
1385
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1386
- PARAMETERS extends Schema<any>
1387
- ? PARAMETERS["_type"]
1388
- : PARAMETERS extends z.ZodTypeAny
1389
- ? z.infer<PARAMETERS>
1390
- : never;
1391
-
1392
- /**
1393
- * This is a wrapper around the ai.tool function that adds extra context to the
1394
- * tool call, including the action context, userId, threadId, and messageId.
1395
- * @param tool The tool. See https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling
1396
- * but swap parameters for args and handler for execute.
1397
- * @returns A tool to be used with the AI SDK.
1398
- */
1399
- export function createTool<PARAMETERS extends ToolParameters, RESULT>(t: {
1400
- /**
1401
- An optional description of what the tool does.
1402
- Will be used by the language model to decide whether to use the tool.
1403
- Not used for provider-defined tools.
1404
- */
1405
- description?: string;
1406
- /**
1407
- The schema of the input that the tool expects. The language model will use this to generate the input.
1408
- It is also used to validate the output of the language model.
1409
- Use descriptions to make the input understandable for the language model.
1410
- */
1411
- args: PARAMETERS;
1412
- /**
1413
- An async function that is called with the arguments from the tool call and produces a result.
1414
- If not provided, the tool will not be executed automatically.
1415
-
1416
- @args is the input of the tool call.
1417
- @options.abortSignal is a signal that can be used to abort the tool call.
1418
- */
1419
- handler: (
1420
- ctx: ToolCtx,
1421
- args: inferParameters<PARAMETERS>,
1422
- options: ToolExecutionOptions
1423
- ) => PromiseLike<RESULT>;
1424
- ctx?: ToolCtx;
1425
- }): Tool<PARAMETERS, RESULT> & {
1426
- execute: (
1427
- args: inferParameters<PARAMETERS>,
1428
- options: ToolExecutionOptions
1429
- ) => PromiseLike<RESULT>;
1430
- } {
1431
- const args = {
1432
- __acceptsCtx: true,
1433
- ctx: t.ctx,
1434
- description: t.description,
1435
- parameters: t.args,
1436
- async execute(
1437
- args: inferParameters<PARAMETERS>,
1438
- options: ToolExecutionOptions
1439
- ) {
1440
- if (!this.ctx) {
1441
- throw new Error(
1442
- "To use a Convex tool, you must either provide the ctx" +
1443
- " at definition time (dynamically in an action), or use the Agent to" +
1444
- " call it (which injects the ctx, userId and threadId)"
1445
- );
1446
- }
1447
- return t.handler(this.ctx, args, options);
1448
- },
1449
- };
1450
- return tool(args);
1451
- }
1452
-
1453
- function wrapTools(
1454
- ctx: ToolCtx,
1455
- ...toolSets: (ToolSet | undefined)[]
1456
- ): ToolSet {
1457
- const output = {} as ToolSet;
1458
- for (const toolSet of toolSets) {
1459
- if (!toolSet) {
1460
- continue;
1461
- }
1462
- for (const [name, tool] of Object.entries(toolSet)) {
1463
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1464
- if (!(tool as any).__acceptsCtx) {
1465
- output[name] = tool;
1466
- } else {
1467
- const out = { ...tool, ctx };
1468
- output[name] = out;
1469
- }
1470
- }
1471
- }
1472
- return output;
1473
- }
1474
-
1475
- type Options = {
1476
- /**
1477
- * The context options to use for passing in message history to the LLM.
1478
- */
1479
- contextOptions?: ContextOptions;
1480
- /**
1481
- * The storage options to use for saving the input and output messages to the thread.
1482
- */
1483
- storageOptions?: StorageOptions;
1484
- };
1485
-
1486
- type TextArgs<
1487
- AgentTools extends ToolSet,
1488
- TOOLS extends ToolSet | undefined = undefined,
1489
- OUTPUT = never,
1490
- OUTPUT_PARTIAL = never,
1491
- > = Omit<
1492
- Parameters<
1493
- typeof generateText<
1494
- TOOLS extends undefined ? AgentTools : TOOLS,
1495
- OUTPUT,
1496
- OUTPUT_PARTIAL
1497
- >
1498
- >[0],
1499
- "toolChoice" | "tools" | "model"
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;
1508
- /**
1509
- * The model to use for the tool calls. This will override the model specified
1510
- * in the Agent constructor.
1511
- */
1512
- model?: LanguageModelV1;
1513
- /**
1514
- * The tools to use for the tool calls. This will override tools specified
1515
- * in the Agent constructor or createThread / continueThread.
1516
- */
1517
- tools?: TOOLS;
1518
- /**
1519
- * The tool choice to use for the tool calls. This must be one of the tools
1520
- * specified in the tools array. e.g. {toolName: "getWeather", type: "tool"}
1521
- */
1522
- toolChoice?: ToolChoice<TOOLS extends undefined ? AgentTools : TOOLS>;
1523
- };
1524
-
1525
- type StreamingTextArgs<
1526
- AgentTools extends ToolSet,
1527
- TOOLS extends ToolSet | undefined = undefined,
1528
- OUTPUT = never,
1529
- OUTPUT_PARTIAL = never,
1530
- > = Omit<
1531
- Parameters<
1532
- typeof streamText<
1533
- TOOLS extends undefined ? AgentTools : TOOLS,
1534
- OUTPUT,
1535
- OUTPUT_PARTIAL
1536
- >
1537
- >[0],
1538
- "toolChoice" | "tools" | "model"
1539
- > & {
1540
- /**
1541
- * The model to use for the tool calls. This will override the model specified
1542
- * in the Agent constructor.
1543
- */
1544
- model?: LanguageModelV1;
1545
- /**
1546
- * The tools to use for the tool calls. This will override tools specified
1547
- * in the Agent constructor or createThread / continueThread.
1548
- */
1549
- tools?: TOOLS;
1550
- /**
1551
- * The tool choice to use for the tool calls. This must be one of the tools
1552
- * specified in the tools array. e.g. {toolName: "getWeather", type: "tool"}
1553
- */
1554
- toolChoice?: ToolChoice<TOOLS extends undefined ? AgentTools : TOOLS>;
1555
- };
1556
-
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
- };
1591
-
1592
- type GenerateObjectObjectOptions<T extends Record<string, unknown>> =
1593
- BaseGenerateObjectOptions & {
1594
- output?: "object";
1595
- mode?: "auto" | "json" | "tool";
1596
- schema: z.Schema<T>;
1597
- schemaName?: string;
1598
- schemaDescription?: string;
1599
- };
1600
-
1601
- type GenerateObjectArrayOptions<T> = BaseGenerateObjectOptions & {
1602
- output: "array";
1603
- mode?: "auto" | "json" | "tool";
1604
- schema: z.Schema<T>;
1605
- schemaName?: string;
1606
- schemaDescription?: string;
1607
- };
1608
-
1609
- type GenerateObjectWithEnumOptions<T extends string> =
1610
- BaseGenerateObjectOptions & {
1611
- output: "enum";
1612
- enum: Array<T>;
1613
- mode?: "auto" | "json" | "tool";
1614
- };
1615
-
1616
- type GenerateObjectNoSchemaOptions = BaseGenerateObjectOptions & {
1617
- schema?: undefined;
1618
- mode?: "json";
1619
- };
1620
-
1621
- // TODO: simplify this to just use the generateObject args, with an optional
1622
- // model and tool/toolChoice types
1623
- type GenerateObjectArgs<T> =
1624
- T extends Record<string, unknown>
1625
- ? GenerateObjectObjectOptions<T>
1626
- : T extends Array<unknown>
1627
- ? GenerateObjectArrayOptions<T>
1628
- : T extends string
1629
- ? GenerateObjectWithEnumOptions<T>
1630
- : GenerateObjectNoSchemaOptions;
1631
-
1632
- type StreamObjectArgs<T> =
1633
- T extends Record<string, unknown>
1634
- ? GenerateObjectObjectOptions<T>
1635
- : T extends Array<unknown>
1636
- ? GenerateObjectArrayOptions<T>
1637
- : GenerateObjectNoSchemaOptions;
1638
-
1639
- type OurObjectArgs<T> = GenerateObjectArgs<T> &
1640
- Pick<
1641
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1642
- Parameters<typeof generateObject<any>>[0],
1643
- "experimental_repairText" | "abortSignal"
1644
- >;
1645
-
1646
- type OurStreamObjectArgs<T> = StreamObjectArgs<T> &
1647
- Pick<
1648
- Parameters<typeof streamObject<T>>[0],
1649
- "onError" | "onFinish" | "abortSignal"
1650
- >;
1651
-
1652
- type ThreadOutputMetadata = GenerationOutputMetadata & {
1653
- messageId: string;
1654
- };
1655
-
1656
- /**
1657
- * The interface for a thread returned from {@link createThread} or {@link continueThread}.
1658
- * This is contextual to a thread and/or user.
1659
- */
1660
- interface Thread<DefaultTools extends ToolSet> {
1661
- /**
1662
- * The target threadId, from the startThread or continueThread initializers.
1663
- */
1664
- threadId: string;
1665
- /**
1666
- * This behaves like {@link generateText} from the "ai" package except that
1667
- * it add context based on the userId and threadId and saves the input and
1668
- * resulting messages to the thread, if specified.
1669
- * Use {@link continueThread} to get a version of this function already scoped
1670
- * to a thread (and optionally userId).
1671
- * @param args The arguments to the generateText function, along with extra controls
1672
- * for the {@link ContextOptions} and {@link StorageOptions}.
1673
- * @returns The result of the generateText function.
1674
- */
1675
- generateText<
1676
- TOOLS extends ToolSet | undefined = undefined,
1677
- OUTPUT = never,
1678
- OUTPUT_PARTIAL = never,
1679
- >(
1680
- args: TextArgs<
1681
- TOOLS extends undefined ? DefaultTools : TOOLS,
1682
- TOOLS,
1683
- OUTPUT,
1684
- OUTPUT_PARTIAL
1685
- >,
1686
- options?: Options
1687
- ): Promise<
1688
- GenerateTextResult<TOOLS extends undefined ? DefaultTools : TOOLS, OUTPUT> &
1689
- ThreadOutputMetadata
1690
- >;
1691
-
1692
- /**
1693
- * This behaves like {@link streamText} from the "ai" package except that
1694
- * it add context based on the userId and threadId and saves the input and
1695
- * resulting messages to the thread, if specified.
1696
- * Use {@link continueThread} to get a version of this function already scoped
1697
- * to a thread (and optionally userId).
1698
- * @param args The arguments to the streamText function, along with extra controls
1699
- * for the {@link ContextOptions} and {@link StorageOptions}.
1700
- * @returns The result of the streamText function.
1701
- */
1702
- streamText<
1703
- TOOLS extends ToolSet | undefined = undefined,
1704
- OUTPUT = never,
1705
- PARTIAL_OUTPUT = never,
1706
- >(
1707
- args: StreamingTextArgs<
1708
- TOOLS extends undefined ? DefaultTools : TOOLS,
1709
- TOOLS,
1710
- OUTPUT,
1711
- PARTIAL_OUTPUT
1712
- >,
1713
- options?: Options
1714
- ): Promise<
1715
- StreamTextResult<
1716
- TOOLS extends undefined ? DefaultTools : TOOLS,
1717
- PARTIAL_OUTPUT
1718
- > &
1719
- ThreadOutputMetadata
1720
- >;
1721
- /**
1722
- * This behaves like {@link generateObject} from the "ai" package except that
1723
- * it add context based on the userId and threadId and saves the input and
1724
- * resulting messages to the thread, if specified. This overload is for objects, arrays, and enums.
1725
- * Use {@link continueThread} to get a version of this function already scoped
1726
- * to a thread (and optionally userId).
1727
- * @param args The arguments to the generateObject function, along with extra controls
1728
- * for the {@link ContextOptions} and {@link StorageOptions}.
1729
- * @returns The result of the generateObject function.
1730
- */
1731
- generateObject<T>(
1732
- args: OurObjectArgs<T>,
1733
- options?: Options
1734
- ): Promise<GenerateObjectResult<T> & ThreadOutputMetadata>;
1735
- /**
1736
- * This behaves like {@link generateObject} from the "ai" package except that
1737
- * it add context based on the userId and threadId and saves the input and
1738
- * resulting messages to the thread, if specified. This overload is for when there's no schema.
1739
- * Use {@link continueThread} to get a version of this function already scoped
1740
- * to a thread (and optionally userId).
1741
- * @param args The arguments to the generateObject function, along with extra controls
1742
- * for the {@link ContextOptions} and {@link StorageOptions}.
1743
- * @returns The result of the generateObject function.
1744
- */
1745
- generateObject(
1746
- args: GenerateObjectNoSchemaOptions,
1747
- options?: Options
1748
- ): Promise<GenerateObjectResult<JSONValue> & ThreadOutputMetadata>;
1749
- /**
1750
- * This behaves like {@link streamObject} from the "ai" package except that
1751
- * it add context based on the userId and threadId and saves the input and
1752
- * resulting messages to the thread, if specified.
1753
- * Use {@link continueThread} to get a version of this function already scoped
1754
- * to a thread (and optionally userId).
1755
- * @param args The arguments to the streamObject function, along with extra controls
1756
- * for the {@link ContextOptions} and {@link StorageOptions}.
1757
- * @returns The result of the streamObject function.
1758
- */
1759
- streamObject<T>(
1760
- args: OurStreamObjectArgs<T>,
1761
- options?: Options
1762
- ): Promise<
1763
- StreamObjectResult<DeepPartial<T>, T, never> & ThreadOutputMetadata
1764
- >;
1765
- }
1766
-
1767
1485
  type MessageWithMetadata = OpaqueIds<InnerMessageWithMetadata>;
1768
-
1769
- export function toUIMessages(messages: MessageDoc[]): UIMessage[] {
1770
- const uiMessages: UIMessage[] = [];
1771
- let assistantMessage: UIMessage | undefined;
1772
- for (const message of messages) {
1773
- const coreMessage = message.message && deserializeMessage(message.message);
1774
- const text = message.text ?? "";
1775
- const content = coreMessage?.content;
1776
- const nonStringContent =
1777
- content && typeof content !== "string" ? content : [];
1778
- if (!coreMessage) continue;
1779
- if (coreMessage.role === "system") {
1780
- uiMessages.push({
1781
- id: message.id ?? message._id,
1782
- createdAt: new Date(message._creationTime),
1783
- role: "system",
1784
- content: text,
1785
- parts: [{ type: "text", text }],
1786
- });
1787
- } else if (coreMessage.role === "user") {
1788
- const parts: UIMessage["parts"] = [];
1789
- if (text) {
1790
- parts.push({ type: "text", text });
1791
- }
1792
- if (message.files) {
1793
- parts.push(...message.files.map(toUIFilePart));
1794
- }
1795
- uiMessages.push({
1796
- id: message.id ?? message._id,
1797
- createdAt: new Date(message._creationTime),
1798
- role: "user",
1799
- content: message.text ?? "",
1800
- parts,
1801
- });
1802
- } else {
1803
- if (coreMessage.role === "tool" && !assistantMessage) {
1804
- console.warn(
1805
- "Tool message without preceding assistant message.. skipping",
1806
- message
1807
- );
1808
- continue;
1809
- }
1810
- if (!assistantMessage) {
1811
- assistantMessage = {
1812
- id: message.id ?? message._id,
1813
- createdAt: new Date(message._creationTime),
1814
- role: "assistant",
1815
- content: message.text ?? "",
1816
- parts: [],
1817
- };
1818
- uiMessages.push(assistantMessage);
1819
- }
1820
- // update it to the last message's id
1821
- assistantMessage.id = message.id ?? message._id;
1822
- if (message.text) {
1823
- assistantMessage.parts.push({
1824
- type: "text",
1825
- text: message.text,
1826
- });
1827
- assistantMessage.content += message.text;
1828
- }
1829
- if (message.reasoning) {
1830
- assistantMessage.parts.push({
1831
- type: "reasoning",
1832
- reasoning: message.reasoning,
1833
- details: message.reasoningDetails ?? [],
1834
- });
1835
- }
1836
- for (const source of message.sources ?? []) {
1837
- assistantMessage.parts.push({
1838
- type: "source",
1839
- source,
1840
- });
1841
- }
1842
- for (const file of message.files ?? []) {
1843
- assistantMessage.parts.push(toUIFilePart(file));
1844
- }
1845
- for (const contentPart of nonStringContent) {
1846
- switch (contentPart.type) {
1847
- case "tool-call":
1848
- assistantMessage.parts.push({
1849
- type: "step-start",
1850
- });
1851
- assistantMessage.parts.push({
1852
- type: "tool-invocation",
1853
- toolInvocation: {
1854
- state: "call",
1855
- step: assistantMessage.parts.filter(
1856
- (part) => part.type === "tool-invocation"
1857
- ).length,
1858
- toolCallId: contentPart.toolCallId,
1859
- toolName: contentPart.toolName,
1860
- args: contentPart.args,
1861
- },
1862
- });
1863
- break;
1864
- case "tool-result": {
1865
- const call = assistantMessage.parts.find(
1866
- (part) =>
1867
- part.type === "tool-invocation" &&
1868
- part.toolInvocation.toolCallId === contentPart.toolCallId
1869
- ) as ToolInvocationUIPart | undefined;
1870
- const toolInvocation: ToolInvocationUIPart["toolInvocation"] = {
1871
- state: "result",
1872
- toolCallId: contentPart.toolCallId,
1873
- toolName: contentPart.toolName,
1874
- args: call?.toolInvocation.args,
1875
- result: contentPart.result,
1876
- step:
1877
- call?.toolInvocation.step ??
1878
- assistantMessage.parts.filter(
1879
- (part) => part.type === "tool-invocation"
1880
- ).length,
1881
- };
1882
- if (call) {
1883
- (call as ToolInvocationUIPart).toolInvocation = toolInvocation;
1884
- } else {
1885
- console.warn(
1886
- "Tool result without preceding tool call.. adding anyways",
1887
- contentPart
1888
- );
1889
- assistantMessage.parts.push({
1890
- type: "tool-invocation",
1891
- toolInvocation,
1892
- });
1893
- }
1894
- break;
1895
- }
1896
- }
1897
- }
1898
- if (!message.tool) {
1899
- // Reset it so the next set of tool calls will create a new assistant message
1900
- assistantMessage = undefined;
1901
- }
1902
- }
1903
- }
1904
- return uiMessages;
1905
- }
1486
+ type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };