@convex-dev/agent 0.0.1-alpha.0 → 0.0.1-alpha.1

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 (45) hide show
  1. package/dist/commonjs/client/index.d.ts +76 -41
  2. package/dist/commonjs/client/index.d.ts.map +1 -1
  3. package/dist/commonjs/client/index.js +104 -68
  4. package/dist/commonjs/client/index.js.map +1 -1
  5. package/dist/commonjs/component/messages.d.ts +12 -8
  6. package/dist/commonjs/component/messages.d.ts.map +1 -1
  7. package/dist/commonjs/component/messages.js +23 -6
  8. package/dist/commonjs/component/messages.js.map +1 -1
  9. package/dist/commonjs/component/schema.d.ts +6 -6
  10. package/dist/commonjs/component/schema.d.ts.map +1 -1
  11. package/dist/commonjs/component/schema.js +1 -1
  12. package/dist/commonjs/component/schema.js.map +1 -1
  13. package/dist/commonjs/component/vector/index.d.ts +41 -0
  14. package/dist/commonjs/component/vector/index.d.ts.map +1 -0
  15. package/dist/commonjs/component/vector/index.js +120 -0
  16. package/dist/commonjs/component/vector/index.js.map +1 -0
  17. package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
  18. package/dist/commonjs/component/vector/tables.js +4 -2
  19. package/dist/commonjs/component/vector/tables.js.map +1 -1
  20. package/dist/esm/client/index.d.ts +76 -41
  21. package/dist/esm/client/index.d.ts.map +1 -1
  22. package/dist/esm/client/index.js +104 -68
  23. package/dist/esm/client/index.js.map +1 -1
  24. package/dist/esm/component/messages.d.ts +12 -8
  25. package/dist/esm/component/messages.d.ts.map +1 -1
  26. package/dist/esm/component/messages.js +23 -6
  27. package/dist/esm/component/messages.js.map +1 -1
  28. package/dist/esm/component/schema.d.ts +6 -6
  29. package/dist/esm/component/schema.d.ts.map +1 -1
  30. package/dist/esm/component/schema.js +1 -1
  31. package/dist/esm/component/schema.js.map +1 -1
  32. package/dist/esm/component/vector/index.d.ts +41 -0
  33. package/dist/esm/component/vector/index.d.ts.map +1 -0
  34. package/dist/esm/component/vector/index.js +120 -0
  35. package/dist/esm/component/vector/index.js.map +1 -0
  36. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  37. package/dist/esm/component/vector/tables.js +4 -2
  38. package/dist/esm/component/vector/tables.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/client/index.ts +248 -154
  41. package/src/component/_generated/api.d.ts +131 -5
  42. package/src/component/messages.ts +30 -8
  43. package/src/component/schema.ts +1 -1
  44. package/src/component/vector/index.ts +144 -0
  45. package/src/component/vector/tables.ts +7 -5
@@ -1,38 +1,44 @@
1
- import { api } from "../component/_generated/api";
2
- import { RunActionCtx, RunMutationCtx, RunQueryCtx, UseApi } from "./types";
3
1
  import type { EmbeddingModelV1, LanguageModelV1 } from "@ai-sdk/provider";
4
- import { Message, MessageStatus, SearchOptions, Step } from "../validators";
5
2
  import type {
3
+ CoreMessage,
4
+ DeepPartial,
5
+ GenerateObjectResult,
6
+ GenerateTextResult,
7
+ StepResult,
8
+ StreamObjectResult,
6
9
  StreamTextResult,
7
10
  Tool,
11
+ ToolChoice,
12
+ ToolExecutionOptions,
8
13
  ToolSet,
9
- StepResult,
10
14
  Message as UIMessage,
11
- CoreMessage,
12
- GenerateObjectResult,
13
- StreamObjectResult,
14
- DeepPartial,
15
- GenerateTextResult,
16
15
  } from "ai";
17
16
  import {
18
- generateText,
19
- generateObject,
20
- streamText,
21
- streamObject,
22
17
  convertToCoreMessages,
23
18
  coreMessageSchema,
19
+ generateObject,
20
+ generateText,
21
+ streamObject,
22
+ streamText,
24
23
  } from "ai";
