@convex-dev/agent 0.2.6-alpha.0 → 0.2.6

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 (98) hide show
  1. package/dist/client/definePlaygroundAPI.d.ts +6 -4
  2. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  3. package/dist/client/definePlaygroundAPI.js +15 -6
  4. package/dist/client/definePlaygroundAPI.js.map +1 -1
  5. package/dist/client/index.d.ts +26 -120
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +48 -373
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/messages.d.ts +1 -1
  10. package/dist/client/messages.d.ts.map +1 -1
  11. package/dist/client/mockModel.d.ts +3 -3
  12. package/dist/client/mockModel.d.ts.map +1 -1
  13. package/dist/client/mockModel.js +22 -17
  14. package/dist/client/mockModel.js.map +1 -1
  15. package/dist/client/saveInputMessages.d.ts +20 -0
  16. package/dist/client/saveInputMessages.d.ts.map +1 -0
  17. package/dist/client/saveInputMessages.js +57 -0
  18. package/dist/client/saveInputMessages.js.map +1 -0
  19. package/dist/client/search.d.ts +110 -9
  20. package/dist/client/search.d.ts.map +1 -1
  21. package/dist/client/search.js +271 -39
  22. package/dist/client/search.js.map +1 -1
  23. package/dist/client/start.d.ts +83 -0
  24. package/dist/client/start.d.ts.map +1 -0
  25. package/dist/client/start.js +171 -0
  26. package/dist/client/start.js.map +1 -0
  27. package/dist/client/streaming.d.ts +8 -8
  28. package/dist/client/streaming.d.ts.map +1 -1
  29. package/dist/client/streaming.js +2 -1
  30. package/dist/client/streaming.js.map +1 -1
  31. package/dist/client/textStreamParts.d.ts.map +1 -1
  32. package/dist/client/textStreamParts.js +2 -9
  33. package/dist/client/textStreamParts.js.map +1 -1
  34. package/dist/client/threads.d.ts +1 -1
  35. package/dist/client/threads.d.ts.map +1 -1
  36. package/dist/client/types.d.ts +137 -5
  37. package/dist/client/types.d.ts.map +1 -1
  38. package/dist/component/_generated/api.d.ts +11 -3
  39. package/dist/component/messages.d.ts +13 -4
  40. package/dist/component/messages.d.ts.map +1 -1
  41. package/dist/component/messages.js +67 -25
  42. package/dist/component/messages.js.map +1 -1
  43. package/dist/component/schema.d.ts +2 -1643
  44. package/dist/component/schema.d.ts.map +1 -1
  45. package/dist/component/schema.js +0 -24
  46. package/dist/component/schema.js.map +1 -1
  47. package/dist/mapping.d.ts +7 -9
  48. package/dist/mapping.d.ts.map +1 -1
  49. package/dist/mapping.js +73 -7
  50. package/dist/mapping.js.map +1 -1
  51. package/dist/react/deltas.d.ts.map +1 -1
  52. package/dist/react/deltas.js +15 -5
  53. package/dist/react/deltas.js.map +1 -1
  54. package/dist/react/fromUIMessages.d.ts +13 -0
  55. package/dist/react/fromUIMessages.d.ts.map +1 -0
  56. package/dist/react/fromUIMessages.js +70 -0
  57. package/dist/react/fromUIMessages.js.map +1 -0
  58. package/dist/react/toUIMessages.d.ts +5 -2
  59. package/dist/react/toUIMessages.d.ts.map +1 -1
  60. package/dist/react/toUIMessages.js +3 -0
  61. package/dist/react/toUIMessages.js.map +1 -1
  62. package/dist/shared.d.ts +10 -0
  63. package/dist/shared.d.ts.map +1 -1
  64. package/dist/shared.js +26 -0
  65. package/dist/shared.js.map +1 -1
  66. package/dist/validators.d.ts +1640 -0
  67. package/dist/validators.d.ts.map +1 -1
  68. package/dist/validators.js +41 -0
  69. package/dist/validators.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/client/definePlaygroundAPI.ts +16 -7
  72. package/src/client/index.test.ts +11 -46
  73. package/src/client/index.ts +99 -558
  74. package/src/client/messages.ts +1 -1
  75. package/src/client/mock.json +68 -0
  76. package/src/client/mockModel.ts +34 -23
  77. package/src/client/saveInputMessages.test.ts +576 -0
  78. package/src/client/saveInputMessages.ts +100 -0
  79. package/src/client/search.test.ts +1017 -0
  80. package/src/client/search.ts +446 -68
  81. package/src/client/start.ts +313 -0
  82. package/src/client/stream.json +48 -0
  83. package/src/client/streaming.ts +3 -3
  84. package/src/client/textStreamParts.ts +2 -11
  85. package/src/client/threads.ts +1 -1
  86. package/src/client/types.ts +143 -3
  87. package/src/component/_generated/api.d.ts +11 -3
  88. package/src/component/messages.ts +73 -27
  89. package/src/component/schema.ts +1 -29
  90. package/src/mapping.ts +84 -7
  91. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  92. package/src/react/deltas.ts +18 -5
  93. package/src/react/fromUIMessages.test.ts +427 -0
  94. package/src/react/fromUIMessages.ts +85 -0
  95. package/src/react/toUIMessages.ts +21 -13
  96. package/src/shared.ts +33 -0
  97. package/src/validators.test.ts +13 -2
  98. package/src/validators.ts +48 -0
