@convex-dev/agent 0.2.6-alpha.0 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/definePlaygroundAPI.d.ts +6 -4
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +15 -6
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/index.d.ts +26 -120
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +48 -373
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +1 -1
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/mockModel.d.ts +3 -3
- package/dist/client/mockModel.d.ts.map +1 -1
- package/dist/client/mockModel.js +22 -17
- package/dist/client/mockModel.js.map +1 -1
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +57 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +110 -9
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +271 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +83 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +171 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streaming.d.ts +8 -8
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +2 -1
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/textStreamParts.d.ts.map +1 -1
- package/dist/client/textStreamParts.js +2 -9
- package/dist/client/textStreamParts.js.map +1 -1
- package/dist/client/threads.d.ts +1 -1
- package/dist/client/threads.d.ts.map +1 -1
- package/dist/client/types.d.ts +137 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +11 -3
- package/dist/component/messages.d.ts +13 -4
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +67 -25
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +2 -1643
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +0 -24
- package/dist/component/schema.js.map +1 -1
- package/dist/mapping.d.ts +7 -9
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +73 -7
- package/dist/mapping.js.map +1 -1
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +15 -5
- package/dist/react/deltas.js.map +1 -1
- package/dist/react/fromUIMessages.d.ts +13 -0
- package/dist/react/fromUIMessages.d.ts.map +1 -0
- package/dist/react/fromUIMessages.js +70 -0
- package/dist/react/fromUIMessages.js.map +1 -0
- package/dist/react/toUIMessages.d.ts +5 -2
- package/dist/react/toUIMessages.d.ts.map +1 -1
- package/dist/react/toUIMessages.js +3 -0
- package/dist/react/toUIMessages.js.map +1 -1
- package/dist/shared.d.ts +10 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +26 -0
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +1640 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +41 -0
- package/dist/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/definePlaygroundAPI.ts +16 -7
- package/src/client/index.test.ts +11 -46
- package/src/client/index.ts +99 -558
- package/src/client/messages.ts +1 -1
- package/src/client/mock.json +68 -0
- package/src/client/mockModel.ts +34 -23
- package/src/client/saveInputMessages.test.ts +576 -0
- package/src/client/saveInputMessages.ts +100 -0
- package/src/client/search.test.ts +1017 -0
- package/src/client/search.ts +446 -68
- package/src/client/start.ts +313 -0
- package/src/client/stream.json +48 -0
- package/src/client/streaming.ts +3 -3
- package/src/client/textStreamParts.ts +2 -11
- package/src/client/threads.ts +1 -1
- package/src/client/types.ts +143 -3
- package/src/component/_generated/api.d.ts +11 -3
- package/src/component/messages.ts +73 -27
- package/src/component/schema.ts +1 -29
- package/src/mapping.ts +84 -7
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/react/deltas.ts +18 -5
- package/src/react/fromUIMessages.test.ts +427 -0
- package/src/react/fromUIMessages.ts +85 -0
- package/src/react/toUIMessages.ts +21 -13
- package/src/shared.ts +33 -0
- package/src/validators.test.ts +13 -2
- package/src/validators.ts +48 -0
package/dist/client/index.js
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { generateObject, generateText, stepCountIs, streamObject, streamText, } from "ai";
|
|
2
2
|
import { assert, omit, pick } from "convex-helpers";
|
|
3
3
|
import { internalActionGeneric, internalMutationGeneric, } from "convex/server";
|
|
4
4
|
import { convexToJson, v } from "convex/values";
|
|
5
|
-
import {
|
|
5
|
+
import {} from "../component/vector/tables.js";
|
|
6
6
|
import { deserializeMessage, serializeMessage, serializeNewMessagesInStep, serializeObjectResult, } from "../mapping.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getModelName, getProviderName } from "../shared.js";
|
|
8
8
|
import { vMessageEmbeddings, vMessageWithMetadata, vSafeObjectArgs, vTextArgs, } from "../validators.js";
|
|
9
|
-
import { wrapTools } from "./createTool.js";
|
|
10
9
|
import { listMessages, saveMessages, } from "./messages.js";
|
|
11
|
-
import {
|
|
10
|
+
import { embedMany, embedMessages, fetchContextMessages, generateAndSaveEmbeddings, } from "./search.js";
|
|
11
|
+
import { start } from "./start.js";
|
|
12
12
|
import { DeltaStreamer, syncStreams, } from "./streaming.js";
|
|
13
13
|
import { mergeTransforms, serializeTextStreamingPartsV5, } from "./textStreamParts.js";
|
|
14
14
|
import { createThread, getThreadMetadata } from "./threads.js";
|
|
15
|
-
import { inlineMessagesFiles } from "./files.js";
|
|
16
15
|
export { stepCountIs } from "ai";
|
|
17
|
-
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
18
16
|
export { deserializeMessage, serializeDataOrUrl, serializeMessage, guessMimeType, toUIFilePart, } from "../mapping.js";
|
|
19
17
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
20
18
|
// a future version may put them all here or move these over there
|
|
21
|
-
export { vAssistantMessage, vContextOptions, vMessage, vPaginationResult, vProviderMetadata, vStorageOptions, vStreamArgs, vSystemMessage, vToolMessage, vUsage, vUserMessage, vSource, vContent, } from "../validators.js";
|
|
19
|
+
export { vAssistantMessage, vContextOptions, vMessage, vMessageDoc, vPaginationResult, vProviderMetadata, vStorageOptions, vStreamArgs, vSystemMessage, vThreadDoc, vToolMessage, vUsage, vUserMessage, vSource, vContent, } from "../validators.js";
|
|
22
20
|
export { definePlaygroundAPI, } from "./definePlaygroundAPI.js";
|
|
23
21
|
export { getFile, storeFile } from "./files.js";
|
|
24
22
|
export { listMessages, saveMessage, saveMessages, } from "./messages.js";
|
|
25
|
-
export { fetchContextMessages, filterOutOrphanedToolMessages, } from "./search.js";
|
|
23
|
+
export { fetchContextMessages, filterOutOrphanedToolMessages, fetchContextWithPrompt, generateAndSaveEmbeddings, embedMessages, embedMany, } from "./search.js";
|
|
26
24
|
export { abortStream, listStreams, syncStreams, vStreamMessagesReturnValue, } from "./streaming.js";
|
|
27
25
|
export { createThread, getThreadMetadata, updateThreadMetadata, searchThreadTitles, } from "./threads.js";
|
|
28
26
|
export { extractText, isTool, sorted } from "../shared.js";
|
|
29
27
|
export { createTool } from "./createTool.js";
|
|
30
28
|
export { mockModel } from "./mockModel.js";
|
|
31
|
-
// 10k characters should be more than enough for most cases, and stays under
|
|
32
|
-
// the 8k token limit for some models.
|
|
33
|
-
const MAX_EMBEDDING_TEXT_LENGTH = 10_000;
|
|
34
29
|
export class Agent {
|
|
35
30
|
component;
|
|
36
31
|
options;
|
|
@@ -84,147 +79,17 @@ export class Agent {
|
|
|
84
79
|
* you pass here.
|
|
85
80
|
*/
|
|
86
81
|
args, options) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
threadId: options?.threadId,
|
|
91
|
-
messages: args.messages,
|
|
92
|
-
prompt: args.prompt,
|
|
93
|
-
promptMessageId: args.promptMessageId,
|
|
94
|
-
...opts,
|
|
95
|
-
});
|
|
96
|
-
let pendingMessageId = context.pendingMessageId;
|
|
97
|
-
const { messages, promptMessageId, order, stepOrder, userId } = context;
|
|
98
|
-
const savedMessages = context.savedMessages ?? [];
|
|
99
|
-
const toolCtx = {
|
|
100
|
-
...ctx,
|
|
101
|
-
userId,
|
|
102
|
-
threadId,
|
|
103
|
-
promptMessageId,
|
|
104
|
-
agent: this,
|
|
105
|
-
};
|
|
106
|
-
const tools = wrapTools(toolCtx, args.tools ?? this.options.tools);
|
|
107
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
108
|
-
const fail = async (reason) => {
|
|
109
|
-
if (threadId && promptMessageId) {
|
|
110
|
-
console.error(`Message failed in thread ${threadId} with promptMessageId ${promptMessageId}: ${reason}`);
|
|
111
|
-
}
|
|
112
|
-
if (pendingMessageId) {
|
|
113
|
-
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
114
|
-
messageId: pendingMessageId,
|
|
115
|
-
result: { status: "failed", error: reason },
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
if (args.abortSignal) {
|
|
120
|
-
const abortSignal = args.abortSignal;
|
|
121
|
-
abortSignal.addEventListener("abort", async () => {
|
|
122
|
-
await fail(abortSignal.reason ?? "Aborted");
|
|
123
|
-
}, { once: true });
|
|
124
|
-
}
|
|
125
|
-
const aiArgs = {
|
|
126
|
-
...this.options.callSettings,
|
|
127
|
-
providerOptions: this.options.providerOptions,
|
|
128
|
-
...omit(args, ["messages", "prompt", "promptMessageId"]),
|
|
129
|
-
model: args.model ?? this.options.languageModel,
|
|
82
|
+
return start(ctx, this.component, {
|
|
83
|
+
...args,
|
|
84
|
+
tools: (args.tools ?? this.options.tools),
|
|
130
85
|
system: args.system ?? this.options.instructions,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!aiArgs._internal?.generateId) {
|
|
139
|
-
aiArgs._internal = {
|
|
140
|
-
...aiArgs._internal,
|
|
141
|
-
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
let activeModel = aiArgs.model;
|
|
146
|
-
return {
|
|
147
|
-
args: aiArgs,
|
|
148
|
-
order: order ?? 0,
|
|
149
|
-
stepOrder: stepOrder ?? 0,
|
|
150
|
-
userId,
|
|
151
|
-
promptMessageId,
|
|
152
|
-
getSavedMessages: () => savedMessages,
|
|
153
|
-
updateModel: (model) => {
|
|
154
|
-
if (model) {
|
|
155
|
-
activeModel = model;
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
fail,
|
|
159
|
-
save: async (toSave, createPendingMessage) => {
|
|
160
|
-
if (threadId && promptMessageId && saveOutput) {
|
|
161
|
-
const metadata = {
|
|
162
|
-
// TODO: get up to date one when user selects mid-generation
|
|
163
|
-
model: getModelName(activeModel),
|
|
164
|
-
provider: getProviderName(activeModel),
|
|
165
|
-
};
|
|
166
|
-
const serialized = "object" in toSave
|
|
167
|
-
? await serializeObjectResult(ctx, this.component, toSave.object, metadata)
|
|
168
|
-
: await serializeNewMessagesInStep(ctx, this.component, toSave.step, metadata);
|
|
169
|
-
const embeddings = await this.generateEmbeddings(ctx, { userId, threadId }, serialized.messages.map((m) => m.message));
|
|
170
|
-
if (createPendingMessage) {
|
|
171
|
-
serialized.messages.push({
|
|
172
|
-
message: { role: "assistant", content: [] },
|
|
173
|
-
status: "pending",
|
|
174
|
-
});
|
|
175
|
-
embeddings?.vectors.push(null);
|
|
176
|
-
}
|
|
177
|
-
const saved = await ctx.runMutation(this.component.messages.addMessages, {
|
|
178
|
-
userId,
|
|
179
|
-
threadId,
|
|
180
|
-
agentName: this.options.name,
|
|
181
|
-
promptMessageId,
|
|
182
|
-
pendingMessageId,
|
|
183
|
-
messages: serialized.messages,
|
|
184
|
-
embeddings,
|
|
185
|
-
failPendingSteps: false,
|
|
186
|
-
});
|
|
187
|
-
const lastMessage = saved.messages.at(-1);
|
|
188
|
-
if (createPendingMessage) {
|
|
189
|
-
if (lastMessage.status === "failed") {
|
|
190
|
-
pendingMessageId = undefined;
|
|
191
|
-
savedMessages.push(...saved.messages);
|
|
192
|
-
await fail(lastMessage.error ??
|
|
193
|
-
"Aborting - the pending message was marked as failed");
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
pendingMessageId = lastMessage._id;
|
|
197
|
-
savedMessages.push(...saved.messages.slice(0, -1));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
pendingMessageId = undefined;
|
|
202
|
-
savedMessages.push(...saved.messages);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
206
|
-
if (this.options.rawRequestResponseHandler) {
|
|
207
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
208
|
-
userId,
|
|
209
|
-
threadId,
|
|
210
|
-
agentName: this.options.name,
|
|
211
|
-
request: output.request,
|
|
212
|
-
response: output.response,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (opts.usageHandler && output.usage) {
|
|
216
|
-
await opts.usageHandler(ctx, {
|
|
217
|
-
userId,
|
|
218
|
-
threadId,
|
|
219
|
-
agentName: this.options.name,
|
|
220
|
-
model: getModelName(activeModel),
|
|
221
|
-
provider: getProviderName(activeModel),
|
|
222
|
-
usage: output.usage,
|
|
223
|
-
providerMetadata: output.providerMetadata,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
};
|
|
86
|
+
stopWhen: (args.stopWhen ?? this.options.stopWhen),
|
|
87
|
+
}, {
|
|
88
|
+
...this.options,
|
|
89
|
+
...options,
|
|
90
|
+
agentName: this.options.name,
|
|
91
|
+
agentForToolCtx: this,
|
|
92
|
+
});
|
|
228
93
|
}
|
|
229
94
|
/**
|
|
230
95
|
* This behaves like {@link generateText} from the "ai" package except that
|
|
@@ -321,9 +186,6 @@ export class Agent {
|
|
|
321
186
|
await streamer?.fail(errorToString(error.error));
|
|
322
187
|
return streamTextArgs.onError?.(error);
|
|
323
188
|
},
|
|
324
|
-
// onFinish: async (event) => {
|
|
325
|
-
// return streamTextArgs.onFinish?.(event);
|
|
326
|
-
// },
|
|
327
189
|
prepareStep: async (options) => {
|
|
328
190
|
const result = await streamTextArgs.prepareStep?.(options);
|
|
329
191
|
if (result) {
|
|
@@ -343,17 +205,17 @@ export class Agent {
|
|
|
343
205
|
return args.onStepFinish?.(step);
|
|
344
206
|
},
|
|
345
207
|
});
|
|
208
|
+
if ((typeof options?.saveStreamDeltas === "object" &&
|
|
209
|
+
!options.saveStreamDeltas.returnImmediately) ||
|
|
210
|
+
options?.saveStreamDeltas === true) {
|
|
211
|
+
await result.consumeStream();
|
|
212
|
+
}
|
|
346
213
|
const metadata = {
|
|
347
214
|
promptMessageId,
|
|
348
215
|
order,
|
|
349
216
|
savedMessages: call.getSavedMessages(),
|
|
350
217
|
messageId: promptMessageId,
|
|
351
218
|
};
|
|
352
|
-
if ((typeof options?.saveStreamDeltas === "object" &&
|
|
353
|
-
!options.saveStreamDeltas.returnImmediately) ||
|
|
354
|
-
options?.saveStreamDeltas === true) {
|
|
355
|
-
await result.consumeStream();
|
|
356
|
-
}
|
|
357
219
|
return Object.assign(result, metadata);
|
|
358
220
|
}
|
|
359
221
|
/**
|
|
@@ -549,7 +411,9 @@ export class Agent {
|
|
|
549
411
|
assert("runAction" in ctx);
|
|
550
412
|
assert(this.options.textEmbeddingModel, "A textEmbeddingModel is required to be set on the Agent that you're doing vector search with");
|
|
551
413
|
return {
|
|
552
|
-
embedding: (await
|
|
414
|
+
embedding: (await embedMany(ctx, {
|
|
415
|
+
...this.options,
|
|
416
|
+
agentName: this.options.name,
|
|
553
417
|
userId: args.userId,
|
|
554
418
|
threadId: args.threadId,
|
|
555
419
|
values: [text],
|
|
@@ -584,40 +448,8 @@ export class Agent {
|
|
|
584
448
|
* @param messages The messages to get the embeddings for.
|
|
585
449
|
* @returns The embeddings for the messages.
|
|
586
450
|
*/
|
|
587
|
-
async generateEmbeddings(ctx,
|
|
588
|
-
|
|
589
|
-
return undefined;
|
|
590
|
-
}
|
|
591
|
-
let embeddings;
|
|
592
|
-
const messageTexts = messages.map((m) => !isTool(m) && extractText(m));
|
|
593
|
-
// Find the indexes of the messages that have text.
|
|
594
|
-
const textIndexes = messageTexts
|
|
595
|
-
.map((t, i) => (t ? i : undefined))
|
|
596
|
-
.filter((i) => i !== undefined);
|
|
597
|
-
if (textIndexes.length === 0) {
|
|
598
|
-
return undefined;
|
|
599
|
-
}
|
|
600
|
-
const values = messageTexts
|
|
601
|
-
.map((t) => t && t.trim().slice(0, MAX_EMBEDDING_TEXT_LENGTH))
|
|
602
|
-
.filter((t) => !!t);
|
|
603
|
-
// Then embed those messages.
|
|
604
|
-
const textEmbeddings = await this.doEmbed(ctx, {
|
|
605
|
-
userId,
|
|
606
|
-
threadId,
|
|
607
|
-
values,
|
|
608
|
-
});
|
|
609
|
-
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
610
|
-
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
611
|
-
textIndexes.forEach((i, j) => {
|
|
612
|
-
embeddingsOrNull[i] = textEmbeddings.embeddings[j];
|
|
613
|
-
});
|
|
614
|
-
if (textEmbeddings.embeddings.length > 0) {
|
|
615
|
-
const dimension = textEmbeddings.embeddings[0].length;
|
|
616
|
-
validateVectorDimension(dimension);
|
|
617
|
-
const model = getModelName(this.options.textEmbeddingModel);
|
|
618
|
-
embeddings = { vectors: embeddingsOrNull, dimension, model };
|
|
619
|
-
}
|
|
620
|
-
return embeddings;
|
|
451
|
+
async generateEmbeddings(ctx, args, messages) {
|
|
452
|
+
return embedMessages(ctx, { ...args, ...this.options, agentName: this.options.name }, messages);
|
|
621
453
|
}
|
|
622
454
|
/**
|
|
623
455
|
* Generate embeddings for a set of messages, and save them to the database.
|
|
@@ -636,9 +468,6 @@ export class Agent {
|
|
|
636
468
|
.filter((id) => !messages.some((m) => m?._id === id))
|
|
637
469
|
.join(", "));
|
|
638
470
|
}
|
|
639
|
-
await this._generateAndSaveEmbeddings(ctx, messages);
|
|
640
|
-
}
|
|
641
|
-
async _generateAndSaveEmbeddings(ctx, messages) {
|
|
642
471
|
if (messages.some((m) => !m.message)) {
|
|
643
472
|
throw new Error("Some messages don't have a message: " +
|
|
644
473
|
messages
|
|
@@ -646,34 +475,17 @@ export class Agent {
|
|
|
646
475
|
.map((m) => m._id)
|
|
647
476
|
.join(", "));
|
|
648
477
|
}
|
|
649
|
-
const
|
|
650
|
-
if (
|
|
651
|
-
|
|
478
|
+
const { textEmbeddingModel } = this.options;
|
|
479
|
+
if (!textEmbeddingModel) {
|
|
480
|
+
throw new Error("No embeddings were generated for the messages. You must pass a textEmbeddingModel to the agent constructor.");
|
|
652
481
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
throw new Error("No embeddings were generated for these messages: " +
|
|
662
|
-
messagesMissingEmbeddings.map((m) => m._id).join(", "));
|
|
663
|
-
}
|
|
664
|
-
await ctx.runMutation(this.component.vector.index.insertBatch, {
|
|
665
|
-
vectorDimension: embeddings.dimension,
|
|
666
|
-
vectors: messagesMissingEmbeddings
|
|
667
|
-
.map((m, i) => ({
|
|
668
|
-
messageId: m._id,
|
|
669
|
-
model: embeddings.model,
|
|
670
|
-
table: "messages",
|
|
671
|
-
userId: m.userId,
|
|
672
|
-
threadId: m.threadId,
|
|
673
|
-
vector: embeddings.vectors[i],
|
|
674
|
-
}))
|
|
675
|
-
.filter((v) => v.vector !== null),
|
|
676
|
-
});
|
|
482
|
+
await generateAndSaveEmbeddings(ctx, this.component, {
|
|
483
|
+
...this.options,
|
|
484
|
+
agentName: this.options.name,
|
|
485
|
+
threadId: messages[0].threadId,
|
|
486
|
+
userId: messages[0].userId,
|
|
487
|
+
textEmbeddingModel,
|
|
488
|
+
}, messages);
|
|
677
489
|
}
|
|
678
490
|
/**
|
|
679
491
|
* Explicitly save a "step" created by the AI SDK.
|
|
@@ -848,143 +660,6 @@ export class Agent {
|
|
|
848
660
|
limit: args.pageSize,
|
|
849
661
|
});
|
|
850
662
|
}
|
|
851
|
-
async _saveMessagesAndFetchContext(ctx, { userId: argsUserId, threadId, contextOptions, storageOptions, ...args }) {
|
|
852
|
-
// If only a promptMessageId is provided, this will be empty.
|
|
853
|
-
const messages = args.messages ?? [];
|
|
854
|
-
const promptArray = !args.prompt
|
|
855
|
-
? []
|
|
856
|
-
: Array.isArray(args.prompt)
|
|
857
|
-
? args.prompt.map((p) => deserializeMessage(p))
|
|
858
|
-
: [{ role: "user", content: args.prompt }];
|
|
859
|
-
const userId = argsUserId ??
|
|
860
|
-
(threadId &&
|
|
861
|
-
(await ctx.runQuery(this.component.threads.getThread, { threadId }))
|
|
862
|
-
?.userId) ??
|
|
863
|
-
undefined;
|
|
864
|
-
// If only a messageId is provided, this will add that message to the end.
|
|
865
|
-
const contextMessages = await this.fetchContextMessages(ctx, {
|
|
866
|
-
userId,
|
|
867
|
-
threadId,
|
|
868
|
-
upToAndIncludingMessageId: args.promptMessageId,
|
|
869
|
-
messages,
|
|
870
|
-
contextOptions,
|
|
871
|
-
});
|
|
872
|
-
// If it was a promptMessageId, pop it off context messages
|
|
873
|
-
// and add to the end of messages.
|
|
874
|
-
const promptMessageIndex = args.promptMessageId
|
|
875
|
-
? contextMessages.findIndex((m) => m._id === args.promptMessageId)
|
|
876
|
-
: -1;
|
|
877
|
-
const promptMessage = promptMessageIndex !== -1
|
|
878
|
-
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
879
|
-
: undefined;
|
|
880
|
-
let promptMessageId = promptMessage?._id;
|
|
881
|
-
let order = promptMessage?.order;
|
|
882
|
-
let stepOrder = promptMessage?.stepOrder;
|
|
883
|
-
let savedMessages = undefined;
|
|
884
|
-
let pendingMessageId = undefined;
|
|
885
|
-
if (threadId && storageOptions?.saveMessages !== "none") {
|
|
886
|
-
let saved;
|
|
887
|
-
if (messages.length + promptArray.length &&
|
|
888
|
-
// If it was a promptMessageId, we don't want to save it again.
|
|
889
|
-
(!args.promptMessageId || storageOptions?.saveMessages === "all")) {
|
|
890
|
-
const saveAll = storageOptions?.saveMessages === "all";
|
|
891
|
-
const coreMessages = [
|
|
892
|
-
...messages,
|
|
893
|
-
...promptArray,
|
|
894
|
-
];
|
|
895
|
-
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
896
|
-
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
897
|
-
saved = await this.saveMessages(ctx, {
|
|
898
|
-
threadId,
|
|
899
|
-
userId,
|
|
900
|
-
messages: [...toSave, { role: "assistant", content: [] }],
|
|
901
|
-
metadata: [...metadata, { status: "pending" }],
|
|
902
|
-
// TODO: sanity check
|
|
903
|
-
failPendingSteps: !!args.promptMessageId,
|
|
904
|
-
});
|
|
905
|
-
promptMessageId = saved.messages.at(-2)._id;
|
|
906
|
-
}
|
|
907
|
-
else {
|
|
908
|
-
saved = await this.saveMessages(ctx, {
|
|
909
|
-
threadId,
|
|
910
|
-
userId,
|
|
911
|
-
messages: [{ role: "assistant", content: [] }],
|
|
912
|
-
metadata: [{ status: "pending" }],
|
|
913
|
-
failPendingSteps: !!args.promptMessageId,
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
pendingMessageId = saved.messages.at(-1)._id;
|
|
917
|
-
order = saved.messages.at(-1).order;
|
|
918
|
-
stepOrder = saved.messages.at(-1).stepOrder;
|
|
919
|
-
// Don't return the pending message
|
|
920
|
-
savedMessages = saved.messages.slice(0, -1);
|
|
921
|
-
}
|
|
922
|
-
if (promptMessage?.message) {
|
|
923
|
-
if (!args.prompt) {
|
|
924
|
-
// If they override the prompt, we skip the existing prompt message.
|
|
925
|
-
messages.push(promptMessage.message);
|
|
926
|
-
}
|
|
927
|
-
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
928
|
-
// embeddings yet. This can happen if the message was saved in a mutation
|
|
929
|
-
// where the LLM is not available.
|
|
930
|
-
if (!promptMessage.embeddingId && this.options.textEmbeddingModel) {
|
|
931
|
-
await this._generateAndSaveEmbeddings(ctx, [promptMessage]);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
const prePrompt = contextMessages.map((m) => m.message).filter((m) => !!m);
|
|
935
|
-
let existingResponses = [];
|
|
936
|
-
if (promptMessageIndex !== -1) {
|
|
937
|
-
// pull any messages that already responded to the prompt off
|
|
938
|
-
// and add them after the prompt
|
|
939
|
-
existingResponses = prePrompt.splice(promptMessageIndex);
|
|
940
|
-
}
|
|
941
|
-
let processedMessages = [
|
|
942
|
-
...prePrompt,
|
|
943
|
-
...messages,
|
|
944
|
-
...promptArray,
|
|
945
|
-
...existingResponses,
|
|
946
|
-
].map((m) => deserializeMessage(m));
|
|
947
|
-
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
948
|
-
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
949
|
-
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
950
|
-
}
|
|
951
|
-
return {
|
|
952
|
-
messages: processedMessages,
|
|
953
|
-
userId,
|
|
954
|
-
promptMessageId,
|
|
955
|
-
pendingMessageId,
|
|
956
|
-
savedMessages,
|
|
957
|
-
order,
|
|
958
|
-
stepOrder,
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
async doEmbed(ctx, options) {
|
|
962
|
-
const embeddingModel = this.options.textEmbeddingModel;
|
|
963
|
-
assert(embeddingModel, "a textEmbeddingModel is required to be set on the Agent that you're doing vector search with");
|
|
964
|
-
const result = await embedMany({
|
|
965
|
-
...this.options.callSettings,
|
|
966
|
-
model: embeddingModel,
|
|
967
|
-
values: options.values,
|
|
968
|
-
abortSignal: options.abortSignal,
|
|
969
|
-
headers: options.headers,
|
|
970
|
-
});
|
|
971
|
-
if (this.options.usageHandler && result.usage) {
|
|
972
|
-
await this.options.usageHandler(ctx, {
|
|
973
|
-
userId: options.userId,
|
|
974
|
-
threadId: options.threadId,
|
|
975
|
-
agentName: this.options.name,
|
|
976
|
-
model: getModelName(embeddingModel),
|
|
977
|
-
provider: getProviderName(embeddingModel),
|
|
978
|
-
providerMetadata: undefined,
|
|
979
|
-
usage: {
|
|
980
|
-
inputTokens: result.usage.tokens,
|
|
981
|
-
outputTokens: 0,
|
|
982
|
-
totalTokens: result.usage.tokens,
|
|
983
|
-
},
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
return { embeddings: result.embeddings };
|
|
987
|
-
}
|
|
988
663
|
/**
|
|
989
664
|
* WORKFLOW UTILITIES
|
|
990
665
|
*/
|
|
@@ -1030,22 +705,22 @@ export class Agent {
|
|
|
1030
705
|
args: vTextArgs,
|
|
1031
706
|
handler: async (ctx_, args) => {
|
|
1032
707
|
const stream = args.stream === true ? spec?.stream || true : (spec?.stream ?? false);
|
|
1033
|
-
const
|
|
708
|
+
const { userId, threadId, prompt, messages, maxSteps, ...rest } = args;
|
|
709
|
+
const targetArgs = { userId, threadId };
|
|
1034
710
|
const llmArgs = {
|
|
1035
|
-
stopWhen: spec?.stopWhen
|
|
711
|
+
stopWhen: spec?.stopWhen,
|
|
1036
712
|
...overrides,
|
|
1037
|
-
...omit(
|
|
1038
|
-
messages:
|
|
1039
|
-
prompt: Array.isArray(
|
|
1040
|
-
?
|
|
1041
|
-
:
|
|
713
|
+
...omit(rest, ["storageOptions", "contextOptions", "stream"]),
|
|
714
|
+
messages: messages?.map(deserializeMessage),
|
|
715
|
+
prompt: Array.isArray(prompt)
|
|
716
|
+
? prompt.map(deserializeMessage)
|
|
717
|
+
: prompt,
|
|
1042
718
|
toolChoice: args.toolChoice,
|
|
1043
719
|
};
|
|
1044
|
-
if (
|
|
1045
|
-
llmArgs.stopWhen = stepCountIs(
|
|
720
|
+
if (maxSteps) {
|
|
721
|
+
llmArgs.stopWhen = stepCountIs(maxSteps);
|
|
1046
722
|
}
|
|
1047
723
|
const opts = {
|
|
1048
|
-
...this.options,
|
|
1049
724
|
...pick(spec, ["contextOptions", "storageOptions"]),
|
|
1050
725
|
...pick(args, ["contextOptions", "storageOptions"]),
|
|
1051
726
|
saveStreamDeltas: stream,
|
|
@@ -1062,7 +737,7 @@ export class Agent {
|
|
|
1062
737
|
promptMessageId: result.promptMessageId,
|
|
1063
738
|
order: result.order,
|
|
1064
739
|
finishReason: await result.finishReason,
|
|
1065
|
-
warnings: result.warnings,
|
|
740
|
+
warnings: await result.warnings,
|
|
1066
741
|
savedMessageIds: result.savedMessages?.map((m) => m._id) ?? [],
|
|
1067
742
|
};
|
|
1068
743
|
}
|