24
+ import type { ZodType } from "zod";
25
+ import { api } from "../component/_generated/api";
26
+ import { Message, MessageStatus, SearchOptions } from "../validators";
27
+ import { RunActionCtx, RunMutationCtx, RunQueryCtx, UseApi } from "./types";
25
28
  // TODO: is this the only dependency that needs helpers in client?
26
29
  import { assert } from "convex-helpers";
27
- import { DEFAULT_MESSAGE_RANGE, extractText } from "../shared";
30
+ import { convexToZod } from "convex-helpers/server/zod";
31
+ import { GenericActionCtx, GenericDataModel } from "convex/server";
32
+ import { Infer, Validator } from "convex/values";
28
33
  import {
29
- serializeMessage,
30
34
  serializeMessageWithId,
31
35
  serializeNewMessagesInStep,
32
36
  serializeStep,
33
37
  } from "../mapping";
38
+ import { DEFAULT_MESSAGE_RANGE, extractText } from "../shared";
34
39
 
35
40
  export type ContextOptions = {
41
+ parentMessageId?: string;
36
42
  includeToolMessages?: boolean;
37
43
  recentMessages?: number;
38
44
  searchOptions?: {
@@ -44,9 +50,20 @@ export type ContextOptions = {
44
50
  searchOtherChats?: boolean;
45
51
  };
46
52
 
53
+ export type StorageOptions = {
54
+ // Defaults to false, allowing you to pass in arbitrary context that will
55
+ // be in addition to automatically fetched content.
56
+ // Pass true to have all input messages saved to the chat history.
57
+ saveAllInputMessages?: boolean;
58
+ // Defaults to true
59
+ saveOutputMessages?: boolean;
60
+ };
61
+
62
+ export type GenerationOutputMetadata = { messageId: string };
63
+
47
64
  type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };
48
65
 
49
- export class Agent {
66
+ export class Agent<AgentTools extends ToolSet> {
50
67
  constructor(
51
68
  public component: UseApi<typeof api>,
52
69
  public options: {
@@ -54,7 +71,7 @@ export class Agent {
54
71
  chat: LanguageModelV1;
55
72
  textEmbedding?: EmbeddingModelV1<string>;
56
73
  defaultSystemPrompt?: string;
57
- tools?: Record<string, Tool>;
74
+ tools?: AgentTools;
58
75
  }
59
76
  ) {}
60
77
 
@@ -93,7 +110,7 @@ export class Agent {
93
110
  }
94
111
  ): Promise<{
95
112
  chatId: string;
96
- chat: Chat;
113
+ chat: Chat<AgentTools>;
97
114
  }>;
98
115
  /**
99
116
  * Start a new chat with the agent. This will have a fresh history, though if
@@ -125,7 +142,7 @@ export class Agent {
125
142
  }
126
143
  ): Promise<{
127
144
  chatId: string;
128
- chat?: Chat;
145
+ chat?: Chat<AgentTools>;
129
146
  }> {
130
147
  const chatDoc = await ctx.runMutation(this.component.messages.createChat, {
131
148
  defaultSystemPrompt: this.options.defaultSystemPrompt,
@@ -157,7 +174,7 @@ export class Agent {
157
174
  userId?: string;
158
175
  }
159
176
  ): Promise<{
160
- chat: Chat;
177
+ chat: Chat<AgentTools>;
161
178
  }> {
162
179
  // return this.component.continueChat(ctx, args);
163
180
  return {
@@ -166,7 +183,7 @@ export class Agent {
166
183
  streamText: this.streamText.bind(this, ctx, { userId, chatId }),
167
184
  generateObject: this.generateObject.bind(this, ctx, { userId, chatId }),
168
185
  streamObject: this.streamObject.bind(this, ctx, { userId, chatId }),
169
- } as Chat,
186
+ } as Chat<AgentTools>,
170
187
  };
171
188
  }
172
189
 
@@ -190,9 +207,11 @@ export class Agent {
190
207
  {
191
208
  userId: args.searchOtherChats ? args.userId : undefined,
192
209
  chatId: args.chatId,
193
- ...(await this.searchWithDefaults(args, args.messages)),
210
+ parentMessageId: args.parentMessageId,
211
+ ...(await this.searchOptionsWithDefaults(args, args.messages)),
194
212
  }
195
213
  );
214
+ // TODO: track what messages we used for context
196
215
  contextMessages.push(...searchMessages.map((m) => m.message!));
197
216
  }
198
217
  if (args.chatId) {
@@ -202,6 +221,7 @@ export class Agent {
202
221
  chatId: args.chatId,
203
222
  isTool: args.includeToolMessages ?? false,
204
223
  limit: args.recentMessages,
224
+ parentMessageId: args.parentMessageId,
205
225
  order: "desc",
206
226
  statuses: ["success"],
207
227
  }
@@ -216,22 +236,12 @@ export class Agent {
216
236
  args: {
217
237
  chatId: string;
218
238
  messages: CoreMessageMaybeWithId[];
219
- steps?: StepResult<ToolSet>[];
220
239
  pending?: boolean;
240
+ parentMessageId?: string;
221
241
  }
222
242
  ): Promise<{
223
243
  lastMessageId: string;
224
- }>;
225
- async saveMessages(
226
- ctx: RunMutationCtx,
227
- args: {
228
- chatId: string;
229
- messages: CoreMessageMaybeWithId[];
230
- steps?: StepResult<ToolSet>[];
231
- pending?: boolean;
232
- }
233
- ): Promise<{
234
- lastMessageId?: string;
244
+ messageIds: string[];
235
245
  }> {
236
246
  const result = await ctx.runMutation(this.component.messages.addMessages, {
237
247
  chatId: args.chatId,
@@ -240,19 +250,14 @@ export class Agent {
240
250
  messages: args.messages.map(serializeMessageWithId),
241
251
  failPendingSteps: true,
242
252
  pending: args.pending ?? false,
253
+ parentMessageId: args.parentMessageId,
243
254
  });
244
255
  return {
245
- lastMessageId: result.messages.at(-1)?._id,
256
+ lastMessageId: result.messages.at(-1)!._id,
257
+ messageIds: result.messages.map((m) => m._id),
246
258
  };
247
259
  }
248
260
 
249
- async replaceMessages(
250
- ctx: RunMutationCtx,
251
- args: { chatId: string; messages: Message[] }
252
- ): Promise<void> {
253
- throw new Error("Not implemented");
254
- }
255
-
256
261
  async saveStep<TOOLS extends ToolSet>(
257
262
  ctx: RunMutationCtx,
258
263
  args: { chatId: string; messageId: string; step: StepResult<TOOLS> }
@@ -267,6 +272,7 @@ export class Agent {
267
272
  });
268
273
  }
269
274
 
275
+ // If you manually create a message, call this to either commit or reset it.
270
276
  async completeMessage<TOOLS extends ToolSet>(
271
277
  ctx: RunMutationCtx,
272
278
  args: {
@@ -274,10 +280,7 @@ export class Agent {
274
280
  messageId: string;
275
281
  result:
276
282
  | { kind: "error"; error: string }
277
- | {
278
- kind: "success";
279
- value: { steps: StepResult<TOOLS>[] };
280
- };
283
+ | { kind: "success"; value: { steps: StepResult<TOOLS>[] } };
281
284
  }
282
285
  ): Promise<void> {
283
286
  const result = args.result;
@@ -315,13 +318,16 @@ export class Agent {
315
318
  chatId,
316
319
  }: {
317
320
  userId?: string;
318
- chatId?: string;
321
+ chatId: string;
319
322
  },
320
- args: Partial<
323
+ args: TextArgs<
324
+ AgentTools,
325
+ TOOLS,
321
326
  Parameters<typeof generateText<TOOLS, OUTPUT, OUTPUT_PARTIAL>>[0]
322
- > &
323
- ContextOptions
324
- ): Promise<GenerateTextResult<TOOLS, OUTPUT>> {
327
+ >
328
+ ): Promise<
329
+ GenerateTextResult<TOOLS & AgentTools, OUTPUT> & GenerationOutputMetadata
330
+ > {
325
331
  const { prompt, messages: raw, ...rest } = args;
326
332
  const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
327
333
  const contextMessages = await this.fetchContextMessages(ctx, {
@@ -330,24 +336,25 @@ export class Agent {
330
336
  chatId,
331
337
  messages,
332
338
  });
333
- let messageId: string | undefined;
334
- if (chatId) {
335
- const { lastMessageId } = await this.saveMessages(ctx, {
336
- chatId,
337
- // TODO: only save the last message unless explicitly told to save all
338
- messages,
339
- pending: true,
340
- });
341
- messageId = lastMessageId;
342
- }
339
+ const { lastMessageId: messageId } = await this.saveMessages(ctx, {
340
+ chatId,
341
+ messages: args.saveAllInputMessages ? messages : messages.slice(-1),
342
+ pending: true,
343
+ parentMessageId: args.parentMessageId,
344
+ });
345
+ const defaults = this.options.tools;
346
+ const tools = wrapTools(ctx, chatId, userId, defaults, args.tools) as TOOLS;
343
347
  try {
344
348
  const result = await generateText({
345
349
  model: this.options.chat,
346
350
  messages: [...contextMessages, ...messages],
347
351
  system: this.options.defaultSystemPrompt,
352
+ tools,
353
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
354
+ toolChoice: args.toolChoice as any,
348
355
  ...rest,
349
356
  onStepFinish: async (step) => {
350
- if (chatId && messageId) {
357
+ if (chatId && messageId && args.saveOutputMessages) {
351
358
  await this.saveStep(ctx, {
352
359
  chatId,
353
360
  messageId,
@@ -357,7 +364,7 @@ export class Agent {
357
364
  return args.onStepFinish?.(step);
358
365
  },
359
366
  });
360
- return result;
367
+ return { ...result, messageId };
361
368
  } catch (error) {
362
369
  if (chatId && messageId) {
363
370
  await ctx.runMutation(this.component.messages.rollbackMessage, {
@@ -374,13 +381,16 @@ export class Agent {
374
381
  OUTPUT = never,
375
382
  PARTIAL_OUTPUT = never,
376
383
  >(
377
- ctx: RunMutationCtx,
378
- { userId, chatId }: { userId?: string; chatId?: string },
384
+ ctx: RunActionCtx,
385
+ { userId, chatId }: { userId?: string; chatId: string },
379
386
  args: Partial<
380
387
  Parameters<typeof streamText<TOOLS, OUTPUT, PARTIAL_OUTPUT>>[0]
381
388
  > &
382
- ContextOptions
383
- ): Promise<StreamTextResult<TOOLS, PARTIAL_OUTPUT>> {
389
+ ContextOptions &
390
+ StorageOptions
391
+ ): Promise<
392
+ StreamTextResult<TOOLS, PARTIAL_OUTPUT> & GenerationOutputMetadata
393
+ > {
384
394
  const { prompt, messages: raw, ...rest } = args;
385
395
  const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
386
396
  const contextMessages = await this.fetchContextMessages(ctx, {
@@ -389,20 +399,21 @@ export class Agent {
389
399
  chatId,
390
400
  messages,
391
401
  });
392
- let messageId: string | undefined;
393
- if (chatId) {
394
- const { lastMessageId } = await this.saveMessages(ctx, {
395
- chatId,
396
- // TODO: only save the last message unless explicitly told to save all
397
- messages,
398
- pending: true,
399
- });
400
- messageId = lastMessageId;
401
- }
402
- return streamText({
402
+ const { lastMessageId: messageId } = await this.saveMessages(ctx, {
403
+ chatId,
404
+ messages: args.saveAllInputMessages ? messages : messages.slice(-1),
405
+ pending: true,
406
+ parentMessageId: args.parentMessageId,
407
+ });
408
+ const defaults = this.options.tools;
409
+ const tools = wrapTools(ctx, chatId, userId, defaults, args.tools) as TOOLS;
410
+ const result = streamText({
403
411
  model: this.options.chat,
404
412
  messages: [...contextMessages, ...messages],
405
413
  system: this.options.defaultSystemPrompt,
414
+ tools,
415
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
416
+ toolChoice: args.toolChoice as any,
406
417
  ...rest,
407
418
  onChunk: async (chunk) => {
408
419
  console.log("onChunk", chunk);
@@ -436,16 +447,18 @@ export class Agent {
436
447
  return args.onStepFinish?.(step);
437
448
  },
438
449
  });
450
+ return { ...result, messageId };
439
451
  }
440
452
 
441
- // TODO: not sure why it needs to extend string
442
- async generateObject<OBJECT extends string>(
453
+ // TODO: add the crazy number of overloads to get types through
454
+ async generateObject<T>(
443
455
  ctx: RunActionCtx,
444
- { userId, chatId }: { userId?: string; chatId?: string },
445
- args: Omit<Parameters<typeof generateObject<OBJECT>>[0], "model"> & {
456
+ { userId, chatId }: { userId?: string; chatId: string },
457
+ args: Omit<Parameters<typeof generateObject>[0], "model"> & {
446
458
  model?: LanguageModelV1;
447
- } & ContextOptions
448
- ): Promise<GenerateObjectResult<OBJECT>> {
459
+ } & ContextOptions &
460
+ StorageOptions
461
+ ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
449
462
  const { prompt, messages: raw, ...rest } = args;
450
463
  const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
451
464
  const contextMessages = await this.fetchContextMessages(ctx, {
@@ -454,20 +467,29 @@ export class Agent {
454
467
  chatId,
455
468
  messages,
456
469
  });
457
- return generateObject({
470
+ const { lastMessageId: messageId } = await this.saveMessages(ctx, {
471
+ chatId,
472
+ messages: args.saveAllInputMessages ? messages : messages.slice(-1),
473
+ pending: true,
474
+ });
475
+ const result = (await generateObject({
458
476
  model: this.options.chat,
459
477
  messages: [...contextMessages, ...messages],
460
478
  ...rest,
461
- }) as Promise<GenerateObjectResult<OBJECT>>;
479
+ })) as GenerateObjectResult<T>;
480
+ return { ...result, messageId };
462
481
  }
463
482
 
464
483
  async streamObject<T>(
465
484
  ctx: RunMutationCtx,
466
- { userId, chatId }: { userId?: string; chatId?: string },
485
+ { userId, chatId }: { userId?: string; chatId: string },
467
486
  args: Omit<Parameters<typeof streamObject<T>>[0], "model"> & {
468
487
  model?: LanguageModelV1;
469
- } & ContextOptions
470
- ) {
488
+ } & ContextOptions &
489
+ StorageOptions
490
+ ): Promise<
491
+ StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
492
+ > {
471
493
  const { prompt, messages: raw, ...rest } = args;
472
494
  const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
473
495
  const contextMessages = await this.fetchContextMessages(ctx, {
@@ -476,7 +498,12 @@ export class Agent {
476
498
  chatId,
477
499
  messages,
478
500
  });
479
- return streamObject<T>({
501
+ const { lastMessageId: messageId } = await this.saveMessages(ctx, {
502
+ chatId,
503
+ messages: args.saveAllInputMessages ? messages : messages.slice(-1),
504
+ pending: true,
505
+ });
506
+ const result = streamObject<T>({
480
507
  model: this.options.chat,
481
508
  messages: [...contextMessages, ...messages],
482
509
  ...rest,
@@ -488,9 +515,10 @@ export class Agent {
488
515
  console.log("onFinish", result);
489
516
  },
490
517
  }) as StreamObjectResult<DeepPartial<T>, T, never>;
518
+ return { ...result, messageId };
491
519
  }
492
520
 
493
- async searchWithDefaults(
521
+ async searchOptionsWithDefaults(
494
522
  searchArgs: ContextOptions,
495
523
  messages: CoreMessage[]
496
524
  ): Promise<SearchOptions> {
@@ -558,71 +586,8 @@ export class Agent {
558
586
  isDone: messages.isDone,
559
587
  };
560
588
  }
561
-
562
- async getSteps(
563
- ctx: RunQueryCtx,
564
- args: { messageId: string }
565
- ): Promise<{
566
- steps: Step[];
567
- }> {
568
- return { steps: [] };
569
- }
570
- }
571
-
572
- interface Chat {
573
- generateText<TOOLS extends ToolSet, OUTPUT = never, OUTPUT_PARTIAL = never>(
574
- args: Partial<
575
- Parameters<typeof generateText<TOOLS, OUTPUT, OUTPUT_PARTIAL>>[0]
576
- >
577
- ): Promise<GenerateTextResult<TOOLS, OUTPUT>>;
578
- streamText<TOOLS extends ToolSet, OUTPUT = never, PARTIAL_OUTPUT = never>(
579
- args: Partial<
580
- Parameters<typeof streamText<TOOLS, OUTPUT, PARTIAL_OUTPUT>>[0]
581
- >
582
- ): Promise<StreamTextResult<TOOLS, PARTIAL_OUTPUT>>;
583
- generateObject<OBJECT extends string>(
584
- args: Omit<Parameters<typeof generateObject<OBJECT>>[0], "model"> & {
585
- model?: LanguageModelV1;
586
- }
587
- ): Promise<GenerateObjectResult<OBJECT>>;
588
- streamObject<T>(
589
- args: Omit<Parameters<typeof streamObject<T>>[0], "model"> & {
590
- model?: LanguageModelV1;
591
- }
592
- ): Promise<StreamObjectResult<DeepPartial<T>, T, never>>;
593
589
  }
594
590
 
595
- // type ToolParameters = ZodTypeAny | Schema<unknown>; // TODO: support convex validator
596
- // type inferParameters<PARAMETERS extends ToolParameters> =
597
- // PARAMETERS extends Schema<unknown>
598
- // ? PARAMETERS["_type"]
599
- // : PARAMETERS extends z.ZodTypeAny
600
- // ? z.infer<PARAMETERS>
601
- // : never;
602
- // /**
603
- // * This is a wrapper around the ai.tool function that adds support for
604
- // * userId and chatId to the tool, if they're called within a chat from an agent.
605
- // * @param tool The AI tool. See https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling
606
- // * @returns The same tool, but with userId and chatId args support added.
607
- // */
608
- // export function tool<PARAMETERS extends ToolParameters, RESULT>(
609
- // tool: Tool<PARAMETERS, RESULT> & {
610
- // execute: (
611
- // args: inferParameters<PARAMETERS> & { userId?: string; chatId?: string },
612
- // options: ToolExecutionOptions
613
- // ) => PromiseLike<RESULT>;
614
- // }
615
- // ): Tool<PARAMETERS, RESULT> & {
616
- // execute: (
617
- // args: inferParameters<PARAMETERS>,
618
- // options: ToolExecutionOptions
619
- // ) => PromiseLike<RESULT>;
620
- // } {
621
- // // eslint-disable-next-line @typescript-eslint/no-explicit-any
622
- // (tool as any).__acceptUserIdAndChatId = true;
623
- // return tool;
624
- // }
625
-
626
591
  export function promptOrMessagesToCoreMessages(args: {
627
592
  system?: string;
628
593
  prompt?: string;
@@ -654,6 +619,135 @@ export function promptOrMessagesToCoreMessages(args: {
654
619
  return messages;
655
620
  }
656
621
 
622
+ type TextArgs<
623
+ AgentTools extends ToolSet,
624
+ TOOLS extends ToolSet,
625
+ T extends {
626
+ toolChoice?: ToolChoice<TOOLS & AgentTools>;
627
+ tools?: TOOLS;
628
+ model: LanguageModelV1;
629
+ },
630
+ > = Omit<T, "toolChoice" | "tools" | "model"> & {
631
+ model?: LanguageModelV1;
632
+ } & {
633
+ tools?: TOOLS;
634
+ toolChoice?: ToolChoice<{ [key in keyof TOOLS | keyof AgentTools]: unknown }>;
635
+ } & ContextOptions &
636
+ StorageOptions;
637
+
638
+ type ObjectArgs<
639
+ T extends {
640
+ model: LanguageModelV1;
641
+ },
642
+ > = Omit<T, "model"> & {
643
+ model?: LanguageModelV1;
644
+ } & ContextOptions &
645
+ StorageOptions;
646
+
647
+ interface Chat<AgentTools extends ToolSet> {
648
+ generateText<TOOLS extends ToolSet, OUTPUT = never, OUTPUT_PARTIAL = never>(
649
+ args: TextArgs<
650
+ AgentTools,
651
+ TOOLS,
652
+ Parameters<typeof generateText<TOOLS, OUTPUT, OUTPUT_PARTIAL>>[0]
653
+ >
654
+ ): Promise<
655
+ GenerateTextResult<TOOLS & AgentTools, OUTPUT> & GenerationOutputMetadata
656
+ >;
657
+
658
+ streamText<TOOLS extends ToolSet, OUTPUT = never, PARTIAL_OUTPUT = never>(
659
+ args: TextArgs<
660
+ AgentTools,
661
+ TOOLS,
662
+ Parameters<typeof streamText<TOOLS, OUTPUT, PARTIAL_OUTPUT>>[0]
663
+ >
664
+ ): Promise<
665
+ StreamTextResult<TOOLS & AgentTools, PARTIAL_OUTPUT> &
666
+ GenerationOutputMetadata
667
+ >;
668
+ // TODO: add all the overloads
669
+ generateObject<T>(
670
+ args: ObjectArgs<Parameters<typeof generateObject>[0]>
671
+ ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata>;
672
+ streamObject<T>(
673
+ args: ObjectArgs<Parameters<typeof streamObject<T>>[0]>
674
+ ): Promise<
675
+ StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
676
+ >;
677
+ }
678
+
679
+ // type ToolParameters = ZodTypeAny | Schema<unknown>; // TODO: support convex validator
680
+ // type inferParameters<PARAMETERS extends ToolParameters> =
681
+ // PARAMETERS extends Schema<unknown>
682
+ // ? PARAMETERS["_type"]
683
+ // : PARAMETERS extends z.ZodTypeAny
684
+ // ? z.infer<PARAMETERS>
685
+ // : never;
686
+ /**
687
+ * This is a wrapper around the ai.tool function that adds support for
688
+ * userId and chatId to the tool, if they're called within a chat from an agent.
689
+ * @param tool The AI tool. See https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling
690
+ * @returns The same tool, but with userId and chatId args support added.
691
+ */
692
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
693
+ export function tool<V extends Validator<any, any, any>, RESULT>(convexTool: {
694
+ args: V;
695
+ description?: string;
696
+ handler: (
697
+ ctx: GenericActionCtx<GenericDataModel> & {
698
+ userId?: string;
699
+ chatId?: string;
700
+ },
701
+ args: Infer<V>,
702
+ options: ToolExecutionOptions
703
+ ) => PromiseLike<RESULT>;
704
+ ctx?: GenericActionCtx<GenericDataModel> & {
705
+ userId?: string;
706
+ chatId?: string;
707
+ };
708
+ }): Tool<ZodType<Infer<V>>, RESULT> {
709
+ const tool = {
710
+ __acceptUserIdAndChatId: true,
711
+ description: convexTool.description,
712
+ parameters: convexToZod(convexTool.args),
713
+ execute: async (args: Infer<V>, options: ToolExecutionOptions) => {
714
+ if (!convexTool.ctx) {
715
+ throw new Error(
716
+ "To use a Convex tool, you must either provide the ctx" +
717
+ " at definition time (dynamically in an action), or use the Agent to" +
718
+ " call it (which injects the ctx, userId and chatId)"
719
+ );
720
+ }
721
+ return convexTool.handler(convexTool.ctx, args, options);
722
+ },
723
+ };
724
+ return tool;
725
+ }
726
+
727
+ export function wrapTools(
728
+ actionCtx: RunActionCtx,
729
+ chatId: string,
730
+ userId?: string,
731
+ ...toolSets: (ToolSet | undefined)[]
732
+ ): ToolSet {
733
+ const ctx = { ...actionCtx, chatId, userId };
734
+ const output = {} as ToolSet;
735
+ for (const toolSet of toolSets) {
736
+ if (!toolSet) {
737
+ continue;
738
+ }
739
+ for (const [name, tool] of Object.entries(toolSet)) {
740
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
741
+ if (!(tool as any).__acceptUserIdAndChatId) {
742
+ output[name] = tool;
743
+ } else {
744
+ const out = { ...tool, ctx };
745
+ output[name] = out;
746
+ }
747
+ }
748
+ }
749
+ return output;
750
+ }
657
751
  // export function convexValidatorSchema<T>(validator: Validator<unknown>) {
658
752
  // return ai.jsonSchema(convexToJsonSchema(validator));
659
753
  // }