@convex-dev/agent 0.0.1-alpha.4 → 0.0.2

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 (66) hide show
  1. package/README.md +35 -7
  2. package/dist/commonjs/client/index.d.ts +800 -84
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +176 -130
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/component/messages.d.ts +124 -105
  7. package/dist/commonjs/component/messages.d.ts.map +1 -1
  8. package/dist/commonjs/component/messages.js +145 -247
  9. package/dist/commonjs/component/messages.js.map +1 -1
  10. package/dist/commonjs/component/schema.d.ts +822 -746
  11. package/dist/commonjs/component/schema.d.ts.map +1 -1
  12. package/dist/commonjs/component/schema.js +13 -3
  13. package/dist/commonjs/component/schema.js.map +1 -1
  14. package/dist/commonjs/component/vector/index.d.ts +21 -6
  15. package/dist/commonjs/component/vector/index.d.ts.map +1 -1
  16. package/dist/commonjs/component/vector/index.js +32 -20
  17. package/dist/commonjs/component/vector/index.js.map +1 -1
  18. package/dist/commonjs/component/vector/tables.d.ts +25 -5
  19. package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
  20. package/dist/commonjs/component/vector/tables.js +14 -6
  21. package/dist/commonjs/component/vector/tables.js.map +1 -1
  22. package/dist/commonjs/mapping.d.ts +3 -2
  23. package/dist/commonjs/mapping.d.ts.map +1 -1
  24. package/dist/commonjs/mapping.js +45 -13
  25. package/dist/commonjs/mapping.js.map +1 -1
  26. package/dist/commonjs/validators.d.ts +2817 -145
  27. package/dist/commonjs/validators.d.ts.map +1 -1
  28. package/dist/commonjs/validators.js +79 -7
  29. package/dist/commonjs/validators.js.map +1 -1
  30. package/dist/esm/client/index.d.ts +800 -84
  31. package/dist/esm/client/index.d.ts.map +1 -1
  32. package/dist/esm/client/index.js +176 -130
  33. package/dist/esm/client/index.js.map +1 -1
  34. package/dist/esm/component/messages.d.ts +124 -105
  35. package/dist/esm/component/messages.d.ts.map +1 -1
  36. package/dist/esm/component/messages.js +145 -247
  37. package/dist/esm/component/messages.js.map +1 -1
  38. package/dist/esm/component/schema.d.ts +822 -746
  39. package/dist/esm/component/schema.d.ts.map +1 -1
  40. package/dist/esm/component/schema.js +13 -3
  41. package/dist/esm/component/schema.js.map +1 -1
  42. package/dist/esm/component/vector/index.d.ts +21 -6
  43. package/dist/esm/component/vector/index.d.ts.map +1 -1
  44. package/dist/esm/component/vector/index.js +32 -20
  45. package/dist/esm/component/vector/index.js.map +1 -1
  46. package/dist/esm/component/vector/tables.d.ts +25 -5
  47. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  48. package/dist/esm/component/vector/tables.js +14 -6
  49. package/dist/esm/component/vector/tables.js.map +1 -1
  50. package/dist/esm/mapping.d.ts +3 -2
  51. package/dist/esm/mapping.d.ts.map +1 -1
  52. package/dist/esm/mapping.js +45 -13
  53. package/dist/esm/mapping.js.map +1 -1
  54. package/dist/esm/validators.d.ts +2817 -145
  55. package/dist/esm/validators.d.ts.map +1 -1
  56. package/dist/esm/validators.js +79 -7
  57. package/dist/esm/validators.js.map +1 -1
  58. package/package.json +7 -7
  59. package/src/client/index.ts +347 -170
  60. package/src/component/_generated/api.d.ts +33 -16
  61. package/src/component/messages.ts +177 -290
  62. package/src/component/schema.ts +14 -3
  63. package/src/component/vector/index.ts +59 -26
  64. package/src/component/vector/tables.ts +38 -6
  65. package/src/mapping.ts +60 -19
  66. package/src/validators.ts +112 -12
@@ -4,36 +4,48 @@ import type {
4
4
  DeepPartial,
5
5
  GenerateObjectResult,
6
6
  GenerateTextResult,
7
+ JSONValue,
8
+ RepairTextFunction,
7
9
  StepResult,
8
10
  StreamObjectResult,
9
11
  StreamTextResult,
12
+ TelemetrySettings,
10
13
  Tool,
11
14
  ToolChoice,
12
15
  ToolExecutionOptions,
13
16
  ToolSet,
17
+ Message as UIMessage,
14
18
  } from "ai";