@@ -0,0 +1,313 @@
1
+ import {
2
+ stepCountIs,
3
+ type CallSettings,
4
+ type GenerateObjectResult,
5
+ type IdGenerator,
6
+ type LanguageModel,
7
+ type ModelMessage,
8
+ type StepResult,
9
+ type StopCondition,
10
+ type ToolSet,
11
+ } from "ai";
12
+ import {
13
+ serializeNewMessagesInStep,
14
+ serializeObjectResult,
15
+ } from "../mapping.js";
16
+ import { embedMessages, fetchContextWithPrompt } from "./search.js";
17
+ import type {
18
+ ActionCtx,
19
+ AgentComponent,
20
+ Config,
21
+ Options,
22
+ UserActionCtx,
23
+ } from "./types.js";
24
+ import { saveMessages } from "./messages.js";
25
+ import type { Message, MessageDoc } from "../validators.js";
26
+ import {
27
+ getModelName,
28
+ getProviderName,
29
+ type ModelOrMetadata,
30
+ } from "../shared.js";
31
+ import { wrapTools, type ToolCtx } from "./createTool.js";
32
+ import type { Agent } from "./index.js";
33
+ import { omit } from "convex-helpers";
34
+ import { saveInputMessages } from "./saveInputMessages.js";
35
+
36
+ export async function start<
37
+ T,
38
+ Tools extends ToolSet = ToolSet,
39
+ CustomCtx extends object = object,
40
+ >(
41
+ ctx: ActionCtx & CustomCtx,
42
+ component: AgentComponent,
43
+ /**
44
+ * These are the arguments you'll pass to the LLM call such as
45
+ * `generateText` or `streamText`. This function will look up the context
46
+ * and provide functions to save the steps, abort the generation, and more.
47
+ * The type of the arguments returned infers from the type of the arguments
48
+ * you pass here.
49
+ */
50
+ args: T & {
51
+ /**
52
+ * If provided, this message will be used as the "prompt" for the LLM call,
53
+ * instead of the prompt or messages.
54
+ * This is useful if you want to first save a user message, then use it as
55
+ * the prompt for the LLM call in another call.
56
+ */
57
+ promptMessageId?: string;
58
+ /**
59
+ * The model to use for the LLM calls. This will override the model specified
60
+ * in the Agent constructor.
61
+ */
62
+ model?: LanguageModel;
63
+ /**
64
+ * The tools to use for the tool calls. This will override tools specified
65
+ * in the Agent constructor or createThread / continueThread.
66
+ */
67
+ tools?: Tools;
68
+ /**
69
+ * The single prompt message to use for the LLM call. This will be the
70
+ * last message in the context. If it's a string, it will be a user role.
71
+ */
72
+ prompt?: string | (ModelMessage | Message)[];
73
+ /**
74
+ * If provided alongside prompt, the ordering will be:
75
+ * 1. system prompt
76
+ * 2. search context
77
+ * 3. recent messages
78
+ * 4. these messages
79
+ * 5. prompt messages, including those already on the same `order` as
80
+ * the promptMessageId message, if provided.
81
+ */
82
+ messages?: (ModelMessage | Message)[];
83
+ /**
84
+ * The abort signal to be passed to the LLM call. If triggered, it will
85
+ * mark the pending message as failed. If the generation is asynchronously
86
+ * aborted, it will trigger this signal when detected.
87
+ */
88
+ abortSignal?: AbortSignal;
89
+ stopWhen?: StopCondition<Tools> | Array<StopCondition<Tools>>;
90
+ _internal?: { generateId?: IdGenerator };
91
+ },
92
+ {
93
+ threadId,
94
+ ...opts
95
+ }: Options &
96
+ Config & {
97
+ userId?: string | null;
98
+ threadId?: string;
99
+ languageModel: LanguageModel;
100
+ agentName: string;
101
+ agentForToolCtx?: Agent;
102
+ },
103
+ ): Promise<{
104
+ args: T & {
105
+ system?: string;
106
+ model: LanguageModel;
107
+ messages: ModelMessage[];
108
+ tools?: Tools;
109
+ } & CallSettings;
110
+ order: number;
111
+ stepOrder: number;
112
+ userId: string | undefined;
113
+ promptMessageId: string | undefined;
114
+ updateModel: (model: ModelOrMetadata | undefined) => void;
115
+ save: <TOOLS extends ToolSet>(
116
+ toSave:
117
+ | { step: StepResult<TOOLS> }
118
+ | { object: GenerateObjectResult<unknown> },
119
+ createPendingMessage?: boolean,
120
+ ) => Promise<void>;
121
+ fail: (reason: string) => Promise<void>;
122
+ getSavedMessages: () => MessageDoc[];
123
+ }> {
124
+ const userId =
125
+ opts.userId ??
126
+ (threadId &&
127
+ (await ctx.runQuery(component.threads.getThread, { threadId }))
128
+ ?.userId) ??
129
+ undefined;
130
+
131
+ const context = await fetchContextWithPrompt(ctx, component, {
132
+ ...opts,
133
+ userId,
134
+ threadId,
135
+ messages: args.messages,
136
+ prompt: args.prompt,
137
+ promptMessageId: args.promptMessageId,
138
+ });
139
+
140
+ const saveMessages = opts.storageOptions?.saveMessages ?? "promptAndOutput";
141
+ const { promptMessageId, pendingMessage, savedMessages } =
142
+ threadId && saveMessages !== "none"
143
+ ? await saveInputMessages(ctx, component, {
144
+ ...opts,
145
+ userId,
146
+ threadId,
147
+ prompt: args.prompt,
148
+ messages: args.messages,
149
+ promptMessageId: args.promptMessageId,
150
+ storageOptions: { saveMessages },
151
+ })
152
+ : {
153
+ promptMessageId: args.promptMessageId,
154
+ pendingMessage: undefined,
155
+ savedMessages: [] as MessageDoc[],
156
+ };
157
+
158
+ const order = pendingMessage?.order ?? context.order;
159
+ const stepOrder = pendingMessage?.stepOrder ?? context.stepOrder;
160
+ let pendingMessageId = pendingMessage?._id;
161
+
162
+ const model = args.model ?? opts.languageModel;
163
+ let activeModel: ModelOrMetadata = model;
164
+
165
+ const fail = async (reason: string) => {
166
+ if (pendingMessageId) {
167
+ await ctx.runMutation(component.messages.finalizeMessage, {
168
+ messageId: pendingMessageId,
169
+ result: { status: "failed", error: reason },
170
+ });
171
+ }
172
+ };
173
+ if (args.abortSignal) {
174
+ const abortSignal = args.abortSignal;
175
+ abortSignal.addEventListener(
176
+ "abort",
177
+ async () => {
178
+ await fail(abortSignal.reason?.toString() ?? "abortSignal");
179
+ },
180
+ { once: true },
181
+ );
182
+ }
183
+ const toolCtx = {
184
+ ...(ctx as UserActionCtx & CustomCtx),
185
+ userId,
186
+ threadId,
187
+ promptMessageId,
188
+ agent: opts.agentForToolCtx,
189
+ } satisfies ToolCtx;
190
+ const tools = wrapTools(toolCtx, args.tools) as Tools;
191
+ const aiArgs = {
192
+ ...opts.callSettings,
193
+ providerOptions: opts.providerOptions,
194
+ ...omit(args, ["promptMessageId", "messages", "prompt"]),
195
+ model,
196
+ messages: context.messages,
197
+ stopWhen:
198
+ args.stopWhen ?? (opts.maxSteps ? stepCountIs(opts.maxSteps) : undefined),
199
+ tools,
200
+ } as T & {
201
+ model: LanguageModel;
202
+ messages: ModelMessage[];
203
+ tools?: Tools;
204
+ _internal?: { generateId?: IdGenerator };
205
+ } & CallSettings;
206
+ if (pendingMessageId) {
207
+ if (!aiArgs._internal?.generateId) {
208
+ aiArgs._internal = {
209
+ ...aiArgs._internal,
210
+ generateId: pendingMessageId
211
+ ? () => pendingMessageId ?? crypto.randomUUID()
212
+ : undefined,
213
+ };
214
+ }
215
+ }
216
+ return {
217
+ args: aiArgs,
218
+ order: order ?? 0,
219
+ stepOrder: stepOrder ?? 0,
220
+ userId,
221
+ promptMessageId,
222
+ getSavedMessages: () => savedMessages,
223
+ updateModel: (model: ModelOrMetadata | undefined) => {
224
+ if (model) {
225
+ activeModel = model;
226
+ }
227
+ },
228
+ fail,
229
+ save: async <TOOLS extends ToolSet>(
230
+ toSave:
231
+ | { step: StepResult<TOOLS> }
232
+ | { object: GenerateObjectResult<unknown> },
233
+ createPendingMessage?: boolean,
234
+ ) => {
235
+ if (threadId && saveMessages !== "none") {
236
+ const serialized =
237
+ "object" in toSave
238
+ ? await serializeObjectResult(
239
+ ctx,
240
+ component,
241
+ toSave.object,
242
+ activeModel,
243
+ )
244
+ : await serializeNewMessagesInStep(
245
+ ctx,
246
+ component,
247
+ toSave.step,
248
+ activeModel,
249
+ );
250
+ const embeddings = await embedMessages(
251
+ ctx,
252
+ { threadId, ...opts, userId },
253
+ serialized.messages.map((m) => m.message),
254
+ );
255
+ if (createPendingMessage) {
256
+ serialized.messages.push({
257
+ message: { role: "assistant", content: [] },
258
+ status: "pending",
259
+ });
260
+ embeddings?.vectors.push(null);
261
+ }
262
+ const saved = await ctx.runMutation(component.messages.addMessages, {
263
+ userId,
264
+ threadId,
265
+ agentName: opts.agentName,
266
+ promptMessageId,
267
+ pendingMessageId,
268
+ messages: serialized.messages,
269
+ embeddings,
270
+ failPendingSteps: false,
271
+ });
272
+ const lastMessage = saved.messages.at(-1)!;
273
+ if (createPendingMessage) {
274
+ if (lastMessage.status === "failed") {
275
+ pendingMessageId = undefined;
276
+ savedMessages.push(...saved.messages);
277
+ await fail(
278
+ lastMessage.error ??
279
+ "Aborting - the pending message was marked as failed",
280
+ );
281
+ } else {
282
+ pendingMessageId = lastMessage._id;
283
+ savedMessages.push(...saved.messages.slice(0, -1));
284
+ }
285
+ } else {
286
+ pendingMessageId = undefined;
287
+ savedMessages.push(...saved.messages);
288
+ }
289
+ }
290
+ const output = "object" in toSave ? toSave.object : toSave.step;
291
+ if (opts.rawRequestResponseHandler) {
292
+ await opts.rawRequestResponseHandler(ctx, {
293
+ userId,
294
+ threadId,
295
+ agentName: opts.agentName,
296
+ request: output.request,
297
+ response: output.response,
298
+ });
299
+ }
300
+ if (opts.usageHandler && output.usage) {
301
+ await opts.usageHandler(ctx, {
302
+ userId,
303
+ threadId,
304
+ agentName: opts.agentName,
305
+ model: getModelName(activeModel),
306
+ provider: getProviderName(activeModel),
307
+ usage: output.usage,
308
+ providerMetadata: output.providerMetadata,
309
+ });
310
+ }
311
+ },
312
+ };
313
+ }
@@ -0,0 +1,48 @@
1
+ [
2
+ { "type": "start" },
3
+ { "type": "start-step" },
4
+ { "type": "reasoning-start", "id": "reasoning-0" },
5
+ { "type": "reasoning-delta", "id": "reasoning-0", "delta": "Okay" },
6
+ {
7
+ "type": "reasoning-delta",
8
+ "id": "reasoning-0",
9
+ "delta": ", the user is asking, \"What is the best flavor of ice cream?\" I need to figure out how to respond. Let me check the tools provided. The only tool available is the \"say\" function, which allows me to ask a friend for their favorite ice cream flavor. The function requires a \"question\" parameter.\n\nSo, since I don't have any other functions, I can't look up information or calculate the answer. The best approach is to use the \"say\" function to ask a friend. I should formulate the question to match the friend's parameters. The user is asking for the \"best\" flavor, which is subjective. Therefore, asking a friend's favorite makes sense.\n\nI need to structure the function call correctly. The function name is \"say\" and the argument is the question. The question should be, \"What is your favorite flavor of ice cream?\" That's the parameter required. Let me make sure the JSON is properly formatted with the arguments as a JSON object. Yep, that should work. I'll output the tool_call with the function name and the question argument.\n"
10
+ },
11
+ { "type": "tool-input-start", "toolCallId": "0ychh9k6f", "toolName": "say" },
12
+ {
13
+ "type": "tool-input-delta",
14
+ "toolCallId": "0ychh9k6f",
15
+ "inputTextDelta": "{\"question\":\"What is your favorite flavor of ice cream?\"}"
16
+ },
17
+ {
18
+ "type": "tool-input-available",
19
+ "toolCallId": "0ychh9k6f",
20
+ "toolName": "say",
21
+ "input": { "question": "What is your favorite flavor of ice cream?" }
22
+ },
23
+ {
24
+ "type": "tool-output-available",
25
+ "toolCallId": "0ychh9k6f",
26
+ "output": "I'm sorry I can't help you. Stop asking me questions."
27
+ },
28
+ { "type": "reasoning-end", "id": "reasoning-0" },
29
+ { "type": "finish-step" },
30
+ { "type": "start-step" },
31
+ { "type": "reasoning-start", "id": "reasoning-0" },
32
+ { "type": "reasoning-delta", "id": "reasoning-0", "delta": "Okay" },
33
+ {
34
+ "type": "reasoning-delta",
35
+ "id": "reasoning-0",
36
+ "delta": ", the user initially asked for the best ice cream flavor. I tried using the 'say' function to ask a friend, but the friend didn't help. Now I need to respond. Since I can't get an answer from the friend, I should tell the user that I can't determine the best flavor because it's subjective. Maybe suggest they try different ones. Keep the response friendly and helpful.\n"
37
+ },
38
+ { "type": "text-start", "id": "txt-0" },
39
+ {
40
+ "type": "text-delta",
41
+ "id": "txt-0",
42
+ "delta": "The \"best\" ice cream flavor is subjective—it depends on personal taste! Some people love classic vanilla, while others might prefer adventurous options like matcha or salted caramel. Why not try a few and see which one you like most? 🍦"
43
+ },
44
+ { "type": "reasoning-end", "id": "reasoning-0" },
45
+ { "type": "text-end", "id": "txt-0" },
46
+ { "type": "finish-step" },
47
+ { "type": "finish" }
48
+ ]
@@ -10,13 +10,12 @@ import {
10
10
  } from "../validators.js";
