@convex-dev/agent 0.1.18-alpha.0 → 0.5.0-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/dist/client/createTool.d.ts +30 -9
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +24 -9
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +176 -201
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +12 -19
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.js +4 -4
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +166 -63
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +75 -45
- package/dist/client/index.js.map +1 -1
- package/dist/client/search.d.ts +27 -6
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js.map +1 -1
- package/dist/client/streaming.d.ts +3 -2
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/types.d.ts +27 -41
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +436 -73
- package/dist/component/messages.d.ts +246 -43
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +2 -2
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +1453 -152
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +2 -2
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +180 -6
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/mapping.d.ts +12 -14
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +187 -47
- package/dist/mapping.js.map +1 -1
- package/dist/react/deltas.d.ts +0 -3
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +140 -44
- package/dist/react/deltas.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +2 -1
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/toUIMessages.d.ts +5 -4
- package/dist/react/toUIMessages.d.ts.map +1 -1
- package/dist/react/toUIMessages.js +103 -40
- package/dist/react/toUIMessages.js.map +1 -1
- package/dist/validators.d.ts +1798 -259
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +79 -15
- package/dist/validators.js.map +1 -1
- package/package.json +2 -2
- package/src/client/createTool.ts +68 -37
- package/src/client/definePlaygroundAPI.ts +25 -27
- package/src/client/files.ts +4 -4
- package/src/client/index.test.ts +14 -12
- package/src/client/index.ts +134 -94
- package/src/client/search.ts +3 -2
- package/src/client/streaming.ts +4 -3
- package/src/client/types.ts +34 -70
- package/src/component/_generated/api.d.ts +436 -73
- package/src/component/messages.ts +2 -2
- package/src/component/schema.ts +2 -2
- package/src/mapping.ts +228 -75
- package/src/react/deltas.ts +165 -52
- package/src/react/optimisticallySendMessage.ts +4 -1
- package/src/react/toUIMessages.test.ts +154 -36
- package/src/react/toUIMessages.ts +136 -57
- package/src/validators.test.ts +2 -99
- package/src/validators.ts +95 -17
package/src/client/index.test.ts
CHANGED
|
@@ -19,9 +19,12 @@ import type {
|
|
|
19
19
|
} from "convex/server";
|
|
20
20
|
import { v } from "convex/values";
|
|
21
21
|
import { defineSchema } from "convex/server";
|
|
22
|
-
import {
|
|
23
|
-
import type {
|
|
24
|
-
|
|
22
|
+
import { MockLanguageModelV2 } from "ai/test";
|
|
23
|
+
import type {
|
|
24
|
+
LanguageModelV2,
|
|
25
|
+
LanguageModelV2StreamPart,
|
|
26
|
+
} from "@ai-sdk/provider";
|
|
27
|
+
import { simulateReadableStream, stepCountIs } from "ai";
|
|
25
28
|
import { components, initConvexTest } from "./setup.test.js";
|
|
26
29
|
import { z } from "zod";
|
|
27
30
|
|
|
@@ -252,16 +255,16 @@ describe("filterOutOrphanedToolMessages", () => {
|
|
|
252
255
|
});
|
|
253
256
|
});
|
|
254
257
|
|
|
255
|
-
function mockModel():
|
|
256
|
-
return new
|
|
258
|
+
function mockModel(): LanguageModelV2 {
|
|
259
|
+
return new MockLanguageModelV2({
|
|
257
260
|
provider: "mock",
|
|
258
261
|
modelId: "mock",
|
|
259
|
-
defaultObjectGenerationMode: "json",
|
|
260
262
|
// supportsStructuredOutputs: true,
|
|
261
263
|
doGenerate: async ({ prompt }) => ({
|
|
262
264
|
finishReason: "stop",
|
|
263
|
-
|
|
264
|
-
|
|
265
|
+
content: [{ type: "text", text: JSON.stringify({ prompt }) }],
|
|
266
|
+
warnings: [],
|
|
267
|
+
usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
|
|
265
268
|
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
266
269
|
text: JSON.stringify({ prompt }),
|
|
267
270
|
}),
|
|
@@ -277,10 +280,9 @@ function mockModel(): LanguageModelV1 {
|
|
|
277
280
|
{
|
|
278
281
|
type: "finish",
|
|
279
282
|
finishReason: "stop",
|
|
280
|
-
|
|
281
|
-
usage: { completionTokens: 10, promptTokens: 3 },
|
|
283
|
+
usage: { outputTokens: 10, inputTokens: 3, totalTokens: 13 },
|
|
282
284
|
},
|
|
283
|
-
] as
|
|
285
|
+
] as LanguageModelV2StreamPart[],
|
|
284
286
|
}),
|
|
285
287
|
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
286
288
|
}),
|
|
@@ -300,7 +302,7 @@ describe("Agent option variations and normal behavior", () => {
|
|
|
300
302
|
instructions: "Test instructions",
|
|
301
303
|
contextOptions: { recentMessages: 5 },
|
|
302
304
|
storageOptions: { saveMessages: "all" },
|
|
303
|
-
|
|
305
|
+
stopWhen: stepCountIs(2),
|
|
304
306
|
maxRetries: 1,
|
|
305
307
|
usageHandler: async () => {},
|
|
306
308
|
rawRequestResponseHandler: async () => {},
|
package/src/client/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LanguageModelV2, EmbeddingModelV2 } from "@ai-sdk/provider";
|
|
2
2
|
import type {
|
|
3
3
|
AssistantContent,
|
|
4
|
-
|
|
4
|
+
ModelMessage,
|
|
5
5
|
DeepPartial,
|
|
6
6
|
FilePart,
|
|
7
7
|
GenerateObjectResult,
|
|
@@ -12,11 +12,15 @@ import type {
|
|
|
12
12
|
StreamTextResult,
|
|
13
13
|
ToolSet,
|
|
14
14
|
UserContent,
|
|
15
|
+
StopCondition,
|
|
16
|
+
Schema,
|
|
17
|
+
ToolChoice,
|
|
15
18
|
} from "ai";
|
|
16
19
|
import {
|
|
17
20
|
embedMany,
|
|
18
21
|
generateObject,
|
|
19
22
|
generateText,
|
|
23
|
+
stepCountIs,
|
|
20
24
|
streamObject,
|
|
21
25
|
streamText,
|
|
22
26
|
} from "ai";
|
|
@@ -38,15 +42,14 @@ import {
|
|
|
38
42
|
type VectorDimension,
|
|
39
43
|
} from "../component/vector/tables.js";
|
|
40
44
|
import {
|
|
41
|
-
type AIMessageWithoutId,
|
|
42
45
|
deserializeMessage,
|
|
43
|
-
promptOrMessagesToCoreMessages,
|
|
44
46
|
serializeMessage,
|
|
45
47
|
serializeNewMessagesInStep,
|
|
46
48
|
serializeObjectResult,
|
|
47
49
|
} from "../mapping.js";
|
|
48
50
|
import { extractText, isTool } from "../shared.js";
|
|
49
51
|
import {
|
|
52
|
+
type Message,
|
|
50
53
|
type MessageEmbeddings,
|
|
51
54
|
type MessageStatus,
|
|
52
55
|
type MessageWithMetadata,
|
|
@@ -57,7 +60,7 @@ import {
|
|
|
57
60
|
vSafeObjectArgs,
|
|
58
61
|
vTextArgs,
|
|
59
62
|
} from "../validators.js";
|
|
60
|
-
import { createTool, wrapTools } from "./createTool.js";
|
|
63
|
+
import { createTool, wrapTools, type ToolCtx } from "./createTool.js";
|
|
61
64
|
import { listMessages } from "./listMessages.js";
|
|
62
65
|
import { fetchContextMessages } from "./search.js";
|
|
63
66
|
import {
|
|
@@ -86,9 +89,15 @@ import type {
|
|
|
86
89
|
UsageHandler,
|
|
87
90
|
UserActionCtx,
|
|
88
91
|
} from "./types.js";
|
|
92
|
+
import type z from "zod";
|
|
89
93
|
|
|
94
|
+
export { stepCountIs } from "ai";
|
|
90
95
|
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
91
|
-
export {
|
|
96
|
+
export {
|
|
97
|
+
serializeDataOrUrl,
|
|
98
|
+
deserializeMessage,
|
|
99
|
+
serializeMessage,
|
|
100
|
+
} from "../mapping.js";
|
|
92
101
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
93
102
|
// a future version may put them all here or move these over there
|
|
94
103
|
export {
|
|
@@ -106,16 +115,12 @@ export {
|
|
|
106
115
|
} from "../validators.js";
|
|
107
116
|
export type { ToolCtx } from "./createTool.js";
|
|
108
117
|
export { getFile, storeFile } from "./files.js";
|
|
109
|
-
export { filterOutOrphanedToolMessages } from "./search.js";
|
|
110
|
-
export { abortStream, listStreams } from "./streaming.js";
|
|
111
118
|
export {
|
|
112
|
-
|
|
113
|
-
extractText,
|
|
119
|
+
filterOutOrphanedToolMessages,
|
|
114
120
|
fetchContextMessages,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
};
|
|
121
|
+
} from "./search.js";
|
|
122
|
+
export { abortStream, listStreams, syncStreams } from "./streaming.js";
|
|
123
|
+
export { createTool, extractText, isTool, listMessages };
|
|
119
124
|
export {
|
|
120
125
|
definePlaygroundAPI,
|
|
121
126
|
type PlaygroundAPI,
|
|
@@ -175,7 +180,7 @@ export class Agent<
|
|
|
175
180
|
* const myAgent = new Agent(components.agent, {
|
|
176
181
|
* chat: openai.chat("gpt-4o-mini"),
|
|
177
182
|
*/
|
|
178
|
-
chat:
|
|
183
|
+
chat: LanguageModelV2;
|
|
179
184
|
/**
|
|
180
185
|
* The model to use for text embeddings. Optional.
|
|
181
186
|
* If specified, it will use this for generating vector embeddings
|
|
@@ -186,7 +191,7 @@ export class Agent<
|
|
|
186
191
|
* const myAgent = new Agent(components.agent, {
|
|
187
192
|
* textEmbedding: openai.embedding("text-embedding-3-small")
|
|
188
193
|
*/
|
|
189
|
-
textEmbedding?:
|
|
194
|
+
textEmbedding?: EmbeddingModelV2<string>;
|
|
190
195
|
/**
|
|
191
196
|
* The default system prompt to put in each request.
|
|
192
197
|
* Override per-prompt by passing the "system" parameter.
|
|
@@ -212,9 +217,9 @@ export class Agent<
|
|
|
212
217
|
storageOptions?: StorageOptions;
|
|
213
218
|
/**
|
|
214
219
|
* When generating or streaming text with tools available, this
|
|
215
|
-
* determines
|
|
220
|
+
* determines when to stop. Defaults to stepCountIs(1).
|
|
216
221
|
*/
|
|
217
|
-
|
|
222
|
+
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
218
223
|
/**
|
|
219
224
|
* The maximum number of calls to make to an LLM in case it fails.
|
|
220
225
|
* This can be overridden at each generate/stream callsite.
|
|
@@ -471,16 +476,17 @@ export class Agent<
|
|
|
471
476
|
threadId,
|
|
472
477
|
messageId,
|
|
473
478
|
agent: this,
|
|
474
|
-
};
|
|
479
|
+
} satisfies ToolCtx;
|
|
480
|
+
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
475
481
|
const tools = wrapTools(
|
|
476
482
|
toolCtx,
|
|
477
483
|
args.tools ?? threadTools ?? this.options.tools,
|
|
478
|
-
) as
|
|
484
|
+
) as Tools;
|
|
479
485
|
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
480
486
|
try {
|
|
481
|
-
const result = (await generateText({
|
|
487
|
+
const result = (await generateText<Tools, OUTPUT, OUTPUT_PARTIAL>({
|
|
482
488
|
// Can be overridden
|
|
483
|
-
|
|
489
|
+
stopWhen: this.options.stopWhen as StopCondition<Tools> | undefined,
|
|
484
490
|
...aiArgs,
|
|
485
491
|
tools,
|
|
486
492
|
onStepFinish: async (step) => {
|
|
@@ -516,11 +522,7 @@ export class Agent<
|
|
|
516
522
|
}
|
|
517
523
|
return args.onStepFinish?.(step);
|
|
518
524
|
},
|
|
519
|
-
})) as GenerateTextResult<
|
|
520
|
-
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
521
|
-
OUTPUT
|
|
522
|
-
> &
|
|
523
|
-
GenerationOutputMetadata;
|
|
525
|
+
})) as GenerateTextResult<Tools, OUTPUT> & GenerationOutputMetadata;
|
|
524
526
|
result.messageId = messageId;
|
|
525
527
|
result.order = order;
|
|
526
528
|
return result;
|
|
@@ -624,8 +626,8 @@ export class Agent<
|
|
|
624
626
|
: undefined;
|
|
625
627
|
|
|
626
628
|
const result = streamText({
|
|
627
|
-
//
|
|
628
|
-
|
|
629
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
630
|
+
stopWhen: this.options.stopWhen as any, // Can be overridden
|
|
629
631
|
...aiArgs,
|
|
630
632
|
tools,
|
|
631
633
|
abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
|
|
@@ -787,7 +789,7 @@ export class Agent<
|
|
|
787
789
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
788
790
|
* to a thread (and optionally userId).
|
|
789
791
|
*/
|
|
790
|
-
async streamObject<T>(
|
|
792
|
+
async streamObject<T extends z.Schema | Schema>(
|
|
791
793
|
ctx: ActionCtx,
|
|
792
794
|
{
|
|
793
795
|
userId: argsUserId,
|
|
@@ -819,7 +821,8 @@ export class Agent<
|
|
|
819
821
|
});
|
|
820
822
|
const { args: aiArgs, messageId, order, userId } = context;
|
|
821
823
|
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
822
|
-
|
|
824
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
825
|
+
const stream = streamObject<any>({
|
|
823
826
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
824
827
|
...(aiArgs as any),
|
|
825
828
|
onError: async (error) => {
|
|
@@ -840,9 +843,6 @@ export class Agent<
|
|
|
840
843
|
request: await stream.request,
|
|
841
844
|
response: result.response,
|
|
842
845
|
providerMetadata: result.providerMetadata,
|
|
843
|
-
experimental_providerMetadata:
|
|
844
|
-
result.experimental_providerMetadata,
|
|
845
|
-
logprobs: undefined,
|
|
846
846
|
toJsonResponse: stream.toTextStreamResponse,
|
|
847
847
|
},
|
|
848
848
|
model: aiArgs.model.modelId,
|
|
@@ -1037,7 +1037,7 @@ export class Agent<
|
|
|
1037
1037
|
args: {
|
|
1038
1038
|
userId: string | undefined;
|
|
1039
1039
|
threadId: string | undefined;
|
|
1040
|
-
messages:
|
|
1040
|
+
messages: (ModelMessage | Message)[];
|
|
1041
1041
|
/**
|
|
1042
1042
|
* If provided, it will search for messages up to and including this message.
|
|
1043
1043
|
* Note: if this is far in the past, text and vector search results may be more
|
|
@@ -1125,7 +1125,7 @@ export class Agent<
|
|
|
1125
1125
|
userId: string | undefined;
|
|
1126
1126
|
threadId: string | undefined;
|
|
1127
1127
|
},
|
|
1128
|
-
messages:
|
|
1128
|
+
messages: (ModelMessage | Message)[],
|
|
1129
1129
|
) {
|
|
1130
1130
|
if (!this.options.textEmbedding) {
|
|
1131
1131
|
return undefined;
|
|
@@ -1151,7 +1151,6 @@ export class Agent<
|
|
|
1151
1151
|
threadId,
|
|
1152
1152
|
values: messageTexts as string[],
|
|
1153
1153
|
});
|
|
1154
|
-
// TODO: record usage of embeddings
|
|
1155
1154
|
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
1156
1155
|
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
1157
1156
|
textIndexes.forEach((i, j) => {
|
|
@@ -1218,7 +1217,7 @@ export class Agent<
|
|
|
1218
1217
|
userId: messagesMissingEmbeddings[0]!.userId,
|
|
1219
1218
|
threadId: messagesMissingEmbeddings[0]!.threadId,
|
|
1220
1219
|
},
|
|
1221
|
-
messagesMissingEmbeddings.map((m) => m!.message!),
|
|
1220
|
+
messagesMissingEmbeddings.map((m) => deserializeMessage(m!.message!)),
|
|
1222
1221
|
);
|
|
1223
1222
|
if (!embeddings) {
|
|
1224
1223
|
if (!this.options.textEmbedding) {
|
|
@@ -1291,7 +1290,7 @@ export class Agent<
|
|
|
1291
1290
|
const embeddings = await this.generateEmbeddings(
|
|
1292
1291
|
ctx,
|
|
1293
1292
|
{ userId: args.userId, threadId: args.threadId },
|
|
1294
|
-
messages.map((m) => m.message),
|
|
1293
|
+
messages.map((m) => deserializeMessage(m.message)),
|
|
1295
1294
|
);
|
|
1296
1295
|
const saved = await ctx.runMutation(this.component.messages.addMessages, {
|
|
1297
1296
|
userId: args.userId,
|
|
@@ -1336,7 +1335,7 @@ export class Agent<
|
|
|
1336
1335
|
const embeddings = await this.generateEmbeddings(
|
|
1337
1336
|
ctx,
|
|
1338
1337
|
{ userId: args.userId, threadId: args.threadId },
|
|
1339
|
-
messages.map((m) => m.message),
|
|
1338
|
+
messages.map((m) => deserializeMessage(m.message)),
|
|
1340
1339
|
);
|
|
1341
1340
|
|
|
1342
1341
|
await ctx.runMutation(this.component.messages.addMessages, {
|
|
@@ -1392,7 +1391,7 @@ export class Agent<
|
|
|
1392
1391
|
messageId: string;
|
|
1393
1392
|
patch: {
|
|
1394
1393
|
/** The message to replace the existing message. */
|
|
1395
|
-
message:
|
|
1394
|
+
message: (ModelMessage & { id?: string }) | Message;
|
|
1396
1395
|
/** The status to set on the message. */
|
|
1397
1396
|
status: "success" | "error";
|
|
1398
1397
|
/** The error message to set on the message. */
|
|
@@ -1507,8 +1506,8 @@ export class Agent<
|
|
|
1507
1506
|
endOrder: number;
|
|
1508
1507
|
endStepOrder?: number;
|
|
1509
1508
|
},
|
|
1510
|
-
): Promise<
|
|
1511
|
-
|
|
1509
|
+
): Promise<{ isDone: boolean; lastOrder?: number; lastStepOrder?: number }> {
|
|
1510
|
+
return ctx.runMutation(this.component.messages.deleteByOrder, {
|
|
1512
1511
|
threadId: args.threadId,
|
|
1513
1512
|
startOrder: args.startOrder,
|
|
1514
1513
|
startStepOrder: args.startStepOrder,
|
|
@@ -1560,11 +1559,11 @@ export class Agent<
|
|
|
1560
1559
|
async _saveMessagesAndFetchContext<
|
|
1561
1560
|
T extends {
|
|
1562
1561
|
id?: string;
|
|
1563
|
-
prompt?: string;
|
|
1564
|
-
messages?:
|
|
1562
|
+
prompt?: string | (ModelMessage | Message)[];
|
|
1563
|
+
messages?: (ModelMessage | Message)[];
|
|
1565
1564
|
system?: string;
|
|
1566
1565
|
promptMessageId?: string;
|
|
1567
|
-
model?:
|
|
1566
|
+
model?: LanguageModelV2;
|
|
1568
1567
|
maxRetries?: number;
|
|
1569
1568
|
},
|
|
1570
1569
|
>(
|
|
@@ -1580,14 +1579,19 @@ export class Agent<
|
|
|
1580
1579
|
threadId: string | undefined;
|
|
1581
1580
|
} & Options,
|
|
1582
1581
|
): Promise<{
|
|
1583
|
-
args: T & { model:
|
|
1582
|
+
args: T & { model: LanguageModelV2 };
|
|
1584
1583
|
userId: string | undefined;
|
|
1585
1584
|
messageId: string | undefined;
|
|
1586
1585
|
order: number | undefined;
|
|
1587
1586
|
stepOrder: number | undefined;
|
|
1588
1587
|
}> {
|
|
1589
1588
|
// If only a promptMessageId is provided, this will be empty.
|
|
1590
|
-
const messages =
|
|
1589
|
+
const messages = args.messages ?? [];
|
|
1590
|
+
const prompt: (ModelMessage | Message)[] = !args.prompt
|
|
1591
|
+
? []
|
|
1592
|
+
: Array.isArray(args.prompt)
|
|
1593
|
+
? args.prompt
|
|
1594
|
+
: [{ role: "user", content: args.prompt }];
|
|
1591
1595
|
const userId =
|
|
1592
1596
|
argsUserId ??
|
|
1593
1597
|
(threadId &&
|
|
@@ -1603,45 +1607,46 @@ export class Agent<
|
|
|
1603
1607
|
});
|
|
1604
1608
|
// If it was a promptMessageId, pop it off context messages
|
|
1605
1609
|
// and add to the end of messages.
|
|
1606
|
-
|
|
1610
|
+
const promptMessageIndex = args.promptMessageId
|
|
1611
|
+
? contextMessages.findIndex((m) => m._id === args.promptMessageId)
|
|
1612
|
+
: -1;
|
|
1607
1613
|
const promptMessage =
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
? contextMessages.pop()
|
|
1614
|
+
promptMessageIndex !== -1
|
|
1615
|
+
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
1611
1616
|
: undefined;
|
|
1612
|
-
|
|
1613
|
-
// If they specify both a promptMessageId and a prompt, we prefer
|
|
1614
|
-
// the prompt to stand in for the promptMessageId message.
|
|
1615
|
-
promptMessage.message = { role: "user", content: args.prompt };
|
|
1616
|
-
}
|
|
1617
|
+
|
|
1617
1618
|
let messageId = promptMessage?._id;
|
|
1618
1619
|
let order = promptMessage?.order;
|
|
1619
1620
|
let stepOrder = promptMessage?.stepOrder;
|
|
1620
1621
|
if (
|
|
1621
1622
|
threadId &&
|
|
1622
|
-
messages.length &&
|
|
1623
|
+
messages.length + prompt.length &&
|
|
1623
1624
|
storageOptions?.saveMessages !== "none" &&
|
|
1624
1625
|
// If it was a promptMessageId, we don't want to save it again.
|
|
1625
1626
|
(!args.promptMessageId || storageOptions?.saveMessages === "all")
|
|
1626
1627
|
) {
|
|
1627
1628
|
const saveAll = storageOptions?.saveMessages === "all";
|
|
1628
|
-
const coreMessages =
|
|
1629
|
+
const coreMessages = [...messages, ...prompt];
|
|
1630
|
+
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
1631
|
+
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
1632
|
+
metadata[metadata.length - 1] = { id: args.id };
|
|
1629
1633
|
const saved = await this.saveMessages(ctx, {
|
|
1630
1634
|
threadId,
|
|
1631
1635
|
userId,
|
|
1632
|
-
messages:
|
|
1633
|
-
metadata
|
|
1634
|
-
i === coreMessages.length - 1 ? { id: args.id } : {},
|
|
1635
|
-
),
|
|
1636
|
+
messages: toSave,
|
|
1637
|
+
metadata,
|
|
1636
1638
|
failPendingSteps: true,
|
|
1637
1639
|
});
|
|
1638
1640
|
messageId = saved.lastMessageId;
|
|
1639
1641
|
order = saved.messages.at(-1)?.order;
|
|
1640
1642
|
stepOrder = saved.messages.at(-1)?.stepOrder;
|
|
1641
1643
|
}
|
|
1644
|
+
|
|
1642
1645
|
if (promptMessage?.message) {
|
|
1643
|
-
|
|
1644
|
-
|
|
1646
|
+
if (!args.prompt) {
|
|
1647
|
+
// If they override the prompt, we skip the existing prompt message.
|
|
1648
|
+
messages.push(deserializeMessage(promptMessage.message));
|
|
1649
|
+
}
|
|
1645
1650
|
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
1646
1651
|
// embeddings yet. This can happen if the message was saved in a mutation
|
|
1647
1652
|
// where the LLM is not available.
|
|
@@ -1650,10 +1655,28 @@ export class Agent<
|
|
|
1650
1655
|
}
|
|
1651
1656
|
}
|
|
1652
1657
|
|
|
1658
|
+
const prePrompt = contextMessages.map((m) =>
|
|
1659
|
+
deserializeMessage(m.message!),
|
|
1660
|
+
);
|
|
1661
|
+
let existingResponses: ModelMessage[] = [];
|
|
1662
|
+
if (promptMessageIndex !== -1) {
|
|
1663
|
+
// pull any messages that already responded to the prompt off
|
|
1664
|
+
// and add them after the prompt
|
|
1665
|
+
existingResponses = prePrompt.splice(promptMessageIndex);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1653
1668
|
let processedMessages = [
|
|
1654
|
-
...
|
|
1669
|
+
...prePrompt,
|
|
1655
1670
|
...messages,
|
|
1671
|
+
...prompt,
|
|
1672
|
+
...existingResponses,
|
|
1656
1673
|
];
|
|
1674
|
+
if (promptMessageIndex === -1) {
|
|
1675
|
+
processedMessages.push(...prompt);
|
|
1676
|
+
} else {
|
|
1677
|
+
// We add the prompt where the prompt message was
|
|
1678
|
+
processedMessages.splice(promptMessageIndex, 0, ...prompt);
|
|
1679
|
+
}
|
|
1657
1680
|
|
|
1658
1681
|
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
1659
1682
|
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
@@ -1668,7 +1691,7 @@ export class Agent<
|
|
|
1668
1691
|
model: model ?? this.options.chat,
|
|
1669
1692
|
system: args.system ?? this.options.instructions,
|
|
1670
1693
|
messages: processedMessages,
|
|
1671
|
-
} as T & { model:
|
|
1694
|
+
} as T & { model: LanguageModelV2 },
|
|
1672
1695
|
userId,
|
|
1673
1696
|
messageId,
|
|
1674
1697
|
order,
|
|
@@ -1707,8 +1730,8 @@ export class Agent<
|
|
|
1707
1730
|
provider: embeddingModel.provider,
|
|
1708
1731
|
providerMetadata: undefined,
|
|
1709
1732
|
usage: {
|
|
1710
|
-
|
|
1711
|
-
|
|
1733
|
+
inputTokens: result.usage.tokens,
|
|
1734
|
+
outputTokens: 0,
|
|
1712
1735
|
totalTokens: result.usage.tokens,
|
|
1713
1736
|
},
|
|
1714
1737
|
});
|
|
@@ -1722,11 +1745,11 @@ export class Agent<
|
|
|
1722
1745
|
* able to access localhost URLs.
|
|
1723
1746
|
*/
|
|
1724
1747
|
private async _inlineMessagesFiles(
|
|
1725
|
-
messages:
|
|
1726
|
-
): Promise<
|
|
1748
|
+
messages: (ModelMessage | Message)[],
|
|
1749
|
+
): Promise<(ModelMessage | Message)[]> {
|
|
1727
1750
|
// Process each message to convert localhost URLs to base64
|
|
1728
1751
|
return Promise.all(
|
|
1729
|
-
messages.map(async (message): Promise<
|
|
1752
|
+
messages.map(async (message): Promise<ModelMessage | Message> => {
|
|
1730
1753
|
if (
|
|
1731
1754
|
(message.role !== "user" && message.role !== "assistant") ||
|
|
1732
1755
|
typeof message.content === "string" ||
|
|
@@ -1845,15 +1868,15 @@ export class Agent<
|
|
|
1845
1868
|
* Create an action out of this agent so you can call it from workflows or other actions
|
|
1846
1869
|
* without a wrapping function.
|
|
1847
1870
|
* @param spec Configuration for the agent acting as an action, including
|
|
1848
|
-
* {@link ContextOptions}, {@link StorageOptions}, and
|
|
1871
|
+
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
|
|
1849
1872
|
*/
|
|
1850
1873
|
asTextAction<DataModel extends GenericDataModel>(
|
|
1851
1874
|
spec?: {
|
|
1852
1875
|
/**
|
|
1853
|
-
*
|
|
1854
|
-
* Defaults to the {@link Agent.
|
|
1876
|
+
* When to stop generating text.
|
|
1877
|
+
* Defaults to the {@link Agent["options"].stopWhen} option.
|
|
1855
1878
|
*/
|
|
1856
|
-
|
|
1879
|
+
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
1857
1880
|
/**
|
|
1858
1881
|
* The {@link ContextOptions} to use for fetching contextual messages and
|
|
1859
1882
|
* saving input/output messages.
|
|
@@ -1902,7 +1925,7 @@ export class Agent<
|
|
|
1902
1925
|
}
|
|
1903
1926
|
: { customCtx?: never }),
|
|
1904
1927
|
) {
|
|
1905
|
-
const
|
|
1928
|
+
const stopWhen = spec?.stopWhen ?? this.options.stopWhen;
|
|
1906
1929
|
return internalActionGeneric({
|
|
1907
1930
|
args: vTextArgs,
|
|
1908
1931
|
handler: async (ctx_, args) => {
|
|
@@ -1910,9 +1933,17 @@ export class Agent<
|
|
|
1910
1933
|
args.stream === true ? spec?.stream || true : spec?.stream ?? false;
|
|
1911
1934
|
const targetArgs = { userId: args.userId, threadId: args.threadId };
|
|
1912
1935
|
const llmArgs = {
|
|
1913
|
-
|
|
1936
|
+
stopWhen,
|
|
1914
1937
|
...omit(args, ["storageOptions", "contextOptions"]),
|
|
1915
|
-
|
|
1938
|
+
messages: args.messages?.map(deserializeMessage),
|
|
1939
|
+
prompt: Array.isArray(args.prompt)
|
|
1940
|
+
? args.prompt.map(deserializeMessage)
|
|
1941
|
+
: args.prompt,
|
|
1942
|
+
toolChoice: args.toolChoice as ToolChoice<AgentTools>,
|
|
1943
|
+
} satisfies StreamingTextArgs<AgentTools>;
|
|
1944
|
+
if (args.maxSteps) {
|
|
1945
|
+
llmArgs.stopWhen = stepCountIs(args.maxSteps);
|
|
1946
|
+
}
|
|
1916
1947
|
const opts = {
|
|
1917
1948
|
...this.options,
|
|
1918
1949
|
...(spec && pick(spec, ["contextOptions", "storageOptions"])),
|
|
@@ -1925,7 +1956,13 @@ export class Agent<
|
|
|
1925
1956
|
: ctx_
|
|
1926
1957
|
) as UserActionCtx & CustomCtx;
|
|
1927
1958
|
if (stream) {
|
|
1928
|
-
|
|
1959
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1960
|
+
const result = await this.streamText<any>(
|
|
1961
|
+
ctx,
|
|
1962
|
+
targetArgs,
|
|
1963
|
+
llmArgs,
|
|
1964
|
+
opts,
|
|
1965
|
+
);
|
|
1929
1966
|
await result.consumeStream();
|
|
1930
1967
|
return {
|
|
1931
1968
|
text: await result.text,
|
|
@@ -1935,7 +1972,13 @@ export class Agent<
|
|
|
1935
1972
|
warnings: result.warnings,
|
|
1936
1973
|
};
|
|
1937
1974
|
} else {
|
|
1938
|
-
|
|
1975
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1976
|
+
const res = await this.generateText<any>(
|
|
1977
|
+
ctx,
|
|
1978
|
+
targetArgs,
|
|
1979
|
+
llmArgs,
|
|
1980
|
+
opts,
|
|
1981
|
+
);
|
|
1939
1982
|
return {
|
|
1940
1983
|
text: res.text,
|
|
1941
1984
|
messageId: res.messageId,
|
|
@@ -1952,28 +1995,27 @@ export class Agent<
|
|
|
1952
1995
|
* it from workflows or other actions without a wrapping function.
|
|
1953
1996
|
* @param spec Configuration for the agent acting as an action, including
|
|
1954
1997
|
* the normal parameters to {@link generateObject}, plus {@link ContextOptions}
|
|
1955
|
-
* and
|
|
1998
|
+
* and stopWhen.
|
|
1956
1999
|
*/
|
|
1957
2000
|
asObjectAction<T>(
|
|
1958
|
-
spec: OurObjectArgs<T
|
|
2001
|
+
spec: OurObjectArgs<T>,
|
|
1959
2002
|
options?: {
|
|
1960
2003
|
contextOptions?: ContextOptions;
|
|
1961
2004
|
storageOptions?: StorageOptions;
|
|
1962
2005
|
},
|
|
1963
2006
|
) {
|
|
1964
|
-
const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
|
|
1965
2007
|
return internalActionGeneric({
|
|
1966
2008
|
args: vSafeObjectArgs,
|
|
1967
2009
|
handler: async (ctx, args) => {
|
|
1968
|
-
const
|
|
2010
|
+
const { userId, threadId, ...rest } = args;
|
|
2011
|
+
const overrides = pick(rest, ["contextOptions", "storageOptions"]);
|
|
1969
2012
|
const value = await this.generateObject(
|
|
1970
2013
|
ctx,
|
|
1971
|
-
{ userId
|
|
2014
|
+
{ userId, threadId },
|
|
1972
2015
|
{
|
|
1973
2016
|
...spec,
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
} as unknown as OurObjectArgs<unknown>,
|
|
2017
|
+
...omit(rest, ["contextOptions", "storageOptions"]),
|
|
2018
|
+
} as OurObjectArgs<unknown>,
|
|
1977
2019
|
{ ...this.options, ...options, ...overrides },
|
|
1978
2020
|
);
|
|
1979
2021
|
return {
|
|
@@ -2020,7 +2062,7 @@ export class Agent<
|
|
|
2020
2062
|
handler: async (ctx, args) => {
|
|
2021
2063
|
const { lastMessageId, messages } = await this.saveMessages(ctx, {
|
|
2022
2064
|
...args,
|
|
2023
|
-
messages: args.messages.map((m) => m.message),
|
|
2065
|
+
messages: args.messages.map((m) => deserializeMessage(m.message)),
|
|
2024
2066
|
metadata: args.messages.map(({ message: _, ...m }) => m),
|
|
2025
2067
|
});
|
|
2026
2068
|
return {
|
|
@@ -2032,8 +2074,6 @@ export class Agent<
|
|
|
2032
2074
|
}
|
|
2033
2075
|
}
|
|
2034
2076
|
|
|
2035
|
-
type CoreMessageMaybeWithId = CoreMessage & { id?: string | undefined };
|
|
2036
|
-
|
|
2037
2077
|
/**
|
|
2038
2078
|
* Create a thread to store messages with an Agent.
|
|
2039
2079
|
* @param ctx The context from a mutation or action.
|
|
@@ -2092,7 +2132,7 @@ type SaveMessagesArgs = {
|
|
|
2092
2132
|
/**
|
|
2093
2133
|
* The messages to save.
|
|
2094
2134
|
*/
|
|
2095
|
-
messages:
|
|
2135
|
+
messages: ((ModelMessage & { id?: string | undefined }) | Message)[];
|
|
2096
2136
|
/**
|
|
2097
2137
|
* Metadata to save with the messages. Each element corresponds to the
|
|
2098
2138
|
* message at the same index.
|
|
@@ -2186,7 +2226,7 @@ type SaveMessageArgs = {
|
|
|
2186
2226
|
/**
|
|
2187
2227
|
* The message to save.
|
|
2188
2228
|
*/
|
|
2189
|
-
message:
|
|
2229
|
+
message: ModelMessage | Message;
|
|
2190
2230
|
}
|
|
2191
2231
|
| {
|
|
2192
2232
|
/*
|
package/src/client/search.ts
CHANGED
|
@@ -5,13 +5,14 @@ import type {
|
|
|
5
5
|
RunQueryCtx,
|
|
6
6
|
} from "./types.js";
|
|
7
7
|
import type { MessageDoc } from "../component/schema.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { ModelMessage } from "ai";
|
|
9
9
|
import { assert } from "convex-helpers";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_MESSAGE_RANGE,
|
|
12
12
|
DEFAULT_RECENT_MESSAGES,
|
|
13
13
|
extractText,
|
|
14
14
|
} from "../shared.js";
|
|
15
|
+
import type { Message } from "../validators.js";
|
|
15
16
|
|
|
16
17
|
const DEFAULT_VECTOR_SCORE_THRESHOLD = 0.0;
|
|
17
18
|
|
|
@@ -34,7 +35,7 @@ export async function fetchContextMessages(
|
|
|
34
35
|
args: {
|
|
35
36
|
userId: string | undefined;
|
|
36
37
|
threadId: string | undefined;
|
|
37
|
-
messages:
|
|
38
|
+
messages: (ModelMessage | Message)[];
|
|
38
39
|
/**
|
|
39
40
|
* If provided, it will search for messages up to and including this message.
|
|
40
41
|
* Note: if this is far in the past, text and vector search results may be more
|