15
19
  import { generateObject, generateText, streamObject, streamText } from "ai";
16
- import { api } from "../component/_generated/api";
17
- import {
18
- SearchOptions,
19
- vThreadArgs,
20
- vContextOptions,
21
- vObjectArgs,
22
- vStorageOptions,
23
- } from "../validators";
24
- import { RunActionCtx, RunMutationCtx, RunQueryCtx, UseApi } from "./types";
25
- // TODO: is this the only dependency that needs helpers in client?
26
20
  import { assert } from "convex-helpers";
27
21
  import { ConvexToZod, convexToZod } from "convex-helpers/server/zod";
28
22
  import { internalActionGeneric } from "convex/server";
29
23
  import { Infer, v, Validator } from "convex/values";
24
+ import { z } from "zod";
25
+ import { api } from "../component/_generated/api";
26
+ import {
27
+ validateVectorDimension,
28
+ VectorDimension,
29
+ } from "../component/vector/tables";
30
30
  import {
31
31
  promptOrMessagesToCoreMessages,
32
32
  serializeMessageWithId,
33
33
  serializeNewMessagesInStep,
34
+ serializeObjectResult,
34
35
  serializeStep,
35
36
  } from "../mapping";
36
37
  import { DEFAULT_MESSAGE_RANGE, extractText } from "../shared";
38
+ import {
39
+ CallSettings,
40
+ ProviderMetadata,
41
+ ProviderOptions,
42
+ SearchOptions,
43
+ vContextOptions,
44
+ vSafeObjectArgs,
45
+ vStorageOptions,
46
+ vTextArgs,
47
+ } from "../validators";
48
+ import { RunActionCtx, RunMutationCtx, RunQueryCtx, UseApi } from "./types";
37
49
 
