@convex-dev/agent 0.2.0-alpha.2 → 0.2.0
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 +3 -5
- 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 +8 -1
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +60 -5
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +223 -239
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +368 -382
- 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 +21 -22
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +35 -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/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 +4 -9
- package/src/client/files.ts +88 -10
- package/src/client/index.test.ts +9 -21
- package/src/client/index.ts +664 -650
- package/src/client/messages.ts +16 -2
- package/src/client/search.ts +10 -7
- package/src/client/streaming.ts +53 -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/validators.test.ts +0 -17
- package/src/validators.ts +8 -101
package/src/client/index.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import type { LanguageModelV2 } from "@ai-sdk/provider";
|
|
2
1
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
FlexibleSchema,
|
|
3
|
+
IdGenerator,
|
|
4
|
+
InferSchema,
|
|
5
|
+
} from "@ai-sdk/provider-utils";
|
|
6
|
+
import type {
|
|
7
|
+
CallSettings,
|
|
8
|
+
EmbeddingModel,
|
|
7
9
|
GenerateObjectResult,
|
|
8
10
|
GenerateTextResult,
|
|
9
|
-
|
|
11
|
+
LanguageModel,
|
|
12
|
+
ModelMessage,
|
|
10
13
|
StepResult,
|
|
11
|
-
StreamObjectResult,
|
|
12
|
-
StreamTextResult,
|
|
13
|
-
ToolSet,
|
|
14
|
-
UserContent,
|
|
15
14
|
StopCondition,
|
|
16
|
-
|
|
15
|
+
StreamTextResult,
|
|
17
16
|
ToolChoice,
|
|
18
|
-
|
|
19
|
-
EmbeddingModel,
|
|
20
|
-
CallSettings,
|
|
17
|
+
ToolSet,
|
|
21
18
|
} from "ai";
|
|
22
19
|
import {
|
|
23
20
|
embedMany,
|
|
@@ -31,13 +28,12 @@ import { assert, omit, pick } from "convex-helpers";
|
|
|
31
28
|
import {
|
|
32
29
|
internalActionGeneric,
|
|
33
30
|
internalMutationGeneric,
|
|
34
|
-
type GenericActionCtx,
|
|
35
31
|
type GenericDataModel,
|
|
36
32
|
type PaginationOptions,
|
|
37
33
|
type PaginationResult,
|
|
38
34
|
type WithoutSystemFields,
|
|
39
35
|
} from "convex/server";
|
|
40
|
-
import { v } from "convex/values";
|
|
36
|
+
import { convexToJson, v, type Value } from "convex/values";
|
|
41
37
|
import type { MessageDoc, ThreadDoc } from "../component/schema.js";
|
|
42
38
|
import type { threadFieldsSupportingPatch } from "../component/threads.js";
|
|
43
39
|
import {
|
|
@@ -52,16 +48,16 @@ import {
|
|
|
52
48
|
} from "../mapping.js";
|
|
53
49
|
import { extractText, isTool } from "../shared.js";
|
|
54
50
|
import {
|
|
51
|
+
vMessageEmbeddings,
|
|
52
|
+
vMessageWithMetadata,
|
|
53
|
+
vSafeObjectArgs,
|
|
54
|
+
vTextArgs,
|
|
55
55
|
type Message,
|
|
56
56
|
type MessageStatus,
|
|
57
57
|
type MessageWithMetadata,
|
|
58
58
|
type ProviderMetadata,
|
|
59
59
|
type StreamArgs,
|
|
60
60
|
type Usage,
|
|
61
|
-
vMessageEmbeddings,
|
|
62
|
-
vMessageWithMetadata,
|
|
63
|
-
vSafeObjectArgs,
|
|
64
|
-
vTextArgs,
|
|
65
61
|
} from "../validators.js";
|
|
66
62
|
import { createTool, wrapTools, type ToolCtx } from "./createTool.js";
|
|
67
63
|
import {
|
|
@@ -70,7 +66,6 @@ import {
|
|
|
70
66
|
type SaveMessageArgs,
|
|
71
67
|
type SaveMessagesArgs,
|
|
72
68
|
} from "./messages.js";
|
|
73
|
-
import { createThread, getThreadMetadata } from "./threads.js";
|
|
74
69
|
import {
|
|
75
70
|
fetchContextMessages,
|
|
76
71
|
getModelName,
|
|
@@ -79,36 +74,42 @@ import {
|
|
|
79
74
|
import {
|
|
80
75
|
DeltaStreamer,
|
|
81
76
|
mergeTransforms,
|
|
82
|
-
type StreamingOptions,
|
|
83
77
|
syncStreams,
|
|
78
|
+
type StreamingOptions,
|
|
84
79
|
} from "./streaming.js";
|
|
80
|
+
import { createThread, getThreadMetadata } from "./threads.js";
|
|
85
81
|
import type {
|
|
86
82
|
ActionCtx,
|
|
87
83
|
AgentComponent,
|
|
88
84
|
ContextOptions,
|
|
85
|
+
DefaultObjectSchema,
|
|
89
86
|
GenerationOutputMetadata,
|
|
87
|
+
MaybeCustomCtx,
|
|
88
|
+
GenerateObjectArgs,
|
|
89
|
+
ObjectMode,
|
|
90
|
+
ObjectSchema,
|
|
90
91
|
Options,
|
|
91
|
-
OurObjectArgs,
|
|
92
|
-
OurStreamObjectArgs,
|
|
93
92
|
RawRequestResponseHandler,
|
|
94
93
|
RunActionCtx,
|
|
95
94
|
RunMutationCtx,
|
|
96
95
|
RunQueryCtx,
|
|
97
96
|
StorageOptions,
|
|
98
97
|
StreamingTextArgs,
|
|
98
|
+
StreamObjectArgs,
|
|
99
99
|
SyncStreamsReturnValue,
|
|
100
100
|
TextArgs,
|
|
101
101
|
Thread,
|
|
102
102
|
UsageHandler,
|
|
103
103
|
UserActionCtx,
|
|
104
104
|
} from "./types.js";
|
|
105
|
-
import
|
|
105
|
+
import { inlineMessagesFiles } from "./files.js";
|
|
106
|
+
import type { DataModel } from "../component/_generated/dataModel.js";
|
|
106
107
|
|
|
107
108
|
export { stepCountIs } from "ai";
|
|
108
109
|
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
109
110
|
export {
|
|
110
|
-
serializeDataOrUrl,
|
|
111
111
|
deserializeMessage,
|
|
112
|
+
serializeDataOrUrl,
|
|
112
113
|
serializeMessage,
|
|
113
114
|
} from "../mapping.js";
|
|
114
115
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
@@ -127,12 +128,12 @@ export {
|
|
|
127
128
|
vUserMessage,
|
|
128
129
|
} from "../validators.js";
|
|
129
130
|
export type { ToolCtx } from "./createTool.js";
|
|
130
|
-
export { getFile, storeFile } from "./files.js";
|
|
131
131
|
export {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
definePlaygroundAPI,
|
|
133
|
+
type AgentsFn,
|
|
134
|
+
type PlaygroundAPI,
|
|
135
|
+
} from "./definePlaygroundAPI.js";
|
|
136
|
+
export { getFile, storeFile } from "./files.js";
|
|
136
137
|
export {
|
|
137
138
|
listMessages,
|
|
138
139
|
saveMessage,
|
|
@@ -140,13 +141,13 @@ export {
|
|
|
140
141
|
type SaveMessageArgs,
|
|
141
142
|
type SaveMessagesArgs,
|
|
142
143
|
} from "./messages.js";
|
|
144
|
+
export {
|
|
145
|
+
fetchContextMessages,
|
|
146
|
+
filterOutOrphanedToolMessages,
|
|
147
|
+
} from "./search.js";
|
|
148
|
+
export { abortStream, listStreams, syncStreams } from "./streaming.js";
|
|
143
149
|
export { createThread, getThreadMetadata } from "./threads.js";
|
|
144
150
|
export { createTool, extractText, isTool };
|
|
145
|
-
export {
|
|
146
|
-
definePlaygroundAPI,
|
|
147
|
-
type PlaygroundAPI,
|
|
148
|
-
type AgentsFn,
|
|
149
|
-
} from "./definePlaygroundAPI.js";
|
|
150
151
|
export type {
|
|
151
152
|
AgentComponent,
|
|
152
153
|
ContextOptions,
|
|
@@ -162,6 +163,55 @@ export type {
|
|
|
162
163
|
UsageHandler,
|
|
163
164
|
};
|
|
164
165
|
|
|
166
|
+
export type Config = {
|
|
167
|
+
/**
|
|
168
|
+
* The LLM model to use for generating / streaming text and objects.
|
|
169
|
+
* e.g.
|
|
170
|
+
* import { openai } from "@ai-sdk/openai"
|
|
171
|
+
* const myAgent = new Agent(components.agent, {
|
|
172
|
+
* languageModel: openai.chat("gpt-4o-mini"),
|
|
173
|
+
*/
|
|
174
|
+
languageModel?: LanguageModel;
|
|
175
|
+
/**
|
|
176
|
+
* The model to use for text embeddings. Optional.
|
|
177
|
+
* If specified, it will use this for generating vector embeddings
|
|
178
|
+
* of chats, and can opt-in to doing vector search for automatic context
|
|
179
|
+
* on generateText, etc.
|
|
180
|
+
* e.g.
|
|
181
|
+
* import { openai } from "@ai-sdk/openai"
|
|
182
|
+
* const myAgent = new Agent(components.agent, {
|
|
183
|
+
* ...
|
|
184
|
+
* textEmbeddingModel: openai.embedding("text-embedding-3-small")
|
|
185
|
+
*/
|
|
186
|
+
textEmbeddingModel?: EmbeddingModel<string>;
|
|
187
|
+
/**
|
|
188
|
+
* Options to determine what messages are included as context in message
|
|
189
|
+
* generation. To disable any messages automatically being added, pass:
|
|
190
|
+
* { recentMessages: 0 }
|
|
191
|
+
*/
|
|
192
|
+
contextOptions?: ContextOptions;
|
|
193
|
+
/**
|
|
194
|
+
* Determines whether messages are automatically stored when passed as
|
|
195
|
+
* arguments or generated.
|
|
196
|
+
*/
|
|
197
|
+
storageOptions?: StorageOptions;
|
|
198
|
+
/**
|
|
199
|
+
* The default settings to use for the LLM calls.
|
|
200
|
+
* This can be overridden at each generate/stream callsite on a per-field
|
|
201
|
+
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
202
|
+
*/
|
|
203
|
+
callSettings?: CallSettings;
|
|
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
|
+
|
|
165
215
|
export class Agent<
|
|
166
216
|
/**
|
|
167
217
|
* You can require that all `ctx` args to generateText & streamText
|
|
@@ -188,7 +238,7 @@ export class Agent<
|
|
|
188
238
|
> {
|
|
189
239
|
constructor(
|
|
190
240
|
public component: AgentComponent,
|
|
191
|
-
public options: {
|
|
241
|
+
public options: Config & {
|
|
192
242
|
/**
|
|
193
243
|
* The name for the agent. This will be attributed on each message
|
|
194
244
|
* created by this agent.
|
|
@@ -199,20 +249,9 @@ export class Agent<
|
|
|
199
249
|
* e.g.
|
|
200
250
|
* import { openai } from "@ai-sdk/openai"
|
|
201
251
|
* const myAgent = new Agent(components.agent, {
|
|
202
|
-
*
|
|
252
|
+
* languageModel: openai.chat("gpt-4o-mini"),
|
|
203
253
|
*/
|
|
204
|
-
|
|
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")
|
|
214
|
-
*/
|
|
215
|
-
textEmbedding?: EmbeddingModel<string>;
|
|
254
|
+
languageModel: LanguageModel;
|
|
216
255
|
/**
|
|
217
256
|
* The default system prompt to put in each request.
|
|
218
257
|
* Override per-prompt by passing the "system" parameter.
|
|
@@ -225,37 +264,15 @@ export class Agent<
|
|
|
225
264
|
* (import { createTool } from "@convex-dev/agent")
|
|
226
265
|
*/
|
|
227
266
|
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
267
|
/**
|
|
240
268
|
* When generating or streaming text with tools available, this
|
|
241
269
|
* determines when to stop. Defaults to stepCountIs(1).
|
|
242
270
|
*/
|
|
243
271
|
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
244
272
|
/**
|
|
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.
|
|
273
|
+
* @deprecated Use `languageEmbeddingModel` instead.
|
|
257
274
|
*/
|
|
258
|
-
|
|
275
|
+
chat?: LanguageModel;
|
|
259
276
|
},
|
|
260
277
|
) {}
|
|
261
278
|
|
|
@@ -269,7 +286,7 @@ export class Agent<
|
|
|
269
286
|
* @param args The thread metadata.
|
|
270
287
|
* @returns The threadId of the new thread and the thread object.
|
|
271
288
|
*/
|
|
272
|
-
async createThread
|
|
289
|
+
async createThread(
|
|
273
290
|
ctx: RunActionCtx & CustomCtx,
|
|
274
291
|
args?: {
|
|
275
292
|
/**
|
|
@@ -285,21 +302,8 @@ export class Agent<
|
|
|
285
302
|
* The summary of the thread. Not currently used for anything.
|
|
286
303
|
*/
|
|
287
304
|
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
305
|
},
|
|
299
|
-
): Promise<{
|
|
300
|
-
threadId: string;
|
|
301
|
-
thread: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
|
|
302
|
-
}>;
|
|
306
|
+
): Promise<{ threadId: string; thread: Thread<AgentTools> }>;
|
|
303
307
|
/**
|
|
304
308
|
* Start a new thread with the agent. This will have a fresh history, though if
|
|
305
309
|
* you pass in a userId you can have it search across other threads for relevant
|
|
@@ -309,7 +313,7 @@ export class Agent<
|
|
|
309
313
|
* @param args The thread metadata.
|
|
310
314
|
* @returns The threadId of the new thread.
|
|
311
315
|
*/
|
|
312
|
-
async createThread
|
|
316
|
+
async createThread(
|
|
313
317
|
ctx: RunMutationCtx,
|
|
314
318
|
args?: {
|
|
315
319
|
/**
|
|
@@ -325,31 +329,12 @@ export class Agent<
|
|
|
325
329
|
* The summary of the thread. Not currently used for anything.
|
|
326
330
|
*/
|
|
327
331
|
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
332
|
},
|
|
339
333
|
): Promise<{ threadId: string }>;
|
|
340
|
-
async createThread
|
|
334
|
+
async createThread(
|
|
341
335
|
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
|
-
}> {
|
|
336
|
+
args?: { userId: string | null; title?: string; summary?: string },
|
|
337
|
+
): Promise<{ threadId: string; thread?: Thread<AgentTools> }> {
|
|
353
338
|
const threadId = await createThread(ctx, this.component, args);
|
|
354
339
|
if (!("runAction" in ctx) || "workflowId" in ctx) {
|
|
355
340
|
return { threadId };
|
|
@@ -357,8 +342,6 @@ export class Agent<
|
|
|
357
342
|
const { thread } = await this.continueThread(ctx, {
|
|
358
343
|
threadId,
|
|
359
344
|
userId: args?.userId,
|
|
360
|
-
usageHandler: args?.usageHandler,
|
|
361
|
-
tools: args?.tools,
|
|
362
345
|
});
|
|
363
346
|
return { threadId, thread };
|
|
364
347
|
}
|
|
@@ -371,7 +354,7 @@ export class Agent<
|
|
|
371
354
|
* @param { threadId, userId }: the thread and user to associate the messages with.
|
|
372
355
|
* @returns Functions bound to the userId and threadId on a `{thread}` object.
|
|
373
356
|
*/
|
|
374
|
-
async continueThread
|
|
357
|
+
async continueThread(
|
|
375
358
|
ctx: ActionCtx & CustomCtx,
|
|
376
359
|
args: {
|
|
377
360
|
/**
|
|
@@ -383,20 +366,8 @@ export class Agent<
|
|
|
383
366
|
* relevant messages from the same user as context for the LLM calls.
|
|
384
367
|
*/
|
|
385
368
|
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
369
|
},
|
|
397
|
-
): Promise<{
|
|
398
|
-
thread: Thread<ThreadTools extends undefined ? AgentTools : ThreadTools>;
|
|
399
|
-
}> {
|
|
370
|
+
): Promise<{ thread: Thread<AgentTools> }> {
|
|
400
371
|
return {
|
|
401
372
|
thread: {
|
|
402
373
|
threadId: args.threadId,
|
|
@@ -412,7 +383,250 @@ export class Agent<
|
|
|
412
383
|
streamText: this.streamText.bind(this, ctx, args),
|
|
413
384
|
generateObject: this.generateObject.bind(this, ctx, args),
|
|
414
385
|
streamObject: this.streamObject.bind(this, ctx, args),
|
|
415
|
-
} as Thread<
|
|
386
|
+
} as Thread<AgentTools>,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async start<TOOLS extends ToolSet | undefined, T>(
|
|
391
|
+
ctx: ActionCtx & CustomCtx,
|
|
392
|
+
/**
|
|
393
|
+
* These are the arguments you'll pass to the LLM call such as
|
|
394
|
+
* `generateText` or `streamText`. This function will look up the context
|
|
395
|
+
* and provide functions to save the steps, abort the generation, and more.
|
|
396
|
+
* The type of the arguments returned infers from the type of the arguments
|
|
397
|
+
* you pass here.
|
|
398
|
+
*/
|
|
399
|
+
args: T & {
|
|
400
|
+
/**
|
|
401
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
402
|
+
* instead of the prompt or messages.
|
|
403
|
+
* This is useful if you want to first save a user message, then use it as
|
|
404
|
+
* the prompt for the LLM call in another call.
|
|
405
|
+
*/
|
|
406
|
+
promptMessageId?: string;
|
|
407
|
+
/**
|
|
408
|
+
* The model to use for the LLM calls. This will override the model specified
|
|
409
|
+
* in the Agent constructor.
|
|
410
|
+
*/
|
|
411
|
+
model?: LanguageModel;
|
|
412
|
+
/**
|
|
413
|
+
* The tools to use for the tool calls. This will override tools specified
|
|
414
|
+
* in the Agent constructor or createThread / continueThread.
|
|
415
|
+
*/
|
|
416
|
+
tools?: TOOLS;
|
|
417
|
+
/**
|
|
418
|
+
* The single prompt message to use for the LLM call. This will be the
|
|
419
|
+
* last message in the context. If it's a string, it will be a user role.
|
|
420
|
+
*/
|
|
421
|
+
prompt?: string | (ModelMessage | Message)[];
|
|
422
|
+
/**
|
|
423
|
+
* If provided alongside prompt, the ordering will be:
|
|
424
|
+
* 1. system prompt
|
|
425
|
+
* 2. search context
|
|
426
|
+
* 3. recent messages
|
|
427
|
+
* 4. these messages
|
|
428
|
+
* 5. prompt messages, including those already on the same `order` as
|
|
429
|
+
* the promptMessageId message, if provided.
|
|
430
|
+
*/
|
|
431
|
+
messages?: (ModelMessage | Message)[];
|
|
432
|
+
/**
|
|
433
|
+
* This will be the first message in the context, and overrides the
|
|
434
|
+
* agent's instructions.
|
|
435
|
+
*/
|
|
436
|
+
system?: string;
|
|
437
|
+
/**
|
|
438
|
+
* The abort signal to be passed to the LLM call. If triggered, it will
|
|
439
|
+
* mark the pending message as failed. If the generation is asynchronously
|
|
440
|
+
* aborted, it will trigger this signal when detected.
|
|
441
|
+
*/
|
|
442
|
+
abortSignal?: AbortSignal;
|
|
443
|
+
// We optimistically override the generateId function to use the pending
|
|
444
|
+
// message id.
|
|
445
|
+
_internal?: { generateId?: IdGenerator };
|
|
446
|
+
},
|
|
447
|
+
options?: Options & { userId?: string | null; threadId?: string },
|
|
448
|
+
): Promise<{
|
|
449
|
+
args: T & {
|
|
450
|
+
system?: string;
|
|
451
|
+
model: LanguageModel;
|
|
452
|
+
messages: ModelMessage[];
|
|
453
|
+
// abortSignal?: AbortSignal;
|
|
454
|
+
tools?: TOOLS extends undefined ? AgentTools : TOOLS;
|
|
455
|
+
} & CallSettings;
|
|
456
|
+
order: number;
|
|
457
|
+
stepOrder: number;
|
|
458
|
+
userId: string | undefined;
|
|
459
|
+
promptMessageId: string | undefined;
|
|
460
|
+
updateModel: (model: LanguageModel | undefined) => void;
|
|
461
|
+
save: <TOOLS extends ToolSet>(
|
|
462
|
+
toSave:
|
|
463
|
+
| { step: StepResult<TOOLS> }
|
|
464
|
+
| { object: GenerateObjectResult<unknown> },
|
|
465
|
+
createPendingMessage?: boolean,
|
|
466
|
+
) => Promise<void>;
|
|
467
|
+
fail: (reason: string) => Promise<void>;
|
|
468
|
+
getSavedMessages: () => MessageDoc[];
|
|
469
|
+
}> {
|
|
470
|
+
const { threadId, ...opts } = { ...this.options, ...options };
|
|
471
|
+
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
472
|
+
userId: options?.userId,
|
|
473
|
+
threadId: options?.threadId,
|
|
474
|
+
...opts,
|
|
475
|
+
});
|
|
476
|
+
let pendingMessageId = context.pendingMessageId;
|
|
477
|
+
// TODO: extract pending message if one exists
|
|
478
|
+
const { args: aiArgs, promptMessageId, order, stepOrder, userId } = context;
|
|
479
|
+
const messages = context.savedMessages ?? [];
|
|
480
|
+
if (pendingMessageId) {
|
|
481
|
+
if (!aiArgs._internal?.generateId) {
|
|
482
|
+
aiArgs._internal = {
|
|
483
|
+
...aiArgs._internal,
|
|
484
|
+
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const toolCtx = {
|
|
489
|
+
...(ctx as UserActionCtx & CustomCtx),
|
|
490
|
+
userId,
|
|
491
|
+
threadId,
|
|
492
|
+
promptMessageId,
|
|
493
|
+
agent: this,
|
|
494
|
+
} satisfies ToolCtx;
|
|
495
|
+
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
496
|
+
const tools = wrapTools(toolCtx, args.tools ?? this.options.tools) as Tools;
|
|
497
|
+
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
498
|
+
const fail = async (reason: string) => {
|
|
499
|
+
if (threadId && promptMessageId) {
|
|
500
|
+
console.error("RollbackMessage", promptMessageId, reason);
|
|
501
|
+
}
|
|
502
|
+
if (pendingMessageId) {
|
|
503
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
504
|
+
messageId: pendingMessageId,
|
|
505
|
+
result: { status: "failed", error: reason },
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
let activeModel = aiArgs.model;
|
|
510
|
+
if (aiArgs.abortSignal) {
|
|
511
|
+
const abortSignal = aiArgs.abortSignal;
|
|
512
|
+
aiArgs.abortSignal.addEventListener(
|
|
513
|
+
"abort",
|
|
514
|
+
async () => {
|
|
515
|
+
await fail(abortSignal.reason ?? "Aborted");
|
|
516
|
+
},
|
|
517
|
+
{ once: true },
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
args: {
|
|
522
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
523
|
+
stopWhen: (args as any).stopWhen ?? this.options.stopWhen,
|
|
524
|
+
...aiArgs,
|
|
525
|
+
tools,
|
|
526
|
+
// abortSignal: abortController.signal,
|
|
527
|
+
},
|
|
528
|
+
order: order ?? 0,
|
|
529
|
+
stepOrder: stepOrder ?? 0,
|
|
530
|
+
userId,
|
|
531
|
+
promptMessageId,
|
|
532
|
+
getSavedMessages: () => messages,
|
|
533
|
+
updateModel: (model: LanguageModel | undefined) => {
|
|
534
|
+
if (model) {
|
|
535
|
+
activeModel = model;
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
fail,
|
|
539
|
+
save: async <TOOLS extends ToolSet>(
|
|
540
|
+
toSave:
|
|
541
|
+
| { step: StepResult<TOOLS> }
|
|
542
|
+
| { object: GenerateObjectResult<unknown> },
|
|
543
|
+
createPendingMessage?: boolean,
|
|
544
|
+
) => {
|
|
545
|
+
if (threadId && promptMessageId && saveOutput) {
|
|
546
|
+
const metadata = {
|
|
547
|
+
// TODO: get up to date one when user selects mid-generation
|
|
548
|
+
model: getModelName(activeModel),
|
|
549
|
+
provider: getProviderName(activeModel),
|
|
550
|
+
};
|
|
551
|
+
const serialized =
|
|
552
|
+
"object" in toSave
|
|
553
|
+
? await serializeObjectResult(
|
|
554
|
+
ctx,
|
|
555
|
+
this.component,
|
|
556
|
+
toSave.object,
|
|
557
|
+
metadata,
|
|
558
|
+
)
|
|
559
|
+
: await serializeNewMessagesInStep(
|
|
560
|
+
ctx,
|
|
561
|
+
this.component,
|
|
562
|
+
toSave.step,
|
|
563
|
+
metadata,
|
|
564
|
+
);
|
|
565
|
+
const embeddings = await this.generateEmbeddings(
|
|
566
|
+
ctx,
|
|
567
|
+
{ userId, threadId },
|
|
568
|
+
serialized.messages.map((m) => m.message),
|
|
569
|
+
);
|
|
570
|
+
if (createPendingMessage) {
|
|
571
|
+
serialized.messages.push({
|
|
572
|
+
message: { role: "assistant", content: [] },
|
|
573
|
+
status: "pending",
|
|
574
|
+
});
|
|
575
|
+
embeddings?.vectors.push(null);
|
|
576
|
+
}
|
|
577
|
+
const saved = await ctx.runMutation(
|
|
578
|
+
this.component.messages.addMessages,
|
|
579
|
+
{
|
|
580
|
+
userId,
|
|
581
|
+
threadId,
|
|
582
|
+
agentName: this.options.name,
|
|
583
|
+
promptMessageId,
|
|
584
|
+
pendingMessageId,
|
|
585
|
+
messages: serialized.messages,
|
|
586
|
+
embeddings,
|
|
587
|
+
failPendingSteps: false,
|
|
588
|
+
},
|
|
589
|
+
);
|
|
590
|
+
const lastMessage = saved.messages.at(-1)!;
|
|
591
|
+
if (createPendingMessage) {
|
|
592
|
+
if (lastMessage.status === "failed") {
|
|
593
|
+
pendingMessageId = undefined;
|
|
594
|
+
messages.push(...saved.messages);
|
|
595
|
+
await fail(
|
|
596
|
+
lastMessage.error ??
|
|
597
|
+
"Aborting - the pending message was marked as failed",
|
|
598
|
+
);
|
|
599
|
+
} else {
|
|
600
|
+
pendingMessageId = lastMessage._id;
|
|
601
|
+
messages.push(...saved.messages.slice(0, -1));
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
pendingMessageId = undefined;
|
|
605
|
+
messages.push(...saved.messages);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
609
|
+
if (this.options.rawRequestResponseHandler) {
|
|
610
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
611
|
+
userId,
|
|
612
|
+
threadId,
|
|
613
|
+
agentName: this.options.name,
|
|
614
|
+
request: output.request,
|
|
615
|
+
response: output.response,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
if (opts.usageHandler && output.usage) {
|
|
619
|
+
await opts.usageHandler(ctx, {
|
|
620
|
+
userId,
|
|
621
|
+
threadId,
|
|
622
|
+
agentName: this.options.name,
|
|
623
|
+
model: getModelName(activeModel),
|
|
624
|
+
provider: getProviderName(activeModel),
|
|
625
|
+
usage: output.usage,
|
|
626
|
+
providerMetadata: output.providerMetadata,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
},
|
|
416
630
|
};
|
|
417
631
|
}
|
|
418
632
|
|
|
@@ -424,7 +638,7 @@ export class Agent<
|
|
|
424
638
|
* to a thread (and optionally userId).
|
|
425
639
|
* @param ctx The context passed from the action function calling this.
|
|
426
640
|
* @param { userId, threadId }: The user and thread to associate the message with
|
|
427
|
-
* @param
|
|
641
|
+
* @param generateTextArgs The arguments to the generateText function, along with extra controls
|
|
428
642
|
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
429
643
|
* @returns The result of the generateText function.
|
|
430
644
|
*/
|
|
@@ -434,102 +648,44 @@ export class Agent<
|
|
|
434
648
|
OUTPUT_PARTIAL = never,
|
|
435
649
|
>(
|
|
436
650
|
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>,
|
|
651
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
652
|
+
generateTextArgs: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>,
|
|
454
653
|
options?: Options,
|
|
455
654
|
): Promise<
|
|
456
655
|
GenerateTextResult<TOOLS extends undefined ? AgentTools : TOOLS, OUTPUT> &
|
|
457
656
|
GenerationOutputMetadata
|
|
458
657
|
> {
|
|
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;
|
|
658
|
+
const { args, promptMessageId, order, ...call } = await this.start(
|
|
659
|
+
ctx,
|
|
660
|
+
generateTextArgs,
|
|
661
|
+
{ ...threadOpts, ...options },
|
|
662
|
+
);
|
|
663
|
+
|
|
474
664
|
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";
|
|
665
|
+
const steps: StepResult<Tools>[] = [];
|
|
480
666
|
try {
|
|
481
667
|
const result = (await generateText<Tools, OUTPUT, OUTPUT_PARTIAL>({
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
668
|
+
...args,
|
|
669
|
+
prepareStep: async (options) => {
|
|
670
|
+
const result = await generateTextArgs.prepareStep?.(options);
|
|
671
|
+
call.updateModel(result?.model ?? options.model);
|
|
672
|
+
return result;
|
|
673
|
+
},
|
|
486
674
|
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);
|
|
675
|
+
steps.push(step);
|
|
676
|
+
await call.save({ step }, await willContinue(steps, args.stopWhen));
|
|
677
|
+
return generateTextArgs.onStepFinish?.(step);
|
|
519
678
|
},
|
|
520
|
-
})) as GenerateTextResult<Tools, OUTPUT
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
679
|
+
})) as GenerateTextResult<Tools, OUTPUT>;
|
|
680
|
+
const metadata: GenerationOutputMetadata = {
|
|
681
|
+
promptMessageId,
|
|
682
|
+
order,
|
|
683
|
+
savedMessages: call.getSavedMessages(),
|
|
684
|
+
messageId: promptMessageId,
|
|
685
|
+
};
|
|
686
|
+
return Object.assign(result, metadata);
|
|
525
687
|
} 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
|
-
}
|
|
688
|
+
await call.fail(errorToString(error));
|
|
533
689
|
throw error;
|
|
534
690
|
}
|
|
535
691
|
}
|
|
@@ -547,22 +703,16 @@ export class Agent<
|
|
|
547
703
|
PARTIAL_OUTPUT = never,
|
|
548
704
|
>(
|
|
549
705
|
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
|
-
},
|
|
706
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
562
707
|
/**
|
|
563
708
|
* The arguments to the streamText function, similar to the ai `streamText` function.
|
|
564
709
|
*/
|
|
565
|
-
|
|
710
|
+
streamTextArgs: StreamingTextArgs<
|
|
711
|
+
AgentTools,
|
|
712
|
+
TOOLS,
|
|
713
|
+
OUTPUT,
|
|
714
|
+
PARTIAL_OUTPUT
|
|
715
|
+
>,
|
|
566
716
|
/**
|
|
567
717
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
568
718
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
@@ -587,112 +737,80 @@ export class Agent<
|
|
|
587
737
|
> &
|
|
588
738
|
GenerationOutputMetadata
|
|
589
739
|
> {
|
|
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";
|
|
740
|
+
const { threadId } = threadOpts;
|
|
741
|
+
const { args, userId, order, stepOrder, promptMessageId, ...call } =
|
|
742
|
+
await this.start(ctx, streamTextArgs, { ...threadOpts, ...options });
|
|
743
|
+
|
|
744
|
+
type Tools = TOOLS extends undefined ? AgentTools : TOOLS;
|
|
745
|
+
const steps: StepResult<Tools>[] = [];
|
|
746
|
+
|
|
747
|
+
const opts = { ...this.options, ...options };
|
|
610
748
|
const streamer =
|
|
611
749
|
threadId && opts.saveStreamDeltas
|
|
612
750
|
? new DeltaStreamer(this.component, ctx, opts.saveStreamDeltas, {
|
|
613
751
|
threadId,
|
|
614
752
|
userId,
|
|
615
753
|
agentName: this.options.name,
|
|
616
|
-
model:
|
|
617
|
-
provider:
|
|
618
|
-
providerOptions:
|
|
754
|
+
model: getModelName(args.model),
|
|
755
|
+
provider: getProviderName(args.model),
|
|
756
|
+
providerOptions: args.providerOptions,
|
|
619
757
|
order,
|
|
620
758
|
stepOrder,
|
|
621
|
-
abortSignal:
|
|
759
|
+
abortSignal: args.abortSignal,
|
|
622
760
|
})
|
|
623
761
|
: undefined;
|
|
624
762
|
|
|
625
763
|
const result = streamText({
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
tools,
|
|
630
|
-
abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
|
|
764
|
+
...args,
|
|
765
|
+
abortSignal: streamer?.abortController.signal ?? args.abortSignal,
|
|
766
|
+
// TODO: this is probably why reasoning isn't streaming
|
|
631
767
|
experimental_transform: mergeTransforms(
|
|
632
768
|
options?.saveStreamDeltas,
|
|
633
|
-
|
|
769
|
+
streamTextArgs.experimental_transform,
|
|
634
770
|
),
|
|
635
771
|
onChunk: async (event) => {
|
|
636
772
|
await streamer?.addParts([event.chunk]);
|
|
637
773
|
// console.log("onChunk", chunk);
|
|
638
|
-
return
|
|
774
|
+
return streamTextArgs.onChunk?.(event);
|
|
639
775
|
},
|
|
640
776
|
onError: async (error) => {
|
|
641
777
|
console.error("onError", error);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
778
|
+
await call.fail(errorToString(error.error));
|
|
779
|
+
await streamer?.fail(errorToString(error.error));
|
|
780
|
+
return streamTextArgs.onError?.(error);
|
|
781
|
+
},
|
|
782
|
+
// onFinish: async (event) => {
|
|
783
|
+
// return streamTextArgs.onFinish?.(event);
|
|
784
|
+
// },
|
|
785
|
+
prepareStep: async (options) => {
|
|
786
|
+
const result = await streamTextArgs.prepareStep?.(options);
|
|
787
|
+
if (result) {
|
|
788
|
+
const model = result.model ?? options.model;
|
|
789
|
+
call.updateModel(model);
|
|
790
|
+
return result;
|
|
647
791
|
}
|
|
648
|
-
|
|
649
|
-
return args.onError?.(error);
|
|
792
|
+
return undefined;
|
|
650
793
|
},
|
|
651
794
|
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
|
-
});
|
|
795
|
+
steps.push(step);
|
|
796
|
+
const createPendingMessage = await willContinue(steps, args.stopWhen);
|
|
797
|
+
await call.save({ step }, createPendingMessage);
|
|
798
|
+
if (!createPendingMessage) {
|
|
799
|
+
await streamer?.finish();
|
|
684
800
|
}
|
|
685
801
|
return args.onStepFinish?.(step);
|
|
686
802
|
},
|
|
687
803
|
}) as StreamTextResult<
|
|
688
804
|
TOOLS extends undefined ? AgentTools : TOOLS,
|
|
689
805
|
PARTIAL_OUTPUT
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
806
|
+
>;
|
|
807
|
+
const metadata: GenerationOutputMetadata = {
|
|
808
|
+
promptMessageId,
|
|
809
|
+
order,
|
|
810
|
+
savedMessages: call.getSavedMessages(),
|
|
811
|
+
messageId: promptMessageId,
|
|
812
|
+
};
|
|
813
|
+
return Object.assign(result, metadata);
|
|
696
814
|
}
|
|
697
815
|
|
|
698
816
|
/**
|
|
@@ -702,84 +820,45 @@ export class Agent<
|
|
|
702
820
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
703
821
|
* to a thread (and optionally userId).
|
|
704
822
|
*/
|
|
705
|
-
async generateObject<
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
},
|
|
823
|
+
async generateObject<
|
|
824
|
+
SCHEMA extends ObjectSchema = DefaultObjectSchema,
|
|
825
|
+
OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string
|
|
826
|
+
? "enum"
|
|
827
|
+
: "object",
|
|
828
|
+
RESULT = OUTPUT extends "array"
|
|
829
|
+
? Array<InferSchema<SCHEMA>>
|
|
830
|
+
: InferSchema<SCHEMA>,
|
|
831
|
+
>(
|
|
832
|
+
ctx: ActionCtx & CustomCtx,
|
|
833
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
716
834
|
/**
|
|
717
835
|
* The arguments to the generateObject function, similar to the ai.generateObject function.
|
|
718
836
|
*/
|
|
719
|
-
|
|
837
|
+
generateObjectArgs: GenerateObjectArgs<SCHEMA, OUTPUT, RESULT>,
|
|
720
838
|
/**
|
|
721
839
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
722
840
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
723
841
|
*/
|
|
724
842
|
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";
|
|
843
|
+
): Promise<GenerateObjectResult<RESULT> & GenerationOutputMetadata> {
|
|
844
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } =
|
|
845
|
+
await this.start(ctx, generateObjectArgs, { ...threadOpts, ...options });
|
|
846
|
+
|
|
735
847
|
try {
|
|
736
848
|
const result = (await generateObject(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
)) as GenerateObjectResult<T> & GenerationOutputMetadata;
|
|
849
|
+
args,
|
|
850
|
+
)) as GenerateObjectResult<RESULT>;
|
|
740
851
|
|
|
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;
|
|
852
|
+
await save({ object: result });
|
|
853
|
+
const metadata: GenerationOutputMetadata = {
|
|
854
|
+
promptMessageId,
|
|
855
|
+
order,
|
|
856
|
+
savedMessages: getSavedMessages(),
|
|
857
|
+
messageId: promptMessageId,
|
|
858
|
+
};
|
|
859
|
+
return Object.assign(result, metadata);
|
|
776
860
|
} catch (error) {
|
|
777
|
-
|
|
778
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
779
|
-
messageId,
|
|
780
|
-
error: (error as Error).message,
|
|
781
|
-
});
|
|
782
|
-
}
|
|
861
|
+
await fail(errorToString(error));
|
|
783
862
|
throw error;
|
|
784
863
|
}
|
|
785
864
|
}
|
|
@@ -791,97 +870,83 @@ export class Agent<
|
|
|
791
870
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
792
871
|
* to a thread (and optionally userId).
|
|
793
872
|
*/
|
|
794
|
-
async streamObject<
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
},
|
|
873
|
+
async streamObject<
|
|
874
|
+
SCHEMA extends ObjectSchema = DefaultObjectSchema,
|
|
875
|
+
OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string
|
|
876
|
+
? "enum"
|
|
877
|
+
: "object",
|
|
878
|
+
RESULT = OUTPUT extends "array"
|
|
879
|
+
? Array<InferSchema<SCHEMA>>
|
|
880
|
+
: InferSchema<SCHEMA>,
|
|
881
|
+
>(
|
|
882
|
+
ctx: ActionCtx & CustomCtx,
|
|
883
|
+
threadOpts: { userId?: string | null; threadId?: string },
|
|
805
884
|
/**
|
|
806
885
|
* The arguments to the streamObject function, similar to the ai `streamObject` function.
|
|
807
886
|
*/
|
|
808
|
-
|
|
887
|
+
streamObjectArgs: StreamObjectArgs<SCHEMA, OUTPUT, RESULT> & {
|
|
888
|
+
/**
|
|
889
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
890
|
+
* instead of the prompt or messages.
|
|
891
|
+
* This is useful if you want to first save a user message, then use it as
|
|
892
|
+
* the prompt for the LLM call in another call.
|
|
893
|
+
*/
|
|
894
|
+
promptMessageId?: string;
|
|
895
|
+
/**
|
|
896
|
+
* The model to use for the LLM calls. This will override the model specified
|
|
897
|
+
* in the Agent constructor.
|
|
898
|
+
*/
|
|
899
|
+
model?: LanguageModel;
|
|
900
|
+
/**
|
|
901
|
+
* The tools to use for the tool calls. This will override tools specified
|
|
902
|
+
* in the Agent constructor or createThread / continueThread.
|
|
903
|
+
*/
|
|
904
|
+
},
|
|
809
905
|
/**
|
|
810
906
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
811
907
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
812
908
|
*/
|
|
813
909
|
options?: Options,
|
|
814
910
|
): Promise<
|
|
815
|
-
|
|
911
|
+
ReturnType<typeof streamObject<SCHEMA, OUTPUT, RESULT>> &
|
|
912
|
+
GenerationOutputMetadata
|
|
816
913
|
> {
|
|
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>({
|
|
914
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } =
|
|
915
|
+
await this.start(ctx, streamObjectArgs, { ...threadOpts, ...options });
|
|
916
|
+
|
|
917
|
+
const stream = streamObject<SCHEMA, OUTPUT, RESULT>({
|
|
829
918
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
830
|
-
...(
|
|
919
|
+
...(args as any),
|
|
831
920
|
onError: async (error) => {
|
|
832
|
-
console.error("onError", error);
|
|
921
|
+
console.error(" streamObject onError", error);
|
|
922
|
+
// TODO: content that we have so far
|
|
923
|
+
// content: stream.fullStream.
|
|
924
|
+
await fail(errorToString(error.error));
|
|
833
925
|
return args.onError?.(error);
|
|
834
926
|
},
|
|
835
927
|
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,
|
|
928
|
+
await save({
|
|
929
|
+
object: {
|
|
930
|
+
object: result.object,
|
|
931
|
+
finishReason: result.error ? "error" : "stop",
|
|
863
932
|
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,
|
|
933
|
+
warnings: result.warnings,
|
|
872
934
|
request: await stream.request,
|
|
873
935
|
response: result.response,
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
936
|
+
providerMetadata: result.providerMetadata,
|
|
937
|
+
toJsonResponse: stream.toTextStreamResponse,
|
|
938
|
+
},
|
|
939
|
+
});
|
|
940
|
+
return args.onFinish?.(result);
|
|
878
941
|
},
|
|
879
|
-
})
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
942
|
+
});
|
|
943
|
+
const metadata: GenerationOutputMetadata = {
|
|
944
|
+
promptMessageId,
|
|
945
|
+
order,
|
|
946
|
+
savedMessages: getSavedMessages(),
|
|
947
|
+
messageId: promptMessageId,
|
|
948
|
+
};
|
|
949
|
+
return Object.assign(stream, metadata);
|
|
885
950
|
}
|
|
886
951
|
|
|
887
952
|
/**
|
|
@@ -915,6 +980,7 @@ export class Agent<
|
|
|
915
980
|
: [args.message],
|
|
916
981
|
metadata: args.metadata ? [args.metadata] : undefined,
|
|
917
982
|
skipEmbeddings: args.skipEmbeddings,
|
|
983
|
+
pendingMessageId: args.pendingMessageId,
|
|
918
984
|
});
|
|
919
985
|
const message = messages.at(-1)!;
|
|
920
986
|
return { messageId: message._id, message };
|
|
@@ -944,7 +1010,7 @@ export class Agent<
|
|
|
944
1010
|
const { skipEmbeddings, ...rest } = args;
|
|
945
1011
|
if (args.embeddings) {
|
|
946
1012
|
embeddings = args.embeddings;
|
|
947
|
-
} else if (!skipEmbeddings && this.options.
|
|
1013
|
+
} else if (!skipEmbeddings && this.options.textEmbeddingModel) {
|
|
948
1014
|
if (!("runAction" in ctx)) {
|
|
949
1015
|
console.warn(
|
|
950
1016
|
"You're trying to save messages and generate embeddings, but you're in a mutation. " +
|
|
@@ -1050,8 +1116,8 @@ export class Agent<
|
|
|
1050
1116
|
getEmbedding: async (text) => {
|
|
1051
1117
|
assert("runAction" in ctx);
|
|
1052
1118
|
assert(
|
|
1053
|
-
this.options.
|
|
1054
|
-
"A
|
|
1119
|
+
this.options.textEmbeddingModel,
|
|
1120
|
+
"A textEmbeddingModel is required to be set on the Agent that you're doing vector search with",
|
|
1055
1121
|
);
|
|
1056
1122
|
return {
|
|
1057
1123
|
embedding: (
|
|
@@ -1061,7 +1127,7 @@ export class Agent<
|
|
|
1061
1127
|
values: [text],
|
|
1062
1128
|
})
|
|
1063
1129
|
).embeddings[0],
|
|
1064
|
-
|
|
1130
|
+
textEmbeddingModel: this.options.textEmbeddingModel,
|
|
1065
1131
|
};
|
|
1066
1132
|
},
|
|
1067
1133
|
});
|
|
@@ -1116,7 +1182,7 @@ export class Agent<
|
|
|
1116
1182
|
}: { userId: string | undefined; threadId: string | undefined },
|
|
1117
1183
|
messages: (ModelMessage | Message)[],
|
|
1118
1184
|
) {
|
|
1119
|
-
if (!this.options.
|
|
1185
|
+
if (!this.options.textEmbeddingModel) {
|
|
1120
1186
|
return undefined;
|
|
1121
1187
|
}
|
|
1122
1188
|
let embeddings:
|
|
@@ -1134,11 +1200,12 @@ export class Agent<
|
|
|
1134
1200
|
if (textIndexes.length === 0) {
|
|
1135
1201
|
return undefined;
|
|
1136
1202
|
}
|
|
1203
|
+
const values = messageTexts.filter((t): t is string => !!t);
|
|
1137
1204
|
// Then embed those messages.
|
|
1138
1205
|
const textEmbeddings = await this.doEmbed(ctx, {
|
|
1139
1206
|
userId,
|
|
1140
1207
|
threadId,
|
|
1141
|
-
values
|
|
1208
|
+
values,
|
|
1142
1209
|
});
|
|
1143
1210
|
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
1144
1211
|
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
@@ -1148,7 +1215,7 @@ export class Agent<
|
|
|
1148
1215
|
if (textEmbeddings.embeddings.length > 0) {
|
|
1149
1216
|
const dimension = textEmbeddings.embeddings[0].length;
|
|
1150
1217
|
validateVectorDimension(dimension);
|
|
1151
|
-
const model = getModelName(this.options.
|
|
1218
|
+
const model = getModelName(this.options.textEmbeddingModel);
|
|
1152
1219
|
embeddings = { vectors: embeddingsOrNull, dimension, model };
|
|
1153
1220
|
}
|
|
1154
1221
|
return embeddings;
|
|
@@ -1204,9 +1271,9 @@ export class Agent<
|
|
|
1204
1271
|
messagesMissingEmbeddings.map((m) => deserializeMessage(m!.message!)),
|
|
1205
1272
|
);
|
|
1206
1273
|
if (!embeddings) {
|
|
1207
|
-
if (!this.options.
|
|
1274
|
+
if (!this.options.textEmbeddingModel) {
|
|
1208
1275
|
throw new Error(
|
|
1209
|
-
"No embeddings were generated for the messages. You must pass a
|
|
1276
|
+
"No embeddings were generated for the messages. You must pass a textEmbeddingModel to the agent constructor.",
|
|
1210
1277
|
);
|
|
1211
1278
|
}
|
|
1212
1279
|
throw new Error(
|
|
@@ -1262,13 +1329,13 @@ export class Agent<
|
|
|
1262
1329
|
provider?: string;
|
|
1263
1330
|
},
|
|
1264
1331
|
): Promise<{ messages: MessageDoc[] }> {
|
|
1265
|
-
const messages = await serializeNewMessagesInStep(
|
|
1332
|
+
const { messages } = await serializeNewMessagesInStep(
|
|
1266
1333
|
ctx,
|
|
1267
1334
|
this.component,
|
|
1268
1335
|
args.step,
|
|
1269
1336
|
{
|
|
1270
|
-
provider: args.provider ?? getProviderName(this.options.
|
|
1271
|
-
model: args.model ?? getModelName(this.options.
|
|
1337
|
+
provider: args.provider ?? getProviderName(this.options.languageModel),
|
|
1338
|
+
model: args.model ?? getModelName(this.options.languageModel),
|
|
1272
1339
|
},
|
|
1273
1340
|
);
|
|
1274
1341
|
const embeddings = await this.generateEmbeddings(
|
|
@@ -1311,14 +1378,20 @@ export class Agent<
|
|
|
1311
1378
|
this.component,
|
|
1312
1379
|
args.result,
|
|
1313
1380
|
{
|
|
1314
|
-
model:
|
|
1315
|
-
|
|
1381
|
+
model:
|
|
1382
|
+
args.model ??
|
|
1383
|
+
args.metadata?.model ??
|
|
1384
|
+
getModelName(this.options.languageModel),
|
|
1385
|
+
provider:
|
|
1386
|
+
args.provider ??
|
|
1387
|
+
args.metadata?.provider ??
|
|
1388
|
+
getProviderName(this.options.languageModel),
|
|
1316
1389
|
},
|
|
1317
1390
|
);
|
|
1318
1391
|
const embeddings = await this.generateEmbeddings(
|
|
1319
1392
|
ctx,
|
|
1320
1393
|
{ userId: args.userId, threadId: args.threadId },
|
|
1321
|
-
messages.map((m) =>
|
|
1394
|
+
messages.map((m) => m.message),
|
|
1322
1395
|
);
|
|
1323
1396
|
|
|
1324
1397
|
return ctx.runMutation(this.component.messages.addMessages, {
|
|
@@ -1340,25 +1413,17 @@ export class Agent<
|
|
|
1340
1413
|
* @param args What message to save. Generally the parent message sent into
|
|
1341
1414
|
* the generateText call.
|
|
1342
1415
|
*/
|
|
1343
|
-
async
|
|
1416
|
+
async finalizeMessage(
|
|
1344
1417
|
ctx: RunMutationCtx,
|
|
1345
1418
|
args: {
|
|
1346
|
-
threadId: string;
|
|
1347
1419
|
messageId: string;
|
|
1348
|
-
result: {
|
|
1420
|
+
result: { status: "failed"; error: string } | { status: "success" };
|
|
1349
1421
|
},
|
|
1350
1422
|
): 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
|
-
}
|
|
1423
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
1424
|
+
messageId: args.messageId,
|
|
1425
|
+
result: args.result,
|
|
1426
|
+
});
|
|
1362
1427
|
}
|
|
1363
1428
|
|
|
1364
1429
|
/**
|
|
@@ -1530,12 +1595,12 @@ export class Agent<
|
|
|
1530
1595
|
|
|
1531
1596
|
async _saveMessagesAndFetchContext<
|
|
1532
1597
|
T extends {
|
|
1533
|
-
id?: string;
|
|
1534
1598
|
prompt?: string | (ModelMessage | Message)[];
|
|
1535
1599
|
messages?: (ModelMessage | Message)[];
|
|
1536
1600
|
system?: string;
|
|
1537
1601
|
promptMessageId?: string;
|
|
1538
|
-
|
|
1602
|
+
pendingMessageId?: string;
|
|
1603
|
+
model?: LanguageModel;
|
|
1539
1604
|
},
|
|
1540
1605
|
>(
|
|
1541
1606
|
ctx: RunActionCtx,
|
|
@@ -1545,11 +1610,16 @@ export class Agent<
|
|
|
1545
1610
|
threadId,
|
|
1546
1611
|
contextOptions,
|
|
1547
1612
|
storageOptions,
|
|
1548
|
-
}: {
|
|
1613
|
+
}: {
|
|
1614
|
+
userId: string | null | undefined;
|
|
1615
|
+
threadId: string | undefined;
|
|
1616
|
+
} & Options,
|
|
1549
1617
|
): Promise<{
|
|
1550
|
-
args: T
|
|
1618
|
+
args: Extract<T, { model: LanguageModel; messages: ModelMessage[] }> &
|
|
1619
|
+
CallSettings;
|
|
1551
1620
|
userId: string | undefined;
|
|
1552
|
-
|
|
1621
|
+
promptMessageId: string | undefined;
|
|
1622
|
+
pendingMessageId: string | undefined;
|
|
1553
1623
|
order: number | undefined;
|
|
1554
1624
|
stepOrder: number | undefined;
|
|
1555
1625
|
savedMessages: MessageDoc[] | undefined;
|
|
@@ -1565,7 +1635,8 @@ export class Agent<
|
|
|
1565
1635
|
argsUserId ??
|
|
1566
1636
|
(threadId &&
|
|
1567
1637
|
(await ctx.runQuery(this.component.threads.getThread, { threadId }))
|
|
1568
|
-
?.userId)
|
|
1638
|
+
?.userId) ??
|
|
1639
|
+
undefined;
|
|
1569
1640
|
// If only a messageId is provided, this will add that message to the end.
|
|
1570
1641
|
const contextMessages = await this.fetchContextMessages(ctx, {
|
|
1571
1642
|
userId,
|
|
@@ -1584,32 +1655,46 @@ export class Agent<
|
|
|
1584
1655
|
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
1585
1656
|
: undefined;
|
|
1586
1657
|
|
|
1587
|
-
let
|
|
1658
|
+
let promptMessageId = promptMessage?._id;
|
|
1588
1659
|
let order = promptMessage?.order;
|
|
1589
1660
|
let stepOrder = promptMessage?.stepOrder;
|
|
1590
1661
|
let savedMessages = undefined;
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
messages
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1662
|
+
let pendingMessageId = undefined;
|
|
1663
|
+
if (threadId && storageOptions?.saveMessages !== "none") {
|
|
1664
|
+
let saved: { messages: MessageDoc[] };
|
|
1665
|
+
if (
|
|
1666
|
+
messages.length + prompt.length &&
|
|
1667
|
+
// If it was a promptMessageId, we don't want to save it again.
|
|
1668
|
+
(!args.promptMessageId || storageOptions?.saveMessages === "all")
|
|
1669
|
+
) {
|
|
1670
|
+
const saveAll = storageOptions?.saveMessages === "all";
|
|
1671
|
+
const coreMessages = [...messages, ...prompt];
|
|
1672
|
+
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
1673
|
+
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
1674
|
+
saved = await this.saveMessages(ctx, {
|
|
1675
|
+
threadId,
|
|
1676
|
+
userId,
|
|
1677
|
+
messages: [...toSave, { role: "assistant", content: [] }],
|
|
1678
|
+
metadata: [...metadata, { status: "pending" }],
|
|
1679
|
+
failPendingSteps: true,
|
|
1680
|
+
pendingMessageId: args.pendingMessageId,
|
|
1681
|
+
});
|
|
1682
|
+
promptMessageId = saved.messages.at(-2)!._id;
|
|
1683
|
+
} else {
|
|
1684
|
+
saved = await this.saveMessages(ctx, {
|
|
1685
|
+
threadId,
|
|
1686
|
+
userId,
|
|
1687
|
+
messages: [{ role: "assistant", content: [] }],
|
|
1688
|
+
metadata: [{ status: "pending" }],
|
|
1689
|
+
failPendingSteps: true,
|
|
1690
|
+
pendingMessageId: args.pendingMessageId,
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
pendingMessageId = saved.messages.at(-1)!._id;
|
|
1610
1694
|
order = saved.messages.at(-1)!.order;
|
|
1611
1695
|
stepOrder = saved.messages.at(-1)!.stepOrder;
|
|
1612
|
-
|
|
1696
|
+
// Don't return the pending message
|
|
1697
|
+
savedMessages = saved.messages.slice(0, -1);
|
|
1613
1698
|
}
|
|
1614
1699
|
|
|
1615
1700
|
if (promptMessage?.message) {
|
|
@@ -1620,7 +1705,7 @@ export class Agent<
|
|
|
1620
1705
|
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
1621
1706
|
// embeddings yet. This can happen if the message was saved in a mutation
|
|
1622
1707
|
// where the LLM is not available.
|
|
1623
|
-
if (!promptMessage.embeddingId && this.options.
|
|
1708
|
+
if (!promptMessage.embeddingId && this.options.textEmbeddingModel) {
|
|
1624
1709
|
await this._generateAndSaveEmbeddings(ctx, [promptMessage]);
|
|
1625
1710
|
}
|
|
1626
1711
|
}
|
|
@@ -1650,7 +1735,7 @@ export class Agent<
|
|
|
1650
1735
|
|
|
1651
1736
|
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
1652
1737
|
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
1653
|
-
processedMessages = await
|
|
1738
|
+
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
1654
1739
|
}
|
|
1655
1740
|
|
|
1656
1741
|
const { prompt: _, model, ...rest } = args;
|
|
@@ -1658,12 +1743,14 @@ export class Agent<
|
|
|
1658
1743
|
args: {
|
|
1659
1744
|
...this.options.callSettings,
|
|
1660
1745
|
...rest,
|
|
1661
|
-
model: model ?? this.options.
|
|
1746
|
+
model: model ?? this.options.languageModel,
|
|
1662
1747
|
system: args.system ?? this.options.instructions,
|
|
1663
1748
|
messages: processedMessages,
|
|
1664
|
-
} as T
|
|
1749
|
+
} as Extract<T, { model: LanguageModel; messages: ModelMessage[] }> &
|
|
1750
|
+
CallSettings,
|
|
1665
1751
|
userId,
|
|
1666
|
-
|
|
1752
|
+
promptMessageId,
|
|
1753
|
+
pendingMessageId,
|
|
1667
1754
|
savedMessages,
|
|
1668
1755
|
order,
|
|
1669
1756
|
stepOrder,
|
|
@@ -1680,10 +1767,10 @@ export class Agent<
|
|
|
1680
1767
|
headers?: Record<string, string>;
|
|
1681
1768
|
},
|
|
1682
1769
|
): Promise<{ embeddings: number[][] }> {
|
|
1683
|
-
const embeddingModel = this.options.
|
|
1770
|
+
const embeddingModel = this.options.textEmbeddingModel;
|
|
1684
1771
|
assert(
|
|
1685
1772
|
embeddingModel,
|
|
1686
|
-
"a
|
|
1773
|
+
"a textEmbeddingModel is required to be set on the Agent that you're doing vector search with",
|
|
1687
1774
|
);
|
|
1688
1775
|
const result = await embedMany({
|
|
1689
1776
|
...this.options.callSettings,
|
|
@@ -1710,83 +1797,6 @@ export class Agent<
|
|
|
1710
1797
|
return { embeddings: result.embeddings };
|
|
1711
1798
|
}
|
|
1712
1799
|
|
|
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
1800
|
/**
|
|
1791
1801
|
* WORKFLOW UTILITIES
|
|
1792
1802
|
*/
|
|
@@ -1830,23 +1840,7 @@ export class Agent<
|
|
|
1830
1840
|
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
|
|
1831
1841
|
*/
|
|
1832
1842
|
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;
|
|
1843
|
+
spec: MaybeCustomCtx<CustomCtx, DataModel, AgentTools> & {
|
|
1850
1844
|
/**
|
|
1851
1845
|
* Whether to stream the text.
|
|
1852
1846
|
* If false, it will generate the text in a single call. (default)
|
|
@@ -1855,36 +1849,14 @@ export class Agent<
|
|
|
1855
1849
|
* defaults if you pass true.
|
|
1856
1850
|
*/
|
|
1857
1851
|
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 }),
|
|
1852
|
+
/**
|
|
1853
|
+
* When to stop generating text.
|
|
1854
|
+
* Defaults to the {@link Agent["options"].stopWhen} option.
|
|
1855
|
+
*/
|
|
1856
|
+
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
|
|
1857
|
+
} & Options,
|
|
1858
|
+
overrides?: CallSettings,
|
|
1886
1859
|
) {
|
|
1887
|
-
const stopWhen = spec?.stopWhen ?? this.options.stopWhen;
|
|
1888
1860
|
return internalActionGeneric({
|
|
1889
1861
|
args: vTextArgs,
|
|
1890
1862
|
handler: async (ctx_, args) => {
|
|
@@ -1892,7 +1864,8 @@ export class Agent<
|
|
|
1892
1864
|
args.stream === true ? spec?.stream || true : (spec?.stream ?? false);
|
|
1893
1865
|
const targetArgs = { userId: args.userId, threadId: args.threadId };
|
|
1894
1866
|
const llmArgs = {
|
|
1895
|
-
stopWhen,
|
|
1867
|
+
stopWhen: spec?.stopWhen ?? this.options.stopWhen,
|
|
1868
|
+
...overrides,
|
|
1896
1869
|
...omit(args, ["storageOptions", "contextOptions"]),
|
|
1897
1870
|
messages: args.messages?.map(deserializeMessage),
|
|
1898
1871
|
prompt: Array.isArray(args.prompt)
|
|
@@ -1905,7 +1878,7 @@ export class Agent<
|
|
|
1905
1878
|
}
|
|
1906
1879
|
const opts = {
|
|
1907
1880
|
...this.options,
|
|
1908
|
-
...
|
|
1881
|
+
...pick(spec, ["contextOptions", "storageOptions"]),
|
|
1909
1882
|
...pick(args, ["contextOptions", "storageOptions"]),
|
|
1910
1883
|
saveStreamDeltas: stream,
|
|
1911
1884
|
};
|
|
@@ -1925,10 +1898,11 @@ export class Agent<
|
|
|
1925
1898
|
await result.consumeStream();
|
|
1926
1899
|
return {
|
|
1927
1900
|
text: await result.text,
|
|
1928
|
-
|
|
1901
|
+
promptMessageId: result.promptMessageId,
|
|
1929
1902
|
order: result.order,
|
|
1930
1903
|
finishReason: await result.finishReason,
|
|
1931
1904
|
warnings: result.warnings,
|
|
1905
|
+
savedMessageIds: result.savedMessages?.map((m) => m._id) ?? [],
|
|
1932
1906
|
};
|
|
1933
1907
|
} else {
|
|
1934
1908
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1940,10 +1914,11 @@ export class Agent<
|
|
|
1940
1914
|
);
|
|
1941
1915
|
return {
|
|
1942
1916
|
text: res.text,
|
|
1943
|
-
|
|
1917
|
+
promptMessageId: res.promptMessageId,
|
|
1944
1918
|
order: res.order,
|
|
1945
1919
|
finishReason: res.finishReason,
|
|
1946
1920
|
warnings: res.warnings,
|
|
1921
|
+
savedMessageIds: res.savedMessages?.map((m) => m._id) ?? [],
|
|
1947
1922
|
};
|
|
1948
1923
|
}
|
|
1949
1924
|
},
|
|
@@ -1957,32 +1932,44 @@ export class Agent<
|
|
|
1957
1932
|
* and stopWhen.
|
|
1958
1933
|
*/
|
|
1959
1934
|
asObjectAction<T>(
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1935
|
+
objectArgs: Omit<
|
|
1936
|
+
Parameters<typeof generateObject<FlexibleSchema<T>>>[0],
|
|
1937
|
+
"model"
|
|
1938
|
+
>,
|
|
1939
|
+
options?: Options & MaybeCustomCtx<CustomCtx, DataModel, AgentTools>,
|
|
1965
1940
|
) {
|
|
1966
1941
|
return internalActionGeneric({
|
|
1967
1942
|
args: vSafeObjectArgs,
|
|
1968
|
-
handler: async (
|
|
1969
|
-
const { userId, threadId, ...rest } = args;
|
|
1943
|
+
handler: async (ctx_, args) => {
|
|
1944
|
+
const { userId, threadId, callSettings, ...rest } = args;
|
|
1970
1945
|
const overrides = pick(rest, ["contextOptions", "storageOptions"]);
|
|
1971
|
-
const
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1946
|
+
const targetArgs = { userId, threadId };
|
|
1947
|
+
const llmArgs = {
|
|
1948
|
+
...objectArgs,
|
|
1949
|
+
...callSettings,
|
|
1950
|
+
...omit(rest, ["storageOptions", "contextOptions"]),
|
|
1951
|
+
messages: args.messages?.map(deserializeMessage),
|
|
1952
|
+
prompt: Array.isArray(args.prompt)
|
|
1953
|
+
? args.prompt.map(deserializeMessage)
|
|
1954
|
+
: args.prompt,
|
|
1955
|
+
} as Omit<Parameters<typeof generateObject>[0], "model">;
|
|
1956
|
+
const ctx = (
|
|
1957
|
+
options?.customCtx
|
|
1958
|
+
? { ...ctx_, ...options.customCtx(ctx_, targetArgs, llmArgs) }
|
|
1959
|
+
: ctx_
|
|
1960
|
+
) as UserActionCtx & CustomCtx;
|
|
1961
|
+
const value = await this.generateObject(ctx, targetArgs, llmArgs, {
|
|
1962
|
+
...this.options,
|
|
1963
|
+
...options,
|
|
1964
|
+
...overrides,
|
|
1965
|
+
});
|
|
1980
1966
|
return {
|
|
1981
|
-
object: value.object as T,
|
|
1982
|
-
|
|
1967
|
+
object: convexToJson(value.object as Value) as T,
|
|
1968
|
+
promptMessageId: value.promptMessageId,
|
|
1983
1969
|
order: value.order,
|
|
1984
1970
|
finishReason: value.finishReason,
|
|
1985
1971
|
warnings: value.warnings,
|
|
1972
|
+
savedMessageIds: value.savedMessages?.map((m) => m._id) ?? [],
|
|
1986
1973
|
};
|
|
1987
1974
|
},
|
|
1988
1975
|
});
|
|
@@ -2033,3 +2020,30 @@ export class Agent<
|
|
|
2033
2020
|
});
|
|
2034
2021
|
}
|
|
2035
2022
|
}
|
|
2023
|
+
|
|
2024
|
+
async function willContinue(
|
|
2025
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2026
|
+
steps: StepResult<any>[],
|
|
2027
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2028
|
+
stopWhen: StopCondition<any> | Array<StopCondition<any>> | undefined,
|
|
2029
|
+
): Promise<boolean> {
|
|
2030
|
+
const step = steps.at(-1)!;
|
|
2031
|
+
// we aren't doing another round after a tool result
|
|
2032
|
+
// TODO: whether to handle continuing after too much context used..
|
|
2033
|
+
if (step.finishReason !== "tool-calls") return false;
|
|
2034
|
+
// we don't have a tool result, so we'll wait for more
|
|
2035
|
+
if (step.toolCalls.length > step.toolResults.length) return false;
|
|
2036
|
+
if (Array.isArray(stopWhen)) {
|
|
2037
|
+
return (await Promise.all(stopWhen.map(async (s) => s({ steps })))).every(
|
|
2038
|
+
(stop) => !stop,
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
return !(await stopWhen?.({ steps }));
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
function errorToString(error: unknown): string {
|
|
2045
|
+
if (error instanceof Error) {
|
|
2046
|
+
return error.message;
|
|
2047
|
+
}
|
|
2048
|
+
return String(error);
|
|
2049
|
+
}
|