@convex-dev/agent 0.1.6 → 0.1.7-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.
- package/README.md +23 -12
- package/dist/commonjs/client/_generated/_ignore.d.ts +1 -0
- package/dist/commonjs/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/commonjs/client/_generated/_ignore.js +3 -0
- package/dist/commonjs/client/_generated/_ignore.js.map +1 -0
- package/dist/commonjs/client/files.d.ts +57 -0
- package/dist/commonjs/client/files.d.ts.map +1 -0
- package/dist/commonjs/client/files.js +124 -0
- package/dist/commonjs/client/files.js.map +1 -0
- package/dist/commonjs/client/index.d.ts +487 -318
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +652 -550
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/types.d.ts +35 -5
- package/dist/commonjs/client/types.d.ts.map +1 -1
- package/dist/commonjs/component/_generated/api.d.ts +47 -711
- package/dist/commonjs/component/files.d.ts +66 -21
- package/dist/commonjs/component/files.d.ts.map +1 -1
- package/dist/commonjs/component/files.js +71 -26
- package/dist/commonjs/component/files.js.map +1 -1
- package/dist/commonjs/component/messages.d.ts +31 -499
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +45 -125
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +949 -3785
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +9 -25
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/streams.d.ts +3 -3
- package/dist/commonjs/mapping.d.ts +28 -19
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js +136 -81
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/react/toUIMessages.d.ts.map +1 -1
- package/dist/commonjs/react/toUIMessages.js +12 -6
- package/dist/commonjs/react/toUIMessages.js.map +1 -1
- package/dist/commonjs/react/usePaginatedQuery.d.ts.map +1 -1
- package/dist/commonjs/react/usePaginatedQuery.js +0 -19
- package/dist/commonjs/react/usePaginatedQuery.js.map +1 -1
- package/dist/commonjs/validators.d.ts +851 -3388
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +11 -37
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/commonjs.tsbuildinfo +1 -1
- package/dist/esm/client/_generated/_ignore.d.ts +1 -0
- package/dist/esm/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/esm/client/_generated/_ignore.js +3 -0
- package/dist/esm/client/_generated/_ignore.js.map +1 -0
- package/dist/esm/client/files.d.ts +57 -0
- package/dist/esm/client/files.d.ts.map +1 -0
- package/dist/esm/client/files.js +124 -0
- package/dist/esm/client/files.js.map +1 -0
- package/dist/esm/client/index.d.ts +487 -318
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +652 -550
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/types.d.ts +35 -5
- package/dist/esm/client/types.d.ts.map +1 -1
- package/dist/esm/component/_generated/api.d.ts +47 -711
- package/dist/esm/component/files.d.ts +66 -21
- package/dist/esm/component/files.d.ts.map +1 -1
- package/dist/esm/component/files.js +71 -26
- package/dist/esm/component/files.js.map +1 -1
- package/dist/esm/component/messages.d.ts +31 -499
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +45 -125
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +949 -3785
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +9 -25
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/streams.d.ts +3 -3
- package/dist/esm/mapping.d.ts +28 -19
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js +136 -81
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/react/toUIMessages.d.ts.map +1 -1
- package/dist/esm/react/toUIMessages.js +12 -6
- package/dist/esm/react/toUIMessages.js.map +1 -1
- package/dist/esm/react/usePaginatedQuery.d.ts.map +1 -1
- package/dist/esm/react/usePaginatedQuery.js +0 -19
- package/dist/esm/react/usePaginatedQuery.js.map +1 -1
- package/dist/esm/validators.d.ts +851 -3388
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +11 -37
- package/dist/esm/validators.js.map +1 -1
- package/dist/esm.tsbuildinfo +1 -1
- package/package.json +11 -9
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/files.ts +167 -0
- package/src/client/index.test.ts +127 -1
- package/src/client/index.ts +970 -812
- package/src/client/setup.test.ts +29 -0
- package/src/client/types.ts +41 -3
- package/src/component/_generated/api.d.ts +47 -711
- package/src/component/files.test.ts +130 -0
- package/src/component/files.ts +83 -27
- package/src/component/messages.test.ts +2 -1
- package/src/component/messages.ts +47 -137
- package/src/component/schema.ts +8 -28
- package/src/mapping.test.ts +163 -0
- package/src/mapping.ts +173 -103
- package/src/react/deltas.test.ts +4 -1
- package/src/react/toUIMessages.ts +12 -6
- package/src/react/usePaginatedQuery.ts +0 -31
- package/src/validators.ts +33 -47
package/src/client/index.ts
CHANGED
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
serializeMessage,
|
|
32
32
|
serializeNewMessagesInStep,
|
|
33
33
|
serializeObjectResult,
|
|
34
|
-
serializeStep,
|
|
35
34
|
} from "../mapping.js";
|
|
36
35
|
import {
|
|
37
36
|
DEFAULT_MESSAGE_RANGE,
|
|
@@ -40,7 +39,7 @@ import {
|
|
|
40
39
|
isTool,
|
|
41
40
|
} from "../shared.js";
|
|
42
41
|
import {
|
|
43
|
-
type MessageWithMetadata
|
|
42
|
+
type MessageWithMetadata,
|
|
44
43
|
type MessageStatus,
|
|
45
44
|
type ProviderMetadata,
|
|
46
45
|
type SearchOptions,
|
|
@@ -60,13 +59,14 @@ import type {
|
|
|
60
59
|
AgentComponent,
|
|
61
60
|
ContextOptions,
|
|
62
61
|
GenerationOutputMetadata,
|
|
63
|
-
OpaqueIds,
|
|
64
62
|
Options,
|
|
65
63
|
OurObjectArgs,
|
|
66
64
|
OurStreamObjectArgs,
|
|
65
|
+
RawRequestResponseHandler,
|
|
67
66
|
RunActionCtx,
|
|
68
67
|
RunMutationCtx,
|
|
69
68
|
RunQueryCtx,
|
|
69
|
+
ActionCtx,
|
|
70
70
|
StorageOptions,
|
|
71
71
|
StreamingTextArgs,
|
|
72
72
|
SyncStreamsReturnValue,
|
|
@@ -75,6 +75,8 @@ import type {
|
|
|
75
75
|
UsageHandler,
|
|
76
76
|
} from "./types.js";
|
|
77
77
|
|
|
78
|
+
export { storeFile, getFile } from "./files.js";
|
|
79
|
+
export { serializeDataOrUrl } from "../mapping.js";
|
|
78
80
|
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
79
81
|
export {
|
|
80
82
|
vAssistantMessage,
|
|
@@ -169,6 +171,11 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
169
171
|
* The usage handler to use for this agent.
|
|
170
172
|
*/
|
|
171
173
|
usageHandler?: UsageHandler;
|
|
174
|
+
/**
|
|
175
|
+
* Called for each LLM request/response, so you can do things like
|
|
176
|
+
* log the raw request body or response headers to a table, or logs.
|
|
177
|
+
*/
|
|
178
|
+
rawRequestResponseHandler?: RawRequestResponseHandler;
|
|
172
179
|
}
|
|
173
180
|
) {}
|
|
174
181
|
|
|
@@ -253,7 +260,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
253
260
|
threadId: string;
|
|
254
261
|
}>;
|
|
255
262
|
async createThread<ThreadTools extends ToolSet | undefined = undefined>(
|
|
256
|
-
ctx:
|
|
263
|
+
ctx: ActionCtx | RunMutationCtx,
|
|
257
264
|
args?: {
|
|
258
265
|
userId: string;
|
|
259
266
|
title?: string;
|
|
@@ -297,7 +304,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
297
304
|
* @returns Functions bound to the userId and threadId on a `{thread}` object.
|
|
298
305
|
*/
|
|
299
306
|
async continueThread<ThreadTools extends ToolSet | undefined = undefined>(
|
|
300
|
-
ctx:
|
|
307
|
+
ctx: ActionCtx,
|
|
301
308
|
args: {
|
|
302
309
|
/**
|
|
303
310
|
* The associated thread created by {@link createThread}
|
|
@@ -342,317 +349,451 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
342
349
|
}
|
|
343
350
|
|
|
344
351
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* @
|
|
352
|
+
* This behaves like {@link generateText} from the "ai" package except that
|
|
353
|
+
* it add context based on the userId and threadId and saves the input and
|
|
354
|
+
* resulting messages to the thread, if specified.
|
|
355
|
+
* Use {@link continueThread} to get a version of this function already scoped
|
|
356
|
+
* to a thread (and optionally userId).
|
|
357
|
+
* @param ctx The context passed from the action function calling this.
|
|
358
|
+
* @param { userId, threadId }: The user and thread to associate the message with
|
|
359
|
+
* @param args The arguments to the generateText function, along with extra controls
|
|
360
|
+
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
361
|
+
* @returns The result of the generateText function.
|
|
349
362
|
*/
|
|
350
|
-
async
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
363
|
+
async generateText<
|
|
364
|
+
TOOLS extends ToolSet | undefined = undefined,
|
|
365
|
+
OUTPUT = never,
|
|
366
|
+
OUTPUT_PARTIAL = never,
|
|
367
|
+
>(
|
|
368
|
+
ctx: ActionCtx,
|
|
369
|
+
{
|
|
370
|
+
userId: argsUserId,
|
|
371
|
+
threadId,
|
|
372
|
+
usageHandler,
|
|
373
|
+
tools: threadTools,
|
|
374
|
+
}: {
|
|
375
|
+
userId?: string;
|
|
376
|
+
threadId?: string;
|
|
377
|
+
/**
|
|
378
|
+
* The usage handler to use for this thread. Overrides any handler
|
|
379
|
+
* set in the agent constructor.
|
|
380
|
+
*/
|
|
381
|
+
usageHandler?: UsageHandler;
|
|
382
|
+
/** @deprecated Pass `tools` in the next parameter instead. This is only intended to pass through thread-default tools. */
|
|
383
|
+
tools?: ToolSet;
|
|
384
|
+
},
|
|
385
|
+
args: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>,
|
|
386
|
+
options?: Options
|
|
387
|
+
): Promise<
|
|
388
|
+
GenerateTextResult<TOOLS extends undefined ? AgentTools : TOOLS, OUTPUT> &
|
|
389
|
+
GenerationOutputMetadata
|
|
390
|
+
> {
|
|
391
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
392
|
+
userId: argsUserId,
|
|
393
|
+
threadId,
|
|
394
|
+
...options,
|
|
356
395
|
});
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
* @param ctx A ctx object from a mutation or action.
|
|
366
|
-
* @param args.threadId The thread to update the metadata for.
|
|
367
|
-
* @param args.patch The patch to apply to the thread.
|
|
368
|
-
* @returns The updated thread metadata.
|
|
369
|
-
*/
|
|
370
|
-
async updateThreadMetadata(
|
|
371
|
-
ctx: RunMutationCtx,
|
|
372
|
-
args: { threadId: string; patch: Partial<WithoutSystemFields<ThreadDoc>> }
|
|
373
|
-
): Promise<ThreadDoc> {
|
|
374
|
-
const thread = await ctx.runMutation(
|
|
375
|
-
this.component.threads.updateThread,
|
|
376
|
-
args
|
|
396
|
+
const { args: aiArgs, messageId, userId } = context;
|
|
397
|
+
const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
|
|
398
|
+
const tools = wrapTools(
|
|
399
|
+
toolCtx,
|
|
400
|
+
args.tools ?? threadTools ?? this.options.tools
|
|
401
|
+
) as TOOLS extends undefined ? AgentTools : TOOLS;
|
|
402
|
+
const saveOutputMessages = this._shouldSaveOutputMessages(
|
|
403
|
+
options?.storageOptions
|
|
377
404
|
);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
threadId
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
messages: await ctx.runQuery(this.component.streams.list, {
|
|
433
|
-
threadId: args.threadId,
|
|
434
|
-
}),
|
|
435
|
-
};
|
|
436
|
-
} else {
|
|
437
|
-
return {
|
|
438
|
-
kind: "deltas",
|
|
439
|
-
deltas: await ctx.runQuery(this.component.streams.listDeltas, {
|
|
440
|
-
threadId: args.threadId,
|
|
441
|
-
cursors: args.streamArgs.cursors,
|
|
442
|
-
}),
|
|
443
|
-
};
|
|
405
|
+
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
406
|
+
try {
|
|
407
|
+
const result = (await generateText({
|
|
408
|
+
// Can be overridden
|
|
409
|
+
maxSteps: this.options.maxSteps,
|
|
410
|
+
...aiArgs,
|
|
411
|
+
tools,
|
|
412
|
+
onStepFinish: async (step) => {
|
|
413
|
+
if (threadId && messageId && saveOutputMessages) {
|
|
414
|
+
await this.saveStep(ctx, {
|
|
415
|
+
userId,
|
|
416
|
+
threadId,
|
|
417
|
+
promptMessageId: messageId,
|
|
418
|
+
step,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
if (this.options.rawRequestResponseHandler) {
|
|
422
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
423
|
+
userId,
|
|
424
|
+
threadId,
|
|
425
|
+
agentName: this.options.name,
|
|
426
|
+
request: step.request,
|
|
427
|
+
response: step.response,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (trackUsage && step.usage) {
|
|
431
|
+
await trackUsage(ctx, {
|
|
432
|
+
userId,
|
|
433
|
+
threadId,
|
|
434
|
+
agentName: this.options.name,
|
|
435
|
+
model: aiArgs.model.modelId,
|
|
436
|
+
provider: aiArgs.model.provider,
|
|
437
|
+
usage: step.usage,
|
|
438
|
+
providerMetadata: step.providerMetadata,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return args.onStepFinish?.(step);
|
|
442
|
+
},
|
|
443
|
+
})) as GenerateTextResult<
|
|
444
|
+
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
445
|
+
OUTPUT
|
|
446
|
+
> &
|
|
447
|
+
GenerationOutputMetadata;
|
|
448
|
+
result.messageId = messageId;
|
|
449
|
+
return result;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (threadId && messageId) {
|
|
452
|
+
console.error("RollbackMessage", messageId);
|
|
453
|
+
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
454
|
+
messageId,
|
|
455
|
+
error: (error as Error).message,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
throw error;
|
|
444
459
|
}
|
|
445
460
|
}
|
|
446
461
|
|
|
447
462
|
/**
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* @returns
|
|
463
|
+
* This behaves like {@link streamText} from the "ai" package except that
|
|
464
|
+
* it add context based on the userId and threadId and saves the input and
|
|
465
|
+
* resulting messages to the thread, if specified.
|
|
466
|
+
* Use {@link continueThread} to get a version of this function already scoped
|
|
467
|
+
* to a thread (and optionally userId).
|
|
454
468
|
*/
|
|
455
|
-
async
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
469
|
+
async streamText<
|
|
470
|
+
TOOLS extends ToolSet | undefined = undefined,
|
|
471
|
+
OUTPUT = never,
|
|
472
|
+
PARTIAL_OUTPUT = never,
|
|
473
|
+
>(
|
|
474
|
+
ctx: ActionCtx,
|
|
475
|
+
{
|
|
476
|
+
userId: argsUserId,
|
|
477
|
+
threadId,
|
|
478
|
+
usageHandler,
|
|
461
479
|
/**
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
* limited, as it's post-filtering the results.
|
|
480
|
+
* @deprecated Pass `tools` in the next parameter instead.
|
|
481
|
+
* This is only intended to pass through thread-default tools.
|
|
465
482
|
*/
|
|
466
|
-
|
|
467
|
-
|
|
483
|
+
tools: threadTools,
|
|
484
|
+
}: {
|
|
485
|
+
userId?: string;
|
|
486
|
+
threadId?: string;
|
|
487
|
+
usageHandler?: UsageHandler;
|
|
488
|
+
tools?: ToolSet;
|
|
489
|
+
},
|
|
490
|
+
/**
|
|
491
|
+
* The arguments to the streamText function, similar to the ai `streamText` function.
|
|
492
|
+
*/
|
|
493
|
+
args: StreamingTextArgs<AgentTools, TOOLS, OUTPUT, PARTIAL_OUTPUT>,
|
|
494
|
+
/**
|
|
495
|
+
* The {@link ContextOptions} and {@link StorageOptions}
|
|
496
|
+
* options to use for fetching contextual messages and saving input/output messages.
|
|
497
|
+
*/
|
|
498
|
+
options?: Options & {
|
|
499
|
+
/**
|
|
500
|
+
* Whether to save incremental data (deltas) from streaming responses.
|
|
501
|
+
* Defaults to false.
|
|
502
|
+
* If false, it will not save any deltas to the database.
|
|
503
|
+
* If true, it will save deltas with {@link DEFAULT_STREAMING_OPTIONS}.
|
|
504
|
+
*
|
|
505
|
+
* Regardless of this option, when streaming you are able to use this
|
|
506
|
+
* `streamText` function as you would with the "ai" package's version:
|
|
507
|
+
* iterating over the text, streaming it over HTTP, etc.
|
|
508
|
+
*/
|
|
509
|
+
saveStreamDeltas?: boolean | StreamingOptions;
|
|
468
510
|
}
|
|
469
|
-
): Promise<
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
511
|
+
): Promise<
|
|
512
|
+
StreamTextResult<
|
|
513
|
+
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
514
|
+
PARTIAL_OUTPUT
|
|
515
|
+
> &
|
|
516
|
+
GenerationOutputMetadata
|
|
517
|
+
> {
|
|
518
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
519
|
+
userId: argsUserId,
|
|
520
|
+
threadId,
|
|
521
|
+
...options,
|
|
522
|
+
});
|
|
523
|
+
const { args: aiArgs, messageId, order, stepOrder, userId } = context;
|
|
524
|
+
const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
|
|
525
|
+
const tools = wrapTools(
|
|
526
|
+
toolCtx,
|
|
527
|
+
args.tools ?? threadTools ?? this.options.tools
|
|
528
|
+
) as TOOLS extends undefined ? AgentTools : TOOLS;
|
|
529
|
+
const saveOutputMessages = this._shouldSaveOutputMessages(
|
|
530
|
+
options?.storageOptions
|
|
531
|
+
);
|
|
532
|
+
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
533
|
+
const streamer =
|
|
534
|
+
threadId && options?.saveStreamDeltas
|
|
535
|
+
? new DeltaStreamer(this.component, ctx, options.saveStreamDeltas, {
|
|
536
|
+
threadId,
|
|
537
|
+
userId,
|
|
538
|
+
agentName: this.options.name,
|
|
539
|
+
model: aiArgs.model.modelId,
|
|
540
|
+
provider: aiArgs.model.provider,
|
|
541
|
+
providerOptions: aiArgs.providerOptions,
|
|
542
|
+
order,
|
|
543
|
+
stepOrder,
|
|
544
|
+
})
|
|
545
|
+
: undefined;
|
|
546
|
+
|
|
547
|
+
const result = streamText({
|
|
548
|
+
// Can be overridden
|
|
549
|
+
maxSteps: this.options.maxSteps,
|
|
550
|
+
...aiArgs,
|
|
551
|
+
tools,
|
|
552
|
+
experimental_transform: mergeTransforms(
|
|
553
|
+
options?.saveStreamDeltas,
|
|
554
|
+
args.experimental_transform
|
|
555
|
+
),
|
|
556
|
+
onChunk: async (event) => {
|
|
557
|
+
await streamer?.addParts([event.chunk]);
|
|
558
|
+
// console.log("onChunk", chunk);
|
|
559
|
+
return args.onChunk?.(event);
|
|
560
|
+
},
|
|
561
|
+
onError: async (error) => {
|
|
562
|
+
console.error("onError", error);
|
|
563
|
+
if (threadId && messageId && saveOutputMessages) {
|
|
564
|
+
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
565
|
+
messageId,
|
|
566
|
+
error: (error.error as Error).message,
|
|
567
|
+
});
|
|
492
568
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (!("runAction" in ctx)) {
|
|
508
|
-
throw new Error("searchUserMessages only works in an action");
|
|
509
|
-
}
|
|
510
|
-
const searchMessages = await ctx.runAction(
|
|
511
|
-
this.component.messages.searchMessages,
|
|
512
|
-
{
|
|
513
|
-
searchAllMessagesForUserId: opts?.searchOtherThreads
|
|
514
|
-
? args.userId ??
|
|
515
|
-
(args.threadId &&
|
|
516
|
-
(
|
|
517
|
-
await ctx.runQuery(this.component.threads.getThread, {
|
|
518
|
-
threadId: args.threadId,
|
|
519
|
-
})
|
|
520
|
-
)?.userId)
|
|
521
|
-
: undefined,
|
|
522
|
-
threadId: args.threadId,
|
|
523
|
-
beforeMessageId: args.upToAndIncludingMessageId,
|
|
524
|
-
...(await this._searchOptionsWithDefaults(opts, messagesToSearch)),
|
|
569
|
+
return args.onError?.(error);
|
|
570
|
+
},
|
|
571
|
+
onStepFinish: async (step) => {
|
|
572
|
+
// console.log("onStepFinish", step);
|
|
573
|
+
// TODO: compare delta to the output. internally drop the deltas when committing
|
|
574
|
+
if (threadId && messageId) {
|
|
575
|
+
const saved = await this.saveStep(ctx, {
|
|
576
|
+
userId,
|
|
577
|
+
threadId,
|
|
578
|
+
promptMessageId: messageId,
|
|
579
|
+
step,
|
|
580
|
+
});
|
|
581
|
+
// TODO: figure out pending/not
|
|
582
|
+
await streamer?.finish(saved.messages);
|
|
525
583
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
584
|
+
if (this.options.rawRequestResponseHandler) {
|
|
585
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
586
|
+
userId,
|
|
587
|
+
threadId,
|
|
588
|
+
agentName: this.options.name,
|
|
589
|
+
request: step.request,
|
|
590
|
+
response: step.response,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (trackUsage && step.usage) {
|
|
594
|
+
await trackUsage(ctx, {
|
|
595
|
+
userId,
|
|
596
|
+
threadId,
|
|
597
|
+
agentName: this.options.name,
|
|
598
|
+
model: aiArgs.model.modelId,
|
|
599
|
+
provider: aiArgs.model.provider,
|
|
600
|
+
usage: step.usage,
|
|
601
|
+
providerMetadata: step.providerMetadata,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
return args.onStepFinish?.(step);
|
|
605
|
+
},
|
|
606
|
+
}) as StreamTextResult<
|
|
607
|
+
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
608
|
+
PARTIAL_OUTPUT
|
|
609
|
+
> &
|
|
610
|
+
GenerationOutputMetadata;
|
|
611
|
+
result.messageId = messageId;
|
|
612
|
+
return result;
|
|
539
613
|
}
|
|
540
614
|
|
|
541
615
|
/**
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
616
|
+
* This behaves like {@link generateObject} from the "ai" package except that
|
|
617
|
+
* it add context based on the userId and threadId and saves the input and
|
|
618
|
+
* resulting messages to the thread, if specified.
|
|
619
|
+
* Use {@link continueThread} to get a version of this function already scoped
|
|
620
|
+
* to a thread (and optionally userId).
|
|
545
621
|
*/
|
|
546
|
-
async
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
622
|
+
async generateObject<T>(
|
|
623
|
+
ctx: RunActionCtx,
|
|
624
|
+
{
|
|
625
|
+
userId: argsUserId,
|
|
626
|
+
threadId,
|
|
627
|
+
usageHandler,
|
|
628
|
+
}: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
|
|
629
|
+
/**
|
|
630
|
+
* The arguments to the generateObject function, similar to the ai.generateObject function.
|
|
631
|
+
*/
|
|
632
|
+
args: OurObjectArgs<T>,
|
|
633
|
+
/**
|
|
634
|
+
* The {@link ContextOptions} and {@link StorageOptions}
|
|
635
|
+
* options to use for fetching contextual messages and saving input/output messages.
|
|
636
|
+
*/
|
|
637
|
+
options?: Options
|
|
638
|
+
): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
|
|
639
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
640
|
+
userId: argsUserId,
|
|
641
|
+
threadId,
|
|
642
|
+
...options,
|
|
643
|
+
});
|
|
644
|
+
const { args: aiArgs, messageId, userId } = context;
|
|
645
|
+
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
646
|
+
const saveOutputMessages = this._shouldSaveOutputMessages(
|
|
647
|
+
options?.storageOptions
|
|
648
|
+
);
|
|
649
|
+
try {
|
|
650
|
+
const result = (await generateObject(
|
|
651
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
652
|
+
aiArgs as any
|
|
653
|
+
)) as GenerateObjectResult<T> & GenerationOutputMetadata;
|
|
654
|
+
|
|
655
|
+
if (threadId && messageId && saveOutputMessages) {
|
|
656
|
+
await this.saveObject(ctx, {
|
|
657
|
+
threadId,
|
|
658
|
+
promptMessageId: messageId,
|
|
659
|
+
result,
|
|
660
|
+
userId,
|
|
661
|
+
});
|
|
562
662
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
if (
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
663
|
+
result.messageId = messageId;
|
|
664
|
+
if (this.options.rawRequestResponseHandler) {
|
|
665
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
666
|
+
userId,
|
|
667
|
+
threadId,
|
|
668
|
+
agentName: this.options.name,
|
|
669
|
+
request: result.request,
|
|
670
|
+
response: result.response,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (trackUsage && result.usage) {
|
|
674
|
+
await trackUsage(ctx, {
|
|
675
|
+
userId,
|
|
676
|
+
threadId,
|
|
677
|
+
agentName: this.options.name,
|
|
678
|
+
model: aiArgs.model.modelId,
|
|
679
|
+
provider: aiArgs.model.provider,
|
|
680
|
+
usage: result.usage,
|
|
681
|
+
providerMetadata: result.providerMetadata,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
return result;
|
|
685
|
+
} catch (error) {
|
|
686
|
+
if (threadId && messageId) {
|
|
687
|
+
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
688
|
+
messageId,
|
|
689
|
+
error: (error as Error).message,
|
|
690
|
+
});
|
|
581
691
|
}
|
|
692
|
+
throw error;
|
|
582
693
|
}
|
|
583
|
-
return embeddings;
|
|
584
694
|
}
|
|
585
695
|
|
|
586
696
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
* @
|
|
591
|
-
*
|
|
697
|
+
* This behaves like `streamObject` from the "ai" package except that
|
|
698
|
+
* it add context based on the userId and threadId and saves the input and
|
|
699
|
+
* resulting messages to the thread, if specified.
|
|
700
|
+
* Use {@link continueThread} to get a version of this function already scoped
|
|
701
|
+
* to a thread (and optionally userId).
|
|
592
702
|
*/
|
|
593
|
-
async
|
|
703
|
+
async streamObject<T>(
|
|
594
704
|
ctx: RunActionCtx,
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
.filter((id): id is string => id !== undefined)
|
|
618
|
-
.join(", ")
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
const messagesMissingEmbeddings = messages.filter((m) => !m.embeddingId);
|
|
622
|
-
if (messagesMissingEmbeddings.length === 0) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
const embeddings = await this.generateEmbeddings(
|
|
626
|
-
messagesMissingEmbeddings.map((m) => m!.message!)
|
|
627
|
-
);
|
|
628
|
-
if (!embeddings) {
|
|
629
|
-
if (!this.options.textEmbedding) {
|
|
630
|
-
throw new Error(
|
|
631
|
-
"No embeddings were generated for the messages. You must pass a textEmbedding model to the agent constructor."
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
throw new Error(
|
|
635
|
-
"No embeddings were generated for these messages: " +
|
|
636
|
-
messagesMissingEmbeddings.map((m) => m!._id).join(", ")
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
await ctx.runMutation(this.component.vector.index.insertBatch, {
|
|
640
|
-
vectorDimension: embeddings.dimension,
|
|
641
|
-
vectors: messagesMissingEmbeddings
|
|
642
|
-
.map((m, i) => ({
|
|
643
|
-
messageId: m!._id,
|
|
644
|
-
model: embeddings.model,
|
|
645
|
-
table: "messages",
|
|
646
|
-
userId: m.userId,
|
|
647
|
-
threadId: m.threadId,
|
|
648
|
-
vector: embeddings.vectors[i],
|
|
649
|
-
}))
|
|
650
|
-
.filter(
|
|
651
|
-
(v): v is Extract<typeof v, { vector: number[] }> => v.vector !== null
|
|
652
|
-
),
|
|
705
|
+
{
|
|
706
|
+
userId: argsUserId,
|
|
707
|
+
threadId,
|
|
708
|
+
usageHandler,
|
|
709
|
+
}: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
|
|
710
|
+
/**
|
|
711
|
+
* The arguments to the streamObject function, similar to the ai `streamObject` function.
|
|
712
|
+
*/
|
|
713
|
+
args: OurStreamObjectArgs<T>,
|
|
714
|
+
/**
|
|
715
|
+
* The {@link ContextOptions} and {@link StorageOptions}
|
|
716
|
+
* options to use for fetching contextual messages and saving input/output messages.
|
|
717
|
+
*/
|
|
718
|
+
options?: Options
|
|
719
|
+
): Promise<
|
|
720
|
+
StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
|
|
721
|
+
> {
|
|
722
|
+
// TODO: unify all this shared code between all the generate* and stream* functions
|
|
723
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
724
|
+
userId: argsUserId,
|
|
725
|
+
threadId,
|
|
726
|
+
...options,
|
|
653
727
|
});
|
|
728
|
+
const { args: aiArgs, messageId, userId } = context;
|
|
729
|
+
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
730
|
+
const saveOutputMessages = this._shouldSaveOutputMessages(
|
|
731
|
+
options?.storageOptions
|
|
732
|
+
);
|
|
733
|
+
const stream = streamObject<T>({
|
|
734
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
735
|
+
...(aiArgs as any),
|
|
736
|
+
onError: async (error) => {
|
|
737
|
+
console.error("onError", error);
|
|
738
|
+
return args.onError?.(error);
|
|
739
|
+
},
|
|
740
|
+
onFinish: async (result) => {
|
|
741
|
+
if (threadId && messageId && saveOutputMessages) {
|
|
742
|
+
await this.saveObject(ctx, {
|
|
743
|
+
userId,
|
|
744
|
+
threadId,
|
|
745
|
+
promptMessageId: messageId,
|
|
746
|
+
result: {
|
|
747
|
+
object: result.object,
|
|
748
|
+
finishReason: "stop",
|
|
749
|
+
usage: result.usage,
|
|
750
|
+
warnings: result.warnings,
|
|
751
|
+
request: await stream.request,
|
|
752
|
+
response: result.response,
|
|
753
|
+
providerMetadata: result.providerMetadata,
|
|
754
|
+
experimental_providerMetadata:
|
|
755
|
+
result.experimental_providerMetadata,
|
|
756
|
+
logprobs: undefined,
|
|
757
|
+
toJsonResponse: stream.toTextStreamResponse,
|
|
758
|
+
},
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
if (trackUsage && result.usage) {
|
|
762
|
+
await trackUsage(ctx, {
|
|
763
|
+
userId,
|
|
764
|
+
threadId,
|
|
765
|
+
agentName: this.options.name,
|
|
766
|
+
model: aiArgs.model.modelId,
|
|
767
|
+
provider: aiArgs.model.provider,
|
|
768
|
+
usage: result.usage,
|
|
769
|
+
providerMetadata: result.providerMetadata,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
if (this.options.rawRequestResponseHandler) {
|
|
773
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
774
|
+
userId,
|
|
775
|
+
threadId,
|
|
776
|
+
agentName: this.options.name,
|
|
777
|
+
request: await stream.request,
|
|
778
|
+
response: result.response,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
782
|
+
return args.onFinish?.(result as any);
|
|
783
|
+
},
|
|
784
|
+
}) as StreamObjectResult<DeepPartial<T>, T, never> &
|
|
785
|
+
GenerationOutputMetadata;
|
|
786
|
+
stream.messageId = messageId;
|
|
787
|
+
return stream;
|
|
654
788
|
}
|
|
655
789
|
|
|
790
|
+
/**
|
|
791
|
+
* Save a message to the thread.
|
|
792
|
+
* @param ctx A ctx object from a mutation or action.
|
|
793
|
+
* @param args The message and what to associate it with (user / thread)
|
|
794
|
+
* You can pass extra metadata alongside the message, e.g. associated fileIds.
|
|
795
|
+
* @returns The messageId of the saved message.
|
|
796
|
+
*/
|
|
656
797
|
async saveMessage(
|
|
657
798
|
ctx: RunMutationCtx,
|
|
658
799
|
args: {
|
|
@@ -687,7 +828,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
687
828
|
}
|
|
688
829
|
)
|
|
689
830
|
) {
|
|
690
|
-
const { lastMessageId } = await this.saveMessages(ctx, {
|
|
831
|
+
const { lastMessageId, messages } = await this.saveMessages(ctx, {
|
|
691
832
|
threadId: args.threadId,
|
|
692
833
|
userId: args.userId,
|
|
693
834
|
messages:
|
|
@@ -697,7 +838,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
697
838
|
metadata: args.metadata ? [args.metadata] : undefined,
|
|
698
839
|
skipEmbeddings: args.skipEmbeddings,
|
|
699
840
|
});
|
|
700
|
-
return { messageId: lastMessageId };
|
|
841
|
+
return { messageId: lastMessageId, message: messages.at(-1)! };
|
|
701
842
|
}
|
|
702
843
|
|
|
703
844
|
/**
|
|
@@ -707,7 +848,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
707
848
|
* @returns
|
|
708
849
|
*/
|
|
709
850
|
async saveMessages(
|
|
710
|
-
ctx: RunMutationCtx,
|
|
851
|
+
ctx: RunMutationCtx | RunActionCtx,
|
|
711
852
|
args: {
|
|
712
853
|
threadId: string;
|
|
713
854
|
userId?: string;
|
|
@@ -766,7 +907,14 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
766
907
|
);
|
|
767
908
|
}
|
|
768
909
|
} else {
|
|
769
|
-
embeddings = await this.generateEmbeddings(
|
|
910
|
+
embeddings = await this.generateEmbeddings(
|
|
911
|
+
ctx,
|
|
912
|
+
{
|
|
913
|
+
userId: args.userId,
|
|
914
|
+
threadId: args.threadId,
|
|
915
|
+
},
|
|
916
|
+
args.messages
|
|
917
|
+
);
|
|
770
918
|
}
|
|
771
919
|
const result = await ctx.runMutation(this.component.messages.addMessages, {
|
|
772
920
|
threadId: args.threadId,
|
|
@@ -774,12 +922,19 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
774
922
|
agentName: this.options.name,
|
|
775
923
|
promptMessageId: args.promptMessageId,
|
|
776
924
|
embeddings,
|
|
777
|
-
messages:
|
|
778
|
-
(m, i) =>
|
|
779
|
-
(
|
|
925
|
+
messages: await Promise.all(
|
|
926
|
+
args.messages.map(async (m, i) => {
|
|
927
|
+
const { message, fileIds } = await serializeMessage(
|
|
928
|
+
ctx,
|
|
929
|
+
this.component,
|
|
930
|
+
m
|
|
931
|
+
);
|
|
932
|
+
return {
|
|
780
933
|
...args.metadata?.[i],
|
|
781
|
-
message
|
|
782
|
-
|
|
934
|
+
message,
|
|
935
|
+
fileIds,
|
|
936
|
+
} as MessageWithMetadata;
|
|
937
|
+
})
|
|
783
938
|
),
|
|
784
939
|
failPendingSteps: args.failPendingSteps ?? false,
|
|
785
940
|
pending: args.pending ?? false,
|
|
@@ -791,361 +946,462 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
791
946
|
}
|
|
792
947
|
|
|
793
948
|
/**
|
|
794
|
-
*
|
|
795
|
-
*
|
|
796
|
-
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
* @returns
|
|
802
|
-
*/
|
|
803
|
-
asSaveMessagesMutation() {
|
|
804
|
-
return internalMutationGeneric({
|
|
805
|
-
args: {
|
|
806
|
-
threadId: v.string(),
|
|
807
|
-
userId: v.optional(v.string()),
|
|
808
|
-
promptMessageId: v.optional(v.string()),
|
|
809
|
-
messages: v.array(vMessageWithMetadata),
|
|
810
|
-
pending: v.optional(v.boolean()),
|
|
811
|
-
failPendingSteps: v.optional(v.boolean()),
|
|
812
|
-
},
|
|
813
|
-
handler: async (ctx, args) => {
|
|
814
|
-
const { lastMessageId, messages } = await this.saveMessages(ctx, {
|
|
815
|
-
...args,
|
|
816
|
-
messages: args.messages.map((m) => m.message),
|
|
817
|
-
metadata: args.messages.map(({ message: _, ...m }) => m),
|
|
818
|
-
});
|
|
819
|
-
return {
|
|
820
|
-
lastMessageId,
|
|
821
|
-
messageIds: messages.map((m) => m._id),
|
|
822
|
-
};
|
|
823
|
-
},
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* Explicitly save a "step" created by the AI SDK.
|
|
829
|
-
* @param ctx The ctx argument to a mutation or action.
|
|
830
|
-
* @param args The Step generated by the AI SDK.
|
|
949
|
+
* List messages from a thread.
|
|
950
|
+
* @param ctx A ctx object from a query, mutation, or action.
|
|
951
|
+
* @param args.threadId The thread to list messages from.
|
|
952
|
+
* @param args.paginationOpts Pagination options (e.g. via usePaginatedQuery).
|
|
953
|
+
* @param args.excludeToolMessages Whether to exclude tool messages.
|
|
954
|
+
* False by default.
|
|
955
|
+
* @param args.statuses What statuses to include. All by default.
|
|
956
|
+
* @returns The MessageDoc's in a format compatible with usePaginatedQuery.
|
|
831
957
|
*/
|
|
832
|
-
async
|
|
833
|
-
ctx:
|
|
958
|
+
async listMessages(
|
|
959
|
+
ctx: RunQueryCtx,
|
|
834
960
|
args: {
|
|
835
|
-
userId?: string;
|
|
836
961
|
threadId: string;
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
promptMessageId: string;
|
|
841
|
-
/**
|
|
842
|
-
* The step to save, possibly including multiple tool calls.
|
|
843
|
-
*/
|
|
844
|
-
step: StepResult<TOOLS>;
|
|
845
|
-
/**
|
|
846
|
-
* The model used to generate the step.
|
|
847
|
-
* Defaults to the chat model for the Agent.
|
|
848
|
-
*/
|
|
849
|
-
model?: string;
|
|
850
|
-
/**
|
|
851
|
-
* The provider of the model used to generate the step.
|
|
852
|
-
* Defaults to the chat provider for the Agent.
|
|
853
|
-
*/
|
|
854
|
-
provider?: string;
|
|
962
|
+
paginationOpts: PaginationOptions;
|
|
963
|
+
excludeToolMessages?: boolean;
|
|
964
|
+
statuses?: MessageStatus[];
|
|
855
965
|
}
|
|
856
|
-
): Promise<MessageDoc
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
threadId: args.threadId,
|
|
868
|
-
promptMessageId: args.promptMessageId,
|
|
869
|
-
step: { step, messages, embeddings },
|
|
870
|
-
failPendingSteps: false,
|
|
966
|
+
): Promise<PaginationResult<MessageDoc>> {
|
|
967
|
+
if (args.paginationOpts.numItems === 0) {
|
|
968
|
+
return {
|
|
969
|
+
page: [],
|
|
970
|
+
isDone: true,
|
|
971
|
+
continueCursor: args.paginationOpts.cursor ?? "",
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return ctx.runQuery(this.component.messages.listMessagesByThreadId, {
|
|
975
|
+
order: "desc",
|
|
976
|
+
...args,
|
|
871
977
|
});
|
|
872
|
-
return saved;
|
|
873
978
|
}
|
|
874
979
|
|
|
875
980
|
/**
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
* @param
|
|
880
|
-
* @param args
|
|
881
|
-
*
|
|
981
|
+
* A function that handles fetching stream deltas, used with the React hooks
|
|
982
|
+
* `useThreadMessages` or `useStreamingThreadMessages`.
|
|
983
|
+
* @param ctx A ctx object from a query, mutation, or action.
|
|
984
|
+
* @param args.threadId The thread to sync streams for.
|
|
985
|
+
* @param args.streamArgs The stream arguments with per-stream cursors.
|
|
986
|
+
* @returns The deltas for each stream from their existing cursor.
|
|
882
987
|
*/
|
|
883
|
-
async
|
|
884
|
-
ctx:
|
|
988
|
+
async syncStreams(
|
|
989
|
+
ctx: RunQueryCtx,
|
|
885
990
|
args: {
|
|
886
991
|
threadId: string;
|
|
887
|
-
|
|
888
|
-
result: { kind: "error"; error: string } | { kind: "success" };
|
|
992
|
+
streamArgs: StreamArgs | undefined;
|
|
889
993
|
}
|
|
890
|
-
): Promise<
|
|
891
|
-
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
994
|
+
): Promise<SyncStreamsReturnValue | undefined> {
|
|
995
|
+
if (!args.streamArgs) return undefined;
|
|
996
|
+
if (args.streamArgs.kind === "list") {
|
|
997
|
+
return {
|
|
998
|
+
kind: "list",
|
|
999
|
+
messages: await ctx.runQuery(this.component.streams.list, {
|
|
1000
|
+
threadId: args.threadId,
|
|
1001
|
+
}),
|
|
1002
|
+
};
|
|
1003
|
+
} else {
|
|
1004
|
+
return {
|
|
1005
|
+
kind: "deltas",
|
|
1006
|
+
deltas: await ctx.runQuery(this.component.streams.listDeltas, {
|
|
1007
|
+
threadId: args.threadId,
|
|
1008
|
+
cursors: args.streamArgs.cursors,
|
|
1009
|
+
}),
|
|
1010
|
+
};
|
|
901
1011
|
}
|
|
902
1012
|
}
|
|
903
1013
|
|
|
904
1014
|
/**
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
*
|
|
910
|
-
* @
|
|
911
|
-
* @param { userId, threadId }: The user and thread to associate the message with
|
|
912
|
-
* @param args The arguments to the generateText function, along with extra controls
|
|
913
|
-
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
914
|
-
* @returns The result of the generateText function.
|
|
1015
|
+
* Fetch the context messages for a thread.
|
|
1016
|
+
* @param ctx Either a query, mutation, or action ctx.
|
|
1017
|
+
* If it is not an action context, you can't do text or
|
|
1018
|
+
* vector search.
|
|
1019
|
+
* @param args The associated thread, user, message
|
|
1020
|
+
* @returns
|
|
915
1021
|
*/
|
|
916
|
-
async
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
{
|
|
923
|
-
userId: argsUserId,
|
|
924
|
-
threadId,
|
|
925
|
-
usageHandler,
|
|
926
|
-
tools: threadTools,
|
|
927
|
-
}: {
|
|
928
|
-
userId?: string;
|
|
929
|
-
threadId?: string;
|
|
1022
|
+
async fetchContextMessages(
|
|
1023
|
+
ctx: RunQueryCtx | RunActionCtx,
|
|
1024
|
+
args: {
|
|
1025
|
+
userId: string | undefined;
|
|
1026
|
+
threadId: string | undefined;
|
|
1027
|
+
messages: CoreMessage[];
|
|
930
1028
|
/**
|
|
931
|
-
*
|
|
932
|
-
*
|
|
1029
|
+
* If provided, it will search for messages up to and including this message.
|
|
1030
|
+
* Note: if this is far in the past, text and vector search results may be more
|
|
1031
|
+
* limited, as it's post-filtering the results.
|
|
933
1032
|
*/
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
args
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
threadId,
|
|
978
|
-
agentName: this.options.name,
|
|
979
|
-
model: aiArgs.model.modelId,
|
|
980
|
-
provider: aiArgs.model.provider,
|
|
981
|
-
usage: step.usage,
|
|
982
|
-
providerMetadata: step.providerMetadata,
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
return args.onStepFinish?.(step);
|
|
986
|
-
},
|
|
987
|
-
})) as GenerateTextResult<
|
|
988
|
-
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
989
|
-
OUTPUT
|
|
990
|
-
> &
|
|
991
|
-
GenerationOutputMetadata;
|
|
992
|
-
result.messageId = messageId;
|
|
993
|
-
return result;
|
|
994
|
-
} catch (error) {
|
|
995
|
-
if (threadId && messageId) {
|
|
996
|
-
console.error("RollbackMessage", messageId);
|
|
997
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
998
|
-
messageId,
|
|
999
|
-
error: (error as Error).message,
|
|
1000
|
-
});
|
|
1033
|
+
upToAndIncludingMessageId?: string;
|
|
1034
|
+
contextOptions: ContextOptions | undefined;
|
|
1035
|
+
}
|
|
1036
|
+
): Promise<MessageDoc[]> {
|
|
1037
|
+
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
1038
|
+
// Fetch the latest messages from the thread
|
|
1039
|
+
let included: Set<string> | undefined;
|
|
1040
|
+
const opts = this._mergedContextOptions(args.contextOptions);
|
|
1041
|
+
const contextMessages: MessageDoc[] = [];
|
|
1042
|
+
if (
|
|
1043
|
+
args.threadId &&
|
|
1044
|
+
(opts.recentMessages !== 0 || args.upToAndIncludingMessageId)
|
|
1045
|
+
) {
|
|
1046
|
+
const { page } = await ctx.runQuery(
|
|
1047
|
+
this.component.messages.listMessagesByThreadId,
|
|
1048
|
+
{
|
|
1049
|
+
threadId: args.threadId,
|
|
1050
|
+
excludeToolMessages:
|
|
1051
|
+
opts.includeToolCalls === true ? false : opts.excludeToolMessages,
|
|
1052
|
+
paginationOpts: {
|
|
1053
|
+
numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
|
|
1054
|
+
cursor: null,
|
|
1055
|
+
},
|
|
1056
|
+
upToAndIncludingMessageId: args.upToAndIncludingMessageId,
|
|
1057
|
+
order: "desc",
|
|
1058
|
+
statuses: ["success"],
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
included = new Set(page.map((m) => m._id));
|
|
1062
|
+
contextMessages.push(
|
|
1063
|
+
// Reverse since we fetched in descending order
|
|
1064
|
+
...page.reverse()
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
|
|
1068
|
+
const targetMessage = contextMessages.find(
|
|
1069
|
+
(m) => m._id === args.upToAndIncludingMessageId
|
|
1070
|
+
)?.message;
|
|
1071
|
+
const messagesToSearch = targetMessage
|
|
1072
|
+
? [targetMessage, ...args.messages]
|
|
1073
|
+
: args.messages;
|
|
1074
|
+
if (!("runAction" in ctx)) {
|
|
1075
|
+
throw new Error("searchUserMessages only works in an action");
|
|
1001
1076
|
}
|
|
1002
|
-
|
|
1077
|
+
const searchMessages = await ctx.runAction(
|
|
1078
|
+
this.component.messages.searchMessages,
|
|
1079
|
+
{
|
|
1080
|
+
searchAllMessagesForUserId: opts?.searchOtherThreads
|
|
1081
|
+
? args.userId ??
|
|
1082
|
+
(args.threadId &&
|
|
1083
|
+
(
|
|
1084
|
+
await ctx.runQuery(this.component.threads.getThread, {
|
|
1085
|
+
threadId: args.threadId,
|
|
1086
|
+
})
|
|
1087
|
+
)?.userId)
|
|
1088
|
+
: undefined,
|
|
1089
|
+
threadId: args.threadId,
|
|
1090
|
+
beforeMessageId: args.upToAndIncludingMessageId,
|
|
1091
|
+
...(await this._searchOptionsWithEmbeddingAndDefaults(
|
|
1092
|
+
ctx,
|
|
1093
|
+
{ userId: args.userId, threadId: args.threadId },
|
|
1094
|
+
opts,
|
|
1095
|
+
messagesToSearch
|
|
1096
|
+
)),
|
|
1097
|
+
}
|
|
1098
|
+
);
|
|
1099
|
+
// TODO: track what messages we used for context
|
|
1100
|
+
contextMessages.unshift(
|
|
1101
|
+
...searchMessages.filter((m) => !included?.has(m._id))
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
// Ensure we don't include tool messages without a corresponding tool call
|
|
1105
|
+
return filterOutOrphanedToolMessages(
|
|
1106
|
+
contextMessages.sort((a, b) =>
|
|
1107
|
+
// Sort the raw MessageDocs by order and stepOrder
|
|
1108
|
+
a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order
|
|
1109
|
+
)
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Get the metadata for a thread.
|
|
1115
|
+
* @param ctx A ctx object from a query, mutation, or action.
|
|
1116
|
+
* @param args.threadId The thread to get the metadata for.
|
|
1117
|
+
* @returns The metadata for the thread.
|
|
1118
|
+
*/
|
|
1119
|
+
async getThreadMetadata(
|
|
1120
|
+
ctx: RunQueryCtx,
|
|
1121
|
+
args: { threadId: string }
|
|
1122
|
+
): Promise<ThreadDoc> {
|
|
1123
|
+
const thread = await ctx.runQuery(this.component.threads.getThread, {
|
|
1124
|
+
threadId: args.threadId,
|
|
1125
|
+
});
|
|
1126
|
+
if (!thread) {
|
|
1127
|
+
throw new Error("Thread not found");
|
|
1003
1128
|
}
|
|
1129
|
+
return thread;
|
|
1004
1130
|
}
|
|
1005
1131
|
|
|
1006
1132
|
/**
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1133
|
+
* Update the metadata for a thread.
|
|
1134
|
+
* @param ctx A ctx object from a mutation or action.
|
|
1135
|
+
* @param args.threadId The thread to update the metadata for.
|
|
1136
|
+
* @param args.patch The patch to apply to the thread.
|
|
1137
|
+
* @returns The updated thread metadata.
|
|
1012
1138
|
*/
|
|
1013
|
-
async
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1139
|
+
async updateThreadMetadata(
|
|
1140
|
+
ctx: RunMutationCtx,
|
|
1141
|
+
args: { threadId: string; patch: Partial<WithoutSystemFields<ThreadDoc>> }
|
|
1142
|
+
): Promise<ThreadDoc> {
|
|
1143
|
+
const thread = await ctx.runMutation(
|
|
1144
|
+
this.component.threads.updateThread,
|
|
1145
|
+
args
|
|
1146
|
+
);
|
|
1147
|
+
return thread;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Get the embeddings for a set of messages.
|
|
1152
|
+
* @param messages The messages to get the embeddings for.
|
|
1153
|
+
* @returns The embeddings for the messages.
|
|
1154
|
+
*/
|
|
1155
|
+
async generateEmbeddings(
|
|
1018
1156
|
ctx: RunActionCtx,
|
|
1019
1157
|
{
|
|
1020
|
-
userId
|
|
1158
|
+
userId,
|
|
1021
1159
|
threadId,
|
|
1022
|
-
usageHandler,
|
|
1023
|
-
/**
|
|
1024
|
-
* @deprecated Pass `tools` in the next parameter instead.
|
|
1025
|
-
* This is only intended to pass through thread-default tools.
|
|
1026
|
-
*/
|
|
1027
|
-
tools: threadTools,
|
|
1028
1160
|
}: {
|
|
1029
|
-
userId
|
|
1030
|
-
threadId
|
|
1031
|
-
usageHandler?: UsageHandler;
|
|
1032
|
-
tools?: ToolSet;
|
|
1161
|
+
userId: string | undefined;
|
|
1162
|
+
threadId: string | undefined;
|
|
1033
1163
|
},
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
* The {@link ContextOptions} and {@link StorageOptions}
|
|
1040
|
-
* options to use for fetching contextual messages and saving input/output messages.
|
|
1041
|
-
*/
|
|
1042
|
-
options?: Options & {
|
|
1043
|
-
/**
|
|
1044
|
-
* Whether to save incremental data (deltas) from streaming responses.
|
|
1045
|
-
* Defaults to false.
|
|
1046
|
-
* If false, it will not save any deltas to the database.
|
|
1047
|
-
* If true, it will save deltas with {@link DEFAULT_STREAMING_OPTIONS}.
|
|
1048
|
-
*
|
|
1049
|
-
* Regardless of this option, when streaming you are able to use this
|
|
1050
|
-
* `streamText` function as you would with the "ai" package's version:
|
|
1051
|
-
* iterating over the text, streaming it over HTTP, etc.
|
|
1052
|
-
*/
|
|
1053
|
-
saveStreamDeltas?: boolean | StreamingOptions;
|
|
1164
|
+
messages: CoreMessage[]
|
|
1165
|
+
) {
|
|
1166
|
+
if (!this.options.textEmbedding) {
|
|
1167
|
+
return undefined;
|
|
1054
1168
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1169
|
+
let embeddings:
|
|
1170
|
+
| {
|
|
1171
|
+
vectors: (number[] | null)[];
|
|
1172
|
+
dimension: VectorDimension;
|
|
1173
|
+
model: string;
|
|
1174
|
+
}
|
|
1175
|
+
| undefined;
|
|
1176
|
+
const messageTexts = messages.map((m) => !isTool(m) && extractText(m));
|
|
1177
|
+
// Find the indexes of the messages that have text.
|
|
1178
|
+
const textIndexes = messageTexts
|
|
1179
|
+
.map((t, i) => (t ? i : undefined))
|
|
1180
|
+
.filter((i) => i !== undefined);
|
|
1181
|
+
if (textIndexes.length === 0) {
|
|
1182
|
+
return undefined;
|
|
1183
|
+
}
|
|
1184
|
+
// Then embed those messages.
|
|
1185
|
+
const textEmbeddings = await this.doEmbed(ctx, {
|
|
1186
|
+
userId,
|
|
1064
1187
|
threadId,
|
|
1065
|
-
|
|
1188
|
+
values: messageTexts.filter((t): t is string => !!t),
|
|
1066
1189
|
});
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
)
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
agentName: this.options.name,
|
|
1085
|
-
model: aiArgs.model.modelId,
|
|
1086
|
-
provider: aiArgs.model.provider,
|
|
1087
|
-
providerOptions: aiArgs.providerOptions,
|
|
1088
|
-
order,
|
|
1089
|
-
stepOrder,
|
|
1090
|
-
})
|
|
1091
|
-
: undefined;
|
|
1190
|
+
// TODO: record usage of embeddings
|
|
1191
|
+
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
1192
|
+
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
1193
|
+
textIndexes.forEach((i, j) => {
|
|
1194
|
+
embeddingsOrNull[i] = textEmbeddings.embeddings[j];
|
|
1195
|
+
});
|
|
1196
|
+
if (textEmbeddings.embeddings.length > 0) {
|
|
1197
|
+
const dimension = textEmbeddings.embeddings[0].length;
|
|
1198
|
+
validateVectorDimension(dimension);
|
|
1199
|
+
embeddings = {
|
|
1200
|
+
vectors: embeddingsOrNull,
|
|
1201
|
+
dimension,
|
|
1202
|
+
model: this.options.textEmbedding.modelId,
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
return embeddings;
|
|
1206
|
+
}
|
|
1092
1207
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
providerMetadata: step.providerMetadata,
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
return args.onStepFinish?.(step);
|
|
1208
|
+
/**
|
|
1209
|
+
* Generate embeddings for a set of messages, and save them to the database.
|
|
1210
|
+
* It will not generate or save embeddings for messages that already have an
|
|
1211
|
+
* embedding.
|
|
1212
|
+
* @param ctx The ctx parameter to an action.
|
|
1213
|
+
* @param args The messageIds to generate embeddings for.
|
|
1214
|
+
*/
|
|
1215
|
+
async generateAndSaveEmbeddings(
|
|
1216
|
+
ctx: RunActionCtx,
|
|
1217
|
+
args: {
|
|
1218
|
+
messageIds: string[];
|
|
1219
|
+
}
|
|
1220
|
+
) {
|
|
1221
|
+
const messages = (
|
|
1222
|
+
await ctx.runQuery(this.component.messages.getMessagesByIds, {
|
|
1223
|
+
messageIds: args.messageIds,
|
|
1224
|
+
})
|
|
1225
|
+
).filter((m): m is NonNullable<typeof m> => m !== null);
|
|
1226
|
+
if (messages.length !== args.messageIds.length) {
|
|
1227
|
+
throw new Error(
|
|
1228
|
+
"Some messages were not found: " +
|
|
1229
|
+
args.messageIds
|
|
1230
|
+
.filter((id) => !messages.some((m) => m?._id === id))
|
|
1231
|
+
.join(", ")
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
if (messages.some((m) => !m.message)) {
|
|
1235
|
+
throw new Error(
|
|
1236
|
+
"Some messages don't have a message: " +
|
|
1237
|
+
args.messageIds
|
|
1238
|
+
.map((id, i) => (!messages[i].message ? id : undefined))
|
|
1239
|
+
.filter((id): id is string => id !== undefined)
|
|
1240
|
+
.join(", ")
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const messagesMissingEmbeddings = messages.filter((m) => !m.embeddingId);
|
|
1244
|
+
if (messagesMissingEmbeddings.length === 0) {
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
const embeddings = await this.generateEmbeddings(
|
|
1248
|
+
ctx,
|
|
1249
|
+
{
|
|
1250
|
+
userId: messagesMissingEmbeddings[0]!.userId,
|
|
1251
|
+
threadId: messagesMissingEmbeddings[0]!.threadId,
|
|
1141
1252
|
},
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1253
|
+
messagesMissingEmbeddings.map((m) => m!.message!)
|
|
1254
|
+
);
|
|
1255
|
+
if (!embeddings) {
|
|
1256
|
+
if (!this.options.textEmbedding) {
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
"No embeddings were generated for the messages. You must pass a textEmbedding model to the agent constructor."
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
throw new Error(
|
|
1262
|
+
"No embeddings were generated for these messages: " +
|
|
1263
|
+
messagesMissingEmbeddings.map((m) => m!._id).join(", ")
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
await ctx.runMutation(this.component.vector.index.insertBatch, {
|
|
1267
|
+
vectorDimension: embeddings.dimension,
|
|
1268
|
+
vectors: messagesMissingEmbeddings
|
|
1269
|
+
.map((m, i) => ({
|
|
1270
|
+
messageId: m!._id,
|
|
1271
|
+
model: embeddings.model,
|
|
1272
|
+
table: "messages",
|
|
1273
|
+
userId: m.userId,
|
|
1274
|
+
threadId: m.threadId,
|
|
1275
|
+
vector: embeddings.vectors[i],
|
|
1276
|
+
}))
|
|
1277
|
+
.filter(
|
|
1278
|
+
(v): v is Extract<typeof v, { vector: number[] }> => v.vector !== null
|
|
1279
|
+
),
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Explicitly save a "step" created by the AI SDK.
|
|
1285
|
+
* @param ctx The ctx argument to a mutation or action.
|
|
1286
|
+
* @param args The Step generated by the AI SDK.
|
|
1287
|
+
*/
|
|
1288
|
+
async saveStep<TOOLS extends ToolSet>(
|
|
1289
|
+
ctx: ActionCtx,
|
|
1290
|
+
args: {
|
|
1291
|
+
userId?: string;
|
|
1292
|
+
threadId: string;
|
|
1293
|
+
/**
|
|
1294
|
+
* The message this step is in response to.
|
|
1295
|
+
*/
|
|
1296
|
+
promptMessageId: string;
|
|
1297
|
+
/**
|
|
1298
|
+
* The step to save, possibly including multiple tool calls.
|
|
1299
|
+
*/
|
|
1300
|
+
step: StepResult<TOOLS>;
|
|
1301
|
+
/**
|
|
1302
|
+
* The model used to generate the step.
|
|
1303
|
+
* Defaults to the chat model for the Agent.
|
|
1304
|
+
*/
|
|
1305
|
+
model?: string;
|
|
1306
|
+
/**
|
|
1307
|
+
* The provider of the model used to generate the step.
|
|
1308
|
+
* Defaults to the chat provider for the Agent.
|
|
1309
|
+
*/
|
|
1310
|
+
provider?: string;
|
|
1311
|
+
}
|
|
1312
|
+
): Promise<{ messages: MessageDoc[]; pending?: MessageDoc }> {
|
|
1313
|
+
const messages = await serializeNewMessagesInStep(
|
|
1314
|
+
ctx,
|
|
1315
|
+
this.component,
|
|
1316
|
+
args.step,
|
|
1317
|
+
{
|
|
1318
|
+
provider: args.provider ?? this.options.chat.provider,
|
|
1319
|
+
model: args.model ?? this.options.chat.modelId,
|
|
1320
|
+
}
|
|
1321
|
+
);
|
|
1322
|
+
const embeddings = await this.generateEmbeddings(
|
|
1323
|
+
ctx,
|
|
1324
|
+
{ userId: args.userId, threadId: args.threadId },
|
|
1325
|
+
messages.map((m) => m.message)
|
|
1326
|
+
);
|
|
1327
|
+
const saved = await ctx.runMutation(this.component.messages.addMessages, {
|
|
1328
|
+
userId: args.userId,
|
|
1329
|
+
threadId: args.threadId,
|
|
1330
|
+
agentName: this.options.name,
|
|
1331
|
+
promptMessageId: args.promptMessageId,
|
|
1332
|
+
messages,
|
|
1333
|
+
embeddings,
|
|
1334
|
+
failPendingSteps: false,
|
|
1335
|
+
});
|
|
1336
|
+
return saved;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* Manually save the result of a generateObject call to the thread.
|
|
1341
|
+
* This happens automatically when using {@link generateObject} or {@link streamObject}
|
|
1342
|
+
* from the `thread` object created by {@link continueThread} or {@link createThread}.
|
|
1343
|
+
* @param ctx The context passed from the mutation or action function calling this.
|
|
1344
|
+
* @param args The arguments to the saveObject function.
|
|
1345
|
+
*/
|
|
1346
|
+
async saveObject(
|
|
1347
|
+
ctx: RunActionCtx,
|
|
1348
|
+
args: {
|
|
1349
|
+
userId: string | undefined;
|
|
1350
|
+
threadId: string;
|
|
1351
|
+
promptMessageId: string;
|
|
1352
|
+
result: GenerateObjectResult<unknown>;
|
|
1353
|
+
metadata?: Omit<MessageWithMetadata, "message">;
|
|
1354
|
+
}
|
|
1355
|
+
): Promise<void> {
|
|
1356
|
+
const { messages } = serializeObjectResult(args.result, {
|
|
1357
|
+
model: this.options.chat.modelId,
|
|
1358
|
+
provider: this.options.chat.provider,
|
|
1359
|
+
});
|
|
1360
|
+
const embeddings = await this.generateEmbeddings(
|
|
1361
|
+
ctx,
|
|
1362
|
+
{ userId: args.userId, threadId: args.threadId },
|
|
1363
|
+
messages.map((m) => m.message)
|
|
1364
|
+
);
|
|
1365
|
+
|
|
1366
|
+
await ctx.runMutation(this.component.messages.addMessages, {
|
|
1367
|
+
userId: args.userId,
|
|
1368
|
+
threadId: args.threadId,
|
|
1369
|
+
promptMessageId: args.promptMessageId,
|
|
1370
|
+
failPendingSteps: false,
|
|
1371
|
+
messages,
|
|
1372
|
+
embeddings,
|
|
1373
|
+
agentName: this.options.name,
|
|
1374
|
+
pending: false,
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* Commit or rollback a message that was pending.
|
|
1380
|
+
* This is done automatically when saving messages by default.
|
|
1381
|
+
* If creating pending messages, you can call this when the full "transaction" is done.
|
|
1382
|
+
* @param ctx The ctx argument to your mutation or action.
|
|
1383
|
+
* @param args What message to save. Generally the parent message sent into
|
|
1384
|
+
* the generateText call.
|
|
1385
|
+
*/
|
|
1386
|
+
async completeMessage(
|
|
1387
|
+
ctx: RunMutationCtx,
|
|
1388
|
+
args: {
|
|
1389
|
+
threadId: string;
|
|
1390
|
+
messageId: string;
|
|
1391
|
+
result: { kind: "error"; error: string } | { kind: "success" };
|
|
1392
|
+
}
|
|
1393
|
+
): Promise<void> {
|
|
1394
|
+
const result = args.result;
|
|
1395
|
+
if (result.kind === "success") {
|
|
1396
|
+
await ctx.runMutation(this.component.messages.commitMessage, {
|
|
1397
|
+
messageId: args.messageId,
|
|
1398
|
+
});
|
|
1399
|
+
} else {
|
|
1400
|
+
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
1401
|
+
messageId: args.messageId,
|
|
1402
|
+
error: result.error,
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1149
1405
|
}
|
|
1150
1406
|
|
|
1151
1407
|
async _saveMessagesAndFetchContext<
|
|
@@ -1222,9 +1478,10 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1222
1478
|
if (
|
|
1223
1479
|
threadId &&
|
|
1224
1480
|
messages.length &&
|
|
1481
|
+
storageOptions?.saveMessages !== "none" &&
|
|
1225
1482
|
storageOptions?.saveAnyInputMessages !== false
|
|
1226
1483
|
) {
|
|
1227
|
-
const saveAll = storageOptions?.
|
|
1484
|
+
const saveAll = storageOptions?.saveMessages === "all";
|
|
1228
1485
|
const coreMessages = saveAll ? messages : messages.slice(-1);
|
|
1229
1486
|
const saved = await this.saveMessages(ctx, {
|
|
1230
1487
|
threadId,
|
|
@@ -1257,195 +1514,9 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1257
1514
|
};
|
|
1258
1515
|
}
|
|
1259
1516
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
* resulting messages to the thread, if specified.
|
|
1264
|
-
* Use {@link continueThread} to get a version of this function already scoped
|
|
1265
|
-
* to a thread (and optionally userId).
|
|
1266
|
-
*/
|
|
1267
|
-
async generateObject<T>(
|
|
1268
|
-
ctx: RunActionCtx,
|
|
1269
|
-
{
|
|
1270
|
-
userId: argsUserId,
|
|
1271
|
-
threadId,
|
|
1272
|
-
usageHandler,
|
|
1273
|
-
}: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
|
|
1274
|
-
/**
|
|
1275
|
-
* The arguments to the generateObject function, similar to the ai.generateObject function.
|
|
1276
|
-
*/
|
|
1277
|
-
args: OurObjectArgs<T>,
|
|
1278
|
-
/**
|
|
1279
|
-
* The {@link ContextOptions} and {@link StorageOptions}
|
|
1280
|
-
* options to use for fetching contextual messages and saving input/output messages.
|
|
1281
|
-
*/
|
|
1282
|
-
options?: Options
|
|
1283
|
-
): Promise<GenerateObjectResult<T> & GenerationOutputMetadata> {
|
|
1284
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
1285
|
-
userId: argsUserId,
|
|
1286
|
-
threadId,
|
|
1287
|
-
...options,
|
|
1288
|
-
});
|
|
1289
|
-
const { args: aiArgs, messageId, userId } = context;
|
|
1290
|
-
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
1291
|
-
const saveOutputMessages =
|
|
1292
|
-
options?.storageOptions?.saveOutputMessages ??
|
|
1293
|
-
this.options.storageOptions?.saveOutputMessages;
|
|
1294
|
-
try {
|
|
1295
|
-
const result = (await generateObject(
|
|
1296
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1297
|
-
aiArgs as any
|
|
1298
|
-
)) as GenerateObjectResult<T> & GenerationOutputMetadata;
|
|
1299
|
-
|
|
1300
|
-
if (threadId && messageId && saveOutputMessages !== false) {
|
|
1301
|
-
await this.saveObject(ctx, {
|
|
1302
|
-
threadId,
|
|
1303
|
-
promptMessageId: messageId,
|
|
1304
|
-
result,
|
|
1305
|
-
userId,
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
result.messageId = messageId;
|
|
1309
|
-
if (trackUsage && result.usage) {
|
|
1310
|
-
await trackUsage(ctx, {
|
|
1311
|
-
userId,
|
|
1312
|
-
threadId,
|
|
1313
|
-
agentName: this.options.name,
|
|
1314
|
-
model: aiArgs.model.modelId,
|
|
1315
|
-
provider: aiArgs.model.provider,
|
|
1316
|
-
usage: result.usage,
|
|
1317
|
-
providerMetadata: result.providerMetadata,
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return result;
|
|
1321
|
-
} catch (error) {
|
|
1322
|
-
if (threadId && messageId) {
|
|
1323
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
1324
|
-
messageId,
|
|
1325
|
-
error: (error as Error).message,
|
|
1326
|
-
});
|
|
1327
|
-
}
|
|
1328
|
-
throw error;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
/**
|
|
1333
|
-
* This behaves like `streamObject` from the "ai" package except that
|
|
1334
|
-
* it add context based on the userId and threadId and saves the input and
|
|
1335
|
-
* resulting messages to the thread, if specified.
|
|
1336
|
-
* Use {@link continueThread} to get a version of this function already scoped
|
|
1337
|
-
* to a thread (and optionally userId).
|
|
1338
|
-
*/
|
|
1339
|
-
async streamObject<T>(
|
|
1340
|
-
ctx: RunActionCtx,
|
|
1341
|
-
{
|
|
1342
|
-
userId: argsUserId,
|
|
1343
|
-
threadId,
|
|
1344
|
-
usageHandler,
|
|
1345
|
-
}: { userId?: string; threadId?: string; usageHandler?: UsageHandler },
|
|
1346
|
-
/**
|
|
1347
|
-
* The arguments to the streamObject function, similar to the ai `streamObject` function.
|
|
1348
|
-
*/
|
|
1349
|
-
args: OurStreamObjectArgs<T>,
|
|
1350
|
-
/**
|
|
1351
|
-
* The {@link ContextOptions} and {@link StorageOptions}
|
|
1352
|
-
* options to use for fetching contextual messages and saving input/output messages.
|
|
1353
|
-
*/
|
|
1354
|
-
options?: Options
|
|
1355
|
-
): Promise<
|
|
1356
|
-
StreamObjectResult<DeepPartial<T>, T, never> & GenerationOutputMetadata
|
|
1357
|
-
> {
|
|
1358
|
-
// TODO: unify all this shared code between all the generate* and stream* functions
|
|
1359
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
1360
|
-
userId: argsUserId,
|
|
1361
|
-
threadId,
|
|
1362
|
-
...options,
|
|
1363
|
-
});
|
|
1364
|
-
const { args: aiArgs, messageId, userId } = context;
|
|
1365
|
-
const trackUsage = usageHandler ?? this.options.usageHandler;
|
|
1366
|
-
const saveOutputMessages =
|
|
1367
|
-
options?.storageOptions?.saveOutputMessages ??
|
|
1368
|
-
this.options.storageOptions?.saveOutputMessages;
|
|
1369
|
-
const stream = streamObject<T>({
|
|
1370
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1371
|
-
...(aiArgs as any),
|
|
1372
|
-
onError: async (error) => {
|
|
1373
|
-
console.error("onError", error);
|
|
1374
|
-
return args.onError?.(error);
|
|
1375
|
-
},
|
|
1376
|
-
onFinish: async (result) => {
|
|
1377
|
-
if (threadId && messageId && saveOutputMessages !== false) {
|
|
1378
|
-
await this.saveObject(ctx, {
|
|
1379
|
-
userId,
|
|
1380
|
-
threadId,
|
|
1381
|
-
promptMessageId: messageId,
|
|
1382
|
-
result: {
|
|
1383
|
-
object: result.object,
|
|
1384
|
-
finishReason: "stop",
|
|
1385
|
-
usage: result.usage,
|
|
1386
|
-
warnings: result.warnings,
|
|
1387
|
-
request: await stream.request,
|
|
1388
|
-
response: result.response,
|
|
1389
|
-
providerMetadata: result.providerMetadata,
|
|
1390
|
-
experimental_providerMetadata:
|
|
1391
|
-
result.experimental_providerMetadata,
|
|
1392
|
-
logprobs: undefined,
|
|
1393
|
-
toJsonResponse: stream.toTextStreamResponse,
|
|
1394
|
-
},
|
|
1395
|
-
});
|
|
1396
|
-
}
|
|
1397
|
-
if (trackUsage && result.usage) {
|
|
1398
|
-
await trackUsage(ctx, {
|
|
1399
|
-
userId,
|
|
1400
|
-
threadId,
|
|
1401
|
-
agentName: this.options.name,
|
|
1402
|
-
model: aiArgs.model.modelId,
|
|
1403
|
-
provider: aiArgs.model.provider,
|
|
1404
|
-
usage: result.usage,
|
|
1405
|
-
providerMetadata: result.providerMetadata,
|
|
1406
|
-
});
|
|
1407
|
-
}
|
|
1408
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1409
|
-
return args.onFinish?.(result as any);
|
|
1410
|
-
},
|
|
1411
|
-
}) as StreamObjectResult<DeepPartial<T>, T, never> &
|
|
1412
|
-
GenerationOutputMetadata;
|
|
1413
|
-
stream.messageId = messageId;
|
|
1414
|
-
return stream;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
/**
|
|
1418
|
-
* Manually save the result of a generateObject call to the thread.
|
|
1419
|
-
* This happens automatically when using {@link generateObject} or {@link streamObject}
|
|
1420
|
-
* from the `thread` object created by {@link continueThread} or {@link createThread}.
|
|
1421
|
-
* @param ctx The context passed from the mutation or action function calling this.
|
|
1422
|
-
* @param args The arguments to the saveObject function.
|
|
1423
|
-
*/
|
|
1424
|
-
async saveObject(
|
|
1425
|
-
ctx: RunMutationCtx,
|
|
1426
|
-
args: {
|
|
1427
|
-
userId: string | undefined;
|
|
1428
|
-
threadId: string;
|
|
1429
|
-
promptMessageId: string;
|
|
1430
|
-
result: GenerateObjectResult<unknown>;
|
|
1431
|
-
metadata?: Omit<MessageWithMetadata, "message">;
|
|
1432
|
-
}
|
|
1433
|
-
): Promise<void> {
|
|
1434
|
-
const { step, messages } = serializeObjectResult(args.result, {
|
|
1435
|
-
model: this.options.chat.modelId,
|
|
1436
|
-
provider: this.options.chat.provider,
|
|
1437
|
-
});
|
|
1438
|
-
const embeddings = await this.generateEmbeddings(
|
|
1439
|
-
messages.map((m) => m.message)
|
|
1440
|
-
);
|
|
1441
|
-
|
|
1442
|
-
await ctx.runMutation(this.component.messages.addStep, {
|
|
1443
|
-
userId: args.userId,
|
|
1444
|
-
threadId: args.threadId,
|
|
1445
|
-
promptMessageId: args.promptMessageId,
|
|
1446
|
-
failPendingSteps: false,
|
|
1447
|
-
step: { step, messages, embeddings },
|
|
1448
|
-
});
|
|
1517
|
+
_shouldSaveOutputMessages(storageOpts?: StorageOptions): boolean {
|
|
1518
|
+
const opts = storageOpts ?? this.options.storageOptions;
|
|
1519
|
+
return opts?.saveOutputMessages !== false && opts?.saveMessages !== "none";
|
|
1449
1520
|
}
|
|
1450
1521
|
|
|
1451
1522
|
_mergedContextOptions(opts: ContextOptions | undefined): ContextOptions {
|
|
@@ -1462,7 +1533,9 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1462
1533
|
};
|
|
1463
1534
|
}
|
|
1464
1535
|
|
|
1465
|
-
async
|
|
1536
|
+
async _searchOptionsWithEmbeddingAndDefaults(
|
|
1537
|
+
ctx: RunActionCtx,
|
|
1538
|
+
{ userId, threadId }: { userId?: string; threadId?: string },
|
|
1466
1539
|
contextOptions: ContextOptions,
|
|
1467
1540
|
messages: CoreMessage[]
|
|
1468
1541
|
): Promise<SearchOptions> {
|
|
@@ -1487,16 +1560,58 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1487
1560
|
this.options.textEmbedding
|
|
1488
1561
|
) {
|
|
1489
1562
|
search.vector = (
|
|
1490
|
-
await this.
|
|
1563
|
+
await this.doEmbed(ctx, {
|
|
1564
|
+
threadId,
|
|
1565
|
+
userId,
|
|
1491
1566
|
values: [text],
|
|
1492
1567
|
})
|
|
1493
1568
|
).embeddings[0];
|
|
1494
|
-
// TODO: record usage of embeddings
|
|
1495
1569
|
search.vectorModel = this.options.textEmbedding.modelId;
|
|
1496
1570
|
}
|
|
1497
1571
|
return search;
|
|
1498
1572
|
}
|
|
1499
1573
|
|
|
1574
|
+
async doEmbed(
|
|
1575
|
+
ctx: RunActionCtx,
|
|
1576
|
+
options: {
|
|
1577
|
+
userId: string | undefined;
|
|
1578
|
+
threadId: string | undefined;
|
|
1579
|
+
values: string[];
|
|
1580
|
+
abortSignal?: AbortSignal;
|
|
1581
|
+
headers?: Record<string, string | undefined>;
|
|
1582
|
+
}
|
|
1583
|
+
): Promise<{ embeddings: number[][] }> {
|
|
1584
|
+
const embedding = this.options.textEmbedding;
|
|
1585
|
+
assert(embedding, "textEmbedding is required");
|
|
1586
|
+
const result = await embedding.doEmbed({
|
|
1587
|
+
values: options.values,
|
|
1588
|
+
abortSignal: options.abortSignal,
|
|
1589
|
+
headers: options.headers,
|
|
1590
|
+
});
|
|
1591
|
+
if (this.options.usageHandler && result.usage) {
|
|
1592
|
+
await this.options.usageHandler(ctx, {
|
|
1593
|
+
userId: options.userId,
|
|
1594
|
+
threadId: options.threadId,
|
|
1595
|
+
agentName: this.options.name,
|
|
1596
|
+
model: embedding.modelId,
|
|
1597
|
+
provider: embedding.provider,
|
|
1598
|
+
providerMetadata: result.rawResponse
|
|
1599
|
+
? { [embedding.provider]: result.rawResponse }
|
|
1600
|
+
: undefined,
|
|
1601
|
+
usage: {
|
|
1602
|
+
promptTokens: result.usage.tokens,
|
|
1603
|
+
completionTokens: 0,
|
|
1604
|
+
totalTokens: result.usage.tokens,
|
|
1605
|
+
},
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
return { embeddings: result.embeddings };
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* WORKFLOW UTILITIES
|
|
1613
|
+
*/
|
|
1614
|
+
|
|
1500
1615
|
/**
|
|
1501
1616
|
* Create a mutation that creates a thread so you can call it from a Workflow.
|
|
1502
1617
|
* e.g.
|
|
@@ -1522,7 +1637,7 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1522
1637
|
title: v.optional(v.string()),
|
|
1523
1638
|
summary: v.optional(v.string()),
|
|
1524
1639
|
},
|
|
1525
|
-
handler: async (ctx, args) => {
|
|
1640
|
+
handler: async (ctx, args): Promise<{ threadId: string }> => {
|
|
1526
1641
|
const { threadId } = await this.createThread(ctx, args);
|
|
1527
1642
|
return { threadId };
|
|
1528
1643
|
},
|
|
@@ -1643,6 +1758,50 @@ export class Agent<AgentTools extends ToolSet> {
|
|
|
1643
1758
|
},
|
|
1644
1759
|
});
|
|
1645
1760
|
}
|
|
1761
|
+
|
|
1762
|
+
/**
|
|
1763
|
+
* Save messages to the thread.
|
|
1764
|
+
* Useful as a step in Workflows, e.g.
|
|
1765
|
+
* ```ts
|
|
1766
|
+
* const saveMessages = agent.asSaveMessagesMutation();
|
|
1767
|
+
*
|
|
1768
|
+
* const myWorkflow = workflow.define({
|
|
1769
|
+
* args: {...},
|
|
1770
|
+
* handler: async (step, args) => {
|
|
1771
|
+
* // do things to create (but not save)messages
|
|
1772
|
+
* const { messageIds } = await step.runMutation(internal.foo.saveMessages, {
|
|
1773
|
+
* threadId: args.threadId,
|
|
1774
|
+
* messages: args.messages,
|
|
1775
|
+
* });
|
|
1776
|
+
* // ...
|
|
1777
|
+
* },
|
|
1778
|
+
* })
|
|
1779
|
+
* ```
|
|
1780
|
+
* @returns A mutation that can be used to save messages to the thread.
|
|
1781
|
+
*/
|
|
1782
|
+
asSaveMessagesMutation() {
|
|
1783
|
+
return internalMutationGeneric({
|
|
1784
|
+
args: {
|
|
1785
|
+
threadId: v.string(),
|
|
1786
|
+
userId: v.optional(v.string()),
|
|
1787
|
+
promptMessageId: v.optional(v.string()),
|
|
1788
|
+
messages: v.array(vMessageWithMetadata),
|
|
1789
|
+
pending: v.optional(v.boolean()),
|
|
1790
|
+
failPendingSteps: v.optional(v.boolean()),
|
|
1791
|
+
},
|
|
1792
|
+
handler: async (ctx, args) => {
|
|
1793
|
+
const { lastMessageId, messages } = await this.saveMessages(ctx, {
|
|
1794
|
+
...args,
|
|
1795
|
+
messages: args.messages.map((m) => m.message),
|
|
1796
|
+
metadata: args.messages.map(({ message: _, ...m }) => m),
|
|
1797
|
+
});
|
|
1798
|
+
return {
|
|
1799
|
+
lastMessageId,
|
|
1800
|
+
messageIds: messages.map((m) => m._id),
|
|
1801
|
+
};
|
|
1802
|
+
},
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1646
1805
|
}
|
|
1647
1806
|
|
|
1648
1807
|
export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
|
|
@@ -1672,5 +1831,4 @@ export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
|
|
|
1672
1831
|
return result;
|
|
1673
1832
|
}
|
|
1674
1833
|
|
|
1675
|
-
type MessageWithMetadata = OpaqueIds<InnerMessageWithMetadata>;
|
|
1676
1834
|
type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };
|