38
50
  export type ContextOptions = {
39
51
  /**
@@ -80,11 +92,13 @@ export type StorageOptions = {
80
92
  // be in addition to automatically fetched content.
81
93
  // Pass true to have all input messages saved to the thread history.
82
94
  saveAllInputMessages?: boolean;
83
- // Defaults to true
95
+ // Defaults to true, saving the prompt, or last message passed to generateText.
96
+ saveAnyInputMessages?: boolean;
97
+ // Defaults to true.
84
98
  saveOutputMessages?: boolean;
85
99
  };
86
100
 
87
- export type GenerationOutputMetadata = { messageId: string };
101
+ export type GenerationOutputMetadata = { messageId?: string };
88
102
 
89
103
  type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };
90
104
 
@@ -93,7 +107,7 @@ export class Agent<AgentTools extends ToolSet> {
93
107
  public component: UseApi<typeof api>,
94
108
  public options: {
95
109
  name?: string;
96
- thread: LanguageModelV1;
110
+ chat: LanguageModelV1;
97
111
  textEmbedding?: EmbeddingModelV1<string>;
98
112
  instructions?: string;
99
113
  tools?: AgentTools;
@@ -116,7 +130,7 @@ export class Agent<AgentTools extends ToolSet> {
116
130
  */
117
131
  async createThread(
118
132
  ctx: RunActionCtx,
119
- args: {
133
+ args?: {
120
134
  /**
121
135
  * The userId to associate with the thread. If not provided, the thread will be
122
136
  * anonymous.
@@ -152,7 +166,7 @@ export class Agent<AgentTools extends ToolSet> {
152
166
  */
153
167
  async createThread(
154
168
  ctx: RunMutationCtx,
155
- args: {
169
+ args?: {
156
170
  userId?: string;
157
171
  parentThreadIds?: string[];
158
172
  title?: string;
@@ -163,7 +177,7 @@ export class Agent<AgentTools extends ToolSet> {
163
177
  }>;
164
178
  async createThread(
165
179
  ctx: RunActionCtx | RunMutationCtx,
166
- args: {
180
+ args?: {
167
181
  userId: string;
168
182
  parentThreadIds?: string[];
169
183
  title?: string;
@@ -177,10 +191,10 @@ export class Agent<AgentTools extends ToolSet> {
177
191
  this.component.messages.createThread,
178
192
  {
179
193
  defaultSystemPrompt: this.options.instructions,
180
- userId: args.userId,
181
- title: args.title,
182
- summary: args.summary,
183
- parentThreadIds: args.parentThreadIds,
194
+ userId: args?.userId,
195
+ title: args?.title,
196
+ summary: args?.summary,
197
+ parentThreadIds: args?.parentThreadIds,
184
198
  }
185
199
  );
186
200
  if (!("runAction" in ctx)) {
@@ -188,7 +202,7 @@ export class Agent<AgentTools extends ToolSet> {
188
202
  }
189
203
  const { thread } = await this.continueThread(ctx, {
190
204
  threadId: threadDoc._id,
191
- userId: args.userId,
205
+ userId: args?.userId,
192
206
  });
193
207
  return {
194
208
  threadId: threadDoc._id,
@@ -215,6 +229,7 @@ export class Agent<AgentTools extends ToolSet> {
215
229
  // return this.component.continueThread(ctx, args);
216
230
  return {
217
231
  thread: {
232
+ threadId,
218
233
  generateText: this.generateText.bind(this, ctx, { userId, threadId }),
219
234
  streamText: this.streamText.bind(this, ctx, { userId, threadId }),
220
235
  generateObject: this.generateObject.bind(this, ctx, {
@@ -238,6 +253,7 @@ export class Agent<AgentTools extends ToolSet> {
238
253
  assert(args.userId || args.threadId, "Specify userId or threadId");
239
254
  // Fetch the latest messages from the thread
240
255
  const contextMessages: CoreMessage[] = [];
256
+ let included: Set<string> | undefined;
241
257
  const opts = this.mergedContextOptions(args);
242
258
  if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
243
259
  if (!("runAction" in ctx)) {
@@ -253,6 +269,7 @@ export class Agent<AgentTools extends ToolSet> {
253
269
  }
254
270
  );
255
271
  // TODO: track what messages we used for context
272
+ included = new Set(searchMessages.map((m) => m._id));
256
273
  contextMessages.push(...searchMessages.map((m) => m.message!));
257
274
  }
258
275
  if (args.threadId) {
@@ -267,15 +284,56 @@ export class Agent<AgentTools extends ToolSet> {
267
284
  statuses: ["success"],
268
285
  }
269
286
  );
270
- contextMessages.push(...messages.map((m) => m.message!));
287
+ contextMessages.push(
288
+ ...messages.filter((m) => !included?.has(m._id)).map((m) => m.message!)
289
+ );
271
290
  }
272
291
  return contextMessages;
273
292
  }
274
293
 
294
+ async getEmbeddings(messages: CoreMessage[]) {
295
+ let embeddings:
296
+ | {
297
+ vectors: (number[] | null)[];
298
+ dimension: VectorDimension;
299
+ model: string;
300
+ }
301
+ | undefined;
302
+ if (this.options.textEmbedding) {
303
+ const messageTexts = messages.map((m) => extractText(m));
304
+ // Find the indexes of the messages that have text.
305
+ const textIndexes = messageTexts
306
+ .map((t, i) => (t ? i : undefined))
307
+ .filter((i) => i !== undefined);
308
+ if (textIndexes.length === 0) {
309
+ return undefined;
310
+ }
311
+ // Then embed those messages.
312
+ const textEmbeddings = await this.options.textEmbedding.doEmbed({
313
+ values: messageTexts.filter((t): t is string => !!t),
314
+ });
315
+ // Then assemble the embeddings into a single array with nulls for the messages without text.
316
+ const embeddingsOrNull = Array(messages.length).fill(null);
317
+ textIndexes.forEach((i, j) => {
318
+ embeddingsOrNull[i] = textEmbeddings.embeddings[j];
319
+ });
320
+ if (textEmbeddings.embeddings.length > 0) {
321
+ const dimension = textEmbeddings.embeddings[0].length;
322
+ validateVectorDimension(dimension);
323
+ embeddings = {
324
+ vectors: embeddingsOrNull,
325
+ dimension,
326
+ model: this.options.textEmbedding.modelId,
327
+ };
328
+ }
329
+ }
330
+ return embeddings;
331
+ }
332
+
275
333
  async saveMessages(
276
334
  ctx: RunMutationCtx,
277
335
  args: {
278
- threadId?: string;
336
+ threadId: string;
279
337
  userId?: string;
280
338
  messages: CoreMessageMaybeWithId[];
281
339
  pending?: boolean;
@@ -290,8 +348,9 @@ export class Agent<AgentTools extends ToolSet> {
290
348
  threadId: args.threadId,
291
349
  userId: args.userId,
292
350
  agentName: this.options.name,
293
- model: this.options.thread.modelId,
351
+ model: this.options.chat.modelId,
294
352
  messages: args.messages.map(serializeMessageWithId),
353
+ embeddings: await this.getEmbeddings(args.messages),
295
354
  failPendingSteps: args.failPendingSteps ?? true,
296
355
  pending: args.pending ?? false,
297
356
  parentMessageId: args.parentMessageId,
@@ -308,11 +367,12 @@ export class Agent<AgentTools extends ToolSet> {
308
367
  ): Promise<void> {
309
368
  const step = serializeStep(args.step as StepResult<ToolSet>);
310
369
  const messages = serializeNewMessagesInStep(args.step);
311
- await ctx.runMutation(this.component.messages.addSteps, {
370
+ await ctx.runMutation(this.component.messages.addStep, {
312
371
  threadId: args.threadId,
313
372
  messageId: args.messageId,
314
- steps: [{ step, messages: messages }],
373
+ step: { step, messages },
315
374
  failPendingSteps: false,
375
+ embeddings: await this.getEmbeddings(messages.map((m) => m.message)),
316
376
  });
317
377
  }
318
378
 
@@ -333,11 +393,9 @@ export class Agent<AgentTools extends ToolSet> {
333
393
  messageId: args.messageId,
334
394
  });
335
395
  } else {
336
- await ctx.runMutation(this.component.messages.addSteps, {
337
- threadId: args.threadId,
396
+ await ctx.runMutation(this.component.messages.rollbackMessage, {
338
397
  messageId: args.messageId,
339
- steps: [],
340
- failPendingSteps: true,
398
+ error: result.error,
341
399
  });
342
400
  }
343
401
  }
@@ -372,36 +430,23 @@ export class Agent<AgentTools extends ToolSet> {
372
430
  ): Promise<
373
431
  GenerateTextResult<TOOLS & AgentTools, OUTPUT> & GenerationOutputMetadata
374
432
  > {
375
- const { prompt, messages: raw, ...rest } = args;
376
- const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
377
- const contextMessages = await this.fetchContextMessages(ctx, {
378
- ...args,
379
- userId,
380
- threadId,
381
- messages,
382
- });
383
- const { lastMessageId: messageId } = await this.saveMessages(ctx, {
384
- threadId,
385
- userId,
386
- messages: args.saveAllInputMessages ? messages : messages.slice(-1),
387
- pending: true,
388
- parentMessageId: args.parentMessageId,
389
- });
433
+ const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
434
+ ctx,
435
+ { ...args, userId, threadId }
436
+ );
390
437
  const toolCtx = { ...ctx, userId, threadId, messageId };
391
438
  const tools = wrapTools(toolCtx, this.options.tools, args.tools) as TOOLS;
439
+ const maxSteps = args.maxSteps ?? this.options.maxSteps;
392
440
  try {
393
441
  const result = await generateText({
394
- model: this.options.thread,
395
- messages: [...contextMessages, ...messages],
396
- system: this.options.instructions,
397
- maxSteps: this.options.maxSteps,
442
+ model: this.options.chat,
443
+ ...aiArgs,
444
+ maxSteps,
398
445
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
399
446
  toolChoice: args.toolChoice as any,
400
- ...rest,
401
447
  tools,
402
448
  onStepFinish: async (step) => {
403
- if (threadId && messageId && args.saveOutputMessages) {
404
- console.log("onStepFinish", step);
449
+ if (threadId && messageId && args.saveOutputMessages !== false) {
405
450
  await this.saveStep(ctx, {
406
451
  threadId,
407
452
  messageId,
@@ -439,38 +484,27 @@ export class Agent<AgentTools extends ToolSet> {
439
484
  ): Promise<
440
485
  StreamTextResult<TOOLS, PARTIAL_OUTPUT> & GenerationOutputMetadata
441
486
  > {
442
- const { prompt, messages: raw, ...rest } = args;
443
- const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
444
- const contextMessages = await this.fetchContextMessages(ctx, {
445
- ...args,
446
- userId,
447
- threadId,
448
- messages,
449
- });
450
- const { lastMessageId: messageId } = await this.saveMessages(ctx, {
451
- threadId,
452
- messages: args.saveAllInputMessages ? messages : messages.slice(-1),
453
- pending: true,
454
- parentMessageId: args.parentMessageId,
455
- });
487
+ const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
488
+ ctx,
489
+ { ...args, userId, threadId }
490
+ );
456
491
  const toolCtx = { ...ctx, userId, threadId, messageId };
457
492
  const tools = wrapTools(toolCtx, this.options.tools, args.tools) as TOOLS;
493
+ const maxSteps = args.maxSteps ?? this.options.maxSteps;
458
494
  const result = streamText({
459
- model: this.options.thread,
460
- messages: [...contextMessages, ...messages],
461
- system: this.options.instructions,
462
- maxSteps: this.options.maxSteps,
495
+ model: this.options.chat,
496
+ ...aiArgs,
497
+ maxSteps,
463
498
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
464
499
  toolChoice: args.toolChoice as any,
465
- ...rest,
466
500
  tools,
467
501
  onChunk: async (chunk) => {
468
- console.log("onChunk", chunk);
502
+ // console.log("onChunk", chunk);
469
503
  return args.onChunk?.(chunk);
470
504
  },
471
505
  onError: async (error) => {
472
506
  console.error("onError", error);
473
- if (threadId && messageId) {
507
+ if (threadId && messageId && args.saveOutputMessages !== false) {
474
508
  await ctx.runMutation(this.component.messages.rollbackMessage, {
475
509
  messageId,
476
510
  error: (error.error as Error).message,
@@ -478,14 +512,9 @@ export class Agent<AgentTools extends ToolSet> {
478
512
  }
479
513
  return args.onError?.(error);
480
514
  },
481
- onFinish: async (result) => {
482
- result.response.messages.forEach((message) => {
483
- console.log("onFinish", message);
484
- });
485
- return args.onFinish?.(result);
486
- },
487
515
  onStepFinish: async (step) => {
488
- console.log("onStepFinish", step);
516
+ // console.log("onStepFinish", step);
517
+ // TODO: compare delta to the output. internally drop the deltas when committing
489
518
  if (threadId && messageId) {
490
519
  await this.saveStep(ctx, {
491
520
  threadId,
@@ -499,72 +528,160 @@ export class Agent<AgentTools extends ToolSet> {
499
528
  return { ...result, messageId };
500
529
  }
501
530
 
502
- // TODO: add the crazy number of overloads to get types through
503
- async generateObject<T>(
504
- ctx: RunActionCtx,
505
- { userId, threadId }: { userId?: string; threadId?: string },
506
- args: Omit<Parameters<typeof generateObject>[0], "model"> & {
507
- model?: LanguageModelV1;
508
- } & { parentMessageId?: string } & ContextOptions &
509
- StorageOptions
510
- ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
511
- const { prompt, messages: raw, ...rest } = args;
512
- const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
513
- const contextMessages = await this.fetchContextMessages(ctx, {
514
- ...args,
531
+ async saveMessagesAndFetchContext<
532
+ T extends {
533
+ prompt?: string;
534
+ messages?: CoreMessage[] | Omit<UIMessage, "id">[];
535
+ system?: string;
536
+ },
537
+ >(
538
+ ctx: RunActionCtx | RunMutationCtx,
539
+ {
515
540
  userId,
516
541
  threadId,
542
+ parentMessageId,
543
+ saveAllInputMessages,
544
+ system,
545
+ ...args
546
+ }: {
547
+ userId: string | undefined;
548
+ threadId: string | undefined;
549
+ parentMessageId?: string;
550
+ saveAllInputMessages?: boolean;
551
+ saveAnyInputMessages?: boolean;
552
+ } & ContextOptions &
553
+ T
554
+ ): Promise<{
555
+ args: T;
556
+ messageId: string | undefined;
557
+ }> {
558
+ const messages = promptOrMessagesToCoreMessages(args);
559
+ const contextMessages = await this.fetchContextMessages(ctx, {
517
560
  messages,
518
- });
519
- const { lastMessageId: messageId } = await this.saveMessages(ctx, {
561
+ parentMessageId,
562
+ userId,
520
563
  threadId,
521
- messages: args.saveAllInputMessages ? messages : messages.slice(-1),
522
- pending: true,
564
+ ...args,
523
565
  });
524
- const result = (await generateObject({
525
- model: this.options.thread,
526
- messages: [...contextMessages, ...messages],
527
- ...rest,
528
- })) as GenerateObjectResult<T>;
529
- return { ...result, messageId };
566
+ let messageId: string | undefined;
567
+ if (threadId) {
568
+ const saved = await this.saveMessages(ctx, {
569
+ threadId,
570
+ userId,
571
+ messages: saveAllInputMessages ? messages : messages.slice(-1),
572
+ pending: true,
573
+ // We should just fail if you pass in an ID for the message, fail those children
574
+ // failPendingSteps: true,
575
+ parentMessageId,
576
+ });
577
+ messageId = saved.lastMessageId;
578
+ }
579
+ const { prompt: _, ...rest } = args;
580
+ return {
581
+ args: {
582
+ ...rest,
583
+ system: system ?? this.options.instructions,
584
+ messages: [...contextMessages, ...messages],
585
+ } as T,
586
+ messageId,
587
+ };
588
+ }
589
+
590
+ async generateObject<T>(
591
+ ctx: RunActionCtx,
592
+ { userId, threadId }: { userId?: string; threadId?: string },
593
+ args: OurObjectArgs<T>
594
+ ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
595
+ const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
596
+ ctx,
597
+ { ...args, userId, threadId }
598
+ );
599
+
600
+ try {
601
+ const result = (await generateObject({
602
+ model: this.options.chat,
603
+ ...aiArgs,
604
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
605
+ } as any)) as GenerateObjectResult<T>;
606
+
607
+ if (threadId && messageId && args.saveOutputMessages !== false) {
608
+ await this.saveObject(ctx, { threadId, messageId, result });
609
+ }
610
+ return { ...result, messageId };
611
+ } catch (error) {
612
+ if (threadId && messageId) {
613
+ await ctx.runMutation(this.component.messages.rollbackMessage, {
614
+ messageId,
615
+ error: (error as Error).message,
616
+ });
617
+ }
618
+ throw error;
619
+ }
530
620
  }
531
621
 
532
622
  async streamObject<T>(
533
623
  ctx: RunMutationCtx,
534
624
  { userId, threadId }: { userId?: string; threadId?: string },
535
- args: Omit<Parameters<typeof streamObject<T>>[0], "model"> & {
536
- model?: LanguageModelV1;
537
- } & { parentMessageId?: string } & ContextOptions &
538
- StorageOptions
625
+ args: OurStreamObjectArgs<T>
539
626
  ): Promise<
540
627
  StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
541
628
  > {
542
- const { prompt, messages: raw, ...rest } = args;
543
- const messages = promptOrMessagesToCoreMessages({ prompt, messages: raw });
544
- const contextMessages = await this.fetchContextMessages(ctx, {
545
- ...args,
546
- userId,
547
- threadId,
548
- messages,
549
- });
550
- const { lastMessageId: messageId } = await this.saveMessages(ctx, {
551
- threadId,
552
- messages: args.saveAllInputMessages ? messages : messages.slice(-1),
553
- pending: true,
554
- });
555
- const result = streamObject<T>({
556
- model: this.options.thread,
557
- messages: [...contextMessages, ...messages],
558
- ...rest,
629
+ // TODO: unify all this shared code between all the generate* and stream* functions
630
+ const { args: aiArgs, messageId } = await this.saveMessagesAndFetchContext(
631
+ ctx,
632
+ { ...args, userId, threadId }
633
+ );
634
+ const stream = streamObject<T>({
635
+ model: this.options.chat,
636
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
637
+ ...(aiArgs as any),
559
638
  onError: async (error) => {
560
639
  console.error("onError", error);
561
640
  return args.onError?.(error);
562
641
  },
563
642
  onFinish: async (result) => {
564
- console.log("onFinish", result);
643
+ if (threadId && messageId && args.saveOutputMessages !== false) {
644
+ await this.saveObject(ctx, {
645
+ threadId,
646
+ messageId,
647
+ result: {
648
+ object: result.object,
649
+ finishReason: "stop",
650
+ usage: result.usage,
651
+ warnings: result.warnings,
652
+ request: await stream.request,
653
+ response: result.response,
654
+ providerMetadata: result.providerMetadata,
655
+ experimental_providerMetadata:
656
+ result.experimental_providerMetadata,
657
+ logprobs: undefined,
658
+ toJsonResponse: stream.toTextStreamResponse,
659
+ },
660
+ });
661
+ }
662
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
663
+ return args.onFinish?.(result as any);
565
664
  },
566
665
  }) as StreamObjectResult<DeepPartial<T>, T, never>;
567
- return { ...result, messageId };
666
+ return { ...stream, messageId };
667
+ }
668
+
669
+ async saveObject(
670
+ ctx: RunMutationCtx,
671
+ args: {
672
+ threadId: string;
673
+ messageId: string;
674
+ result: GenerateObjectResult<unknown>;
675
+ }
676
+ ): Promise<void> {
677
+ const step = serializeObjectResult(args.result);
678
+ await ctx.runMutation(this.component.messages.addStep, {
679
+ threadId: args.threadId,
680
+ messageId: args.messageId,
681
+ failPendingSteps: false,
682
+ embeddings: await this.getEmbeddings([step.messages[0].message]),
683
+ step,
684
+ });
568
685
  }
569
686
 
570
687
  mergedContextOptions(opts: ContextOptions): ContextOptions {
@@ -618,7 +735,7 @@ export class Agent<AgentTools extends ToolSet> {
618
735
  /**
619
736
  *
620
737
  */
621
- asAction(spec: { contextOptions?: ContextOptions; maxSteps?: number }) {
738
+ asAction(spec?: { contextOptions?: ContextOptions; maxSteps?: number }) {
622
739
  return internalActionGeneric({
623
740
  args: {
624
741
  userId: v.optional(v.string()),
@@ -626,6 +743,7 @@ export class Agent<AgentTools extends ToolSet> {
626
743
  contextOptions: v.optional(vContextOptions),
627
744
  storageOptions: v.optional(vStorageOptions),
628
745
  maxRetries: v.optional(v.number()),
746
+ parentMessageId: v.optional(v.string()),
629
747
 
630
748
  createThread: v.optional(
631
749
  v.object({
@@ -635,67 +753,59 @@ export class Agent<AgentTools extends ToolSet> {
635
753
  summary: v.optional(v.string()),
636
754
  })
637
755
  ),
638
- continueThread: v.optional(
639
- v.object({
640
- threadId: v.string(),
641
- userId: v.optional(v.string()),
642
- })
643
- ),
644
- generateText: v.optional(vThreadArgs),
645
- streamText: v.optional(vThreadArgs),
646
- generateObject: v.optional(vObjectArgs),
647
- streamObject: v.optional(
648
- v.object({ ...vObjectArgs.fields, schema: v.any() })
649
- ),
756
+ generateText: v.optional(vTextArgs),
757
+ streamText: v.optional(vTextArgs),
758
+ generateObject: v.optional(vSafeObjectArgs),
759
+ streamObject: v.optional(vSafeObjectArgs),
650
760
  },
651
761
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
652
762
  handler: async (ctx, args): Promise<any> => {
653
763
  const contextOptions =
654
- spec.contextOptions && this.mergedContextOptions(spec.contextOptions);
655
- const maxSteps = spec.maxSteps ?? this.options.maxSteps;
656
- const maxRetries = args.maxRetries;
764
+ spec?.contextOptions &&
765
+ this.mergedContextOptions(spec.contextOptions);
766
+ const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
657
767
  const commonArgs = {
658
768
  userId: args.userId,
659
769
  threadId: args.threadId,
770
+ parentMessageId: args.parentMessageId,
660
771
  ...contextOptions,
661
772
  ...args.storageOptions,
662
773
  };
663
774
  if (args.createThread) {
664
- return this.createThread(ctx, {
775
+ const { threadId } = await this.createThread(ctx, {
665
776
  userId: args.createThread.userId,
666
777
  parentThreadIds: args.createThread.parentThreadIds,
667
778
  title: args.createThread.title,
668
779
  summary: args.createThread.summary,
669
780
  });
670
- } else if (args.continueThread) {
671
- return this.continueThread(ctx, {
672
- threadId: args.continueThread.threadId,
673
- userId: args.continueThread.userId,
674
- });
781
+ return threadId;
675
782
  } else if (args.generateText) {
676
- return this.generateText(ctx, commonArgs, {
783
+ const value = await this.generateText(ctx, commonArgs, {
677
784
  ...args.generateText,
678
785
  maxSteps: args.generateText.maxSteps ?? maxSteps,
679
- maxRetries,
680
786
  });
787
+ return value.text;
681
788
  } else if (args.streamText) {
682
- return this.streamText(ctx, commonArgs, {
789
+ const value = await this.streamText(ctx, commonArgs, {
683
790
  ...args.streamText,
684
791
  maxSteps: args.streamText.maxSteps ?? maxSteps,
685
- maxRetries,
686
792
  });
793
+ return value.text;
687
794
  } else if (args.generateObject) {
688
- return this.generateObject(ctx, commonArgs, {
689
- ...args.generateObject,
690
- output: args.generateObject.output ?? "string",
691
- maxRetries,
795
+ const value = await this.generateObject(ctx, commonArgs, {
796
+ ...(args.generateObject as GenerateObjectArgs<unknown>),
692
797
  });
798
+ return value.object;
693
799
  } else if (args.streamObject) {
694
- return this.streamObject(ctx, commonArgs, {
695
- ...args.streamObject,
696
- output: args.streamObject.output ?? "string",
697
- maxRetries,
800
+ const value = await this.streamObject(ctx, commonArgs, {
801
+ ...(args.streamObject as StreamObjectArgs<unknown>),
698
802
  });
803
+ return value.object;
804
+ } else {
805
+ throw new Error(
806
+ "No action specified. Maybe try :" +
807
+ 'generateText: { prompt: "Hello world" }'
808
+ );
699
809
  }
700
810
  },
701
811
  });
@@ -819,16 +929,80 @@ type TextArgs<
819
929
  } & ContextOptions &
820
930
  StorageOptions;
821
931
 
822
- type ObjectArgs<
823
- T extends {
824
- model: LanguageModelV1;
825
- },
826
- > = Omit<T, "model"> & {
827
- model?: LanguageModelV1;
828
- } & ContextOptions &
829
- StorageOptions;
932
+ type BaseGenerateObjectOptions = StorageOptions &
933
+ ContextOptions &
934
+ CallSettings & {
935
+ model?: LanguageModelV1;
936
+ parentMessageId?: string;
937
+ system?: string;
938
+ prompt?: string;
939
+ messages?: CoreMessage[];
940
+ experimental_repairText?: RepairTextFunction;
941
+ experimental_telemetry?: TelemetrySettings;
942
+ providerOptions?: ProviderOptions;
943
+ experimental_providerMetadata?: ProviderMetadata;
944
+ };
945
+
946
+ type GenerateObjectObjectOptions<T extends Record<string, unknown>> =
947
+ BaseGenerateObjectOptions & {
948
+ output: "object";
949
+ mode?: "auto" | "json" | "tool";
950
+ schema: z.Schema<T>;
951
+ schemaName?: string;
952
+ schemaDescription?: string;
953
+ };
954
+
955
+ type GenerateObjectArrayOptions<T> = BaseGenerateObjectOptions & {
956
+ output: "array";
957
+ mode?: "auto" | "json" | "tool";
958
+ schema: z.Schema<T>;
959
+ schemaName?: string;
960
+ schemaDescription?: string;
961
+ };
962
+
963
+ type GenerateObjectWithEnumOptions<T extends string> =
964
+ BaseGenerateObjectOptions & {
965
+ output: "enum";
966
+ enum: Array<T>;
967
+ mode?: "auto" | "json" | "tool";
968
+ };
969
+
970
+ type GenerateObjectNoSchemaOptions = BaseGenerateObjectOptions & {
971
+ schema?: undefined;
972
+ mode?: "json";
973
+ };
974
+
975
+ type GenerateObjectArgs<T> =
976
+ T extends Record<string, unknown>
977
+ ? GenerateObjectObjectOptions<T>
978
+ : T extends Array<unknown>
979
+ ? GenerateObjectArrayOptions<T>
980
+ : T extends string
981
+ ? GenerateObjectWithEnumOptions<T>
982
+ : GenerateObjectNoSchemaOptions;
983
+
984
+ type StreamObjectArgs<T> =
985
+ T extends Record<string, unknown>
986
+ ? GenerateObjectObjectOptions<T>
987
+ : T extends Array<unknown>
988
+ ? GenerateObjectArrayOptions<T>
989
+ : GenerateObjectNoSchemaOptions;
990
+
991
+ type OurObjectArgs<T> = GenerateObjectArgs<T> &
992
+ Pick<
993
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
994
+ Parameters<typeof generateObject<any>>[0],
995
+ "experimental_repairText" | "abortSignal"
996
+ >;
997
+
998
+ type OurStreamObjectArgs<T> = StreamObjectArgs<T> &
999
+ Pick<
1000
+ Parameters<typeof streamObject<T>>[0],
1001
+ "onError" | "onFinish" | "abortSignal"
1002
+ >;
830
1003
 
831
1004
  interface Thread<AgentTools extends ToolSet> {
1005
+ threadId: string;
832
1006
  generateText<TOOLS extends ToolSet, OUTPUT = never, OUTPUT_PARTIAL = never>(
833
1007
  args: TextArgs<
834
1008
  AgentTools,
@@ -851,10 +1025,13 @@ interface Thread<AgentTools extends ToolSet> {
851
1025
  >;
852
1026
  // TODO: add all the overloads
853
1027
  generateObject<T>(
854
- args: ObjectArgs<Parameters<typeof generateObject>[0]>
1028
+ args: OurObjectArgs<T>
855
1029
  ): Promise<GenerateObjectResult<T> & GenerationOutputMetadata>;
1030
+ generateObject(
1031
+ args: GenerateObjectNoSchemaOptions
1032
+ ): Promise<GenerateObjectResult<JSONValue> & GenerationOutputMetadata>;
856
1033
  streamObject<T>(
857
- args: ObjectArgs<Parameters<typeof streamObject<T>>[0]>
1034
+ args: OurStreamObjectArgs<T>
858
1035
  ): Promise<
859
1036
  StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
860
1037
  >;