@convex-dev/agent 0.2.0-alpha.2 → 0.2.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 +4 -6
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +0 -1
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +28 -28
- package/dist/client/files.d.ts +9 -2
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +61 -6
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +232 -242
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +375 -383
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +91 -373
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/messages.js +7 -1
- package/dist/client/messages.js.map +1 -1
- package/dist/client/search.d.ts +15 -15
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +3 -3
- package/dist/client/search.js.map +1 -1
- package/dist/client/streaming.d.ts +30 -22
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +36 -40
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/types.d.ts +148 -61
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +47 -357
- package/dist/component/messages.d.ts +157 -150
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +124 -70
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +1147 -2687
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +5 -4
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +29 -323
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +104 -60
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +4 -4
- package/dist/component/users.d.ts +3 -3
- package/dist/mapping.d.ts +4 -1
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +17 -1
- package/dist/mapping.js.map +1 -1
- package/dist/parts.d.ts +3 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +34 -0
- package/dist/parts.js.map +1 -0
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/deltas.d.ts +3 -2
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +19 -79
- package/dist/react/deltas.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +31 -11
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +2 -8
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +1 -1
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/shared.d.ts +3 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +109 -1590
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +4 -81
- package/dist/validators.js.map +1 -1
- package/package.json +3 -3
- package/src/client/createTool.ts +5 -10
- package/src/client/files.ts +89 -11
- package/src/client/index.test.ts +25 -35
- package/src/client/index.ts +686 -652
- package/src/client/messages.ts +16 -2
- package/src/client/search.ts +10 -7
- package/src/client/streaming.ts +62 -57
- package/src/client/types.ts +227 -97
- package/src/component/_generated/api.d.ts +47 -357
- package/src/component/messages.ts +157 -96
- package/src/component/schema.ts +4 -4
- package/src/component/streams.ts +180 -69
- package/src/mapping.ts +23 -2
- package/src/parts.ts +39 -0
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/deltas.test.ts +38 -42
- package/src/react/deltas.ts +24 -86
- package/src/react/index.ts +35 -13
- package/src/react/optimisticallySendMessage.ts +2 -8
- package/src/react/useSmoothText.ts +14 -15
- package/src/shared.ts +3 -3
- package/src/validators.test.ts +0 -17
- package/src/validators.ts +8 -101
package/src/client/index.ts
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import type { LanguageModelV2 } from "@ai-sdk/provider";
|
|
2
1
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
FlexibleSchema,
|
|
3
|
+
IdGenerator,
|
|
4
|
+
InferSchema,
|
|
5
|
+
ProviderOptions,
|
|
6
|
+
} from "@ai-sdk/provider-utils";
|
|
7
|
+
import type {
|
|
8
|
+
CallSettings,
|
|
9
|
+
EmbeddingModel,
|
|
7
10
|
GenerateObjectResult,
|
|
8
11
|
GenerateTextResult,
|
|
9
|
-
|
|
12
|
+
LanguageModel,
|
|
13
|
+
ModelMessage,
|
|
10
14
|
StepResult,
|
|
11
|
-
StreamObjectResult,
|
|
12
|
-
StreamTextResult,
|
|
13
|
-
ToolSet,
|
|
14
|
-
UserContent,
|
|
15
15
|
StopCondition,
|
|
16
|
-
|
|
16
|
+
StreamTextResult,
|
|
17
17
|
ToolChoice,
|
|
18
|
-
|
|
19
|
-
EmbeddingModel,
|
|
20
|
-
CallSettings,
|
|
18
|
+
ToolSet,
|
|
21
19
|
} from "ai";
|
|
22
20
|
import {
|
|
23
21
|
embedMany,
|
|
@@ -31,13 +29,12 @@ import { assert, omit, pick } from "convex-helpers";
|
|
|
31
29
|
import {
|
|
32
30
|
internalActionGeneric,
|
|
33
31
|
internalMutationGeneric,
|
|
34
|
-
type GenericActionCtx,
|
|
35
32
|
type GenericDataModel,
|
|
36
33
|
type PaginationOptions,
|
|
37
34
|
type PaginationResult,
|
|
38
35
|
type WithoutSystemFields,
|
|
39
36
|
} from "convex/server";
|
|
40
|
-
import { v } from "convex/values";
|
|
37
|
+
import { convexToJson, v, type Value } from "convex/values";
|
|
41
38
|
import type { MessageDoc, ThreadDoc } from "../component/schema.js";
|
|
42
39
|
import type { threadFieldsSupportingPatch } from "../component/threads.js";
|
|
43
40
|
import {
|
|
@@ -52,16 +49,16 @@ import {
|
|
|
52
49
|
} from "../mapping.js";
|
|
53
50
|
import { extractText, isTool } from "../shared.js";
|
|
54
51
|
import {
|
|
52
|
+
vMessageEmbeddings,
|
|
53
|
+
vMessageWithMetadata,
|
|
54
|
+
vSafeObjectArgs,
|
|
55
|
+
vTextArgs,
|
|
55
56
|
type Message,
|
|
56
57
|
type MessageStatus,
|
|
57
58
|
type MessageWithMetadata,
|
|
58
59
|
type ProviderMetadata,
|
|
59
60
|
type StreamArgs,
|
|
60
61
|
type Usage,
|
|
61
|
-
vMessageEmbeddings,
|
|
62
|
-
vMessageWithMetadata,
|
|
63
|
-
vSafeObjectArgs,
|
|
64
|
-
vTextArgs,
|
|
65
62
|
} from "../validators.js";
|
|
66
63
|
import { createTool, wrapTools, type ToolCtx } from "./createTool.js";
|
|
67
64
|
import {
|
|
@@ -70,7 +67,6 @@ import {
|
|
|
70
67
|
type SaveMessageArgs,
|
|
71
68
|
type SaveMessagesArgs,
|
|
72
69
|
} from "./messages.js";
|
|
73
|
-
import { createThread, getThreadMetadata } from "./threads.js";
|
|
74
70
|
import {
|
|
75
71
|
fetchContextMessages,
|
|
76
72
|
getModelName,
|
|
@@ -79,36 +75,42 @@ import {
|
|
|
79
75
|
import {
|
|
80
76
|
DeltaStreamer,
|
|
81
77
|
mergeTransforms,
|
|
82
|
-
type StreamingOptions,
|
|
83
78
|
syncStreams,
|
|
79
|
+
type StreamingOptions,
|
|
84
80
|
} from "./streaming.js";
|
|
81
|
+
import { createThread, getThreadMetadata } from "./threads.js";
|
|
85
82
|
import type {
|
|
86
83
|
ActionCtx,
|
|
87
84
|
AgentComponent,
|
|
88
85
|
ContextOptions,
|
|
86
|
+
DefaultObjectSchema,
|
|
89
87
|
GenerationOutputMetadata,
|
|
88
|
+
MaybeCustomCtx,
|
|
89
|
+
GenerateObjectArgs,
|
|
90
|
+
ObjectMode,
|
|
91
|
+
ObjectSchema,
|
|
90
92
|
Options,
|
|
91
|
-
OurObjectArgs,
|
|
92
|
-
OurStreamObjectArgs,
|
|
93
93
|
RawRequestResponseHandler,
|
|
94
94
|
RunActionCtx,
|
|
95
95
|
RunMutationCtx,
|
|
96
96
|
RunQueryCtx,
|
|
97
97
|
StorageOptions,
|
|
98
98
|
StreamingTextArgs,
|
|
99
|
+
StreamObjectArgs,
|
|
99
100
|
SyncStreamsReturnValue,
|
|
100
101
|
TextArgs,
|
|
101
102
|
Thread,
|
|
102
103
|
UsageHandler,
|
|
103
104
|
UserActionCtx,
|
|
104
105
|
} from "./types.js";
|
|
105
|
-
import
|
|
106
|
+
import { inlineMessagesFiles } from "./files.js";
|
|
107
|
+
import type { DataModel } from "../component/_generated/dataModel.js";
|
|
106
108
|
|
|
107
109
|
export { stepCountIs } from "ai";
|
|
108
110
|
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
109
111
|
export {
|
|
110
|
-
serializeDataOrUrl,
|
|
111
112
|
deserializeMessage,
|
|
113
|
+
serializeDataOrUrl,
|
|
112
114
|
serializeMessage,
|
|
113
115
|
} from "../mapping.js";
|
|
114
116
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
@@ -127,12 +129,12 @@ export {
|
|
|
127
129
|
vUserMessage,
|
|
128
130
|
} from "../validators.js";
|
|
129
131
|
export type { ToolCtx } from "./createTool.js";
|
|
130
|
-
export { getFile, storeFile } from "./files.js";
|
|
131
132
|
export {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
definePlaygroundAPI,
|
|
134
|
+
type AgentsFn,
|
|
135
|
+
type PlaygroundAPI,
|
|
136
|
+
} from "./definePlaygroundAPI.js";
|
|
137
|
+
export { getFile, storeFile } from "./files.js";
|
|
136
138
|
export {
|
|
137
139
|
listMessages,
|
|
138
140
|
saveMessage,
|
|
@@ -140,13 +142,18 @@ export {
|
|
|
140
142
|
type SaveMessageArgs,
|
|
141
143
|
type SaveMessagesArgs,
|
|
142
144
|
} from "./messages.js";
|
|
143
|
-
export { createThread, getThreadMetadata } from "./threads.js";
|
|
144
|
-
export { createTool, extractText, isTool };
|
|
145
145
|
export {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} from "./
|
|
146
|
+
fetchContextMessages,
|
|
147
|
+
filterOutOrphanedToolMessages,
|
|
148
|
+
} from "./search.js";
|
|
149
|
+
export { abortStream, listStreams, syncStreams } from "./streaming.js";
|
|
150
|
+
export {
|
|
151
|
+
createThread,
|
|
152
|
+
getThreadMetadata,
|
|
153
|
+
updateThreadMetadata,
|
|
154
|
+
searchThreadTitles,
|
|
155
|
+
} from "./threads.js";
|
|
156
|
+
export { createTool, extractText, isTool };
|
|
150
157
|
export type {
|
|
151
158
|
AgentComponent,
|
|
152
159
|
ContextOptions,
|
|
@@ -162,6 +169,61 @@ export type {
|
|
|
162
169
|
UsageHandler,
|
|
163
170
|
};
|
|
164
171
|
|
|
172
|
+
export type Config = {
|
|
173
|
+
/**
|
|
174
|
+
* The LLM model to use for generating / streaming text and objects.
|
|
175
|
+
* e.g.
|
|
176
|
+
* import { openai } from "@ai-sdk/openai"
|
|
177
|
+
* const myAgent = new Agent(components.agent, {
|
|
178
|
+
* languageModel: openai.chat("gpt-4o-mini"),
|
|
179
|
+
*/
|
|
180
|
+
languageModel?: LanguageModel;
|
|
181
|
+
/**
|
|
182
|
+
* The model to use for text embeddings. Optional.
|
|
183
|
+
* If specified, it will use this for generating vector embeddings
|
|
184
|
+
* of chats, and can opt-in to doing vector search for automatic context
|
|
185
|
+
* on generateText, etc.
|
|
186
|
+
* e.g.
|
|
187
|
+
* import { openai } from "@ai-sdk/openai"
|
|
188
|
+
* const myAgent = new Agent(components.agent, {
|
|
189
|
+
* ...
|
|
190
|
+
* textEmbeddingModel: openai.embedding("text-embedding-3-small")
|
|
191
|
+
*/
|
|
192
|
+
textEmbeddingModel?: EmbeddingModel<string>;
|
|
193
|
+
/**
|
|
194
|
+
* Options to determine what messages are included as context in message
|
|
195
|
+
* generation. To disable any messages automatically being added, pass:
|
|
196
|
+
* { recentMessages: 0 }
|
|
197
|
+
*/
|
|
198
|
+
contextOptions?: ContextOptions;
|
|
199
|
+
/**
|
|
200
|
+
* Determines whether messages are automatically stored when passed as
|
|
201
|
+
* arguments or generated.
|
|
202
|
+
*/
|
|
203
|
+
storageOptions?: StorageOptions;
|
|
204
|
+
/**
|
|
205
|
+
* The usage handler to use for this agent.
|
|
206
|
+
*/
|
|
207
|
+
usageHandler?: UsageHandler;
|
|
208
|
+
/**
|
|
209
|
+
* Called for each LLM request/response, so you can do things like
|
|
210
|
+
* log the raw request body or response headers to a table, or logs.
|
|
211
|
+
*/
|
|
212
|
+
rawRequestResponseHandler?: RawRequestResponseHandler;
|
|
213
|
+
/**
|
|
214
|
+
* Default provider options to pass for the LLM calls.
|
|
215
|
+
* This can be overridden at each generate/stream callsite on a per-field
|
|
216
|
+
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
217
|
+
*/
|
|
218
|
+
providerOptions?: ProviderOptions;
|
|
219
|
+
/**
|
|
220
|
+
* The default settings to use for the LLM calls.
|
|
221
|
+
* This can be overridden at each generate/stream callsite on a per-field
|
|
222
|
+
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
223
|
+
*/
|
|
224
|
+
callSettings?: CallSettings;
|
|
225
|
+
};
|
|
226
|
+
|
|
165
227
|
export class Agent<
|
|
166
228
|
/**
|
|
167
229
|
* You can require that all `ctx` args to generateText & streamText
|
|
@@ -188,31 +250,20 @@ export class Agent<
|
|
|
188
250
|
> {
|
|
189
251
|
constructor(
|
|
190
252
|
public component: AgentComponent,
|
|
191
|
-
public options: {
|
|
253
|
+
public options: Config & {
|
|
192
254
|
/**
|
|
193
255
|
* The name for the agent. This will be attributed on each message
|
|
194
256
|
* created by this agent.
|
|
195
257
|
*/
|
|
196
|
-
name
|
|
258
|
+
name: string;
|
|
197
259
|
/**
|
|
198
260
|
* The LLM model to use for generating / streaming text and objects.
|
|
199
261
|
* e.g.
|
|
200
262
|
* import { openai } from "@ai-sdk/openai"
|
|
201
263
|
* const myAgent = new Agent(components.agent, {
|
|
202
|
-
*
|
|
203
|
-
*/
|
|
204
|
-
chat: LanguageModel;
|
|
205
|
-
/**
|
|
206
|
-
* The model to use for text embeddings. Optional.
|
|
207
|
-
* If specified, it will use this for generating vector embeddings
|
|
208
|
-
* of chats, and can opt-in to doing vector search for automatic context
|
|
209
|
-
* on generateText, etc.
|
|
210
|
-
* e.g.
|
|
211
|
-
* import { openai } from "@ai-sdk/openai"
|
|
212
|
-
* const myAgent = new Agent(components.agent, {
|
|
213
|
-
* textEmbedding: openai.embedding("text-embedding-3-small")
|
|
264
|
+
* languageModel: openai.chat("gpt-4o-mini"),
|
|
214
265
|
*/
|
|
215
|
-
|
|
266
|
+
languageModel: LanguageModel;
|
|
216
267
|
/**
|
|
217
268
|
* The default system prompt to put in each request.
|
|
218
269
|
* Override per-prompt by passing the "system" parameter.
|
|
@@ -225,37 +276,15 @@ export class Agent<
|
|
|
225
276
|
* (import { createTool } from "@convex-dev/agent")
|
|
226
277
|
*/
|
|
227
278
|
tools?: AgentTools;
|
|
228
|
-
/**
|
|
229
|
-
* Options to determine what messages are included as context in message
|
|
230
|
-
* generation. To disable any messages automatically being added, pass:
|
|
231
|
-
* { recentMessages: 0 }
|
|
232
|
-
*/
|
|
233
|
-
contextOptions?: ContextOptions;
|
|
234
|
-
/**
|
|
235
|
-
* Determines whether messages are automatically stored when passed as
|
|
236
|
-
* arguments or generated.
|
|
237
|
-
*/
|
|
238
|
-
storageOptions?: StorageOptions;
|
|
239
279
|
/**
|
|
240
280
|
* When generating or streaming text with tools available, this
|
|
241
281
|
* determines when to stop. Defaults to stepCountIs(1).
|
|
242
282
|
*/
|
|
243
283
|
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
244
284
|
/**
|
|
245
|
-
*
|
|
246
|
-
* This can be overridden at each generate/stream callsite on a per-field
|
|
247
|
-
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
248
|
-
*/
|
|
249
|
-
callSettings?: CallSettings;
|
|
250
|
-
/**
|
|
251
|
-
* The usage handler to use for this agent.
|
|
252
|
-
*/
|
|
253
|
-
usageHandler?: UsageHandler;
|
|
254
|
-
/**
|
|
255
|
-
* Called for each LLM request/response, so you can do things like
|
|
256
|
-
* log the raw request body or response headers to a table, or logs.
|
|
285
|
+
* @deprecated Use `languageEmbeddingModel` instead.
|
|
257
286
|
*/
|
|
258
|
-
|
|
287
|
+
chat?: LanguageModel;
|
|
259
288
|
},
|
|
260
289
|
) {}
|
|
261
290
|
|
|
@@ -269,7 +298,7 @@ export class Agent<
|
|
|
269
298
|
* @param args The thread metadata.
|
|
270
299
|
* @returns The threadId of the new thread and the thread object.
|
|
271
300
|
*/
|
|
272
|
-
async createThread
|
|
301
|
+
async createThread(
|
|
273
302
|
ctx: RunActionCtx & CustomCtx,
|
|
274
303
|
args?: {
|
|
275
304
|
/**
|
|
@@ -285,21 +314,8 @@ export class Agent<
|
|
|
285
314
|
* The summary of the thread. Not currently used for anything.
|
|
286
315
|
*/
|
|
287
316
|
summary?: string;
|
|
288
|
-
/**
|
|
289
|
-
* The usage handler to use for this thread. Overrides any handler
|
|
290
|
-
* set in the agent constructor.
|
|
291
|
-
*/
|
|
292
|
-
usageHandler?: UsageHandler;
|
|
293
|
-
/**
|
|
294
|
-
* The tools to use for this thread.
|
|
295
|
-
* Overrides any tools passed in the agent constructor.
|
|
296
|
-
*/
|
|
297
|
-
tools?: ThreadTools;
|
|
298
317
|
},
|
|
299
|
-
): Promise<{
|
|
300
|
-
threadId: string;
|
|
301
|
-
thread: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
|
|
302
|
-
}>;
|
|
318
|
+
): Promise<{ threadId: string; thread: Thread<AgentTools> }>;
|
|
303
319
|
/**
|
|
304
320
|
* Start a new thread with the agent. This will have a fresh history, though if
|
|
305
321
|
* you pass in a userId you can have it search across other threads for relevant
|
|
@@ -309,7 +325,7 @@ export class Agent<
|
|
|
309
325
|
* @param args The thread metadata.
|
|
310
326
|
* @returns The threadId of the new thread.
|
|
311
327
|
*/
|
|
312
|
-
async createThread
|
|
328
|
+
async createThread(
|
|
313
329
|
ctx: RunMutationCtx,
|
|
314
330
|
args?: {
|
|
315
331
|
/**
|
|
@@ -325,31 +341,12 @@ export class Agent<
|
|
|
325
341
|
* The summary of the thread. Not currently used for anything.
|
|
326
342
|
*/
|
|
327
343
|
summary?: string;
|
|
328
|
-
/**
|
|
329
|
-
* The usage handler to use for this thread. Overrides any handler
|
|
330
|
-
* set in the agent constructor.
|
|
331
|
-
*/
|
|
332
|
-
usageHandler?: UsageHandler;
|
|
333
|
-
/**
|
|
334
|
-
* The tools to use for this thread.
|
|
335
|
-
* Overrides any tools passed in the agent constructor.
|
|
336
|
-
*/
|
|
337
|
-
tools?: ThreadTools;
|
|
338
344
|
},
|
|
339
345
|
): Promise<{ threadId: string }>;
|
|
340
|
-
async createThread
|
|
346
|
+
async createThread(
|
|
341
347
|
ctx: (ActionCtx & CustomCtx) | RunMutationCtx,
|
|
342
|
-
args?: {
|
|
343
|
-
|
|
344
|
-
title?: string;
|
|
345
|
-
summary?: string;
|
|
346
|
-
usageHandler?: UsageHandler;
|
|
347
|
-
tools?: ThreadTools;
|
|
348
|
-
},
|
|
349
|
-
): Promise<{
|
|
350
|
-
threadId: string;
|
|
351
|
-
thread?: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
|
|
352
|
-
}> {
|
|
348
|
+
args?: { userId: string | null; title?: string; summary?: string },
|
|
349
|
+
): Promise<{ threadId: string; thread?: Thread<AgentTools> }> {
|
|
353
350
|
const threadId = await createThread(ctx, this.component, args);
|
|
354
351
|
if (!("runAction" in ctx) || "workflowId" in ctx) {
|
|
355
352
|
return { threadId };
|
|
@@ -357,8 +354,6 @@ export class Agent<
|
|
|
357
354
|
const { thread } = await this.continueThread(ctx, {
|
|
358
355
|
threadId,
|
|
359
356
|
userId: args?.userId,
|
|
360
|
-
usageHandler: args?.usageHandler,
|
|
361
|
-
tools: args?.tools,
|
|
362
357
|
});
|
|
363
358
|
return { threadId, thread };
|
|
364
359
|
}
|
|
@@ -371,7 +366,7 @@ export class Agent<
|
|
|
371
366
|
* @param { threadId, userId }: the thread and user to associate the messages with.
|
|
372
367
|
* @returns Functions bound to the userId and threadId on a `{thread}` object.
|
|
373
368
|
*/
|
|
374
|
-
async continueThread
|
|
369
|
+
async continueThread(
|
|
375
370
|
ctx: ActionCtx & CustomCtx,
|
|
376
371
|
args: {
|
|
377
372
|
/**
|
|
@@ -383,20 +378,8 @@ export class Agent<
|
|
|
383
378
|
* relevant messages from the same user as context for the LLM calls.
|
|
384
379
|
*/
|
|
385
380
|
userId?: string | null;
|
|
386
|
-
/**
|
|
387
|
-
* The usage handler to use for this thread. Overrides any handler
|
|
388
|
-
* set in the agent constructor.
|
|
389
|
-
*/
|
|
390
|
-
usageHandler?: UsageHandler;
|
|
391
|
-
/**
|
|
392
|
-
* The tools to use for this thread.
|
|
393
|
-
* Overrides any tools passed in the agent constructor.
|
|
394
|
-
*/
|
|
395
|
-
tools?: ThreadTools;
|
|
396
381
|
},
|
|
397
|
-
): Promise<{
|
|
398
|
-
thread: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
|
|
399
|
-
}> {
|
|
382
|
+
): Promise<{ thread: Thread<AgentTools> }> {
|
|
400
383
|
return {
|
|
401
384
|
thread: {
|
|
402
385
|
threadId: args.threadId,
|
|
@@ -412,7 +395,250 @@ export class Agent<
|
|
|
412
395
|
streamText: this.streamText.bind(this, ctx, args),
|
|
413
396
|
generateObject: this.generateObject.bind(this, ctx, args),
|
|
414
397
|
streamObject: this.streamObject.bind(this, ctx, args),
|
|
415
|
-
} as Thread<
|
|
398
|
+
} as Thread<AgentTools>,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async start<TOOLS extends ToolSet | undefined, T>(
|
|
403
|
+
ctx: ActionCtx & CustomCtx,
|
|
404
|
+
/**
|
|
405
|
+
* These are the arguments you'll pass to the LLM call such as
|
|
406
|
+
* `generateText` or `streamText`. This function will look up the context
|
|
407
|
+
* and provide functions to save the steps, abort the generation, and more.
|
|
408
|
+
* The type of the arguments returned infers from the type of the arguments
|
|
409
|
+
* you pass here.
|
|
410
|
+
*/
|
|
411
|
+
args: T & {
|
|
412
|
+
/**
|
|
413
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
414
|
+
* instead of the prompt or messages.
|
|
415
|
+
* This is useful if you want to first save a user message, then use it as
|
|
416
|
+
* the prompt for the LLM call in another call.
|
|
417
|
+
*/
|
|
418
|
+
promptMessageId?: string;
|
|
419
|
+
/**
|
|
420
|
+
* The model to use for the LLM calls. This will override the model specified
|
|
421
|
+
* in the Agent constructor.
|
|
422
|
+
*/
|
|
423
|
+
model?: LanguageModel;
|
|
424
|
+
/**
|
|
425
|
+
* The tools to use for the tool calls. This will override tools specified
|
|
426
|
+
* in the Agent constructor or createThread / continueThread.
|
|
427
|
+
*/
|
|
428
|
+
tools?: TOOLS;
|
|
429
|
+
/**
|
|
430
|
+
* The single prompt message to use for the LLM call. This will be the
|
|
431
|
+
* last message in the context. If it's a string, it will be a user role.
|
|
432
|
+
*/
|
|
433
|
+
prompt?: string | (ModelMessage | Message)[];
|
|
434
|
+
/**
|
|
435
|
+
* If provided alongside prompt, the ordering will be:
|
|
436
|
+
* 1. system prompt
|
|
437
|
+
* 2. search context
|
|
438
|
+
* 3. recent messages
|
|
439
|
+
* 4. these messages
|
|
440
|
+
* 5. prompt messages, including those already on the same `order` as
|
|
441
|
+
* the promptMessageId message, if provided.
|
|
442
|
+
*/
|
|
443
|
+
messages?: (ModelMessage | Message)[];
|
|
444
|
+
/**
|
|
445
|
+
* This will be the first message in the context, and overrides the
|
|
446
|
+
* agent's instructions.
|
|
447
|
+
*/
|
|
448
|
+
system?: string;
|
|
449
|
+
/**
|
|
450
|
+
* The abort signal to be passed to the LLM call. If triggered, it will
|
|
451
|
+
* mark the pending message as failed. If the generation is asynchronously
|
|
452
|
+
* aborted, it will trigger this signal when detected.
|
|
453
|
+
*/
|
|
454
|
+
abortSignal?: AbortSignal;
|
|
455
|
+
// We optimistically override the generateId function to use the pending
|
|
456
|
+
// message id.
|
|
457
|
+
_internal?: { generateId?: IdGenerator };
|
|
458
|
+
},
|
|
459
|
+
options?: Options & { userId?: string | null; threadId?: string },
|
|
460
|
+
): Promise<{
|
|
461
|
+
args: T & {
|
|
462
|
+
system?: string;
|
|
463
|
+
model: LanguageModel;
|
|
464
|
+
messages: ModelMessage[];
|
|
465
|
+
// abortSignal?: AbortSignal;
|
|
466
|
+
tools?: TOOLS extends undefined ? AgentTools : TOOLS;
|
|
467
|
+
} & CallSettings;
|
|
468
|
+
order: number;
|
|
469
|
+
stepOrder: number;
|
|
470
|
+
userId: string | undefined;
|
|
471
|
+
promptMessageId: string | undefined;
|
|
472
|
+
updateModel: (model: LanguageModel | undefined) => void;
|
|
473
|
+
save: <TOOLS extends ToolSet>(
|
|
474
|
+
toSave:
|
|
475
|
+
| { step: StepResult<TOOLS> }
|
|
476
|
+
| { object: GenerateObjectResult<unknown> },
|
|
477
|
+
createPendingMessage?: boolean,
|
|
478
|
+
) => Promise<void>;
|
|
479
|
+
fail: (reason: string) => Promise<void>;
|
|
480
|
+
getSavedMessages: () => MessageDoc[];
|
|
481
|
+
}> {
|
|
482
|
+
const { threadId, ...opts } = { ...this.options, ...options };
|
|
483
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
484
|
+
userId: options?.userId,
|
|
485
|
+
threadId: options?.threadId,
|
|
486
|
+
...opts,
|
|
487
|
+
});
|
|
488
|
+
let pendingMessageId = context.pendingMessageId;
|
|
489
|
+
// TODO: extract pending message if one exists
|
|
490
|
+
const { args: aiArgs, promptMessageId, order, stepOrder, userId } = context;
|
|
491
|
+
const messages = context.savedMessages ?? [];
|
|
492
|
+
if (pendingMessageId) {
|
|
493
|
+
if (!aiArgs._internal?.generateId) {
|
|
494
|
+
aiArgs._internal = {
|
|
495
|
+
...aiArgs._internal,
|
|
496
|
+
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const toolCtx = {
|
|
501
|
+
...(ctx as UserActionCtx & CustomCtx),
|
|
502
|
+
userId,
|
|
503
|
+
threadId,
|
|
504
|
+
promptMessageId,
|
|
505
|
+
agent: this,
|
|
506
|
+
} satisfies ToolCtx;
|
|
507
|
+
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
508
|
+
const tools = wrapTools(toolCtx, args.tools ?? this.options.tools) as Tools;
|
|
509
|
+
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
510
|
+
const fail = async (reason: string) => {
|
|
511
|
+
if (threadId && promptMessageId) {
|
|
512
|
+
console.error("RollbackMessage", promptMessageId, reason);
|
|
513
|
+
}
|
|
514
|
+
if (pendingMessageId) {
|
|
515
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
516
|
+
messageId: pendingMessageId,
|
|
517
|
+
result: { status: "failed", error: reason },
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
let activeModel = aiArgs.model;
|
|
522
|
+
if (aiArgs.abortSignal) {
|
|
523
|
+
const abortSignal = aiArgs.abortSignal;
|
|
524
|
+
aiArgs.abortSignal.addEventListener(
|
|
525
|
+
"abort",
|
|
526
|
+
async () => {
|
|
527
|
+
await fail(abortSignal.reason ?? "Aborted");
|
|
528
|
+
},
|
|
529
|
+
{ once: true },
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
return {
|
|
533
|
+
args: {
|
|
534
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
535
|
+
stopWhen: (args as any).stopWhen ?? this.options.stopWhen,
|
|
536
|
+
...aiArgs,
|
|
537
|
+
tools,
|
|
538
|
+
// abortSignal: abortController.signal,
|
|
539
|
+
},
|
|
540
|
+
order: order ?? 0,
|
|
541
|
+
stepOrder: stepOrder ?? 0,
|
|
542
|
+
userId,
|
|
543
|
+
promptMessageId,
|
|
544
|
+
getSavedMessages: () => messages,
|
|
545
|
+
updateModel: (model: LanguageModel | undefined) => {
|
|
546
|
+
if (model) {
|
|
547
|
+
activeModel = model;
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
fail,
|
|
551
|
+
save: async <TOOLS extends ToolSet>(
|
|
552
|
+
toSave:
|
|
553
|
+
| { step: StepResult<TOOLS> }
|
|
554
|
+
| { object: GenerateObjectResult<unknown> },
|
|
555
|
+
createPendingMessage?: boolean,
|
|
556
|
+
) => {
|
|
557
|
+
if (threadId && promptMessageId && saveOutput) {
|
|
558
|
+
const metadata = {
|
|
559
|
+
// TODO: get up to date one when user selects mid-generation
|
|
560
|
+
model: getModelName(activeModel),
|
|
561
|
+
provider: getProviderName(activeModel),
|
|
562
|
+
};
|
|
563
|
+
const serialized =
|
|
564
|
+
"object" in toSave
|
|
565
|
+
? await serializeObjectResult(
|
|
566
|
+
ctx,
|
|
567
|
+
this.component,
|
|
568
|
+
toSave.object,
|
|
569
|
+
metadata,
|
|
570
|
+
)
|
|
571
|
+
: await serializeNewMessagesInStep(
|
|
572
|
+
ctx,
|
|
573
|
+
this.component,
|
|
574
|
+
toSave.step,
|
|
575
|
+
metadata,
|
|
576
|
+
);
|
|
577
|
+
const embeddings = await this.generateEmbeddings(
|
|
578
|
+
ctx,
|
|
579
|
+
{ userId, threadId },
|
|
580
|
+
serialized.messages.map((m) => m.message),
|
|
581
|
+
);
|
|
582
|
+
if (createPendingMessage) {
|
|
583
|
+
serialized.messages.push({
|
|
584
|
+
message: { role: "assistant", content: [] },
|
|
585
|
+
status: "pending",
|
|
586
|
+
});
|
|
587
|
+
embeddings?.vectors.push(null);
|
|
588
|
+
}
|
|
589
|
+
const saved = await ctx.runMutation(
|
|
590
|
+
this.component.messages.addMessages,
|
|
591
|
+
{
|
|
592
|
+
userId,
|
|
593
|
+
threadId,
|
|
594
|
+
agentName: this.options.name,
|
|
595
|
+
promptMessageId,
|
|
596
|
+
pendingMessageId,
|
|
597
|
+
messages: serialized.messages,
|
|
598
|
+
embeddings,
|
|
599
|
+
failPendingSteps: false,
|
|
600
|
+
},
|
|
601
|
+
);
|
|
602
|
+
const lastMessage = saved.messages.at(-1)!;
|
|
603
|
+
if (createPendingMessage) {
|
|
604
|
+
if (lastMessage.status === "failed") {
|
|
605
|
+
pendingMessageId = undefined;
|
|
606
|
+
messages.push(...saved.messages);
|
|
607
|
+
await fail(
|
|
608
|
+
lastMessage.error ??
|
|
609
|
+
"Aborting - the pending message was marked as failed",
|
|
610
|
+
);
|
|
611
|
+
} else {
|
|
612
|
+
pendingMessageId = lastMessage._id;
|
|
613
|
+
messages.push(...saved.messages.slice(0, -1));
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
pendingMessageId = undefined;
|
|
617
|
+
messages.push(...saved.messages);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
621
|
+
if (this.options.rawRequestResponseHandler) {
|
|
622
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
623
|
+
userId,
|
|
624
|
+
threadId,
|
|
625
|
+
agentName: this.options.name,
|
|
626
|
+
request: output.request,
|
|
627
|
+
response: output.response,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
if (opts.usageHandler && output.usage) {
|
|
631
|
+
await opts.usageHandler(ctx, {
|
|
632
|
+
userId,
|
|
633
|
+
threadId,
|
|
634
|
+
agentName: this.options.name,
|
|
635
|
+
model: getModelName(activeModel),
|
|
636
|
+
provider: getProviderName(activeModel),
|
|
637
|
+
usage: output.usage,
|
|
638
|
+
providerMetadata: output.providerMetadata,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
},
|
|
416
642
|
};
|
|
417
643
|
}
|
|
418
644
|
|
|
@@ -424,7 +650,7 @@ export class Agent<
|
|
|
424
650
|
* to a thread (and optionally userId).
|
|
425
651
|
* @param ctx The context passed from the action function calling this.
|
|
426
652
|
* @param { userId, threadId }: The user and thread to associate the message with
|
|
427
|
-
* @param
|
|
653
|
+
* @param generateTextArgs The arguments to the generateText function, along with extra controls
|
|
428
654
|
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
429
655
|
* @returns The result of the generateText function.
|
|
430
656
|
*/
|
|
@@ -434,102 +660,44 @@ export class Agent<
|
|
|
434
660
|
OUTPUT_PARTIAL = never,
|
|
435
661
|
>(
|
|
436
662
|
ctx: ActionCtx & CustomCtx,
|
|
437
|
-
{
|
|
438
|
-
|
|
439
|
-
threadId,
|
|
440
|
-
tools: threadTools,
|
|
441
|
-
...usageHandler
|
|
442
|
-
}: {
|
|
443
|
-
userId?: string | null;
|
|
444
|
-
threadId?: string;
|
|
445
|
-
/**
|
|
446
|
-
* The usage handler to use for this thread. Overrides any handler
|
|
447
|
-
* set in the agent constructor.
|
|
448
|
-
*/
|
|
449
|
-
usageHandler?: UsageHandler;
|
|
450
|
-
/** Note: to get better type inference, pass tools in the next arg */
|
|
451
|
-
tools?: ToolSet;
|
|
452
|
-
},
|
|
453
|
-
args: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>,
|
|
663
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
664
|
+
generateTextArgs: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>,
|
|
454
665
|
options?: Options,
|
|
455
666
|
): Promise<
|
|
456
667
|
GenerateTextResult<TOOLS extends undefined ? AgentTools : TOOLS, OUTPUT> &
|
|
457
668
|
GenerationOutputMetadata
|
|
458
669
|
> {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
466
|
-
const messages = context.savedMessages ?? [];
|
|
467
|
-
const toolCtx = {
|
|
468
|
-
...(ctx as UserActionCtx & CustomCtx),
|
|
469
|
-
userId,
|
|
470
|
-
threadId,
|
|
471
|
-
messageId,
|
|
472
|
-
agent: this,
|
|
473
|
-
} satisfies ToolCtx;
|
|
670
|
+
const { args, promptMessageId, order, ...call } = await this.start(
|
|
671
|
+
ctx,
|
|
672
|
+
generateTextArgs,
|
|
673
|
+
{ ...threadOpts, ...options },
|
|
674
|
+
);
|
|
675
|
+
|
|
474
676
|
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
475
|
-
const
|
|
476
|
-
toolCtx,
|
|
477
|
-
args.tools ?? threadTools ?? this.options.tools,
|
|
478
|
-
) as Tools;
|
|
479
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
677
|
+
const steps: StepResult<Tools>[] = [];
|
|
480
678
|
try {
|
|
481
679
|
const result = (await generateText<Tools, OUTPUT, OUTPUT_PARTIAL>({
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
680
|
+
...args,
|
|
681
|
+
prepareStep: async (options) => {
|
|
682
|
+
const result = await generateTextArgs.prepareStep?.(options);
|
|
683
|
+
call.updateModel(result?.model ?? options.model);
|
|
684
|
+
return result;
|
|
685
|
+
},
|
|
486
686
|
onStepFinish: async (step) => {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
threadId,
|
|
491
|
-
promptMessageId: messageId,
|
|
492
|
-
model: aiArgs.model.modelId,
|
|
493
|
-
provider: aiArgs.model.provider,
|
|
494
|
-
step,
|
|
495
|
-
});
|
|
496
|
-
messages.push(...saved.messages);
|
|
497
|
-
}
|
|
498
|
-
if (this.options.rawRequestResponseHandler) {
|
|
499
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
500
|
-
userId,
|
|
501
|
-
threadId,
|
|
502
|
-
agentName: this.options.name,
|
|
503
|
-
request: step.request,
|
|
504
|
-
response: step.response,
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
if (opts.usageHandler && step.usage) {
|
|
508
|
-
await opts.usageHandler(ctx, {
|
|
509
|
-
userId,
|
|
510
|
-
threadId,
|
|
511
|
-
agentName: this.options.name,
|
|
512
|
-
model: aiArgs.model.modelId,
|
|
513
|
-
provider: aiArgs.model.provider,
|
|
514
|
-
usage: step.usage,
|
|
515
|
-
providerMetadata: step.providerMetadata,
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
return args.onStepFinish?.(step);
|
|
687
|
+
steps.push(step);
|
|
688
|
+
await call.save({ step }, await willContinue(steps, args.stopWhen));
|
|
689
|
+
return generateTextArgs.onStepFinish?.(step);
|
|
519
690
|
},
|
|
520
|
-
})) as GenerateTextResult<Tools, OUTPUT
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
691
|
+
})) as GenerateTextResult<Tools, OUTPUT>;
|
|
692
|
+
const metadata: GenerationOutputMetadata = {
|
|
693
|
+
promptMessageId,
|
|
694
|
+
order,
|
|
695
|
+
savedMessages: call.getSavedMessages(),
|
|
696
|
+
messageId: promptMessageId,
|
|
697
|
+
};
|
|
698
|
+
return Object.assign(result, metadata);
|
|
525
699
|
} catch (error) {
|
|
526
|
-
|
|
527
|
-
console.error("RollbackMessage", messageId);
|
|
528
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
529
|
-
messageId,
|
|
530
|
-
error: (error as Error).message,
|
|
531
|
-
});
|
|
532
|
-
}
|
|
700
|
+
await call.fail(errorToString(error));
|
|
533
701
|
throw error;
|
|
534
702
|
}
|
|
535
703
|
}
|
|
@@ -547,22 +715,16 @@ export class Agent<
|
|
|
547
715
|
PARTIAL_OUTPUT = never,
|
|
548
716
|
>(
|
|
549
717
|
ctx: ActionCtx & CustomCtx,
|
|
550
|
-
{
|
|
551
|
-
userId: argsUserId,
|
|
552
|
-
threadId,
|
|
553
|
-
/** Note: to get better type inference, pass tools in the next arg */
|
|
554
|
-
tools: threadTools,
|
|
555
|
-
...usageHandler
|
|
556
|
-
}: {
|
|
557
|
-
userId?: string | null;
|
|
558
|
-
threadId?: string;
|
|
559
|
-
usageHandler?: UsageHandler;
|
|
560
|
-
tools?: ToolSet;
|
|
561
|
-
},
|
|
718
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
562
719
|
/**
|
|
563
720
|
* The arguments to the streamText function, similar to the ai `streamText` function.
|
|
564
721
|
*/
|
|
565
|
-
|
|
722
|
+
streamTextArgs: StreamingTextArgs<
|
|
723
|
+
AgentTools,
|
|
724
|
+
TOOLS,
|
|
725
|
+
OUTPUT,
|
|
726
|
+
PARTIAL_OUTPUT
|
|
727
|
+
>,
|
|
566
728
|
/**
|
|
567
729
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
568
730
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
@@ -587,112 +749,87 @@ export class Agent<
|
|
|
587
749
|
> &
|
|
588
750
|
GenerationOutputMetadata
|
|
589
751
|
> {
|
|
590
|
-
const
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
const toolCtx = {
|
|
599
|
-
...(ctx as UserActionCtx & CustomCtx),
|
|
600
|
-
userId,
|
|
601
|
-
threadId,
|
|
602
|
-
messageId,
|
|
603
|
-
agent: this,
|
|
604
|
-
};
|
|
605
|
-
const tools = wrapTools(
|
|
606
|
-
toolCtx,
|
|
607
|
-
args.tools ?? threadTools ?? this.options.tools,
|
|
608
|
-
) as TOOLS extends undefined ? AgentTools : TOOLS;
|
|
609
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
752
|
+
const { threadId } = threadOpts;
|
|
753
|
+
const { args, userId, order, stepOrder, promptMessageId, ...call } =
|
|
754
|
+
await this.start(ctx, streamTextArgs, { ...threadOpts, ...options });
|
|
755
|
+
|
|
756
|
+
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
757
|
+
const steps: StepResult<Tools>[] = [];
|
|
758
|
+
|
|
759
|
+
const opts = { ...this.options, ...options };
|
|
610
760
|
const streamer =
|
|
611
761
|
threadId && opts.saveStreamDeltas
|
|
612
762
|
? new DeltaStreamer(this.component, ctx, opts.saveStreamDeltas, {
|
|
613
763
|
threadId,
|
|
614
764
|
userId,
|
|
615
765
|
agentName: this.options.name,
|
|
616
|
-
model:
|
|
617
|
-
provider:
|
|
618
|
-
providerOptions:
|
|
766
|
+
model: getModelName(args.model),
|
|
767
|
+
provider: getProviderName(args.model),
|
|
768
|
+
providerOptions: args.providerOptions,
|
|
619
769
|
order,
|
|
620
770
|
stepOrder,
|
|
621
|
-
abortSignal:
|
|
771
|
+
abortSignal: args.abortSignal,
|
|
622
772
|
})
|
|
623
773
|
: undefined;
|
|
624
774
|
|
|
625
775
|
const result = streamText({
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
tools,
|
|
630
|
-
abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
|
|
776
|
+
...args,
|
|
777
|
+
abortSignal: streamer?.abortController.signal ?? args.abortSignal,
|
|
778
|
+
// TODO: this is probably why reasoning isn't streaming
|
|
631
779
|
experimental_transform: mergeTransforms(
|
|
632
780
|
options?.saveStreamDeltas,
|
|
633
|
-
|
|
781
|
+
streamTextArgs.experimental_transform,
|
|
634
782
|
),
|
|
635
783
|
onChunk: async (event) => {
|
|
636
784
|
await streamer?.addParts([event.chunk]);
|
|
637
785
|
// console.log("onChunk", chunk);
|
|
638
|
-
return
|
|
786
|
+
return streamTextArgs.onChunk?.(event);
|
|
639
787
|
},
|
|
640
788
|
onError: async (error) => {
|
|
641
789
|
console.error("onError", error);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
790
|
+
await call.fail(errorToString(error.error));
|
|
791
|
+
await streamer?.fail(errorToString(error.error));
|
|
792
|
+
return streamTextArgs.onError?.(error);
|
|
793
|
+
},
|
|
794
|
+
// onFinish: async (event) => {
|
|
795
|
+
// return streamTextArgs.onFinish?.(event);
|
|
796
|
+
// },
|
|
797
|
+
prepareStep: async (options) => {
|
|
798
|
+
const result = await streamTextArgs.prepareStep?.(options);
|
|
799
|
+
if (result) {
|
|
800
|
+
const model = result.model ?? options.model;
|
|
801
|
+
call.updateModel(model);
|
|
802
|
+
return result;
|
|
647
803
|
}
|
|
648
|
-
|
|
649
|
-
return args.onError?.(error);
|
|
804
|
+
return undefined;
|
|
650
805
|
},
|
|
651
806
|
onStepFinish: async (step) => {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
model: aiArgs.model.modelId,
|
|
658
|
-
provider: aiArgs.model.provider,
|
|
659
|
-
promptMessageId: messageId,
|
|
660
|
-
step,
|
|
661
|
-
});
|
|
662
|
-
await streamer?.finish(saved.messages);
|
|
663
|
-
messages.push(...saved.messages);
|
|
664
|
-
}
|
|
665
|
-
if (this.options.rawRequestResponseHandler) {
|
|
666
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
667
|
-
userId,
|
|
668
|
-
threadId,
|
|
669
|
-
agentName: this.options.name,
|
|
670
|
-
request: step.request,
|
|
671
|
-
response: step.response,
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
if (opts.usageHandler && step.usage) {
|
|
675
|
-
await opts.usageHandler(ctx, {
|
|
676
|
-
userId,
|
|
677
|
-
threadId,
|
|
678
|
-
agentName: this.options.name,
|
|
679
|
-
model: aiArgs.model.modelId,
|
|
680
|
-
provider: aiArgs.model.provider,
|
|
681
|
-
usage: step.usage,
|
|
682
|
-
providerMetadata: step.providerMetadata,
|
|
683
|
-
});
|
|
807
|
+
steps.push(step);
|
|
808
|
+
const createPendingMessage = await willContinue(steps, args.stopWhen);
|
|
809
|
+
await call.save({ step }, createPendingMessage);
|
|
810
|
+
if (!createPendingMessage) {
|
|
811
|
+
await streamer?.finish();
|
|
684
812
|
}
|
|
685
813
|
return args.onStepFinish?.(step);
|
|
686
814
|
},
|
|
687
815
|
}) as StreamTextResult<
|
|
688
816
|
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
689
817
|
PARTIAL_OUTPUT
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
818
|
+
>;
|
|
819
|
+
const metadata: GenerationOutputMetadata = {
|
|
820
|
+
promptMessageId,
|
|
821
|
+
order,
|
|
822
|
+
savedMessages: call.getSavedMessages(),
|
|
823
|
+
messageId: promptMessageId,
|
|
824
|
+
};
|
|
825
|
+
if (
|
|
826
|
+
(typeof options?.saveStreamDeltas === "object" &&
|
|
827
|
+
!options.saveStreamDeltas.returnImmediately) ||
|
|
828
|
+
options?.saveStreamDeltas === true
|
|
829
|
+
) {
|
|
830
|
+
await result.consumeStream();
|
|
831
|
+
}
|
|
832
|
+
return Object.assign(result, metadata);
|
|
696
833
|
}
|
|
697
834
|
|
|
698
835
|
/**
|
|
@@ -702,84 +839,45 @@ export class Agent<
|
|
|
702
839
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
703
840
|
* to a thread (and optionally userId).
|
|
704
841
|
*/
|
|
705
|
-
async generateObject<
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
},
|
|
842
|
+
async generateObject<
|
|
843
|
+
SCHEMA extends ObjectSchema = DefaultObjectSchema,
|
|
844
|
+
OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string
|
|
845
|
+
? "enum"
|
|
846
|
+
: "object",
|
|
847
|
+
RESULT = OUTPUT extends "array"
|
|
848
|
+
? Array<InferSchema<SCHEMA>>
|
|
849
|
+
: InferSchema<SCHEMA>,
|
|
850
|
+
>(
|
|
851
|
+
ctx: ActionCtx & CustomCtx,
|
|
852
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
716
853
|
/**
|
|
717
854
|
* The arguments to the generateObject function, similar to the ai.generateObject function.
|
|
718
855
|
*/
|
|
719
|
-
|
|
856
|
+
generateObjectArgs: GenerateObjectArgs<SCHEMA, OUTPUT, RESULT>,
|
|
720
857
|
/**
|
|
721
858
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
722
859
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
723
860
|
*/
|
|
724
861
|
options?: Options,
|
|
725
|
-
): Promise<GenerateObjectResult<
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
threadId,
|
|
730
|
-
...opts,
|
|
731
|
-
});
|
|
732
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
733
|
-
const messages = context.savedMessages ?? [];
|
|
734
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
862
|
+
): Promise<GenerateObjectResult<RESULT> & GenerationOutputMetadata> {
|
|
863
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } =
|
|
864
|
+
await this.start(ctx, generateObjectArgs, { ...threadOpts, ...options });
|
|
865
|
+
|
|
735
866
|
try {
|
|
736
867
|
const result = (await generateObject(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
)) as GenerateObjectResult<T> & GenerationOutputMetadata;
|
|
868
|
+
args,
|
|
869
|
+
)) as GenerateObjectResult<RESULT>;
|
|
740
870
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
});
|
|
750
|
-
messages.push(...saved.messages);
|
|
751
|
-
}
|
|
752
|
-
result.messageId = messageId;
|
|
753
|
-
result.order = order;
|
|
754
|
-
result.messages = messages;
|
|
755
|
-
if (this.options.rawRequestResponseHandler) {
|
|
756
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
757
|
-
userId,
|
|
758
|
-
threadId,
|
|
759
|
-
agentName: this.options.name,
|
|
760
|
-
request: result.request,
|
|
761
|
-
response: result.response,
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
if (opts.usageHandler && result.usage) {
|
|
765
|
-
await opts.usageHandler(ctx, {
|
|
766
|
-
userId,
|
|
767
|
-
threadId,
|
|
768
|
-
agentName: this.options.name,
|
|
769
|
-
model: aiArgs.model.modelId,
|
|
770
|
-
provider: aiArgs.model.provider,
|
|
771
|
-
usage: result.usage,
|
|
772
|
-
providerMetadata: result.providerMetadata,
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
return result;
|
|
871
|
+
await save({ object: result });
|
|
872
|
+
const metadata: GenerationOutputMetadata = {
|
|
873
|
+
promptMessageId,
|
|
874
|
+
order,
|
|
875
|
+
savedMessages: getSavedMessages(),
|
|
876
|
+
messageId: promptMessageId,
|
|
877
|
+
};
|
|
878
|
+
return Object.assign(result, metadata);
|
|
776
879
|
} catch (error) {
|
|
777
|
-
|
|
778
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
779
|
-
messageId,
|
|
780
|
-
error: (error as Error).message,
|
|
781
|
-
});
|
|
782
|
-
}
|
|
880
|
+
await fail(errorToString(error));
|
|
783
881
|
throw error;
|
|
784
882
|
}
|
|
785
883
|
}
|
|
@@ -791,97 +889,83 @@ export class Agent<
|
|
|
791
889
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
792
890
|
* to a thread (and optionally userId).
|
|
793
891
|
*/
|
|
794
|
-
async streamObject<
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
},
|
|
892
|
+
async streamObject<
|
|
893
|
+
SCHEMA extends ObjectSchema = DefaultObjectSchema,
|
|
894
|
+
OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string
|
|
895
|
+
? "enum"
|
|
896
|
+
: "object",
|
|
897
|
+
RESULT = OUTPUT extends "array"
|
|
898
|
+
? Array<InferSchema<SCHEMA>>
|
|
899
|
+
: InferSchema<SCHEMA>,
|
|
900
|
+
>(
|
|
901
|
+
ctx: ActionCtx & CustomCtx,
|
|
902
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
805
903
|
/**
|
|
806
904
|
* The arguments to the streamObject function, similar to the ai `streamObject` function.
|
|
807
905
|
*/
|
|
808
|
-
|
|
906
|
+
streamObjectArgs: StreamObjectArgs<SCHEMA, OUTPUT, RESULT> & {
|
|
907
|
+
/**
|
|
908
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
909
|
+
* instead of the prompt or messages.
|
|
910
|
+
* This is useful if you want to first save a user message, then use it as
|
|
911
|
+
* the prompt for the LLM call in another call.
|
|
912
|
+
*/
|
|
913
|
+
promptMessageId?: string;
|
|
914
|
+
/**
|
|
915
|
+
* The model to use for the LLM calls. This will override the model specified
|
|
916
|
+
* in the Agent constructor.
|
|
917
|
+
*/
|
|
918
|
+
model?: LanguageModel;
|
|
919
|
+
/**
|
|
920
|
+
* The tools to use for the tool calls. This will override tools specified
|
|
921
|
+
* in the Agent constructor or createThread / continueThread.
|
|
922
|
+
*/
|
|
923
|
+
},
|
|
809
924
|
/**
|
|
810
925
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
811
926
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
812
927
|
*/
|
|
813
928
|
options?: Options,
|
|
814
929
|
): Promise<
|
|
815
|
-
|
|
930
|
+
ReturnType<typeof streamObject<SCHEMA, OUTPUT, RESULT>> &
|
|
931
|
+
GenerationOutputMetadata
|
|
816
932
|
> {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
threadId,
|
|
822
|
-
...opts,
|
|
823
|
-
});
|
|
824
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
825
|
-
const messages = context.savedMessages ?? [];
|
|
826
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
827
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
828
|
-
const stream = streamObject<any>({
|
|
933
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } =
|
|
934
|
+
await this.start(ctx, streamObjectArgs, { ...threadOpts, ...options });
|
|
935
|
+
|
|
936
|
+
const stream = streamObject<SCHEMA, OUTPUT, RESULT>({
|
|
829
937
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
830
|
-
...(
|
|
938
|
+
...(args as any),
|
|
831
939
|
onError: async (error) => {
|
|
832
|
-
console.error("onError", error);
|
|
940
|
+
console.error(" streamObject onError", error);
|
|
941
|
+
// TODO: content that we have so far
|
|
942
|
+
// content: stream.fullStream.
|
|
943
|
+
await fail(errorToString(error.error));
|
|
833
944
|
return args.onError?.(error);
|
|
834
945
|
},
|
|
835
946
|
onFinish: async (result) => {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
promptMessageId: messageId,
|
|
841
|
-
result: {
|
|
842
|
-
object: result.object,
|
|
843
|
-
finishReason: "stop",
|
|
844
|
-
usage: result.usage,
|
|
845
|
-
warnings: result.warnings,
|
|
846
|
-
request: await stream.request,
|
|
847
|
-
response: result.response,
|
|
848
|
-
providerMetadata: result.providerMetadata,
|
|
849
|
-
toJsonResponse: stream.toTextStreamResponse,
|
|
850
|
-
},
|
|
851
|
-
model: aiArgs.model.modelId,
|
|
852
|
-
provider: aiArgs.model.provider,
|
|
853
|
-
});
|
|
854
|
-
messages.push(...saved.messages);
|
|
855
|
-
}
|
|
856
|
-
if (opts.usageHandler && result.usage) {
|
|
857
|
-
await opts.usageHandler(ctx, {
|
|
858
|
-
userId,
|
|
859
|
-
threadId,
|
|
860
|
-
agentName: this.options.name,
|
|
861
|
-
model: aiArgs.model.modelId,
|
|
862
|
-
provider: aiArgs.model.provider,
|
|
947
|
+
await save({
|
|
948
|
+
object: {
|
|
949
|
+
object: result.object,
|
|
950
|
+
finishReason: result.error ? "error" : "stop",
|
|
863
951
|
usage: result.usage,
|
|
864
|
-
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
if (this.options.rawRequestResponseHandler) {
|
|
868
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
869
|
-
userId,
|
|
870
|
-
threadId,
|
|
871
|
-
agentName: this.options.name,
|
|
952
|
+
warnings: result.warnings,
|
|
872
953
|
request: await stream.request,
|
|
873
954
|
response: result.response,
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
955
|
+
providerMetadata: result.providerMetadata,
|
|
956
|
+
toJsonResponse: stream.toTextStreamResponse,
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
return args.onFinish?.(result);
|
|
878
960
|
},
|
|
879
|
-
})
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
961
|
+
});
|
|
962
|
+
const metadata: GenerationOutputMetadata = {
|
|
963
|
+
promptMessageId,
|
|
964
|
+
order,
|
|
965
|
+
savedMessages: getSavedMessages(),
|
|
966
|
+
messageId: promptMessageId,
|
|
967
|
+
};
|
|
968
|
+
return Object.assign(stream, metadata);
|
|
885
969
|
}
|
|
886
970
|
|
|
887
971
|
/**
|
|
@@ -915,6 +999,7 @@ export class Agent<
|
|
|
915
999
|
: [args.message],
|
|
916
1000
|
metadata: args.metadata ? [args.metadata] : undefined,
|
|
917
1001
|
skipEmbeddings: args.skipEmbeddings,
|
|
1002
|
+
pendingMessageId: args.pendingMessageId,
|
|
918
1003
|
});
|
|
919
1004
|
const message = messages.at(-1)!;
|
|
920
1005
|
return { messageId: message._id, message };
|
|
@@ -944,7 +1029,7 @@ export class Agent<
|
|
|
944
1029
|
const { skipEmbeddings, ...rest } = args;
|
|
945
1030
|
if (args.embeddings) {
|
|
946
1031
|
embeddings = args.embeddings;
|
|
947
|
-
} else if (!skipEmbeddings && this.options.
|
|
1032
|
+
} else if (!skipEmbeddings && this.options.textEmbeddingModel) {
|
|
948
1033
|
if (!("runAction" in ctx)) {
|
|
949
1034
|
console.warn(
|
|
950
1035
|
"You're trying to save messages and generate embeddings, but you're in a mutation. " +
|
|
@@ -1050,8 +1135,8 @@ export class Agent<
|
|
|
1050
1135
|
getEmbedding: async (text) => {
|
|
1051
1136
|
assert("runAction" in ctx);
|
|
1052
1137
|
assert(
|
|
1053
|
-
this.options.
|
|
1054
|
-
"A
|
|
1138
|
+
this.options.textEmbeddingModel,
|
|
1139
|
+
"A textEmbeddingModel is required to be set on the Agent that you're doing vector search with",
|
|
1055
1140
|
);
|
|
1056
1141
|
return {
|
|
1057
1142
|
embedding: (
|
|
@@ -1061,7 +1146,7 @@ export class Agent<
|
|
|
1061
1146
|
values: [text],
|
|
1062
1147
|
})
|
|
1063
1148
|
).embeddings[0],
|
|
1064
|
-
|
|
1149
|
+
textEmbeddingModel: this.options.textEmbeddingModel,
|
|
1065
1150
|
};
|
|
1066
1151
|
},
|
|
1067
1152
|
});
|
|
@@ -1116,7 +1201,7 @@ export class Agent<
|
|
|
1116
1201
|
}: { userId: string | undefined; threadId: string | undefined },
|
|
1117
1202
|
messages: (ModelMessage | Message)[],
|
|
1118
1203
|
) {
|
|
1119
|
-
if (!this.options.
|
|
1204
|
+
if (!this.options.textEmbeddingModel) {
|
|
1120
1205
|
return undefined;
|
|
1121
1206
|
}
|
|
1122
1207
|
let embeddings:
|
|
@@ -1134,11 +1219,12 @@ export class Agent<
|
|
|
1134
1219
|
if (textIndexes.length === 0) {
|
|
1135
1220
|
return undefined;
|
|
1136
1221
|
}
|
|
1222
|
+
const values = messageTexts.filter((t): t is string => !!t);
|
|
1137
1223
|
// Then embed those messages.
|
|
1138
1224
|
const textEmbeddings = await this.doEmbed(ctx, {
|
|
1139
1225
|
userId,
|
|
1140
1226
|
threadId,
|
|
1141
|
-
values
|
|
1227
|
+
values,
|
|
1142
1228
|
});
|
|
1143
1229
|
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
1144
1230
|
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
@@ -1148,7 +1234,7 @@ export class Agent<
|
|
|
1148
1234
|
if (textEmbeddings.embeddings.length > 0) {
|
|
1149
1235
|
const dimension = textEmbeddings.embeddings[0].length;
|
|
1150
1236
|
validateVectorDimension(dimension);
|
|
1151
|
-
const model = getModelName(this.options.
|
|
1237
|
+
const model = getModelName(this.options.textEmbeddingModel);
|
|
1152
1238
|
embeddings = { vectors: embeddingsOrNull, dimension, model };
|
|
1153
1239
|
}
|
|
1154
1240
|
return embeddings;
|
|
@@ -1204,9 +1290,9 @@ export class Agent<
|
|
|
1204
1290
|
messagesMissingEmbeddings.map((m) => deserializeMessage(m!.message!)),
|
|
1205
1291
|
);
|
|
1206
1292
|
if (!embeddings) {
|
|
1207
|
-
if (!this.options.
|
|
1293
|
+
if (!this.options.textEmbeddingModel) {
|
|
1208
1294
|
throw new Error(
|
|
1209
|
-
"No embeddings were generated for the messages. You must pass a
|
|
1295
|
+
"No embeddings were generated for the messages. You must pass a textEmbeddingModel to the agent constructor.",
|
|
1210
1296
|
);
|
|
1211
1297
|
}
|
|
1212
1298
|
throw new Error(
|
|
@@ -1262,13 +1348,13 @@ export class Agent<
|
|
|
1262
1348
|
provider?: string;
|
|
1263
1349
|
},
|
|
1264
1350
|
): Promise<{ messages: MessageDoc[] }> {
|
|
1265
|
-
const messages = await serializeNewMessagesInStep(
|
|
1351
|
+
const { messages } = await serializeNewMessagesInStep(
|
|
1266
1352
|
ctx,
|
|
1267
1353
|
this.component,
|
|
1268
1354
|
args.step,
|
|
1269
1355
|
{
|
|
1270
|
-
provider: args.provider ?? getProviderName(this.options.
|
|
1271
|
-
model: args.model ?? getModelName(this.options.
|
|
1356
|
+
provider: args.provider ?? getProviderName(this.options.languageModel),
|
|
1357
|
+
model: args.model ?? getModelName(this.options.languageModel),
|
|
1272
1358
|
},
|
|
1273
1359
|
);
|
|
1274
1360
|
const embeddings = await this.generateEmbeddings(
|
|
@@ -1311,14 +1397,20 @@ export class Agent<
|
|
|
1311
1397
|
this.component,
|
|
1312
1398
|
args.result,
|
|
1313
1399
|
{
|
|
1314
|
-
model:
|
|
1315
|
-
|
|
1400
|
+
model:
|
|
1401
|
+
args.model ??
|
|
1402
|
+
args.metadata?.model ??
|
|
1403
|
+
getModelName(this.options.languageModel),
|
|
1404
|
+
provider:
|
|
1405
|
+
args.provider ??
|
|
1406
|
+
args.metadata?.provider ??
|
|
1407
|
+
getProviderName(this.options.languageModel),
|
|
1316
1408
|
},
|
|
1317
1409
|
);
|
|
1318
1410
|
const embeddings = await this.generateEmbeddings(
|
|
1319
1411
|
ctx,
|
|
1320
1412
|
{ userId: args.userId, threadId: args.threadId },
|
|
1321
|
-
messages.map((m) =>
|
|
1413
|
+
messages.map((m) => m.message),
|
|
1322
1414
|
);
|
|
1323
1415
|
|
|
1324
1416
|
return ctx.runMutation(this.component.messages.addMessages, {
|
|
@@ -1340,25 +1432,17 @@ export class Agent<
|
|
|
1340
1432
|
* @param args What message to save. Generally the parent message sent into
|
|
1341
1433
|
* the generateText call.
|
|
1342
1434
|
*/
|
|
1343
|
-
async
|
|
1435
|
+
async finalizeMessage(
|
|
1344
1436
|
ctx: RunMutationCtx,
|
|
1345
1437
|
args: {
|
|
1346
|
-
threadId: string;
|
|
1347
1438
|
messageId: string;
|
|
1348
|
-
result: {
|
|
1439
|
+
result: { status: "failed"; error: string } | { status: "success" };
|
|
1349
1440
|
},
|
|
1350
1441
|
): Promise<void> {
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
});
|
|
1356
|
-
} else {
|
|
1357
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
1358
|
-
messageId: args.messageId,
|
|
1359
|
-
error: result.error,
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1442
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
1443
|
+
messageId: args.messageId,
|
|
1444
|
+
result: args.result,
|
|
1445
|
+
});
|
|
1362
1446
|
}
|
|
1363
1447
|
|
|
1364
1448
|
/**
|
|
@@ -1530,12 +1614,12 @@ export class Agent<
|
|
|
1530
1614
|
|
|
1531
1615
|
async _saveMessagesAndFetchContext<
|
|
1532
1616
|
T extends {
|
|
1533
|
-
id?: string;
|
|
1534
1617
|
prompt?: string | (ModelMessage | Message)[];
|
|
1535
1618
|
messages?: (ModelMessage | Message)[];
|
|
1536
1619
|
system?: string;
|
|
1537
1620
|
promptMessageId?: string;
|
|
1538
|
-
|
|
1621
|
+
pendingMessageId?: string;
|
|
1622
|
+
model?: LanguageModel;
|
|
1539
1623
|
},
|
|
1540
1624
|
>(
|
|
1541
1625
|
ctx: RunActionCtx,
|
|
@@ -1545,11 +1629,16 @@ export class Agent<
|
|
|
1545
1629
|
threadId,
|
|
1546
1630
|
contextOptions,
|
|
1547
1631
|
storageOptions,
|
|
1548
|
-
}: {
|
|
1632
|
+
}: {
|
|
1633
|
+
userId: string | null | undefined;
|
|
1634
|
+
threadId: string | undefined;
|
|
1635
|
+
} & Options,
|
|
1549
1636
|
): Promise<{
|
|
1550
|
-
args: T
|
|
1637
|
+
args: Extract<T, { model: LanguageModel; messages: ModelMessage[] }> &
|
|
1638
|
+
CallSettings;
|
|
1551
1639
|
userId: string | undefined;
|
|
1552
|
-
|
|
1640
|
+
promptMessageId: string | undefined;
|
|
1641
|
+
pendingMessageId: string | undefined;
|
|
1553
1642
|
order: number | undefined;
|
|
1554
1643
|
stepOrder: number | undefined;
|
|
1555
1644
|
savedMessages: MessageDoc[] | undefined;
|
|
@@ -1565,7 +1654,8 @@ export class Agent<
|
|
|
1565
1654
|
argsUserId ??
|
|
1566
1655
|
(threadId &&
|
|
1567
1656
|
(await ctx.runQuery(this.component.threads.getThread, { threadId }))
|
|
1568
|
-
?.userId)
|
|
1657
|
+
?.userId) ??
|
|
1658
|
+
undefined;
|
|
1569
1659
|
// If only a messageId is provided, this will add that message to the end.
|
|
1570
1660
|
const contextMessages = await this.fetchContextMessages(ctx, {
|
|
1571
1661
|
userId,
|
|
@@ -1584,32 +1674,46 @@ export class Agent<
|
|
|
1584
1674
|
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
1585
1675
|
: undefined;
|
|
1586
1676
|
|
|
1587
|
-
let
|
|
1677
|
+
let promptMessageId = promptMessage?._id;
|
|
1588
1678
|
let order = promptMessage?.order;
|
|
1589
1679
|
let stepOrder = promptMessage?.stepOrder;
|
|
1590
1680
|
let savedMessages = undefined;
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
messages
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1681
|
+
let pendingMessageId = undefined;
|
|
1682
|
+
if (threadId && storageOptions?.saveMessages !== "none") {
|
|
1683
|
+
let saved: { messages: MessageDoc[] };
|
|
1684
|
+
if (
|
|
1685
|
+
messages.length + prompt.length &&
|
|
1686
|
+
// If it was a promptMessageId, we don't want to save it again.
|
|
1687
|
+
(!args.promptMessageId || storageOptions?.saveMessages === "all")
|
|
1688
|
+
) {
|
|
1689
|
+
const saveAll = storageOptions?.saveMessages === "all";
|
|
1690
|
+
const coreMessages = [...messages, ...prompt];
|
|
1691
|
+
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
1692
|
+
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
1693
|
+
saved = await this.saveMessages(ctx, {
|
|
1694
|
+
threadId,
|
|
1695
|
+
userId,
|
|
1696
|
+
messages: [...toSave, { role: "assistant", content: [] }],
|
|
1697
|
+
metadata: [...metadata, { status: "pending" }],
|
|
1698
|
+
failPendingSteps: true,
|
|
1699
|
+
pendingMessageId: args.pendingMessageId,
|
|
1700
|
+
});
|
|
1701
|
+
promptMessageId = saved.messages.at(-2)!._id;
|
|
1702
|
+
} else {
|
|
1703
|
+
saved = await this.saveMessages(ctx, {
|
|
1704
|
+
threadId,
|
|
1705
|
+
userId,
|
|
1706
|
+
messages: [{ role: "assistant", content: [] }],
|
|
1707
|
+
metadata: [{ status: "pending" }],
|
|
1708
|
+
failPendingSteps: true,
|
|
1709
|
+
pendingMessageId: args.pendingMessageId,
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
pendingMessageId = saved.messages.at(-1)!._id;
|
|
1610
1713
|
order = saved.messages.at(-1)!.order;
|
|
1611
1714
|
stepOrder = saved.messages.at(-1)!.stepOrder;
|
|
1612
|
-
|
|
1715
|
+
// Don't return the pending message
|
|
1716
|
+
savedMessages = saved.messages.slice(0, -1);
|
|
1613
1717
|
}
|
|
1614
1718
|
|
|
1615
1719
|
if (promptMessage?.message) {
|
|
@@ -1620,7 +1724,7 @@ export class Agent<
|
|
|
1620
1724
|
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
1621
1725
|
// embeddings yet. This can happen if the message was saved in a mutation
|
|
1622
1726
|
// where the LLM is not available.
|
|
1623
|
-
if (!promptMessage.embeddingId && this.options.
|
|
1727
|
+
if (!promptMessage.embeddingId && this.options.textEmbeddingModel) {
|
|
1624
1728
|
await this._generateAndSaveEmbeddings(ctx, [promptMessage]);
|
|
1625
1729
|
}
|
|
1626
1730
|
}
|
|
@@ -1650,20 +1754,23 @@ export class Agent<
|
|
|
1650
1754
|
|
|
1651
1755
|
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
1652
1756
|
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
1653
|
-
processedMessages = await
|
|
1757
|
+
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
1654
1758
|
}
|
|
1655
1759
|
|
|
1656
1760
|
const { prompt: _, model, ...rest } = args;
|
|
1657
1761
|
return {
|
|
1658
1762
|
args: {
|
|
1659
1763
|
...this.options.callSettings,
|
|
1764
|
+
...this.options.providerOptions,
|
|
1660
1765
|
...rest,
|
|
1661
|
-
model: model ?? this.options.
|
|
1766
|
+
model: model ?? this.options.languageModel,
|
|
1662
1767
|
system: args.system ?? this.options.instructions,
|
|
1663
1768
|
messages: processedMessages,
|
|
1664
|
-
} as T
|
|
1769
|
+
} as Extract<T, { model: LanguageModel; messages: ModelMessage[] }> &
|
|
1770
|
+
CallSettings,
|
|
1665
1771
|
userId,
|
|
1666
|
-
|
|
1772
|
+
promptMessageId,
|
|
1773
|
+
pendingMessageId,
|
|
1667
1774
|
savedMessages,
|
|
1668
1775
|
order,
|
|
1669
1776
|
stepOrder,
|
|
@@ -1680,10 +1787,10 @@ export class Agent<
|
|
|
1680
1787
|
headers?: Record<string, string>;
|
|
1681
1788
|
},
|
|
1682
1789
|
): Promise<{ embeddings: number[][] }> {
|
|
1683
|
-
const embeddingModel = this.options.
|
|
1790
|
+
const embeddingModel = this.options.textEmbeddingModel;
|
|
1684
1791
|
assert(
|
|
1685
1792
|
embeddingModel,
|
|
1686
|
-
"a
|
|
1793
|
+
"a textEmbeddingModel is required to be set on the Agent that you're doing vector search with",
|
|
1687
1794
|
);
|
|
1688
1795
|
const result = await embedMany({
|
|
1689
1796
|
...this.options.callSettings,
|
|
@@ -1710,83 +1817,6 @@ export class Agent<
|
|
|
1710
1817
|
return { embeddings: result.embeddings };
|
|
1711
1818
|
}
|
|
1712
1819
|
|
|
1713
|
-
/**
|
|
1714
|
-
* Process messages to inline file and image URLs that point to localhost
|
|
1715
|
-
* by converting them to base64. This solves the problem of LLMs not being
|
|
1716
|
-
* able to access localhost URLs.
|
|
1717
|
-
*/
|
|
1718
|
-
private async _inlineMessagesFiles(
|
|
1719
|
-
messages: (ModelMessage | Message)[],
|
|
1720
|
-
): Promise<(ModelMessage | Message)[]> {
|
|
1721
|
-
// Process each message to convert localhost URLs to base64
|
|
1722
|
-
return Promise.all(
|
|
1723
|
-
messages.map(async (message): Promise<ModelMessage | Message> => {
|
|
1724
|
-
if (
|
|
1725
|
-
(message.role !== "user" && message.role !== "assistant") ||
|
|
1726
|
-
typeof message.content === "string" ||
|
|
1727
|
-
!Array.isArray(message.content)
|
|
1728
|
-
) {
|
|
1729
|
-
return message;
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
const processedContent = await Promise.all(
|
|
1733
|
-
message.content.map(async (part) => {
|
|
1734
|
-
if (part.type === "image" && part.image instanceof URL) {
|
|
1735
|
-
assert(
|
|
1736
|
-
message.role === "user",
|
|
1737
|
-
"Images can only be in user messages",
|
|
1738
|
-
);
|
|
1739
|
-
if (this._isLocalhostUrl(part.image)) {
|
|
1740
|
-
const imageData = await this._downloadFile(part.image);
|
|
1741
|
-
return { ...part, image: imageData } as ImagePart;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
// Handle file parts
|
|
1746
|
-
if (part.type === "file" && part.data instanceof URL) {
|
|
1747
|
-
if (this._isLocalhostUrl(part.data)) {
|
|
1748
|
-
const fileData = await this._downloadFile(part.data);
|
|
1749
|
-
return { ...part, data: fileData } as FilePart;
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
return part;
|
|
1754
|
-
}),
|
|
1755
|
-
);
|
|
1756
|
-
if (message.role === "user") {
|
|
1757
|
-
return { ...message, content: processedContent as UserContent };
|
|
1758
|
-
} else {
|
|
1759
|
-
return { ...message, content: processedContent as AssistantContent };
|
|
1760
|
-
}
|
|
1761
|
-
}),
|
|
1762
|
-
);
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
/**
|
|
1766
|
-
* Check if a URL points to localhost
|
|
1767
|
-
*/
|
|
1768
|
-
private _isLocalhostUrl(url: URL): boolean {
|
|
1769
|
-
return (
|
|
1770
|
-
url.hostname === "localhost" ||
|
|
1771
|
-
url.hostname === "127.0.0.1" ||
|
|
1772
|
-
url.hostname === "::1" ||
|
|
1773
|
-
url.hostname === "0.0.0.0"
|
|
1774
|
-
);
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
/**
|
|
1778
|
-
* Download a file from a URL
|
|
1779
|
-
*/
|
|
1780
|
-
private async _downloadFile(url: URL): Promise<ArrayBuffer> {
|
|
1781
|
-
// Fetch the file
|
|
1782
|
-
const response = await fetch(url);
|
|
1783
|
-
if (!response.ok) {
|
|
1784
|
-
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
return await response.arrayBuffer();
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
1820
|
/**
|
|
1791
1821
|
* WORKFLOW UTILITIES
|
|
1792
1822
|
*/
|
|
@@ -1830,23 +1860,7 @@ export class Agent<
|
|
|
1830
1860
|
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
|
|
1831
1861
|
*/
|
|
1832
1862
|
asTextAction<DataModel extends GenericDataModel>(
|
|
1833
|
-
spec
|
|
1834
|
-
/**
|
|
1835
|
-
* When to stop generating text.
|
|
1836
|
-
* Defaults to the {@link Agent["options"].stopWhen} option.
|
|
1837
|
-
*/
|
|
1838
|
-
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
1839
|
-
/**
|
|
1840
|
-
* The {@link ContextOptions} to use for fetching contextual messages and
|
|
1841
|
-
* saving input/output messages.
|
|
1842
|
-
* Defaults to the {@link Agent.contextOptions} option.
|
|
1843
|
-
*/
|
|
1844
|
-
contextOptions?: ContextOptions;
|
|
1845
|
-
/**
|
|
1846
|
-
* The {@link StorageOptions} to use for saving input/output messages.
|
|
1847
|
-
* Defaults to the {@link Agent.storageOptions} option.
|
|
1848
|
-
*/
|
|
1849
|
-
storageOptions?: StorageOptions;
|
|
1863
|
+
spec: MaybeCustomCtx<CustomCtx, DataModel, AgentTools> & {
|
|
1850
1864
|
/**
|
|
1851
1865
|
* Whether to stream the text.
|
|
1852
1866
|
* If false, it will generate the text in a single call. (default)
|
|
@@ -1855,36 +1869,14 @@ export class Agent<
|
|
|
1855
1869
|
* defaults if you pass true.
|
|
1856
1870
|
*/
|
|
1857
1871
|
stream?: boolean | StreamingOptions;
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
* ```ts
|
|
1866
|
-
* const myAgent = new Agent<{ orgId: string }>(...);
|
|
1867
|
-
* const myAction = myAgent.asTextAction({
|
|
1868
|
-
* customCtx: (ctx: ActionCtx, target, llmArgs) => {
|
|
1869
|
-
* const orgId = await lookupOrgId(ctx, target.threadId);
|
|
1870
|
-
* return { orgId };
|
|
1871
|
-
* },
|
|
1872
|
-
* });
|
|
1873
|
-
* ```
|
|
1874
|
-
* Then, in your tools, you can
|
|
1875
|
-
*/
|
|
1876
|
-
customCtx: (
|
|
1877
|
-
ctx: GenericActionCtx<DataModel>,
|
|
1878
|
-
target: {
|
|
1879
|
-
userId?: string | undefined;
|
|
1880
|
-
threadId?: string | undefined;
|
|
1881
|
-
},
|
|
1882
|
-
llmArgs: TextArgs<AgentTools>,
|
|
1883
|
-
) => CustomCtx;
|
|
1884
|
-
}
|
|
1885
|
-
: { customCtx?: never }),
|
|
1872
|
+
/**
|
|
1873
|
+
* When to stop generating text.
|
|
1874
|
+
* Defaults to the {@link Agent["options"].stopWhen} option.
|
|
1875
|
+
*/
|
|
1876
|
+
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
1877
|
+
} & Options,
|
|
1878
|
+
overrides?: CallSettings,
|
|
1886
1879
|
) {
|
|
1887
|
-
const stopWhen = spec?.stopWhen ?? this.options.stopWhen;
|
|
1888
1880
|
return internalActionGeneric({
|
|
1889
1881
|
args: vTextArgs,
|
|
1890
1882
|
handler: async (ctx_, args) => {
|
|
@@ -1892,7 +1884,8 @@ export class Agent<
|
|
|
1892
1884
|
args.stream === true ? spec?.stream || true : (spec?.stream ?? false);
|
|
1893
1885
|
const targetArgs = { userId: args.userId, threadId: args.threadId };
|
|
1894
1886
|
const llmArgs = {
|
|
1895
|
-
stopWhen,
|
|
1887
|
+
stopWhen: spec?.stopWhen ?? this.options.stopWhen,
|
|
1888
|
+
...overrides,
|
|
1896
1889
|
...omit(args, ["storageOptions", "contextOptions"]),
|
|
1897
1890
|
messages: args.messages?.map(deserializeMessage),
|
|
1898
1891
|
prompt: Array.isArray(args.prompt)
|
|
@@ -1905,7 +1898,7 @@ export class Agent<
|
|
|
1905
1898
|
}
|
|
1906
1899
|
const opts = {
|
|
1907
1900
|
...this.options,
|
|
1908
|
-
...
|
|
1901
|
+
...pick(spec, ["contextOptions", "storageOptions"]),
|
|
1909
1902
|
...pick(args, ["contextOptions", "storageOptions"]),
|
|
1910
1903
|
saveStreamDeltas: stream,
|
|
1911
1904
|
};
|
|
@@ -1925,10 +1918,11 @@ export class Agent<
|
|
|
1925
1918
|
await result.consumeStream();
|
|
1926
1919
|
return {
|
|
1927
1920
|
text: await result.text,
|
|
1928
|
-
|
|
1921
|
+
promptMessageId: result.promptMessageId,
|
|
1929
1922
|
order: result.order,
|
|
1930
1923
|
finishReason: await result.finishReason,
|
|
1931
1924
|
warnings: result.warnings,
|
|
1925
|
+
savedMessageIds: result.savedMessages?.map((m) => m._id) ?? [],
|
|
1932
1926
|
};
|
|
1933
1927
|
} else {
|
|
1934
1928
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1940,10 +1934,11 @@ export class Agent<
|
|
|
1940
1934
|
);
|
|
1941
1935
|
return {
|
|
1942
1936
|
text: res.text,
|
|
1943
|
-
|
|
1937
|
+
promptMessageId: res.promptMessageId,
|
|
1944
1938
|
order: res.order,
|
|
1945
1939
|
finishReason: res.finishReason,
|
|
1946
1940
|
warnings: res.warnings,
|
|
1941
|
+
savedMessageIds: res.savedMessages?.map((m) => m._id) ?? [],
|
|
1947
1942
|
};
|
|
1948
1943
|
}
|
|
1949
1944
|
},
|
|
@@ -1957,32 +1952,44 @@ export class Agent<
|
|
|
1957
1952
|
* and stopWhen.
|
|
1958
1953
|
*/
|
|
1959
1954
|
asObjectAction<T>(
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1955
|
+
objectArgs: Omit<
|
|
1956
|
+
Parameters<typeof generateObject<FlexibleSchema<T>>>[0],
|
|
1957
|
+
"model"
|
|
1958
|
+
>,
|
|
1959
|
+
options?: Options & MaybeCustomCtx<CustomCtx, DataModel, AgentTools>,
|
|
1965
1960
|
) {
|
|
1966
1961
|
return internalActionGeneric({
|
|
1967
1962
|
args: vSafeObjectArgs,
|
|
1968
|
-
handler: async (
|
|
1969
|
-
const { userId, threadId, ...rest } = args;
|
|
1963
|
+
handler: async (ctx_, args) => {
|
|
1964
|
+
const { userId, threadId, callSettings, ...rest } = args;
|
|
1970
1965
|
const overrides = pick(rest, ["contextOptions", "storageOptions"]);
|
|
1971
|
-
const
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1966
|
+
const targetArgs = { userId, threadId };
|
|
1967
|
+
const llmArgs = {
|
|
1968
|
+
...objectArgs,
|
|
1969
|
+
...callSettings,
|
|
1970
|
+
...omit(rest, ["storageOptions", "contextOptions"]),
|
|
1971
|
+
messages: args.messages?.map(deserializeMessage),
|
|
1972
|
+
prompt: Array.isArray(args.prompt)
|
|
1973
|
+
? args.prompt.map(deserializeMessage)
|
|
1974
|
+
: args.prompt,
|
|
1975
|
+
} as Omit<Parameters<typeof generateObject>[0], "model">;
|
|
1976
|
+
const ctx = (
|
|
1977
|
+
options?.customCtx
|
|
1978
|
+
? { ...ctx_, ...options.customCtx(ctx_, targetArgs, llmArgs) }
|
|
1979
|
+
: ctx_
|
|
1980
|
+
) as UserActionCtx & CustomCtx;
|
|
1981
|
+
const value = await this.generateObject(ctx, targetArgs, llmArgs, {
|
|
1982
|
+
...this.options,
|
|
1983
|
+
...options,
|
|
1984
|
+
...overrides,
|
|
1985
|
+
});
|
|
1980
1986
|
return {
|
|
1981
|
-
object: value.object as T,
|
|
1982
|
-
|
|
1987
|
+
object: convexToJson(value.object as Value) as T,
|
|
1988
|
+
promptMessageId: value.promptMessageId,
|
|
1983
1989
|
order: value.order,
|
|
1984
1990
|
finishReason: value.finishReason,
|
|
1985
1991
|
warnings: value.warnings,
|
|
1992
|
+
savedMessageIds: value.savedMessages?.map((m) => m._id) ?? [],
|
|
1986
1993
|
};
|
|
1987
1994
|
},
|
|
1988
1995
|
});
|
|
@@ -2033,3 +2040,30 @@ export class Agent<
|
|
|
2033
2040
|
});
|
|
2034
2041
|
}
|
|
2035
2042
|
}
|
|
2043
|
+
|
|
2044
|
+
async function willContinue(
|
|
2045
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2046
|
+
steps: StepResult<any>[],
|
|
2047
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2048
|
+
stopWhen: StopCondition<any> | Array<StopCondition<any>> | undefined,
|
|
2049
|
+
): Promise<boolean> {
|
|
2050
|
+
const step = steps.at(-1)!;
|
|
2051
|
+
// we aren't doing another round after a tool result
|
|
2052
|
+
// TODO: whether to handle continuing after too much context used..
|
|
2053
|
+
if (step.finishReason !== "tool-calls") return false;
|
|
2054
|
+
// we don't have a tool result, so we'll wait for more
|
|
2055
|
+
if (step.toolCalls.length > step.toolResults.length) return false;
|
|
2056
|
+
if (Array.isArray(stopWhen)) {
|
|
2057
|
+
return (await Promise.all(stopWhen.map(async (s) => s({ steps })))).every(
|
|
2058
|
+
(stop) => !stop,
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
return !(await stopWhen?.({ steps }));
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
function errorToString(error: unknown): string {
|
|
2065
|
+
if (error instanceof Error) {
|
|
2066
|
+
return error.message;
|
|
2067
|
+
}
|
|
2068
|
+
return String(error);
|
|
2069
|
+
}
|