11
11
  import type {
12
12
  AgentComponent,
13
- RunActionCtx,
14
13
  RunMutationCtx,
15
14
  RunQueryCtx,
16
15
  SyncStreamsReturnValue,
17
16
  } from "./types.js";
18
17
  import { v } from "convex/values";
19
- import { vMessageDoc } from "../component/schema.js";
18
+ import { vMessageDoc } from "../validators.js";
20
19
 
21
20
  export const vStreamMessagesReturnValue = v.object({
22
21
  ...vPaginationResult(vMessageDoc).fields,
@@ -175,7 +174,7 @@ export class DeltaStreamer<T> {
175
174
 
176
175
  constructor(
177
176
  public readonly component: AgentComponent,
178
- public readonly ctx: RunActionCtx,
177
+ public readonly ctx: RunMutationCtx,
179
178
  config: {
180
179
  stream: true | StreamingOptions;
181
180
  onAsyncAbort: (reason: string) => Promise<void>;
@@ -299,6 +298,7 @@ export class DeltaStreamer<T> {
299
298
  return;
300
299
  }
301
300
  await this.#ongoingWrite;
301
+ await this.#sendDelta();
302
302
  await this.ctx.runMutation(this.component.streams.finish, {
303
303
  streamId: this.streamId,
304
304
  });
@@ -23,21 +23,12 @@ export function serializeTextStreamingPartsV5(
23
23
  ) {
24
24
  last.text += part.text;
25
25
  } else {
26
- if (
27
- part.type === "start-step" ||
28
- part.type === "finish-step" ||
29
- part.type === "start" ||
30
- part.type === "finish"
31
- ) {
32
- continue;
33
- }
34
26
  if (part.type === "file") {
35
27
  compressed.push({
36
28
  type: "file",
37
29
  file: {
38
- mediaType: part.file.mediaType,
39
- base64: part.file.base64,
40
- uint8Array: new Uint8Array([]),
30
+ ...part.file,
31
+ uint8Array: undefined as unknown as Uint8Array,
41
32
  },
42
33
  });
43
34
  }
@@ -1,5 +1,5 @@
1
1
  import type { WithoutSystemFields } from "convex/server";
2
- import type { ThreadDoc } from "../component/schema.js";
2
+ import type { ThreadDoc } from "../validators.js";
3
3
  import type { AgentComponent, RunMutationCtx, RunQueryCtx } from "./types.js";
4
4
 
5
5
  /**
@@ -1,10 +1,12 @@
1
1
  import type {
2
2
  InferSchema,
3
+ ModelMessage,
3
4
  ProviderOptions,
4
5
  Schema,
5
6
  } from "@ai-sdk/provider-utils";
6
7
  import type { JSONValue } from "@ai-sdk/provider";
7
8
  import type {
9
+ EmbeddingModel,
8
10
  GenerateObjectResult,
9
11
  generateText,
10
12
  GenerateTextResult,
@@ -36,16 +38,96 @@ import type {
36
38
  } from "convex/server";
37
39
  import type { GenericId } from "convex/values";
38
40
  import type { Mounts } from "../component/_generated/api.js";
39
- import type { MessageDoc, ThreadDoc } from "../component/schema.js";
40
41
  import type {
42
+ MessageDoc,
41
43
  ProviderMetadata,
42
44
  StreamDelta,
43
45
  StreamMessage,
46
+ ThreadDoc,
44
47
  } from "../validators.js";
45
48
  import type { StreamingOptions } from "./streaming.js";
46
49
  import type * as z3 from "zod/v3";
47
50
  import type * as z4 from "zod/v4";
48
51
 
52
+ export type Config = {
53
+ /**
54
+ * The LLM model to use for generating / streaming text and objects.
55
+ * e.g.
56
+ * import { openai } from "@ai-sdk/openai"
57
+ * const myAgent = new Agent(components.agent, {
58
+ * languageModel: openai.chat("gpt-4o-mini"),
59
+ */
60
+ languageModel?: LanguageModel;
61
+ /**
62
+ * The model to use for text embeddings. Optional.
63
+ * If specified, it will use this for generating vector embeddings
64
+ * of chats, and can opt-in to doing vector search for automatic context
65
+ * on generateText, etc.
66
+ * e.g.
67
+ * import { openai } from "@ai-sdk/openai"
68
+ * const myAgent = new Agent(components.agent, {
69
+ * ...
70
+ * textEmbeddingModel: openai.embedding("text-embedding-3-small")
71
+ */
72
+ textEmbeddingModel?: EmbeddingModel<string>;
73
+ /**
74
+ * Options to determine what messages are included as context in message
75
+ * generation. To disable any messages automatically being added, pass:
76
+ * { recentMessages: 0 }
77
+ */
78
+ contextOptions?: ContextOptions;
79
+ /**
80
+ * Determines whether messages are automatically stored when passed as
81
+ * arguments or generated.
82
+ */
83
+ storageOptions?: StorageOptions;
84
+ /**
85
+ * The usage handler to use for this agent.
86
+ */
87
+ usageHandler?: UsageHandler;
88
+ /**
89
+ * By default, messages are ordered with context in `fetchContextWithPrompt`,
90
+ * but you can override this by providing a context handler. Here you can
91
+ * filter, modify, or enrich the context messages. If provided, the default
92
+ * ordering will not apply. This excludes the system message / instructions.
93
+ */
94
+ contextHandler?: ContextHandler;
95
+ /**
96
+ * Called for each LLM request/response, so you can do things like
97
+ * log the raw request body or response headers to a table, or logs.
98
+ */
99
+ rawRequestResponseHandler?: RawRequestResponseHandler;
100
+ /**
101
+ * @deprecated Reach out if you use this. Otherwise will be removed soon.
102
+ * Default provider options to pass for the LLM calls.
103
+ * This can be overridden at each generate/stream callsite on a per-field
104
+ * basis. To clear a default setting, you'll need to pass `undefined`.
105
+ */
106
+ providerOptions?: ProviderOptions;
107
+ /**
108
+ * The default settings to use for the LLM calls.
109
+ * This can be overridden at each generate/stream callsite on a per-field
110
+ * basis. To clear a default setting, you'll need to pass `undefined`.
111
+ */
112
+ callSettings?: CallSettings;
113
+ /**
114
+ * The maximum number of steps to allow for a single generation.
115
+ *
116
+ * For example, if an agent wants to call a tool, that call and tool response
117
+ * will be one step. Generating a response based on the tool call & response
118
+ * will be a second step.
119
+ * If it runs out of steps, it will return the last step result, which may
120
+ * not be an assistant message.
121
+
122
+ * This becomes the default value when `stopWhen` is not specified in the
123
+ * Agent or generation callsite.
124
+ * AI SDK v5 removed the `maxSteps` argument, but this is kept here for
125
+ * convenience and backwards compatibility.
126
+ * Defaults to 1.
127
+ */
128
+ maxSteps?: number;
129
+ };
130
+
49
131
  /**
50
132
  * Options to configure what messages are fetched as context,
51
133
  * automatically with thread.generateText, or directly via search.
@@ -153,6 +235,50 @@ export type UsageHandler = (
153
235
  },
154
236
  ) => void | Promise<void>;
155
237
 
238
+ /**
239
+ * By default, messages are ordered with context in `fetchContextWithPrompt`,
240
+ * but you can override this by providing a context handler. Here you can filter
241
+ * out, add in, or reorder messages.
242
+ */
243
+ export type ContextHandler = (
244
+ ctx: RunActionCtx,
245
+ args: {
246
+ /**
247
+ * The messages fetched from search.
248
+ */
249
+ search: ModelMessage[];
250
+ /**
251
+ * The recent messages already in the thread history,
252
+ * excluding any messages that came after promptMessageId.
253
+ */
254
+ recent: ModelMessage[];
255
+ /**
256
+ * The messages passed as the `messages` argument to e.g. generateText.
257
+ */
258
+ inputMessages: ModelMessage[];
259
+ /**
260
+ * The message(s) passed as the `prompt` argument to e.g. generateText.
261
+ * Otherwise, if `promptMessageId` was provided, the message at that id.
262
+ * `prompt` will override the message at `promptMessageId`.
263
+ */
264
+ inputPrompt: ModelMessage[];
265
+ /**
266
+ * Any messages on the same `order` as the promptMessageId message after the
267
+ * prompt message. These are presumably existing responses to the prompt
268
+ * message.
269
+ */
270
+ existingResponses: ModelMessage[];
271
+ /**
272
+ * The user associated with the generation, if any.
273
+ */
274
+ userId: string | undefined;
275
+ /**
276
+ * The thread associated with the generation, if any.
277
+ */
278
+ threadId: string | undefined;
279
+ },
280
+ ) => ModelMessage[] | Promise<ModelMessage[]>;
281
+
156
282
  export type RawRequestResponseHandler = (
157
283
  ctx: ActionCtx,
158
284
  args: {
@@ -262,8 +388,15 @@ export type GenerateObjectArgs<
262
388
  ? Array<InferSchema<SCHEMA>>
263
389
  : InferSchema<SCHEMA>,
264
390
  > = Omit<CallSettings, "stopSequences"> &
265
- Prompt &
266
- (OUTPUT extends "enum"
391
+ Prompt & {
392
+ /**
393
+ * If provided, this message will be used as the "prompt" for the LLM call,
394
+ * instead of the prompt or messages.
395
+ * This is useful if you want to first save a user message, then use it as
396
+ * the prompt for the LLM call in another call.
397
+ */
398
+ promptMessageId?: string;
399
+ } & (OUTPUT extends "enum"
267
400
  ? {
268
401
  /**
269
402
  The enum values that the model should use.
@@ -570,6 +703,13 @@ export type Options = {
570
703
  * set in the agent constructor.
571
704
  */
572
705
  usageHandler?: UsageHandler;
706
+ /**
707
+ * By default, messages are ordered with context in `fetchContextWithPrompt`,
708
+ * but you can override this by providing a context handler. Here you can
709
+ * filter, modify, or enrich the context messages. If provided, the default
710
+ * ordering will not apply. This excludes the system message / instructions.
711
+ */
712
+ contextHandler?: ContextHandler;
573
713
  };
574
714
 
575
715
  export type SyncStreamsReturnValue =
@@ -689,6 +689,12 @@ export type Mounts = {
689
689
  },
690
690
  null
691
691
  >;
692
+ getMessageSearchFields: FunctionReference<
693
+ "query",
694
+ "public",
695
+ { messageId: string },
696
+ { embedding?: Array<number>; embeddingModel?: string; text?: string }
697
+ >;
692
698
  getMessagesByIds: FunctionReference<
693
699
  "query",
694
700
  "public",
@@ -1217,15 +1223,17 @@ export type Mounts = {
1217
1223
  "action",
1218
1224
  "public",
1219
1225
  {
1220
- beforeMessageId?: string;
1221
1226
  embedding?: Array<number>;
1222
1227
  embeddingModel?: string;
1223
1228
  limit: number;
1224
1229
  messageRange?: { after: number; before: number };
1225
1230
  searchAllMessagesForUserId?: string;
1231
+ targetMessageId?: string;
1226
1232
  text?: string;
1233
+ textSearch?: boolean;
1227
1234
  threadId?: string;
1228
1235
  vectorScoreThreshold?: number;
1236
+ vectorSearch?: boolean;
1229
1237
  },
1230
1238
  Array<{
1231
1239
  _creationTime: number;
@@ -1464,10 +1472,10 @@ export type Mounts = {
1464
1472
  "query",
1465
1473
  "public",
1466
1474
  {
1467
- beforeMessageId?: string;
1468
1475
  limit: number;
1469
1476
  searchAllMessagesForUserId?: string;
1470
- text: string;
1477
+ targetMessageId?: string;
1478
+ text?: string;
1471
1479
  threadId?: string;
1472
1480
  },
1473
1481
  Array<{