@convex-dev/agent 0.1.18 → 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 +30 -11
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +24 -10
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/defaultComponent.d.ts +11 -0
- package/dist/client/defaultComponent.d.ts.map +1 -0
- package/dist/client/defaultComponent.js +7 -0
- package/dist/client/defaultComponent.js.map +1 -0
- package/dist/client/definePlaygroundAPI.d.ts +68 -23
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +7 -20
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +14 -4
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +66 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +323 -780
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +445 -543
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +256 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +97 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/search.d.ts +35 -12
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +21 -4
- package/dist/client/search.js.map +1 -1
- package/dist/client/streaming.d.ts +21 -21
- 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/threads.d.ts +46 -0
- package/dist/client/threads.d.ts.map +1 -0
- package/dist/client/threads.js +49 -0
- package/dist/client/threads.js.map +1 -0
- package/dist/client/types.d.ts +159 -75
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +261 -209
- package/dist/component/messages.d.ts +314 -105
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +141 -83
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +631 -870
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +11 -16
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +29 -149
- 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 +15 -16
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +208 -62
- 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 -5
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +89 -53
- 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/toUIMessages.d.ts +5 -4
- package/dist/react/toUIMessages.d.ts.map +1 -1
- package/dist/react/toUIMessages.js +103 -40
- package/dist/react/toUIMessages.js.map +1 -1
- package/dist/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 +751 -1464
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +39 -80
- package/dist/validators.js.map +1 -1
- package/package.json +34 -30
- package/src/client/createTool.ts +66 -40
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +17 -31
- package/src/client/files.ts +94 -17
- package/src/client/index.test.ts +28 -38
- package/src/client/index.ts +785 -977
- package/src/client/messages.ts +205 -0
- package/src/client/search.ts +37 -10
- package/src/client/streaming.ts +53 -56
- package/src/client/threads.ts +78 -0
- package/src/client/types.ts +239 -131
- package/src/component/_generated/api.d.ts +261 -209
- package/src/component/messages.test.ts +182 -40
- package/src/component/messages.ts +165 -108
- package/src/component/schema.ts +11 -17
- package/src/component/streams.ts +180 -69
- package/src/mapping.ts +253 -96
- 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 +116 -65
- package/src/react/index.ts +35 -13
- package/src/react/optimisticallySendMessage.ts +2 -8
- package/src/react/toUIMessages.test.ts +154 -36
- package/src/react/toUIMessages.ts +136 -57
- package/src/react/useSmoothText.ts +14 -15
- package/src/validators.test.ts +1 -117
- package/src/validators.ts +57 -107
- package/dist/client/listMessages.d.ts +0 -22
- package/dist/client/listMessages.d.ts.map +0 -1
- package/dist/client/listMessages.js +0 -25
- package/dist/client/listMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
package/dist/client/index.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import { embedMany, generateObject, generateText, streamObject, streamText, } from "ai";
|
|
1
|
+
import { embedMany, generateObject, generateText, stepCountIs, streamObject, streamText, } from "ai";
|
|
2
2
|
import { assert, omit, pick } from "convex-helpers";
|
|
3
3
|
import { internalActionGeneric, internalMutationGeneric, } from "convex/server";
|
|
4
|
-
import { v } from "convex/values";
|
|
4
|
+
import { convexToJson, v } from "convex/values";
|
|
5
5
|
import { validateVectorDimension, } from "../component/vector/tables.js";
|
|
6
|
-
import { deserializeMessage,
|
|
6
|
+
import { deserializeMessage, serializeMessage, serializeNewMessagesInStep, serializeObjectResult, } from "../mapping.js";
|
|
7
7
|
import { extractText, isTool } from "../shared.js";
|
|
8
|
-
import { vMessageWithMetadata, vSafeObjectArgs, vTextArgs, } from "../validators.js";
|
|
8
|
+
import { vMessageEmbeddings, vMessageWithMetadata, vSafeObjectArgs, vTextArgs, } from "../validators.js";
|
|
9
9
|
import { createTool, wrapTools } from "./createTool.js";
|
|
10
|
-
import { listMessages } from "./
|
|
11
|
-
import { fetchContextMessages } from "./search.js";
|
|
10
|
+
import { listMessages, saveMessages, } from "./messages.js";
|
|
11
|
+
import { fetchContextMessages, getModelName, getProviderName, } from "./search.js";
|
|
12
12
|
import { DeltaStreamer, mergeTransforms, syncStreams, } from "./streaming.js";
|
|
13
|
+
import { createThread, getThreadMetadata } from "./threads.js";
|
|
14
|
+
import { inlineMessagesFiles } from "./files.js";
|
|
15
|
+
export { stepCountIs } from "ai";
|
|
13
16
|
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
|
|
14
|
-
export { serializeDataOrUrl } from "../mapping.js";
|
|
17
|
+
export { deserializeMessage, serializeDataOrUrl, serializeMessage, } from "../mapping.js";
|
|
15
18
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
16
19
|
// a future version may put them all here or move these over there
|
|
17
20
|
export { vAssistantMessage, vContextOptions, vMessage, vPaginationResult, vProviderMetadata, vStorageOptions, vStreamArgs, vSystemMessage, vToolMessage, vUsage, vUserMessage, } from "../validators.js";
|
|
18
|
-
export { getFile, storeFile } from "./files.js";
|
|
19
|
-
export { filterOutOrphanedToolMessages } from "./search.js";
|
|
20
|
-
export { abortStream, listStreams } from "./streaming.js";
|
|
21
|
-
export { createTool, extractText, fetchContextMessages, isTool, listMessages, syncStreams, };
|
|
22
21
|
export { definePlaygroundAPI, } from "./definePlaygroundAPI.js";
|
|
22
|
+
export { getFile, storeFile } from "./files.js";
|
|
23
|
+
export { listMessages, saveMessage, saveMessages, } from "./messages.js";
|
|
24
|
+
export { fetchContextMessages, filterOutOrphanedToolMessages, } from "./search.js";
|
|
25
|
+
export { abortStream, listStreams, syncStreams } from "./streaming.js";
|
|
26
|
+
export { createThread, getThreadMetadata } from "./threads.js";
|
|
27
|
+
export { createTool, extractText, isTool };
|
|
23
28
|
export class Agent {
|
|
24
29
|
component;
|
|
25
30
|
options;
|
|
@@ -35,13 +40,8 @@ export class Agent {
|
|
|
35
40
|
const { thread } = await this.continueThread(ctx, {
|
|
36
41
|
threadId,
|
|
37
42
|
userId: args?.userId,
|
|
38
|
-
usageHandler: args?.usageHandler,
|
|
39
|
-
tools: args?.tools,
|
|
40
43
|
});
|
|
41
|
-
return {
|
|
42
|
-
threadId,
|
|
43
|
-
thread,
|
|
44
|
-
};
|
|
44
|
+
return { threadId, thread };
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Continues a thread using this agent. Note: threads can be continued
|
|
@@ -69,99 +69,188 @@ export class Agent {
|
|
|
69
69
|
},
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
+
async start(ctx,
|
|
72
73
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return ctx.runQuery(this.component.threads.searchThreadTitles, {
|
|
79
|
-
userId,
|
|
80
|
-
query,
|
|
81
|
-
limit: limit ?? 10,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* This behaves like {@link generateText} from the "ai" package except that
|
|
86
|
-
* it add context based on the userId and threadId and saves the input and
|
|
87
|
-
* resulting messages to the thread, if specified.
|
|
88
|
-
* Use {@link continueThread} to get a version of this function already scoped
|
|
89
|
-
* to a thread (and optionally userId).
|
|
90
|
-
* @param ctx The context passed from the action function calling this.
|
|
91
|
-
* @param { userId, threadId }: The user and thread to associate the message with
|
|
92
|
-
* @param args The arguments to the generateText function, along with extra controls
|
|
93
|
-
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
94
|
-
* @returns The result of the generateText function.
|
|
74
|
+
* These are the arguments you'll pass to the LLM call such as
|
|
75
|
+
* `generateText` or `streamText`. This function will look up the context
|
|
76
|
+
* and provide functions to save the steps, abort the generation, and more.
|
|
77
|
+
* The type of the arguments returned infers from the type of the arguments
|
|
78
|
+
* you pass here.
|
|
95
79
|
*/
|
|
96
|
-
|
|
97
|
-
const opts = { ...this.options, ...options
|
|
80
|
+
args, options) {
|
|
81
|
+
const { threadId, ...opts } = { ...this.options, ...options };
|
|
98
82
|
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
99
|
-
userId:
|
|
100
|
-
threadId,
|
|
83
|
+
userId: options?.userId,
|
|
84
|
+
threadId: options?.threadId,
|
|
101
85
|
...opts,
|
|
102
86
|
});
|
|
103
|
-
|
|
87
|
+
let pendingMessageId = context.pendingMessageId;
|
|
88
|
+
// TODO: extract pending message if one exists
|
|
89
|
+
const { args: aiArgs, promptMessageId, order, stepOrder, userId } = context;
|
|
90
|
+
const messages = context.savedMessages ?? [];
|
|
91
|
+
if (pendingMessageId) {
|
|
92
|
+
if (!aiArgs._internal?.generateId) {
|
|
93
|
+
aiArgs._internal = {
|
|
94
|
+
...aiArgs._internal,
|
|
95
|
+
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
104
99
|
const toolCtx = {
|
|
105
100
|
...ctx,
|
|
106
101
|
userId,
|
|
107
102
|
threadId,
|
|
108
|
-
|
|
103
|
+
promptMessageId,
|
|
109
104
|
agent: this,
|
|
110
105
|
};
|
|
111
|
-
const tools = wrapTools(toolCtx, args.tools ??
|
|
106
|
+
const tools = wrapTools(toolCtx, args.tools ?? this.options.tools);
|
|
112
107
|
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
108
|
+
const fail = async (reason) => {
|
|
109
|
+
if (threadId && promptMessageId) {
|
|
110
|
+
console.error("RollbackMessage", promptMessageId, reason);
|
|
111
|
+
}
|
|
112
|
+
if (pendingMessageId) {
|
|
113
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
114
|
+
messageId: pendingMessageId,
|
|
115
|
+
result: { status: "failed", error: reason },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
let activeModel = aiArgs.model;
|
|
120
|
+
if (aiArgs.abortSignal) {
|
|
121
|
+
const abortSignal = aiArgs.abortSignal;
|
|
122
|
+
aiArgs.abortSignal.addEventListener("abort", async () => {
|
|
123
|
+
await fail(abortSignal.reason ?? "Aborted");
|
|
124
|
+
}, { once: true });
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
args: {
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
+
stopWhen: args.stopWhen ?? this.options.stopWhen,
|
|
117
130
|
...aiArgs,
|
|
118
131
|
tools,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
// abortSignal: abortController.signal,
|
|
133
|
+
},
|
|
134
|
+
order: order ?? 0,
|
|
135
|
+
stepOrder: stepOrder ?? 0,
|
|
136
|
+
userId,
|
|
137
|
+
promptMessageId,
|
|
138
|
+
getSavedMessages: () => messages,
|
|
139
|
+
updateModel: (model) => {
|
|
140
|
+
if (model) {
|
|
141
|
+
activeModel = model;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
fail,
|
|
145
|
+
save: async (toSave, createPendingMessage) => {
|
|
146
|
+
if (threadId && promptMessageId && saveOutput) {
|
|
147
|
+
const metadata = {
|
|
148
|
+
// TODO: get up to date one when user selects mid-generation
|
|
149
|
+
model: getModelName(activeModel),
|
|
150
|
+
provider: getProviderName(activeModel),
|
|
151
|
+
};
|
|
152
|
+
const serialized = "object" in toSave
|
|
153
|
+
? await serializeObjectResult(ctx, this.component, toSave.object, metadata)
|
|
154
|
+
: await serializeNewMessagesInStep(ctx, this.component, toSave.step, metadata);
|
|
155
|
+
const embeddings = await this.generateEmbeddings(ctx, { userId, threadId }, serialized.messages.map((m) => m.message));
|
|
156
|
+
if (createPendingMessage) {
|
|
157
|
+
serialized.messages.push({
|
|
158
|
+
message: { role: "assistant", content: [] },
|
|
159
|
+
status: "pending",
|
|
128
160
|
});
|
|
161
|
+
embeddings?.vectors.push(null);
|
|
129
162
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
const saved = await ctx.runMutation(this.component.messages.addMessages, {
|
|
164
|
+
userId,
|
|
165
|
+
threadId,
|
|
166
|
+
agentName: this.options.name,
|
|
167
|
+
promptMessageId,
|
|
168
|
+
pendingMessageId,
|
|
169
|
+
messages: serialized.messages,
|
|
170
|
+
embeddings,
|
|
171
|
+
failPendingSteps: false,
|
|
172
|
+
});
|
|
173
|
+
const lastMessage = saved.messages.at(-1);
|
|
174
|
+
if (createPendingMessage) {
|
|
175
|
+
if (lastMessage.status === "failed") {
|
|
176
|
+
pendingMessageId = undefined;
|
|
177
|
+
messages.push(...saved.messages);
|
|
178
|
+
await fail(lastMessage.error ??
|
|
179
|
+
"Aborting - the pending message was marked as failed");
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
pendingMessageId = lastMessage._id;
|
|
183
|
+
messages.push(...saved.messages.slice(0, -1));
|
|
184
|
+
}
|
|
138
185
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
threadId,
|
|
143
|
-
agentName: this.options.name,
|
|
144
|
-
model: aiArgs.model.modelId,
|
|
145
|
-
provider: aiArgs.model.provider,
|
|
146
|
-
usage: step.usage,
|
|
147
|
-
providerMetadata: step.providerMetadata,
|
|
148
|
-
});
|
|
186
|
+
else {
|
|
187
|
+
pendingMessageId = undefined;
|
|
188
|
+
messages.push(...saved.messages);
|
|
149
189
|
}
|
|
150
|
-
|
|
190
|
+
}
|
|
191
|
+
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
192
|
+
if (this.options.rawRequestResponseHandler) {
|
|
193
|
+
await this.options.rawRequestResponseHandler(ctx, {
|
|
194
|
+
userId,
|
|
195
|
+
threadId,
|
|
196
|
+
agentName: this.options.name,
|
|
197
|
+
request: output.request,
|
|
198
|
+
response: output.response,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (opts.usageHandler && output.usage) {
|
|
202
|
+
await opts.usageHandler(ctx, {
|
|
203
|
+
userId,
|
|
204
|
+
threadId,
|
|
205
|
+
agentName: this.options.name,
|
|
206
|
+
model: getModelName(activeModel),
|
|
207
|
+
provider: getProviderName(activeModel),
|
|
208
|
+
usage: output.usage,
|
|
209
|
+
providerMetadata: output.providerMetadata,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* This behaves like {@link generateText} from the "ai" package except that
|
|
217
|
+
* it add context based on the userId and threadId and saves the input and
|
|
218
|
+
* resulting messages to the thread, if specified.
|
|
219
|
+
* Use {@link continueThread} to get a version of this function already scoped
|
|
220
|
+
* to a thread (and optionally userId).
|
|
221
|
+
* @param ctx The context passed from the action function calling this.
|
|
222
|
+
* @param { userId, threadId }: The user and thread to associate the message with
|
|
223
|
+
* @param generateTextArgs The arguments to the generateText function, along with extra controls
|
|
224
|
+
* for the {@link ContextOptions} and {@link StorageOptions}.
|
|
225
|
+
* @returns The result of the generateText function.
|
|
226
|
+
*/
|
|
227
|
+
async generateText(ctx, threadOpts, generateTextArgs, options) {
|
|
228
|
+
const { args, promptMessageId, order, ...call } = await this.start(ctx, generateTextArgs, { ...threadOpts, ...options });
|
|
229
|
+
const steps = [];
|
|
230
|
+
try {
|
|
231
|
+
const result = (await generateText({
|
|
232
|
+
...args,
|
|
233
|
+
prepareStep: async (options) => {
|
|
234
|
+
const result = await generateTextArgs.prepareStep?.(options);
|
|
235
|
+
call.updateModel(result?.model ?? options.model);
|
|
236
|
+
return result;
|
|
237
|
+
},
|
|
238
|
+
onStepFinish: async (step) => {
|
|
239
|
+
steps.push(step);
|
|
240
|
+
await call.save({ step }, await willContinue(steps, args.stopWhen));
|
|
241
|
+
return generateTextArgs.onStepFinish?.(step);
|
|
151
242
|
},
|
|
152
243
|
}));
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
244
|
+
const metadata = {
|
|
245
|
+
promptMessageId,
|
|
246
|
+
order,
|
|
247
|
+
savedMessages: call.getSavedMessages(),
|
|
248
|
+
messageId: promptMessageId,
|
|
249
|
+
};
|
|
250
|
+
return Object.assign(result, metadata);
|
|
156
251
|
}
|
|
157
252
|
catch (error) {
|
|
158
|
-
|
|
159
|
-
console.error("RollbackMessage", messageId);
|
|
160
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
161
|
-
messageId,
|
|
162
|
-
error: error.message,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
253
|
+
await call.fail(errorToString(error));
|
|
165
254
|
throw error;
|
|
166
255
|
}
|
|
167
256
|
}
|
|
@@ -172,109 +261,78 @@ export class Agent {
|
|
|
172
261
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
173
262
|
* to a thread (and optionally userId).
|
|
174
263
|
*/
|
|
175
|
-
async streamText(ctx,
|
|
176
|
-
/** Note: to get better type inference, pass tools in the next arg */
|
|
177
|
-
tools: threadTools, ...usageHandler },
|
|
264
|
+
async streamText(ctx, threadOpts,
|
|
178
265
|
/**
|
|
179
266
|
* The arguments to the streamText function, similar to the ai `streamText` function.
|
|
180
267
|
*/
|
|
181
|
-
|
|
268
|
+
streamTextArgs,
|
|
182
269
|
/**
|
|
183
270
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
184
271
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
185
272
|
*/
|
|
186
273
|
options) {
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
...opts,
|
|
192
|
-
});
|
|
193
|
-
const { args: aiArgs, messageId, order, stepOrder, userId } = context;
|
|
194
|
-
const toolCtx = {
|
|
195
|
-
...ctx,
|
|
196
|
-
userId,
|
|
197
|
-
threadId,
|
|
198
|
-
messageId,
|
|
199
|
-
agent: this,
|
|
200
|
-
};
|
|
201
|
-
const tools = wrapTools(toolCtx, args.tools ?? threadTools ?? this.options.tools);
|
|
202
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
274
|
+
const { threadId } = threadOpts;
|
|
275
|
+
const { args, userId, order, stepOrder, promptMessageId, ...call } = await this.start(ctx, streamTextArgs, { ...threadOpts, ...options });
|
|
276
|
+
const steps = [];
|
|
277
|
+
const opts = { ...this.options, ...options };
|
|
203
278
|
const streamer = threadId && opts.saveStreamDeltas
|
|
204
279
|
? new DeltaStreamer(this.component, ctx, opts.saveStreamDeltas, {
|
|
205
280
|
threadId,
|
|
206
281
|
userId,
|
|
207
282
|
agentName: this.options.name,
|
|
208
|
-
model:
|
|
209
|
-
provider:
|
|
210
|
-
providerOptions:
|
|
283
|
+
model: getModelName(args.model),
|
|
284
|
+
provider: getProviderName(args.model),
|
|
285
|
+
providerOptions: args.providerOptions,
|
|
211
286
|
order,
|
|
212
287
|
stepOrder,
|
|
213
|
-
abortSignal:
|
|
288
|
+
abortSignal: args.abortSignal,
|
|
214
289
|
})
|
|
215
290
|
: undefined;
|
|
216
291
|
const result = streamText({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
|
|
222
|
-
experimental_transform: mergeTransforms(options?.saveStreamDeltas, args.experimental_transform),
|
|
292
|
+
...args,
|
|
293
|
+
abortSignal: streamer?.abortController.signal ?? args.abortSignal,
|
|
294
|
+
// TODO: this is probably why reasoning isn't streaming
|
|
295
|
+
experimental_transform: mergeTransforms(options?.saveStreamDeltas, streamTextArgs.experimental_transform),
|
|
223
296
|
onChunk: async (event) => {
|
|
224
297
|
await streamer?.addParts([event.chunk]);
|
|
225
298
|
// console.log("onChunk", chunk);
|
|
226
|
-
return
|
|
299
|
+
return streamTextArgs.onChunk?.(event);
|
|
227
300
|
},
|
|
228
301
|
onError: async (error) => {
|
|
229
302
|
console.error("onError", error);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
303
|
+
await call.fail(errorToString(error.error));
|
|
304
|
+
await streamer?.fail(errorToString(error.error));
|
|
305
|
+
return streamTextArgs.onError?.(error);
|
|
306
|
+
},
|
|
307
|
+
// onFinish: async (event) => {
|
|
308
|
+
// return streamTextArgs.onFinish?.(event);
|
|
309
|
+
// },
|
|
310
|
+
prepareStep: async (options) => {
|
|
311
|
+
const result = await streamTextArgs.prepareStep?.(options);
|
|
312
|
+
if (result) {
|
|
313
|
+
const model = result.model ?? options.model;
|
|
314
|
+
call.updateModel(model);
|
|
315
|
+
return result;
|
|
235
316
|
}
|
|
236
|
-
|
|
237
|
-
return args.onError?.(error);
|
|
317
|
+
return undefined;
|
|
238
318
|
},
|
|
239
319
|
onStepFinish: async (step) => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
model: aiArgs.model.modelId,
|
|
246
|
-
provider: aiArgs.model.provider,
|
|
247
|
-
promptMessageId: messageId,
|
|
248
|
-
step,
|
|
249
|
-
});
|
|
250
|
-
await streamer?.finish(saved.messages);
|
|
251
|
-
}
|
|
252
|
-
if (this.options.rawRequestResponseHandler) {
|
|
253
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
254
|
-
userId,
|
|
255
|
-
threadId,
|
|
256
|
-
agentName: this.options.name,
|
|
257
|
-
request: step.request,
|
|
258
|
-
response: step.response,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
if (opts.usageHandler && step.usage) {
|
|
262
|
-
await opts.usageHandler(ctx, {
|
|
263
|
-
userId,
|
|
264
|
-
threadId,
|
|
265
|
-
agentName: this.options.name,
|
|
266
|
-
model: aiArgs.model.modelId,
|
|
267
|
-
provider: aiArgs.model.provider,
|
|
268
|
-
usage: step.usage,
|
|
269
|
-
providerMetadata: step.providerMetadata,
|
|
270
|
-
});
|
|
320
|
+
steps.push(step);
|
|
321
|
+
const createPendingMessage = await willContinue(steps, args.stopWhen);
|
|
322
|
+
await call.save({ step }, createPendingMessage);
|
|
323
|
+
if (!createPendingMessage) {
|
|
324
|
+
await streamer?.finish();
|
|
271
325
|
}
|
|
272
326
|
return args.onStepFinish?.(step);
|
|
273
327
|
},
|
|
274
328
|
});
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
329
|
+
const metadata = {
|
|
330
|
+
promptMessageId,
|
|
331
|
+
order,
|
|
332
|
+
savedMessages: call.getSavedMessages(),
|
|
333
|
+
messageId: promptMessageId,
|
|
334
|
+
};
|
|
335
|
+
return Object.assign(result, metadata);
|
|
278
336
|
}
|
|
279
337
|
/**
|
|
280
338
|
* This behaves like {@link generateObject} from the "ai" package except that
|
|
@@ -283,69 +341,30 @@ export class Agent {
|
|
|
283
341
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
284
342
|
* to a thread (and optionally userId).
|
|
285
343
|
*/
|
|
286
|
-
async generateObject(ctx,
|
|
344
|
+
async generateObject(ctx, threadOpts,
|
|
287
345
|
/**
|
|
288
346
|
* The arguments to the generateObject function, similar to the ai.generateObject function.
|
|
289
347
|
*/
|
|
290
|
-
|
|
348
|
+
generateObjectArgs,
|
|
291
349
|
/**
|
|
292
350
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
293
351
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
294
352
|
*/
|
|
295
353
|
options) {
|
|
296
|
-
const
|
|
297
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
298
|
-
userId: argsUserId ?? undefined,
|
|
299
|
-
threadId,
|
|
300
|
-
...opts,
|
|
301
|
-
});
|
|
302
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
303
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
354
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } = await this.start(ctx, generateObjectArgs, { ...threadOpts, ...options });
|
|
304
355
|
try {
|
|
305
|
-
const result = (await generateObject(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
model: aiArgs.model.modelId,
|
|
315
|
-
provider: aiArgs.model.provider,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
result.messageId = messageId;
|
|
319
|
-
result.order = order;
|
|
320
|
-
if (this.options.rawRequestResponseHandler) {
|
|
321
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
322
|
-
userId,
|
|
323
|
-
threadId,
|
|
324
|
-
agentName: this.options.name,
|
|
325
|
-
request: result.request,
|
|
326
|
-
response: result.response,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
if (opts.usageHandler && result.usage) {
|
|
330
|
-
await opts.usageHandler(ctx, {
|
|
331
|
-
userId,
|
|
332
|
-
threadId,
|
|
333
|
-
agentName: this.options.name,
|
|
334
|
-
model: aiArgs.model.modelId,
|
|
335
|
-
provider: aiArgs.model.provider,
|
|
336
|
-
usage: result.usage,
|
|
337
|
-
providerMetadata: result.providerMetadata,
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
return result;
|
|
356
|
+
const result = (await generateObject(args));
|
|
357
|
+
await save({ object: result });
|
|
358
|
+
const metadata = {
|
|
359
|
+
promptMessageId,
|
|
360
|
+
order,
|
|
361
|
+
savedMessages: getSavedMessages(),
|
|
362
|
+
messageId: promptMessageId,
|
|
363
|
+
};
|
|
364
|
+
return Object.assign(result, metadata);
|
|
341
365
|
}
|
|
342
366
|
catch (error) {
|
|
343
|
-
|
|
344
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
345
|
-
messageId,
|
|
346
|
-
error: error.message,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
367
|
+
await fail(errorToString(error));
|
|
349
368
|
throw error;
|
|
350
369
|
}
|
|
351
370
|
}
|
|
@@ -356,81 +375,50 @@ export class Agent {
|
|
|
356
375
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
357
376
|
* to a thread (and optionally userId).
|
|
358
377
|
*/
|
|
359
|
-
async streamObject(ctx,
|
|
378
|
+
async streamObject(ctx, threadOpts,
|
|
360
379
|
/**
|
|
361
380
|
* The arguments to the streamObject function, similar to the ai `streamObject` function.
|
|
362
381
|
*/
|
|
363
|
-
|
|
382
|
+
streamObjectArgs,
|
|
364
383
|
/**
|
|
365
384
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
366
385
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
367
386
|
*/
|
|
368
387
|
options) {
|
|
369
|
-
|
|
370
|
-
const opts = { ...this.options, ...options, ...usageHandler };
|
|
371
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
372
|
-
userId: argsUserId ?? undefined,
|
|
373
|
-
threadId,
|
|
374
|
-
...opts,
|
|
375
|
-
});
|
|
376
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
377
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
388
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } = await this.start(ctx, streamObjectArgs, { ...threadOpts, ...options });
|
|
378
389
|
const stream = streamObject({
|
|
379
390
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
380
|
-
...
|
|
391
|
+
...args,
|
|
381
392
|
onError: async (error) => {
|
|
382
|
-
console.error("onError", error);
|
|
393
|
+
console.error(" streamObject onError", error);
|
|
394
|
+
// TODO: content that we have so far
|
|
395
|
+
// content: stream.fullStream.
|
|
396
|
+
await fail(errorToString(error.error));
|
|
383
397
|
return args.onError?.(error);
|
|
384
398
|
},
|
|
385
399
|
onFinish: async (result) => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
promptMessageId: messageId,
|
|
391
|
-
result: {
|
|
392
|
-
object: result.object,
|
|
393
|
-
finishReason: "stop",
|
|
394
|
-
usage: result.usage,
|
|
395
|
-
warnings: result.warnings,
|
|
396
|
-
request: await stream.request,
|
|
397
|
-
response: result.response,
|
|
398
|
-
providerMetadata: result.providerMetadata,
|
|
399
|
-
experimental_providerMetadata: result.experimental_providerMetadata,
|
|
400
|
-
logprobs: undefined,
|
|
401
|
-
toJsonResponse: stream.toTextStreamResponse,
|
|
402
|
-
},
|
|
403
|
-
model: aiArgs.model.modelId,
|
|
404
|
-
provider: aiArgs.model.provider,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
if (opts.usageHandler && result.usage) {
|
|
408
|
-
await opts.usageHandler(ctx, {
|
|
409
|
-
userId,
|
|
410
|
-
threadId,
|
|
411
|
-
agentName: this.options.name,
|
|
412
|
-
model: aiArgs.model.modelId,
|
|
413
|
-
provider: aiArgs.model.provider,
|
|
400
|
+
await save({
|
|
401
|
+
object: {
|
|
402
|
+
object: result.object,
|
|
403
|
+
finishReason: result.error ? "error" : "stop",
|
|
414
404
|
usage: result.usage,
|
|
415
|
-
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
if (this.options.rawRequestResponseHandler) {
|
|
419
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
420
|
-
userId,
|
|
421
|
-
threadId,
|
|
422
|
-
agentName: this.options.name,
|
|
405
|
+
warnings: result.warnings,
|
|
423
406
|
request: await stream.request,
|
|
424
407
|
response: result.response,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
408
|
+
providerMetadata: result.providerMetadata,
|
|
409
|
+
toJsonResponse: stream.toTextStreamResponse,
|
|
410
|
+
},
|
|
411
|
+
});
|
|
428
412
|
return args.onFinish?.(result);
|
|
429
413
|
},
|
|
430
414
|
});
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
415
|
+
const metadata = {
|
|
416
|
+
promptMessageId,
|
|
417
|
+
order,
|
|
418
|
+
savedMessages: getSavedMessages(),
|
|
419
|
+
messageId: promptMessageId,
|
|
420
|
+
};
|
|
421
|
+
return Object.assign(stream, metadata);
|
|
434
422
|
}
|
|
435
423
|
/**
|
|
436
424
|
* Save a message to the thread.
|
|
@@ -440,22 +428,21 @@ export class Agent {
|
|
|
440
428
|
* @returns The messageId of the saved message.
|
|
441
429
|
*/
|
|
442
430
|
async saveMessage(ctx, args) {
|
|
443
|
-
const {
|
|
431
|
+
const { messages } = await this.saveMessages(ctx, {
|
|
444
432
|
threadId: args.threadId,
|
|
445
433
|
userId: args.userId,
|
|
446
434
|
embeddings: args.embedding
|
|
447
|
-
? {
|
|
448
|
-
model: args.embedding.model,
|
|
449
|
-
vectors: [args.embedding.vector],
|
|
450
|
-
}
|
|
435
|
+
? { model: args.embedding.model, vectors: [args.embedding.vector] }
|
|
451
436
|
: undefined,
|
|
452
437
|
messages: args.prompt !== undefined
|
|
453
438
|
? [{ role: "user", content: args.prompt }]
|
|
454
439
|
: [args.message],
|
|
455
440
|
metadata: args.metadata ? [args.metadata] : undefined,
|
|
456
441
|
skipEmbeddings: args.skipEmbeddings,
|
|
442
|
+
pendingMessageId: args.pendingMessageId,
|
|
457
443
|
});
|
|
458
|
-
|
|
444
|
+
const message = messages.at(-1);
|
|
445
|
+
return { messageId: message._id, message };
|
|
459
446
|
}
|
|
460
447
|
/**
|
|
461
448
|
* Explicitly save messages associated with the thread (& user if provided)
|
|
@@ -471,7 +458,7 @@ export class Agent {
|
|
|
471
458
|
if (args.embeddings) {
|
|
472
459
|
embeddings = args.embeddings;
|
|
473
460
|
}
|
|
474
|
-
else if (!skipEmbeddings && this.options.
|
|
461
|
+
else if (!skipEmbeddings && this.options.textEmbeddingModel) {
|
|
475
462
|
if (!("runAction" in ctx)) {
|
|
476
463
|
console.warn("You're trying to save messages and generate embeddings, but you're in a mutation. " +
|
|
477
464
|
"Pass `skipEmbeddings: true` to skip generating embeddings in the mutation and skip this warning. " +
|
|
@@ -485,10 +472,7 @@ export class Agent {
|
|
|
485
472
|
"You can explicitly generate them asynchronously by using the scheduler to run an action later that calls `agent.generateAndSaveEmbeddings`.");
|
|
486
473
|
}
|
|
487
474
|
else {
|
|
488
|
-
embeddings = await this.generateEmbeddings(ctx, {
|
|
489
|
-
userId: args.userId ?? undefined,
|
|
490
|
-
threadId: args.threadId,
|
|
491
|
-
}, args.messages);
|
|
475
|
+
embeddings = await this.generateEmbeddings(ctx, { userId: args.userId ?? undefined, threadId: args.threadId }, args.messages);
|
|
492
476
|
}
|
|
493
477
|
}
|
|
494
478
|
return saveMessages(ctx, this.component, {
|
|
@@ -540,14 +524,14 @@ export class Agent {
|
|
|
540
524
|
contextOptions,
|
|
541
525
|
getEmbedding: async (text) => {
|
|
542
526
|
assert("runAction" in ctx);
|
|
543
|
-
assert(this.options.
|
|
527
|
+
assert(this.options.textEmbeddingModel, "A textEmbeddingModel is required to be set on the Agent that you're doing vector search with");
|
|
544
528
|
return {
|
|
545
529
|
embedding: (await this.doEmbed(ctx, {
|
|
546
530
|
userId: args.userId,
|
|
547
531
|
threadId: args.threadId,
|
|
548
532
|
values: [text],
|
|
549
533
|
})).embeddings[0],
|
|
550
|
-
|
|
534
|
+
textEmbeddingModel: this.options.textEmbeddingModel,
|
|
551
535
|
};
|
|
552
536
|
},
|
|
553
537
|
});
|
|
@@ -578,7 +562,7 @@ export class Agent {
|
|
|
578
562
|
* @returns The embeddings for the messages.
|
|
579
563
|
*/
|
|
580
564
|
async generateEmbeddings(ctx, { userId, threadId, }, messages) {
|
|
581
|
-
if (!this.options.
|
|
565
|
+
if (!this.options.textEmbeddingModel) {
|
|
582
566
|
return undefined;
|
|
583
567
|
}
|
|
584
568
|
let embeddings;
|
|
@@ -590,13 +574,13 @@ export class Agent {
|
|
|
590
574
|
if (textIndexes.length === 0) {
|
|
591
575
|
return undefined;
|
|
592
576
|
}
|
|
577
|
+
const values = messageTexts.filter((t) => !!t);
|
|
593
578
|
// Then embed those messages.
|
|
594
579
|
const textEmbeddings = await this.doEmbed(ctx, {
|
|
595
580
|
userId,
|
|
596
581
|
threadId,
|
|
597
|
-
values
|
|
582
|
+
values,
|
|
598
583
|
});
|
|
599
|
-
// TODO: record usage of embeddings
|
|
600
584
|
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
601
585
|
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
602
586
|
textIndexes.forEach((i, j) => {
|
|
@@ -605,11 +589,8 @@ export class Agent {
|
|
|
605
589
|
if (textEmbeddings.embeddings.length > 0) {
|
|
606
590
|
const dimension = textEmbeddings.embeddings[0].length;
|
|
607
591
|
validateVectorDimension(dimension);
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
dimension,
|
|
611
|
-
model: this.options.textEmbedding.modelId,
|
|
612
|
-
};
|
|
592
|
+
const model = getModelName(this.options.textEmbeddingModel);
|
|
593
|
+
embeddings = { vectors: embeddingsOrNull, dimension, model };
|
|
613
594
|
}
|
|
614
595
|
return embeddings;
|
|
615
596
|
}
|
|
@@ -647,10 +628,10 @@ export class Agent {
|
|
|
647
628
|
const embeddings = await this.generateEmbeddings(ctx, {
|
|
648
629
|
userId: messagesMissingEmbeddings[0].userId,
|
|
649
630
|
threadId: messagesMissingEmbeddings[0].threadId,
|
|
650
|
-
}, messagesMissingEmbeddings.map((m) => m.message));
|
|
631
|
+
}, messagesMissingEmbeddings.map((m) => deserializeMessage(m.message)));
|
|
651
632
|
if (!embeddings) {
|
|
652
|
-
if (!this.options.
|
|
653
|
-
throw new Error("No embeddings were generated for the messages. You must pass a
|
|
633
|
+
if (!this.options.textEmbeddingModel) {
|
|
634
|
+
throw new Error("No embeddings were generated for the messages. You must pass a textEmbeddingModel to the agent constructor.");
|
|
654
635
|
}
|
|
655
636
|
throw new Error("No embeddings were generated for these messages: " +
|
|
656
637
|
messagesMissingEmbeddings.map((m) => m._id).join(", "));
|
|
@@ -675,12 +656,12 @@ export class Agent {
|
|
|
675
656
|
* @param args The Step generated by the AI SDK.
|
|
676
657
|
*/
|
|
677
658
|
async saveStep(ctx, args) {
|
|
678
|
-
const messages = await serializeNewMessagesInStep(ctx, this.component, args.step, {
|
|
679
|
-
provider: args.provider ?? this.options.
|
|
680
|
-
model: args.model ?? this.options.
|
|
659
|
+
const { messages } = await serializeNewMessagesInStep(ctx, this.component, args.step, {
|
|
660
|
+
provider: args.provider ?? getProviderName(this.options.languageModel),
|
|
661
|
+
model: args.model ?? getModelName(this.options.languageModel),
|
|
681
662
|
});
|
|
682
663
|
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
|
|
683
|
-
|
|
664
|
+
return ctx.runMutation(this.component.messages.addMessages, {
|
|
684
665
|
userId: args.userId,
|
|
685
666
|
threadId: args.threadId,
|
|
686
667
|
agentName: this.options.name,
|
|
@@ -689,7 +670,6 @@ export class Agent {
|
|
|
689
670
|
embeddings,
|
|
690
671
|
failPendingSteps: false,
|
|
691
672
|
});
|
|
692
|
-
return saved;
|
|
693
673
|
}
|
|
694
674
|
/**
|
|
695
675
|
* Manually save the result of a generateObject call to the thread.
|
|
@@ -700,11 +680,15 @@ export class Agent {
|
|
|
700
680
|
*/
|
|
701
681
|
async saveObject(ctx, args) {
|
|
702
682
|
const { messages } = await serializeObjectResult(ctx, this.component, args.result, {
|
|
703
|
-
model: args.model ??
|
|
704
|
-
|
|
683
|
+
model: args.model ??
|
|
684
|
+
args.metadata?.model ??
|
|
685
|
+
getModelName(this.options.languageModel),
|
|
686
|
+
provider: args.provider ??
|
|
687
|
+
args.metadata?.provider ??
|
|
688
|
+
getProviderName(this.options.languageModel),
|
|
705
689
|
});
|
|
706
690
|
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
|
|
707
|
-
|
|
691
|
+
return ctx.runMutation(this.component.messages.addMessages, {
|
|
708
692
|
userId: args.userId,
|
|
709
693
|
threadId: args.threadId,
|
|
710
694
|
promptMessageId: args.promptMessageId,
|
|
@@ -712,7 +696,6 @@ export class Agent {
|
|
|
712
696
|
messages,
|
|
713
697
|
embeddings,
|
|
714
698
|
agentName: this.options.name,
|
|
715
|
-
pending: false,
|
|
716
699
|
});
|
|
717
700
|
}
|
|
718
701
|
/**
|
|
@@ -723,19 +706,11 @@ export class Agent {
|
|
|
723
706
|
* @param args What message to save. Generally the parent message sent into
|
|
724
707
|
* the generateText call.
|
|
725
708
|
*/
|
|
726
|
-
async
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
else {
|
|
734
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
735
|
-
messageId: args.messageId,
|
|
736
|
-
error: result.error,
|
|
737
|
-
});
|
|
738
|
-
}
|
|
709
|
+
async finalizeMessage(ctx, args) {
|
|
710
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
711
|
+
messageId: args.messageId,
|
|
712
|
+
result: args.result,
|
|
713
|
+
});
|
|
739
714
|
}
|
|
740
715
|
/**
|
|
741
716
|
* Update a message by its id.
|
|
@@ -850,11 +825,17 @@ export class Agent {
|
|
|
850
825
|
}
|
|
851
826
|
async _saveMessagesAndFetchContext(ctx, args, { userId: argsUserId, threadId, contextOptions, storageOptions, }) {
|
|
852
827
|
// If only a promptMessageId is provided, this will be empty.
|
|
853
|
-
const messages =
|
|
828
|
+
const messages = args.messages ?? [];
|
|
829
|
+
const prompt = !args.prompt
|
|
830
|
+
? []
|
|
831
|
+
: Array.isArray(args.prompt)
|
|
832
|
+
? args.prompt
|
|
833
|
+
: [{ role: "user", content: args.prompt }];
|
|
854
834
|
const userId = argsUserId ??
|
|
855
835
|
(threadId &&
|
|
856
836
|
(await ctx.runQuery(this.component.threads.getThread, { threadId }))
|
|
857
|
-
?.userId)
|
|
837
|
+
?.userId) ??
|
|
838
|
+
undefined;
|
|
858
839
|
// If only a messageId is provided, this will add that message to the end.
|
|
859
840
|
const contextMessages = await this.fetchContextMessages(ctx, {
|
|
860
841
|
userId,
|
|
@@ -865,167 +846,132 @@ export class Agent {
|
|
|
865
846
|
});
|
|
866
847
|
// If it was a promptMessageId, pop it off context messages
|
|
867
848
|
// and add to the end of messages.
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
849
|
+
const promptMessageIndex = args.promptMessageId
|
|
850
|
+
? contextMessages.findIndex((m) => m._id === args.promptMessageId)
|
|
851
|
+
: -1;
|
|
852
|
+
const promptMessage = promptMessageIndex !== -1
|
|
853
|
+
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
872
854
|
: undefined;
|
|
873
|
-
|
|
874
|
-
// If they specify both a promptMessageId and a prompt, we prefer
|
|
875
|
-
// the prompt to stand in for the promptMessageId message.
|
|
876
|
-
promptMessage.message = { role: "user", content: args.prompt };
|
|
877
|
-
}
|
|
878
|
-
let messageId = promptMessage?._id;
|
|
855
|
+
let promptMessageId = promptMessage?._id;
|
|
879
856
|
let order = promptMessage?.order;
|
|
880
857
|
let stepOrder = promptMessage?.stepOrder;
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
(
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
858
|
+
let savedMessages = undefined;
|
|
859
|
+
let pendingMessageId = undefined;
|
|
860
|
+
if (threadId && storageOptions?.saveMessages !== "none") {
|
|
861
|
+
let saved;
|
|
862
|
+
if (messages.length + prompt.length &&
|
|
863
|
+
// If it was a promptMessageId, we don't want to save it again.
|
|
864
|
+
(!args.promptMessageId || storageOptions?.saveMessages === "all")) {
|
|
865
|
+
const saveAll = storageOptions?.saveMessages === "all";
|
|
866
|
+
const coreMessages = [...messages, ...prompt];
|
|
867
|
+
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
868
|
+
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
869
|
+
saved = await this.saveMessages(ctx, {
|
|
870
|
+
threadId,
|
|
871
|
+
userId,
|
|
872
|
+
messages: [...toSave, { role: "assistant", content: [] }],
|
|
873
|
+
metadata: [...metadata, { status: "pending" }],
|
|
874
|
+
failPendingSteps: true,
|
|
875
|
+
pendingMessageId: args.pendingMessageId,
|
|
876
|
+
});
|
|
877
|
+
promptMessageId = saved.messages.at(-2)._id;
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
saved = await this.saveMessages(ctx, {
|
|
881
|
+
threadId,
|
|
882
|
+
userId,
|
|
883
|
+
messages: [{ role: "assistant", content: [] }],
|
|
884
|
+
metadata: [{ status: "pending" }],
|
|
885
|
+
failPendingSteps: true,
|
|
886
|
+
pendingMessageId: args.pendingMessageId,
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
pendingMessageId = saved.messages.at(-1)._id;
|
|
890
|
+
order = saved.messages.at(-1).order;
|
|
891
|
+
stepOrder = saved.messages.at(-1).stepOrder;
|
|
892
|
+
// Don't return the pending message
|
|
893
|
+
savedMessages = saved.messages.slice(0, -1);
|
|
898
894
|
}
|
|
899
895
|
if (promptMessage?.message) {
|
|
900
|
-
|
|
901
|
-
|
|
896
|
+
if (!args.prompt) {
|
|
897
|
+
// If they override the prompt, we skip the existing prompt message.
|
|
898
|
+
messages.push(deserializeMessage(promptMessage.message));
|
|
899
|
+
}
|
|
902
900
|
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
903
901
|
// embeddings yet. This can happen if the message was saved in a mutation
|
|
904
902
|
// where the LLM is not available.
|
|
905
|
-
if (!promptMessage.embeddingId && this.options.
|
|
903
|
+
if (!promptMessage.embeddingId && this.options.textEmbeddingModel) {
|
|
906
904
|
await this._generateAndSaveEmbeddings(ctx, [promptMessage]);
|
|
907
905
|
}
|
|
908
906
|
}
|
|
907
|
+
const prePrompt = contextMessages.map((m) => deserializeMessage(m.message));
|
|
908
|
+
let existingResponses = [];
|
|
909
|
+
if (promptMessageIndex !== -1) {
|
|
910
|
+
// pull any messages that already responded to the prompt off
|
|
911
|
+
// and add them after the prompt
|
|
912
|
+
existingResponses = prePrompt.splice(promptMessageIndex);
|
|
913
|
+
}
|
|
909
914
|
let processedMessages = [
|
|
910
|
-
...
|
|
915
|
+
...prePrompt,
|
|
911
916
|
...messages,
|
|
917
|
+
...prompt,
|
|
918
|
+
...existingResponses,
|
|
912
919
|
];
|
|
920
|
+
if (promptMessageIndex === -1) {
|
|
921
|
+
processedMessages.push(...prompt);
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
// We add the prompt where the prompt message was
|
|
925
|
+
processedMessages.splice(promptMessageIndex, 0, ...prompt);
|
|
926
|
+
}
|
|
913
927
|
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
914
928
|
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
915
|
-
processedMessages = await
|
|
929
|
+
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
916
930
|
}
|
|
917
931
|
const { prompt: _, model, ...rest } = args;
|
|
918
932
|
return {
|
|
919
933
|
args: {
|
|
934
|
+
...this.options.callSettings,
|
|
920
935
|
...rest,
|
|
921
|
-
|
|
922
|
-
model: model ?? this.options.chat,
|
|
936
|
+
model: model ?? this.options.languageModel,
|
|
923
937
|
system: args.system ?? this.options.instructions,
|
|
924
938
|
messages: processedMessages,
|
|
925
939
|
},
|
|
926
940
|
userId,
|
|
927
|
-
|
|
941
|
+
promptMessageId,
|
|
942
|
+
pendingMessageId,
|
|
943
|
+
savedMessages,
|
|
928
944
|
order,
|
|
929
945
|
stepOrder,
|
|
930
946
|
};
|
|
931
947
|
}
|
|
932
948
|
async doEmbed(ctx, options) {
|
|
933
|
-
const embeddingModel = this.options.
|
|
934
|
-
assert(embeddingModel, "a
|
|
949
|
+
const embeddingModel = this.options.textEmbeddingModel;
|
|
950
|
+
assert(embeddingModel, "a textEmbeddingModel is required to be set on the Agent that you're doing vector search with");
|
|
935
951
|
const result = await embedMany({
|
|
952
|
+
...this.options.callSettings,
|
|
936
953
|
model: embeddingModel,
|
|
937
954
|
values: options.values,
|
|
938
955
|
abortSignal: options.abortSignal,
|
|
939
956
|
headers: options.headers,
|
|
940
|
-
maxRetries: this.options.maxRetries,
|
|
941
957
|
});
|
|
942
958
|
if (this.options.usageHandler && result.usage) {
|
|
943
959
|
await this.options.usageHandler(ctx, {
|
|
944
960
|
userId: options.userId,
|
|
945
961
|
threadId: options.threadId,
|
|
946
962
|
agentName: this.options.name,
|
|
947
|
-
model: embeddingModel
|
|
948
|
-
provider: embeddingModel
|
|
963
|
+
model: getModelName(embeddingModel),
|
|
964
|
+
provider: getProviderName(embeddingModel),
|
|
949
965
|
providerMetadata: undefined,
|
|
950
966
|
usage: {
|
|
951
|
-
|
|
952
|
-
|
|
967
|
+
inputTokens: result.usage.tokens,
|
|
968
|
+
outputTokens: 0,
|
|
953
969
|
totalTokens: result.usage.tokens,
|
|
954
970
|
},
|
|
955
971
|
});
|
|
956
972
|
}
|
|
957
973
|
return { embeddings: result.embeddings };
|
|
958
974
|
}
|
|
959
|
-
/**
|
|
960
|
-
* Process messages to inline file and image URLs that point to localhost
|
|
961
|
-
* by converting them to base64. This solves the problem of LLMs not being
|
|
962
|
-
* able to access localhost URLs.
|
|
963
|
-
*/
|
|
964
|
-
async _inlineMessagesFiles(messages) {
|
|
965
|
-
// Process each message to convert localhost URLs to base64
|
|
966
|
-
return Promise.all(messages.map(async (message) => {
|
|
967
|
-
if ((message.role !== "user" && message.role !== "assistant") ||
|
|
968
|
-
typeof message.content === "string" ||
|
|
969
|
-
!Array.isArray(message.content)) {
|
|
970
|
-
return message;
|
|
971
|
-
}
|
|
972
|
-
const processedContent = await Promise.all(message.content.map(async (part) => {
|
|
973
|
-
if (part.type === "image" && part.image instanceof URL) {
|
|
974
|
-
assert(message.role === "user", "Images can only be in user messages");
|
|
975
|
-
if (this._isLocalhostUrl(part.image)) {
|
|
976
|
-
const imageData = await this._downloadFile(part.image);
|
|
977
|
-
return {
|
|
978
|
-
...part,
|
|
979
|
-
image: imageData,
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
// Handle file parts
|
|
984
|
-
if (part.type === "file" && part.data instanceof URL) {
|
|
985
|
-
if (this._isLocalhostUrl(part.data)) {
|
|
986
|
-
const fileData = await this._downloadFile(part.data);
|
|
987
|
-
return {
|
|
988
|
-
...part,
|
|
989
|
-
data: fileData,
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
return part;
|
|
994
|
-
}));
|
|
995
|
-
if (message.role === "user") {
|
|
996
|
-
return {
|
|
997
|
-
...message,
|
|
998
|
-
content: processedContent,
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
return {
|
|
1003
|
-
...message,
|
|
1004
|
-
content: processedContent,
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
}));
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Check if a URL points to localhost
|
|
1011
|
-
*/
|
|
1012
|
-
_isLocalhostUrl(url) {
|
|
1013
|
-
return (url.hostname === "localhost" ||
|
|
1014
|
-
url.hostname === "127.0.0.1" ||
|
|
1015
|
-
url.hostname === "::1" ||
|
|
1016
|
-
url.hostname === "0.0.0.0");
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Download a file from a URL
|
|
1020
|
-
*/
|
|
1021
|
-
async _downloadFile(url) {
|
|
1022
|
-
// Fetch the file
|
|
1023
|
-
const response = await fetch(url);
|
|
1024
|
-
if (!response.ok) {
|
|
1025
|
-
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
1026
|
-
}
|
|
1027
|
-
return await response.arrayBuffer();
|
|
1028
|
-
}
|
|
1029
975
|
/**
|
|
1030
976
|
* WORKFLOW UTILITIES
|
|
1031
977
|
*/
|
|
@@ -1064,22 +1010,30 @@ export class Agent {
|
|
|
1064
1010
|
* Create an action out of this agent so you can call it from workflows or other actions
|
|
1065
1011
|
* without a wrapping function.
|
|
1066
1012
|
* @param spec Configuration for the agent acting as an action, including
|
|
1067
|
-
* {@link ContextOptions}, {@link StorageOptions}, and
|
|
1013
|
+
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
|
|
1068
1014
|
*/
|
|
1069
|
-
asTextAction(spec) {
|
|
1070
|
-
const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
|
|
1015
|
+
asTextAction(spec, overrides) {
|
|
1071
1016
|
return internalActionGeneric({
|
|
1072
1017
|
args: vTextArgs,
|
|
1073
1018
|
handler: async (ctx_, args) => {
|
|
1074
|
-
const stream = args.stream === true ? spec?.stream || true : spec?.stream ?? false;
|
|
1019
|
+
const stream = args.stream === true ? spec?.stream || true : (spec?.stream ?? false);
|
|
1075
1020
|
const targetArgs = { userId: args.userId, threadId: args.threadId };
|
|
1076
1021
|
const llmArgs = {
|
|
1077
|
-
|
|
1022
|
+
stopWhen: spec?.stopWhen ?? this.options.stopWhen,
|
|
1023
|
+
...overrides,
|
|
1078
1024
|
...omit(args, ["storageOptions", "contextOptions"]),
|
|
1025
|
+
messages: args.messages?.map(deserializeMessage),
|
|
1026
|
+
prompt: Array.isArray(args.prompt)
|
|
1027
|
+
? args.prompt.map(deserializeMessage)
|
|
1028
|
+
: args.prompt,
|
|
1029
|
+
toolChoice: args.toolChoice,
|
|
1079
1030
|
};
|
|
1031
|
+
if (args.maxSteps) {
|
|
1032
|
+
llmArgs.stopWhen = stepCountIs(args.maxSteps);
|
|
1033
|
+
}
|
|
1080
1034
|
const opts = {
|
|
1081
1035
|
...this.options,
|
|
1082
|
-
...
|
|
1036
|
+
...pick(spec, ["contextOptions", "storageOptions"]),
|
|
1083
1037
|
...pick(args, ["contextOptions", "storageOptions"]),
|
|
1084
1038
|
saveStreamDeltas: stream,
|
|
1085
1039
|
};
|
|
@@ -1087,24 +1041,28 @@ export class Agent {
|
|
|
1087
1041
|
? { ...ctx_, ...spec.customCtx(ctx_, targetArgs, llmArgs) }
|
|
1088
1042
|
: ctx_);
|
|
1089
1043
|
if (stream) {
|
|
1044
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1090
1045
|
const result = await this.streamText(ctx, targetArgs, llmArgs, opts);
|
|
1091
1046
|
await result.consumeStream();
|
|
1092
1047
|
return {
|
|
1093
1048
|
text: await result.text,
|
|
1094
|
-
|
|
1049
|
+
promptMessageId: result.promptMessageId,
|
|
1095
1050
|
order: result.order,
|
|
1096
1051
|
finishReason: await result.finishReason,
|
|
1097
1052
|
warnings: result.warnings,
|
|
1053
|
+
savedMessageIds: result.savedMessages?.map((m) => m._id) ?? [],
|
|
1098
1054
|
};
|
|
1099
1055
|
}
|
|
1100
1056
|
else {
|
|
1057
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1101
1058
|
const res = await this.generateText(ctx, targetArgs, llmArgs, opts);
|
|
1102
1059
|
return {
|
|
1103
1060
|
text: res.text,
|
|
1104
|
-
|
|
1061
|
+
promptMessageId: res.promptMessageId,
|
|
1105
1062
|
order: res.order,
|
|
1106
1063
|
finishReason: res.finishReason,
|
|
1107
1064
|
warnings: res.warnings,
|
|
1065
|
+
savedMessageIds: res.savedMessages?.map((m) => m._id) ?? [],
|
|
1108
1066
|
};
|
|
1109
1067
|
}
|
|
1110
1068
|
},
|
|
@@ -1115,25 +1073,39 @@ export class Agent {
|
|
|
1115
1073
|
* it from workflows or other actions without a wrapping function.
|
|
1116
1074
|
* @param spec Configuration for the agent acting as an action, including
|
|
1117
1075
|
* the normal parameters to {@link generateObject}, plus {@link ContextOptions}
|
|
1118
|
-
* and
|
|
1076
|
+
* and stopWhen.
|
|
1119
1077
|
*/
|
|
1120
|
-
asObjectAction(
|
|
1121
|
-
const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
|
|
1078
|
+
asObjectAction(objectArgs, options) {
|
|
1122
1079
|
return internalActionGeneric({
|
|
1123
1080
|
args: vSafeObjectArgs,
|
|
1124
|
-
handler: async (
|
|
1125
|
-
const
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
...
|
|
1130
|
-
|
|
1081
|
+
handler: async (ctx_, args) => {
|
|
1082
|
+
const { userId, threadId, callSettings, ...rest } = args;
|
|
1083
|
+
const overrides = pick(rest, ["contextOptions", "storageOptions"]);
|
|
1084
|
+
const targetArgs = { userId, threadId };
|
|
1085
|
+
const llmArgs = {
|
|
1086
|
+
...objectArgs,
|
|
1087
|
+
...callSettings,
|
|
1088
|
+
...omit(rest, ["storageOptions", "contextOptions"]),
|
|
1089
|
+
messages: args.messages?.map(deserializeMessage),
|
|
1090
|
+
prompt: Array.isArray(args.prompt)
|
|
1091
|
+
? args.prompt.map(deserializeMessage)
|
|
1092
|
+
: args.prompt,
|
|
1093
|
+
};
|
|
1094
|
+
const ctx = (options?.customCtx
|
|
1095
|
+
? { ...ctx_, ...options.customCtx(ctx_, targetArgs, llmArgs) }
|
|
1096
|
+
: ctx_);
|
|
1097
|
+
const value = await this.generateObject(ctx, targetArgs, llmArgs, {
|
|
1098
|
+
...this.options,
|
|
1099
|
+
...options,
|
|
1100
|
+
...overrides,
|
|
1101
|
+
});
|
|
1131
1102
|
return {
|
|
1132
|
-
object: value.object,
|
|
1133
|
-
|
|
1103
|
+
object: convexToJson(value.object),
|
|
1104
|
+
promptMessageId: value.promptMessageId,
|
|
1134
1105
|
order: value.order,
|
|
1135
1106
|
finishReason: value.finishReason,
|
|
1136
1107
|
warnings: value.warnings,
|
|
1108
|
+
savedMessageIds: value.savedMessages?.map((m) => m._id) ?? [],
|
|
1137
1109
|
};
|
|
1138
1110
|
},
|
|
1139
1111
|
});
|
|
@@ -1165,116 +1137,46 @@ export class Agent {
|
|
|
1165
1137
|
userId: v.optional(v.string()),
|
|
1166
1138
|
promptMessageId: v.optional(v.string()),
|
|
1167
1139
|
messages: v.array(vMessageWithMetadata),
|
|
1168
|
-
pending: v.optional(v.boolean()),
|
|
1169
1140
|
failPendingSteps: v.optional(v.boolean()),
|
|
1141
|
+
embeddings: v.optional(vMessageEmbeddings),
|
|
1170
1142
|
},
|
|
1171
1143
|
handler: async (ctx, args) => {
|
|
1172
|
-
const {
|
|
1144
|
+
const { messages } = await this.saveMessages(ctx, {
|
|
1173
1145
|
...args,
|
|
1174
|
-
messages: args.messages.map((m) => m.message),
|
|
1146
|
+
messages: args.messages.map((m) => deserializeMessage(m.message)),
|
|
1175
1147
|
metadata: args.messages.map(({ message: _, ...m }) => m),
|
|
1148
|
+
skipEmbeddings: true,
|
|
1176
1149
|
});
|
|
1177
1150
|
return {
|
|
1178
|
-
lastMessageId,
|
|
1179
|
-
|
|
1151
|
+
lastMessageId: messages.at(-1)._id,
|
|
1152
|
+
messages: messages.map((m) => pick(m, ["_id", "order", "stepOrder"])),
|
|
1180
1153
|
};
|
|
1181
1154
|
},
|
|
1182
1155
|
});
|
|
1183
1156
|
}
|
|
1184
1157
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Get the metadata for a thread.
|
|
1202
|
-
* @param ctx A ctx object from a query, mutation, or action.
|
|
1203
|
-
* @param args.threadId The thread to get the metadata for.
|
|
1204
|
-
* @returns The metadata for the thread.
|
|
1205
|
-
*/
|
|
1206
|
-
export async function getThreadMetadata(ctx, component, args) {
|
|
1207
|
-
const thread = await ctx.runQuery(component.threads.getThread, {
|
|
1208
|
-
threadId: args.threadId,
|
|
1209
|
-
});
|
|
1210
|
-
if (!thread) {
|
|
1211
|
-
throw new Error("Thread not found");
|
|
1212
|
-
}
|
|
1213
|
-
return thread;
|
|
1214
|
-
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Explicitly save messages associated with the thread (& user if provided)
|
|
1217
|
-
*/
|
|
1218
|
-
export async function saveMessages(ctx, component, args) {
|
|
1219
|
-
let embeddings;
|
|
1220
|
-
if (args.embeddings) {
|
|
1221
|
-
const dimension = args.embeddings.vectors.find((v) => v !== null)?.length;
|
|
1222
|
-
if (dimension) {
|
|
1223
|
-
validateVectorDimension(dimension);
|
|
1224
|
-
embeddings = {
|
|
1225
|
-
model: args.embeddings.model,
|
|
1226
|
-
dimension,
|
|
1227
|
-
vectors: args.embeddings.vectors,
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1158
|
+
async function willContinue(
|
|
1159
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1160
|
+
steps,
|
|
1161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1162
|
+
stopWhen) {
|
|
1163
|
+
const step = steps.at(-1);
|
|
1164
|
+
// we aren't doing another round after a tool result
|
|
1165
|
+
// TODO: whether to handle continuing after too much context used..
|
|
1166
|
+
if (step.finishReason !== "tool-calls")
|
|
1167
|
+
return false;
|
|
1168
|
+
// we don't have a tool result, so we'll wait for more
|
|
1169
|
+
if (step.toolCalls.length > step.toolResults.length)
|
|
1170
|
+
return false;
|
|
1171
|
+
if (Array.isArray(stopWhen)) {
|
|
1172
|
+
return (await Promise.all(stopWhen.map(async (s) => s({ steps })))).every((stop) => !stop);
|
|
1230
1173
|
}
|
|
1231
|
-
|
|
1232
|
-
threadId: args.threadId,
|
|
1233
|
-
userId: args.userId ?? undefined,
|
|
1234
|
-
agentName: args.agentName,
|
|
1235
|
-
promptMessageId: args.promptMessageId,
|
|
1236
|
-
embeddings,
|
|
1237
|
-
messages: await Promise.all(args.messages.map(async (m, i) => {
|
|
1238
|
-
const { message, fileIds } = await serializeMessage(ctx, component, m);
|
|
1239
|
-
return {
|
|
1240
|
-
...args.metadata?.[i],
|
|
1241
|
-
message,
|
|
1242
|
-
fileIds,
|
|
1243
|
-
};
|
|
1244
|
-
})),
|
|
1245
|
-
failPendingSteps: args.failPendingSteps ?? false,
|
|
1246
|
-
pending: args.pending ?? false,
|
|
1247
|
-
});
|
|
1248
|
-
return {
|
|
1249
|
-
lastMessageId: result.messages.at(-1)._id,
|
|
1250
|
-
messages: result.messages,
|
|
1251
|
-
};
|
|
1174
|
+
return !(await stopWhen?.({ steps }));
|
|
1252
1175
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
* @param args The message and what to associate it with (user / thread)
|
|
1257
|
-
* You can pass extra metadata alongside the message, e.g. associated fileIds.
|
|
1258
|
-
* @returns The messageId of the saved message.
|
|
1259
|
-
*/
|
|
1260
|
-
export async function saveMessage(ctx, component, args) {
|
|
1261
|
-
let embeddings;
|
|
1262
|
-
if (args.embedding && args.embedding.vector) {
|
|
1263
|
-
embeddings = {
|
|
1264
|
-
model: args.embedding.model,
|
|
1265
|
-
vectors: [args.embedding.vector],
|
|
1266
|
-
};
|
|
1176
|
+
function errorToString(error) {
|
|
1177
|
+
if (error instanceof Error) {
|
|
1178
|
+
return error.message;
|
|
1267
1179
|
}
|
|
1268
|
-
|
|
1269
|
-
threadId: args.threadId,
|
|
1270
|
-
userId: args.userId ?? undefined,
|
|
1271
|
-
agentName: args.agentName,
|
|
1272
|
-
messages: args.prompt !== undefined
|
|
1273
|
-
? [{ role: "user", content: args.prompt }]
|
|
1274
|
-
: [args.message],
|
|
1275
|
-
metadata: args.metadata ? [args.metadata] : undefined,
|
|
1276
|
-
embeddings,
|
|
1277
|
-
});
|
|
1278
|
-
return { messageId: lastMessageId, message: messages.at(-1) };
|
|
1180
|
+
return String(error);
|
|
1279
1181
|
}
|
|
1280
1182
|
//# sourceMappingURL=index.js.map
|