@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +153 -0
- package/README.md +32 -27
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +546 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +126 -27
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +67 -12
- 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 +1335 -204
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +52 -28
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +20 -7
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +68 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +1116 -978
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +332 -747
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +461 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +106 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/mockModel.d.ts +42 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +182 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +58 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +350 -39
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +350 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +84 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +185 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streamText.d.ts +46 -0
- package/dist/client/streamText.d.ts.map +1 -0
- package/dist/client/streamText.js +117 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3716 -32
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +161 -59
- 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 +266 -128
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.d.ts +4 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +21 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +24 -2178
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js +10 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3120 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +4 -18
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +9 -5
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/files.d.ts +16 -10
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +2578 -366
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +397 -154
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +5697 -3584
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +18 -41
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +39 -339
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +114 -73
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +13 -13
- package/dist/component/users.d.ts +7 -7
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/component/vector/index.d.ts.map +1 -1
- package/dist/component/vector/index.js +1 -3
- package/dist/component/vector/index.js.map +1 -1
- package/dist/deltas.d.ts +43 -0
- package/dist/deltas.d.ts.map +1 -0
- package/dist/deltas.js +446 -0
- package/dist/deltas.js.map +1 -0
- package/dist/mapping.d.ts +38 -20
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +365 -97
- package/dist/mapping.js.map +1 -1
- 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/index.d.ts +5 -77
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -160
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts +36 -3
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +35 -9
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/types.d.ts +4 -18
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.d.ts +10 -0
- package/dist/react/useDeltaStreams.d.ts.map +1 -0
- package/dist/react/useDeltaStreams.js +106 -0
- package/dist/react/useDeltaStreams.js.map +1 -0
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +32 -15
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/react/useStreamingUIMessages.d.ts +22 -0
- package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
- package/dist/react/useStreamingUIMessages.js +92 -0
- package/dist/react/useStreamingUIMessages.js.map +1 -0
- package/dist/react/useThreadMessages.d.ts +104 -0
- package/dist/react/useThreadMessages.d.ts.map +1 -0
- package/dist/react/useThreadMessages.js +148 -0
- package/dist/react/useThreadMessages.js.map +1 -0
- package/dist/react/useUIMessages.d.ts +96 -0
- package/dist/react/useUIMessages.d.ts.map +1 -0
- package/dist/react/useUIMessages.js +108 -0
- package/dist/react/useUIMessages.js.map +1 -0
- package/dist/shared.d.ts +20 -4
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +45 -8
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +22981 -5666
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +245 -137
- package/dist/validators.js.map +1 -1
- package/package.json +101 -51
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +739 -0
- package/src/client/approval.test.ts +350 -0
- package/src/client/createTool.ts +291 -76
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +67 -31
- package/src/client/files.ts +100 -20
- package/src/client/index.test.ts +40 -85
- package/src/client/index.ts +638 -1289
- package/src/client/messages.ts +237 -0
- package/src/client/mockModel.ts +252 -0
- package/src/client/saveInputMessages.test.ts +583 -0
- package/src/client/saveInputMessages.ts +101 -0
- package/src/client/search.test.ts +1207 -0
- package/src/client/search.ts +581 -70
- package/src/client/start.ts +327 -0
- package/src/client/streamText.ts +187 -0
- package/src/client/streaming.test.ts +186 -0
- package/src/client/streaming.ts +241 -97
- package/src/client/threads.ts +83 -0
- package/src/client/types.ts +370 -219
- package/src/client/utils.ts +27 -0
- package/src/component/_generated/api.ts +64 -0
- package/src/component/_generated/component.ts +4902 -0
- package/src/component/_generated/{server.d.ts → server.ts} +33 -21
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +195 -51
- package/src/component/messages.ts +500 -201
- package/src/component/schema.ts +20 -46
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +184 -83
- package/src/component/users.test.ts +0 -1
- package/src/component/vector/index.ts +1 -3
- package/src/deltas.test.ts +626 -0
- package/src/deltas.ts +569 -0
- package/src/fromUIMessages.test.ts +497 -0
- package/src/mapping.test.ts +180 -6
- package/src/mapping.ts +479 -162
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/index.ts +10 -230
- package/src/react/optimisticallySendMessage.ts +55 -12
- package/src/react/types.ts +6 -39
- package/src/react/useDeltaStreams.ts +160 -0
- package/src/react/useSmoothText.ts +56 -36
- package/src/react/useStreamingUIMessages.ts +143 -0
- package/src/react/useThreadMessages.ts +262 -0
- package/src/react/useUIMessages.test.ts +255 -0
- package/src/react/useUIMessages.ts +195 -0
- package/src/shared.ts +88 -12
- package/src/test.ts +18 -0
- package/src/toUIMessages.test.ts +1269 -0
- package/src/validators.test.ts +18 -19
- package/src/validators.ts +325 -185
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- 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/dist/package.json +0 -3
- package/dist/react/deltas.d.ts +0 -26
- package/dist/react/deltas.d.ts.map +0 -1
- package/dist/react/deltas.js +0 -384
- package/dist/react/deltas.js.map +0 -1
- package/dist/react/toUIMessages.d.ts +0 -15
- package/dist/react/toUIMessages.d.ts.map +0 -1
- package/dist/react/toUIMessages.js +0 -211
- package/dist/react/toUIMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
- package/src/component/_generated/api.d.ts +0 -2202
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/src/react/deltas.test.ts +0 -315
- package/src/react/deltas.ts +0 -478
- package/src/react/toUIMessages.test.ts +0 -420
- package/src/react/toUIMessages.ts +0 -253
- package/src/vitest.config.ts +0 -7
- /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
package/dist/client/index.js
CHANGED
|
@@ -1,32 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { generateObject, generateText, stepCountIs, streamObject } from "ai";
|
|
2
|
+
const MIGRATION_URL = "node_modules/@convex-dev/agent/MIGRATION.md";
|
|
3
|
+
const warnedDeprecations = new Set();
|
|
4
|
+
function warnDeprecation(key, message) {
|
|
5
|
+
if (!warnedDeprecations.has(key)) {
|
|
6
|
+
warnedDeprecations.add(key);
|
|
7
|
+
console.warn(`[@convex-dev/agent] ${message}\n See: ${MIGRATION_URL}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
2
10
|
import { assert, omit, pick } from "convex-helpers";
|
|
3
11
|
import { internalActionGeneric, internalMutationGeneric, } from "convex/server";
|
|
4
|
-
import { v } from "convex/values";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { vMessageWithMetadata, vSafeObjectArgs, vTextArgs, } from "../validators.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { convexToJson, v } from "convex/values";
|
|
13
|
+
import {} from "../component/vector/tables.js";
|
|
14
|
+
import { toModelMessage, serializeMessage, serializeNewMessagesInStep, serializeObjectResult, } from "../mapping.js";
|
|
15
|
+
import { getModelName, getProviderName } from "../shared.js";
|
|
16
|
+
import { vMessageEmbeddings, vMessageWithMetadata, vSafeObjectArgs, vTextArgs, } from "../validators.js";
|
|
17
|
+
import { listMessages, saveMessages, } from "./messages.js";
|
|
18
|
+
import { embedMany, embedMessages, fetchContextMessages, generateAndSaveEmbeddings, } from "./search.js";
|
|
19
|
+
import { startGeneration } from "./start.js";
|
|
20
|
+
import { syncStreams } from "./streaming.js";
|
|
21
|
+
import { createThread, getThreadMetadata } from "./threads.js";
|
|
22
|
+
import { streamText } from "./streamText.js";
|
|
23
|
+
import { errorToString, willContinue } from "./utils.js";
|
|
13
24
|
export { stepCountIs } from "ai";
|
|
14
|
-
export {
|
|
15
|
-
|
|
25
|
+
export { docsToModelMessages, toModelMessage,
|
|
26
|
+
//** @deprecated use toModelMessage instead */
|
|
27
|
+
toModelMessage as deserializeMessage, guessMimeType, serializeDataOrUrl, serializeMessage, toUIFilePart, } from "../mapping.js";
|
|
16
28
|
// NOTE: these are also exported via @convex-dev/agent/validators
|
|
17
29
|
// a future version may put them all here or move these over there
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
21
|
-
export { abortStream, listStreams, syncStreams } from "./streaming.js";
|
|
22
|
-
export { createTool, extractText, isTool, listMessages };
|
|
30
|
+
export { extractText, isTool, sorted } from "../shared.js";
|
|
31
|
+
export { vAssistantMessage, vContent, vContextOptions, vMessage, vMessageDoc, vPaginationResult, vProviderMetadata, vSource, vStorageOptions, vStreamArgs, vSystemMessage, vThreadDoc, vToolMessage, vUsage, vUserMessage, } from "../validators.js";
|
|
32
|
+
export { createTool } from "./createTool.js";
|
|
23
33
|
export { definePlaygroundAPI, } from "./definePlaygroundAPI.js";
|
|
34
|
+
export { getFile, storeFile } from "./files.js";
|
|
35
|
+
export { listMessages, listUIMessages, saveMessage, saveMessages, } from "./messages.js";
|
|
36
|
+
export { mockModel } from "./mockModel.js";
|
|
37
|
+
export { fetchContextMessages, filterOutOrphanedToolMessages, fetchContextWithPrompt, generateAndSaveEmbeddings, embedMessages, embedMany, } from "./search.js";
|
|
38
|
+
export { startGeneration } from "./start.js";
|
|
39
|
+
export { DEFAULT_STREAMING_OPTIONS, DeltaStreamer, abortStream, compressUIMessageChunks, listStreams, syncStreams, vStreamMessagesReturnValue, } from "./streaming.js";
|
|
40
|
+
export { createThread, getThreadMetadata, searchThreadTitles, updateThreadMetadata, } from "./threads.js";
|
|
41
|
+
export { toUIMessages, fromUIMessages } from "../UIMessages.js";
|
|
24
42
|
export class Agent {
|
|
25
43
|
component;
|
|
26
44
|
options;
|
|
27
45
|
constructor(component, options) {
|
|
28
46
|
this.component = component;
|
|
29
47
|
this.options = options;
|
|
48
|
+
if (this.options.textEmbeddingModel && !this.options.embeddingModel) {
|
|
49
|
+
warnDeprecation("textEmbeddingModel", "textEmbeddingModel is deprecated. Use embeddingModel instead.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the embedding model, prioritizing embeddingModel over textEmbeddingModel.
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
getEmbeddingModel() {
|
|
57
|
+
return this.options.embeddingModel ?? this.options.textEmbeddingModel;
|
|
30
58
|
}
|
|
31
59
|
async createThread(ctx, args) {
|
|
32
60
|
const threadId = await createThread(ctx, this.component, args);
|
|
@@ -36,13 +64,8 @@ export class Agent {
|
|
|
36
64
|
const { thread } = await this.continueThread(ctx, {
|
|
37
65
|
threadId,
|
|
38
66
|
userId: args?.userId,
|
|
39
|
-
usageHandler: args?.usageHandler,
|
|
40
|
-
tools: args?.tools,
|
|
41
67
|
});
|
|
42
|
-
return {
|
|
43
|
-
threadId,
|
|
44
|
-
thread,
|
|
45
|
-
};
|
|
68
|
+
return { threadId, thread };
|
|
46
69
|
}
|
|
47
70
|
/**
|
|
48
71
|
* Continues a thread using this agent. Note: threads can be continued
|
|
@@ -70,16 +93,25 @@ export class Agent {
|
|
|
70
93
|
},
|
|
71
94
|
};
|
|
72
95
|
}
|
|
96
|
+
async start(ctx,
|
|
73
97
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
98
|
+
* These are the arguments you'll pass to the LLM call such as
|
|
99
|
+
* `generateText` or `streamText`. This function will look up the context
|
|
100
|
+
* and provide functions to save the steps, abort the generation, and more.
|
|
101
|
+
* The type of the arguments returned infers from the type of the arguments
|
|
102
|
+
* you pass here.
|
|
77
103
|
*/
|
|
78
|
-
|
|
79
|
-
return ctx
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
104
|
+
args, options) {
|
|
105
|
+
return startGeneration(ctx, this.component, {
|
|
106
|
+
...args,
|
|
107
|
+
tools: (args.tools ?? this.options.tools),
|
|
108
|
+
system: args.system ?? this.options.instructions,
|
|
109
|
+
stopWhen: (args.stopWhen ?? this.options.stopWhen),
|
|
110
|
+
}, {
|
|
111
|
+
...this.options,
|
|
112
|
+
...options,
|
|
113
|
+
agentName: this.options.name,
|
|
114
|
+
agentForToolCtx: this,
|
|
83
115
|
});
|
|
84
116
|
}
|
|
85
117
|
/**
|
|
@@ -89,80 +121,44 @@ export class Agent {
|
|
|
89
121
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
90
122
|
* to a thread (and optionally userId).
|
|
91
123
|
* @param ctx The context passed from the action function calling this.
|
|
92
|
-
* @param
|
|
93
|
-
* @param
|
|
94
|
-
*
|
|
124
|
+
* @param scope: The user and thread to associate the message with
|
|
125
|
+
* @param generateTextArgs The arguments to the generateText function, along
|
|
126
|
+
* with {@link AgentPrompt} options, such as promptMessageId.
|
|
127
|
+
* @param options Extra controls for the {@link ContextOptions} and {@link StorageOptions}.
|
|
95
128
|
* @returns The result of the generateText function.
|
|
96
129
|
*/
|
|
97
|
-
async generateText(ctx,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
104
|
-
const
|
|
105
|
-
const toolCtx = {
|
|
106
|
-
...ctx,
|
|
107
|
-
userId,
|
|
108
|
-
threadId,
|
|
109
|
-
messageId,
|
|
110
|
-
agent: this,
|
|
111
|
-
};
|
|
112
|
-
const tools = wrapTools(toolCtx, args.tools ?? threadTools ?? this.options.tools);
|
|
113
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
130
|
+
async generateText(ctx, threadOpts,
|
|
131
|
+
/**
|
|
132
|
+
* The arguments to the generateText function, similar to the ai sdk's
|
|
133
|
+
* {@link generateText} function, along with Agent prompt options.
|
|
134
|
+
*/
|
|
135
|
+
generateTextArgs, options) {
|
|
136
|
+
const { args, promptMessageId, order, ...call } = await this.start(ctx, generateTextArgs, { ...threadOpts, ...options });
|
|
137
|
+
const steps = [];
|
|
114
138
|
try {
|
|
115
139
|
const result = (await generateText({
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
...args,
|
|
141
|
+
prepareStep: async (options) => {
|
|
142
|
+
const result = await generateTextArgs.prepareStep?.(options);
|
|
143
|
+
call.updateModel(result?.model ?? options.model);
|
|
144
|
+
return result;
|
|
145
|
+
},
|
|
120
146
|
onStepFinish: async (step) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
threadId,
|
|
125
|
-
promptMessageId: messageId,
|
|
126
|
-
model: aiArgs.model.modelId,
|
|
127
|
-
provider: aiArgs.model.provider,
|
|
128
|
-
step,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
if (this.options.rawRequestResponseHandler) {
|
|
132
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
133
|
-
userId,
|
|
134
|
-
threadId,
|
|
135
|
-
agentName: this.options.name,
|
|
136
|
-
request: step.request,
|
|
137
|
-
response: step.response,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
if (opts.usageHandler && step.usage) {
|
|
141
|
-
await opts.usageHandler(ctx, {
|
|
142
|
-
userId,
|
|
143
|
-
threadId,
|
|
144
|
-
agentName: this.options.name,
|
|
145
|
-
model: aiArgs.model.modelId,
|
|
146
|
-
provider: aiArgs.model.provider,
|
|
147
|
-
usage: step.usage,
|
|
148
|
-
providerMetadata: step.providerMetadata,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return args.onStepFinish?.(step);
|
|
147
|
+
steps.push(step);
|
|
148
|
+
await call.save({ step }, await willContinue(steps, args.stopWhen));
|
|
149
|
+
return generateTextArgs.onStepFinish?.(step);
|
|
152
150
|
},
|
|
153
151
|
}));
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
const metadata = {
|
|
153
|
+
promptMessageId,
|
|
154
|
+
order,
|
|
155
|
+
savedMessages: call.getSavedMessages(),
|
|
156
|
+
messageId: promptMessageId,
|
|
157
|
+
};
|
|
158
|
+
return Object.assign(result, metadata);
|
|
157
159
|
}
|
|
158
160
|
catch (error) {
|
|
159
|
-
|
|
160
|
-
console.error("RollbackMessage", messageId);
|
|
161
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
162
|
-
messageId,
|
|
163
|
-
error: error.message,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
161
|
+
await call.fail(errorToString(error));
|
|
166
162
|
throw error;
|
|
167
163
|
}
|
|
168
164
|
}
|
|
@@ -173,109 +169,30 @@ export class Agent {
|
|
|
173
169
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
174
170
|
* to a thread (and optionally userId).
|
|
175
171
|
*/
|
|
176
|
-
async streamText(ctx,
|
|
177
|
-
/** Note: to get better type inference, pass tools in the next arg */
|
|
178
|
-
tools: threadTools, },
|
|
172
|
+
async streamText(ctx, threadOpts,
|
|
179
173
|
/**
|
|
180
|
-
* The arguments to the streamText function, similar to the ai
|
|
174
|
+
* The arguments to the streamText function, similar to the ai sdk's
|
|
175
|
+
* {@link streamText} function, along with Agent prompt options.
|
|
181
176
|
*/
|
|
182
|
-
|
|
177
|
+
streamTextArgs,
|
|
183
178
|
/**
|
|
184
179
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
185
180
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
186
181
|
*/
|
|
187
182
|
options) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
...
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
agent: this,
|
|
201
|
-
};
|
|
202
|
-
const tools = wrapTools(toolCtx, args.tools ?? threadTools ?? this.options.tools);
|
|
203
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
204
|
-
const streamer = threadId && opts.saveStreamDeltas
|
|
205
|
-
? new DeltaStreamer(this.component, ctx, opts.saveStreamDeltas, {
|
|
206
|
-
threadId,
|
|
207
|
-
userId,
|
|
208
|
-
agentName: this.options.name,
|
|
209
|
-
model: aiArgs.model.modelId,
|
|
210
|
-
provider: aiArgs.model.provider,
|
|
211
|
-
providerOptions: aiArgs.providerOptions,
|
|
212
|
-
order,
|
|
213
|
-
stepOrder,
|
|
214
|
-
abortSignal: aiArgs.abortSignal,
|
|
215
|
-
})
|
|
216
|
-
: undefined;
|
|
217
|
-
const result = streamText({
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
219
|
-
stopWhen: this.options.stopWhen, // Can be overridden
|
|
220
|
-
...aiArgs,
|
|
221
|
-
tools,
|
|
222
|
-
abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
|
|
223
|
-
experimental_transform: mergeTransforms(options?.saveStreamDeltas, args.experimental_transform),
|
|
224
|
-
onChunk: async (event) => {
|
|
225
|
-
await streamer?.addParts([event.chunk]);
|
|
226
|
-
// console.log("onChunk", chunk);
|
|
227
|
-
return args.onChunk?.(event);
|
|
228
|
-
},
|
|
229
|
-
onError: async (error) => {
|
|
230
|
-
console.error("onError", error);
|
|
231
|
-
if (threadId && messageId && saveOutput) {
|
|
232
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
233
|
-
messageId,
|
|
234
|
-
error: error.error.message,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
// TODO: update the streamer to error state
|
|
238
|
-
return args.onError?.(error);
|
|
239
|
-
},
|
|
240
|
-
onStepFinish: async (step) => {
|
|
241
|
-
// console.log("onStepFinish", step);
|
|
242
|
-
if (threadId && messageId && saveOutput) {
|
|
243
|
-
const saved = await this.saveStep(ctx, {
|
|
244
|
-
userId,
|
|
245
|
-
threadId,
|
|
246
|
-
model: aiArgs.model.modelId,
|
|
247
|
-
provider: aiArgs.model.provider,
|
|
248
|
-
promptMessageId: messageId,
|
|
249
|
-
step,
|
|
250
|
-
});
|
|
251
|
-
await streamer?.finish(saved.messages);
|
|
252
|
-
}
|
|
253
|
-
if (this.options.rawRequestResponseHandler) {
|
|
254
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
255
|
-
userId,
|
|
256
|
-
threadId,
|
|
257
|
-
agentName: this.options.name,
|
|
258
|
-
request: step.request,
|
|
259
|
-
response: step.response,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (opts.usageHandler && step.usage) {
|
|
263
|
-
await opts.usageHandler(ctx, {
|
|
264
|
-
userId,
|
|
265
|
-
threadId,
|
|
266
|
-
agentName: this.options.name,
|
|
267
|
-
model: aiArgs.model.modelId,
|
|
268
|
-
provider: aiArgs.model.provider,
|
|
269
|
-
usage: step.usage,
|
|
270
|
-
providerMetadata: step.providerMetadata,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
return args.onStepFinish?.(step);
|
|
274
|
-
},
|
|
183
|
+
return streamText(ctx, this.component, {
|
|
184
|
+
...streamTextArgs,
|
|
185
|
+
model: streamTextArgs.model ?? this.options.languageModel,
|
|
186
|
+
tools: (streamTextArgs.tools ?? this.options.tools),
|
|
187
|
+
system: streamTextArgs.system ?? this.options.instructions,
|
|
188
|
+
stopWhen: (streamTextArgs.stopWhen ?? this.options.stopWhen),
|
|
189
|
+
}, {
|
|
190
|
+
...threadOpts,
|
|
191
|
+
...this.options,
|
|
192
|
+
agentName: this.options.name,
|
|
193
|
+
agentForToolCtx: this,
|
|
194
|
+
...options,
|
|
275
195
|
});
|
|
276
|
-
result.messageId = messageId;
|
|
277
|
-
result.order = order;
|
|
278
|
-
return result;
|
|
279
196
|
}
|
|
280
197
|
/**
|
|
281
198
|
* This behaves like {@link generateObject} from the "ai" package except that
|
|
@@ -284,69 +201,31 @@ export class Agent {
|
|
|
284
201
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
285
202
|
* to a thread (and optionally userId).
|
|
286
203
|
*/
|
|
287
|
-
async generateObject(ctx,
|
|
204
|
+
async generateObject(ctx, threadOpts,
|
|
288
205
|
/**
|
|
289
|
-
* The arguments to the generateObject function, similar to the ai
|
|
206
|
+
* The arguments to the generateObject function, similar to the ai sdk's
|
|
207
|
+
* {@link generateObject} function, along with Agent prompt options.
|
|
290
208
|
*/
|
|
291
|
-
|
|
209
|
+
generateObjectArgs,
|
|
292
210
|
/**
|
|
293
211
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
294
212
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
295
213
|
*/
|
|
296
214
|
options) {
|
|
297
|
-
const
|
|
298
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
299
|
-
userId: argsUserId ?? undefined,
|
|
300
|
-
threadId,
|
|
301
|
-
...opts,
|
|
302
|
-
});
|
|
303
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
304
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
215
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } = await this.start(ctx, generateObjectArgs, { ...threadOpts, ...options });
|
|
305
216
|
try {
|
|
306
|
-
const result = (await generateObject(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
model: aiArgs.model.modelId,
|
|
316
|
-
provider: aiArgs.model.provider,
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
result.messageId = messageId;
|
|
320
|
-
result.order = order;
|
|
321
|
-
if (this.options.rawRequestResponseHandler) {
|
|
322
|
-
await this.options.rawRequestResponseHandler(ctx, {
|
|
323
|
-
userId,
|
|
324
|
-
threadId,
|
|
325
|
-
agentName: this.options.name,
|
|
326
|
-
request: result.request,
|
|
327
|
-
response: result.response,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
if (opts.usageHandler && result.usage) {
|
|
331
|
-
await opts.usageHandler(ctx, {
|
|
332
|
-
userId,
|
|
333
|
-
threadId,
|
|
334
|
-
agentName: this.options.name,
|
|
335
|
-
model: aiArgs.model.modelId,
|
|
336
|
-
provider: aiArgs.model.provider,
|
|
337
|
-
usage: result.usage,
|
|
338
|
-
providerMetadata: result.providerMetadata,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
return result;
|
|
217
|
+
const result = (await generateObject(args));
|
|
218
|
+
await save({ object: result });
|
|
219
|
+
const metadata = {
|
|
220
|
+
promptMessageId,
|
|
221
|
+
order,
|
|
222
|
+
savedMessages: getSavedMessages(),
|
|
223
|
+
messageId: promptMessageId,
|
|
224
|
+
};
|
|
225
|
+
return Object.assign(result, metadata);
|
|
342
226
|
}
|
|
343
227
|
catch (error) {
|
|
344
|
-
|
|
345
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
346
|
-
messageId,
|
|
347
|
-
error: error.message,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
228
|
+
await fail(errorToString(error));
|
|
350
229
|
throw error;
|
|
351
230
|
}
|
|
352
231
|
}
|
|
@@ -357,80 +236,51 @@ export class Agent {
|
|
|
357
236
|
* Use {@link continueThread} to get a version of this function already scoped
|
|
358
237
|
* to a thread (and optionally userId).
|
|
359
238
|
*/
|
|
360
|
-
async streamObject(ctx,
|
|
239
|
+
async streamObject(ctx, threadOpts,
|
|
361
240
|
/**
|
|
362
|
-
* The arguments to the streamObject function, similar to the ai
|
|
241
|
+
* The arguments to the streamObject function, similar to the ai sdk's
|
|
242
|
+
* {@link streamObject} function, along with Agent prompt options.
|
|
363
243
|
*/
|
|
364
|
-
|
|
244
|
+
streamObjectArgs,
|
|
365
245
|
/**
|
|
366
246
|
* The {@link ContextOptions} and {@link StorageOptions}
|
|
367
247
|
* options to use for fetching contextual messages and saving input/output messages.
|
|
368
248
|
*/
|
|
369
249
|
options) {
|
|
370
|
-
|
|
371
|
-
const opts = { ...this.options, ...options, usageHandler };
|
|
372
|
-
const context = await this._saveMessagesAndFetchContext(ctx, args, {
|
|
373
|
-
userId: argsUserId ?? undefined,
|
|
374
|
-
threadId,
|
|
375
|
-
...opts,
|
|
376
|
-
});
|
|
377
|
-
const { args: aiArgs, messageId, order, userId } = context;
|
|
378
|
-
const saveOutput = opts.storageOptions?.saveMessages !== "none";
|
|
379
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
const { args, promptMessageId, order, fail, save, getSavedMessages } = await this.start(ctx, streamObjectArgs, { ...threadOpts, ...options });
|
|
380
251
|
const stream = streamObject({
|
|
381
|
-
|
|
382
|
-
...aiArgs,
|
|
252
|
+
...args,
|
|
383
253
|
onError: async (error) => {
|
|
384
|
-
console.error("onError", error);
|
|
254
|
+
console.error(" streamObject onError", error);
|
|
255
|
+
// TODO: content that we have so far
|
|
256
|
+
// content: stream.fullStream.
|
|
257
|
+
await fail(errorToString(error.error));
|
|
385
258
|
return args.onError?.(error);
|
|
386
259
|
},
|
|
387
260
|
onFinish: async (result) => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
promptMessageId: messageId,
|
|
393
|
-
result: {
|
|
394
|
-
object: result.object,
|
|
395
|
-
finishReason: "stop",
|
|
396
|
-
usage: result.usage,
|
|
397
|
-
warnings: result.warnings,
|
|
398
|
-
request: await stream.request,
|
|
399
|
-
response: result.response,
|
|
400
|
-
providerMetadata: result.providerMetadata,
|
|
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,
|
|
261
|
+
await save({
|
|
262
|
+
object: {
|
|
263
|
+
object: result.object,
|
|
264
|
+
finishReason: result.error ? "error" : "stop",
|
|
414
265
|
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,
|
|
266
|
+
warnings: result.warnings,
|
|
423
267
|
request: await stream.request,
|
|
424
268
|
response: result.response,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
269
|
+
providerMetadata: result.providerMetadata,
|
|
270
|
+
toJsonResponse: stream.toTextStreamResponse,
|
|
271
|
+
reasoning: undefined,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
428
274
|
return args.onFinish?.(result);
|
|
429
275
|
},
|
|
430
276
|
});
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
277
|
+
const metadata = {
|
|
278
|
+
promptMessageId,
|
|
279
|
+
order,
|
|
280
|
+
savedMessages: getSavedMessages(),
|
|
281
|
+
messageId: promptMessageId,
|
|
282
|
+
};
|
|
283
|
+
return Object.assign(stream, metadata);
|
|
434
284
|
}
|
|
435
285
|
/**
|
|
436
286
|
* Save a message to the thread.
|
|
@@ -440,22 +290,22 @@ export class Agent {
|
|
|
440
290
|
* @returns The messageId of the saved message.
|
|
441
291
|
*/
|
|
442
292
|
async saveMessage(ctx, args) {
|
|
443
|
-
const {
|
|
293
|
+
const { messages } = await this.saveMessages(ctx, {
|
|
444
294
|
threadId: args.threadId,
|
|
445
295
|
userId: args.userId,
|
|
446
296
|
embeddings: args.embedding
|
|
447
|
-
? {
|
|
448
|
-
model: args.embedding.model,
|
|
449
|
-
vectors: [args.embedding.vector],
|
|
450
|
-
}
|
|
297
|
+
? { model: args.embedding.model, vectors: [args.embedding.vector] }
|
|
451
298
|
: undefined,
|
|
452
299
|
messages: args.prompt !== undefined
|
|
453
300
|
? [{ role: "user", content: args.prompt }]
|
|
454
301
|
: [args.message],
|
|
455
302
|
metadata: args.metadata ? [args.metadata] : undefined,
|
|
456
303
|
skipEmbeddings: args.skipEmbeddings,
|
|
304
|
+
promptMessageId: args.promptMessageId,
|
|
305
|
+
pendingMessageId: args.pendingMessageId,
|
|
457
306
|
});
|
|
458
|
-
|
|
307
|
+
const message = messages.at(-1);
|
|
308
|
+
return { messageId: message._id, message };
|
|
459
309
|
}
|
|
460
310
|
/**
|
|
461
311
|
* Explicitly save messages associated with the thread (& user if provided)
|
|
@@ -471,7 +321,7 @@ export class Agent {
|
|
|
471
321
|
if (args.embeddings) {
|
|
472
322
|
embeddings = args.embeddings;
|
|
473
323
|
}
|
|
474
|
-
else if (!skipEmbeddings && this.
|
|
324
|
+
else if (!skipEmbeddings && this.getEmbeddingModel()) {
|
|
475
325
|
if (!("runAction" in ctx)) {
|
|
476
326
|
console.warn("You're trying to save messages and generate embeddings, but you're in a mutation. " +
|
|
477
327
|
"Pass `skipEmbeddings: true` to skip generating embeddings in the mutation and skip this warning. " +
|
|
@@ -485,10 +335,7 @@ export class Agent {
|
|
|
485
335
|
"You can explicitly generate them asynchronously by using the scheduler to run an action later that calls `agent.generateAndSaveEmbeddings`.");
|
|
486
336
|
}
|
|
487
337
|
else {
|
|
488
|
-
embeddings = await this.generateEmbeddings(ctx, {
|
|
489
|
-
userId: args.userId ?? undefined,
|
|
490
|
-
threadId: args.threadId,
|
|
491
|
-
}, args.messages);
|
|
338
|
+
embeddings = await this.generateEmbeddings(ctx, { userId: args.userId ?? undefined, threadId: args.threadId }, args.messages);
|
|
492
339
|
}
|
|
493
340
|
}
|
|
494
341
|
return saveMessages(ctx, this.component, {
|
|
@@ -540,14 +387,17 @@ export class Agent {
|
|
|
540
387
|
contextOptions,
|
|
541
388
|
getEmbedding: async (text) => {
|
|
542
389
|
assert("runAction" in ctx);
|
|
543
|
-
|
|
390
|
+
const embeddingModel = this.getEmbeddingModel();
|
|
391
|
+
assert(embeddingModel, "An embeddingModel (or textEmbeddingModel) is required to be set on the Agent that you're doing vector search with");
|
|
544
392
|
return {
|
|
545
|
-
embedding: (await
|
|
393
|
+
embedding: (await embedMany(ctx, {
|
|
394
|
+
...this.options,
|
|
395
|
+
agentName: this.options.name,
|
|
546
396
|
userId: args.userId,
|
|
547
397
|
threadId: args.threadId,
|
|
548
398
|
values: [text],
|
|
549
399
|
})).embeddings[0],
|
|
550
|
-
embeddingModel:
|
|
400
|
+
embeddingModel: embeddingModel,
|
|
551
401
|
};
|
|
552
402
|
},
|
|
553
403
|
});
|
|
@@ -577,40 +427,8 @@ export class Agent {
|
|
|
577
427
|
* @param messages The messages to get the embeddings for.
|
|
578
428
|
* @returns The embeddings for the messages.
|
|
579
429
|
*/
|
|
580
|
-
async generateEmbeddings(ctx,
|
|
581
|
-
|
|
582
|
-
return undefined;
|
|
583
|
-
}
|
|
584
|
-
let embeddings;
|
|
585
|
-
const messageTexts = messages.map((m) => !isTool(m) && extractText(m));
|
|
586
|
-
// Find the indexes of the messages that have text.
|
|
587
|
-
const textIndexes = messageTexts
|
|
588
|
-
.map((t, i) => (t ? i : undefined))
|
|
589
|
-
.filter((i) => i !== undefined);
|
|
590
|
-
if (textIndexes.length === 0) {
|
|
591
|
-
return undefined;
|
|
592
|
-
}
|
|
593
|
-
// Then embed those messages.
|
|
594
|
-
const textEmbeddings = await this.doEmbed(ctx, {
|
|
595
|
-
userId,
|
|
596
|
-
threadId,
|
|
597
|
-
values: messageTexts,
|
|
598
|
-
});
|
|
599
|
-
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
600
|
-
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
601
|
-
textIndexes.forEach((i, j) => {
|
|
602
|
-
embeddingsOrNull[i] = textEmbeddings.embeddings[j];
|
|
603
|
-
});
|
|
604
|
-
if (textEmbeddings.embeddings.length > 0) {
|
|
605
|
-
const dimension = textEmbeddings.embeddings[0].length;
|
|
606
|
-
validateVectorDimension(dimension);
|
|
607
|
-
embeddings = {
|
|
608
|
-
vectors: embeddingsOrNull,
|
|
609
|
-
dimension,
|
|
610
|
-
model: this.options.textEmbedding.modelId,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
return embeddings;
|
|
430
|
+
async generateEmbeddings(ctx, args, messages) {
|
|
431
|
+
return embedMessages(ctx, { ...args, ...this.options, agentName: this.options.name }, messages);
|
|
614
432
|
}
|
|
615
433
|
/**
|
|
616
434
|
* Generate embeddings for a set of messages, and save them to the database.
|
|
@@ -629,9 +447,6 @@ export class Agent {
|
|
|
629
447
|
.filter((id) => !messages.some((m) => m?._id === id))
|
|
630
448
|
.join(", "));
|
|
631
449
|
}
|
|
632
|
-
await this._generateAndSaveEmbeddings(ctx, messages);
|
|
633
|
-
}
|
|
634
|
-
async _generateAndSaveEmbeddings(ctx, messages) {
|
|
635
450
|
if (messages.some((m) => !m.message)) {
|
|
636
451
|
throw new Error("Some messages don't have a message: " +
|
|
637
452
|
messages
|
|
@@ -639,34 +454,108 @@ export class Agent {
|
|
|
639
454
|
.map((m) => m._id)
|
|
640
455
|
.join(", "));
|
|
641
456
|
}
|
|
642
|
-
const
|
|
643
|
-
if (
|
|
644
|
-
|
|
645
|
-
}
|
|
646
|
-
const embeddings = await this.generateEmbeddings(ctx, {
|
|
647
|
-
userId: messagesMissingEmbeddings[0].userId,
|
|
648
|
-
threadId: messagesMissingEmbeddings[0].threadId,
|
|
649
|
-
}, messagesMissingEmbeddings.map((m) => deserializeMessage(m.message)));
|
|
650
|
-
if (!embeddings) {
|
|
651
|
-
if (!this.options.textEmbedding) {
|
|
652
|
-
throw new Error("No embeddings were generated for the messages. You must pass a textEmbedding model to the agent constructor.");
|
|
653
|
-
}
|
|
654
|
-
throw new Error("No embeddings were generated for these messages: " +
|
|
655
|
-
messagesMissingEmbeddings.map((m) => m._id).join(", "));
|
|
457
|
+
const embeddingModel = this.getEmbeddingModel();
|
|
458
|
+
if (!embeddingModel) {
|
|
459
|
+
throw new Error("No embeddings were generated for the messages. You must pass an embeddingModel (or textEmbeddingModel) to the agent constructor.");
|
|
656
460
|
}
|
|
657
|
-
await ctx
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
461
|
+
await generateAndSaveEmbeddings(ctx, this.component, {
|
|
462
|
+
...this.options,
|
|
463
|
+
agentName: this.options.name,
|
|
464
|
+
threadId: messages[0].threadId,
|
|
465
|
+
userId: messages[0].userId,
|
|
466
|
+
embeddingModel,
|
|
467
|
+
}, messages);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Approve a tool call that requires human approval.
|
|
471
|
+
* Saves a `tool-approval-response` message to the thread.
|
|
472
|
+
* After calling this, call `agent.streamText` or `agent.generateText`
|
|
473
|
+
* with `promptMessageId` set to the returned `messageId` to continue
|
|
474
|
+
* generation — the AI SDK will automatically execute the approved tool.
|
|
475
|
+
*
|
|
476
|
+
* The approval response is attached to the same generation order as the
|
|
477
|
+
* original approval request, preserving tool_call/tool_result adjacency in
|
|
478
|
+
* the continuation context even if newer thread messages exist.
|
|
479
|
+
*
|
|
480
|
+
* @param ctx A ctx object from a mutation.
|
|
481
|
+
* @param args.threadId The thread containing the tool call.
|
|
482
|
+
* @param args.approvalId The approval ID from the tool-approval-request part.
|
|
483
|
+
* @param args.reason Optional reason for approval.
|
|
484
|
+
* @returns The messageId of the saved approval response message.
|
|
485
|
+
*/
|
|
486
|
+
async approveToolCall(ctx, args) {
|
|
487
|
+
return this.respondToToolCallApproval(ctx, { ...args, approved: true });
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Deny a tool call that requires human approval.
|
|
491
|
+
* Saves a `tool-approval-response` message to the thread.
|
|
492
|
+
* After calling this, call `agent.streamText` or `agent.generateText`
|
|
493
|
+
* with `promptMessageId` set to the returned `messageId` to continue
|
|
494
|
+
* generation — the AI SDK will automatically create an `execution-denied`
|
|
495
|
+
* result and let the model respond accordingly.
|
|
496
|
+
*
|
|
497
|
+
* @param ctx A ctx object from a mutation.
|
|
498
|
+
* @param args.threadId The thread containing the tool call.
|
|
499
|
+
* @param args.approvalId The approval ID from the tool-approval-request part.
|
|
500
|
+
* @param args.reason Optional reason for denial.
|
|
501
|
+
* @returns The messageId of the saved denial response message.
|
|
502
|
+
*/
|
|
503
|
+
async denyToolCall(ctx, args) {
|
|
504
|
+
return this.respondToToolCallApproval(ctx, { ...args, approved: false });
|
|
505
|
+
}
|
|
506
|
+
async respondToToolCallApproval(ctx, args) {
|
|
507
|
+
const promptMessageId = await this.getApprovalRequestMessageId(ctx, {
|
|
508
|
+
threadId: args.threadId,
|
|
509
|
+
approvalId: args.approvalId,
|
|
510
|
+
});
|
|
511
|
+
const { messageId } = await this.saveMessage(ctx, {
|
|
512
|
+
threadId: args.threadId,
|
|
513
|
+
promptMessageId,
|
|
514
|
+
skipEmbeddings: true,
|
|
515
|
+
message: {
|
|
516
|
+
role: "tool",
|
|
517
|
+
content: [
|
|
518
|
+
{
|
|
519
|
+
type: "tool-approval-response",
|
|
520
|
+
approvalId: args.approvalId,
|
|
521
|
+
approved: args.approved,
|
|
522
|
+
reason: args.reason,
|
|
523
|
+
},
|
|
524
|
+
],
|
|
525
|
+
},
|
|
669
526
|
});
|
|
527
|
+
return { messageId };
|
|
528
|
+
}
|
|
529
|
+
async getApprovalRequestMessageId(ctx, args) {
|
|
530
|
+
// NOTE: This pagination returns messages in descending order (newest first).
|
|
531
|
+
// The "already handled" check (tool-approval-response) relies on seeing
|
|
532
|
+
// responses before their corresponding requests. If the pagination order
|
|
533
|
+
// changes, this logic will need to be updated.
|
|
534
|
+
let cursor = null;
|
|
535
|
+
do {
|
|
536
|
+
const page = await this.listMessages(ctx, {
|
|
537
|
+
threadId: args.threadId,
|
|
538
|
+
paginationOpts: { cursor, numItems: 100 },
|
|
539
|
+
});
|
|
540
|
+
for (const message of page.page) {
|
|
541
|
+
const content = message.message?.content;
|
|
542
|
+
if (!Array.isArray(content))
|
|
543
|
+
continue;
|
|
544
|
+
for (const part of content) {
|
|
545
|
+
const typedPart = part;
|
|
546
|
+
if (typedPart.type === "tool-approval-response" &&
|
|
547
|
+
typedPart.approvalId === args.approvalId) {
|
|
548
|
+
throw new Error(`Approval ${args.approvalId} was already handled`);
|
|
549
|
+
}
|
|
550
|
+
if (typedPart.type === "tool-approval-request" &&
|
|
551
|
+
typedPart.approvalId === args.approvalId) {
|
|
552
|
+
return message._id;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
cursor = page.isDone ? null : page.continueCursor;
|
|
557
|
+
} while (cursor !== null);
|
|
558
|
+
throw new Error(`Approval request ${args.approvalId} was not found in thread ${args.threadId}`);
|
|
670
559
|
}
|
|
671
560
|
/**
|
|
672
561
|
* Explicitly save a "step" created by the AI SDK.
|
|
@@ -674,12 +563,12 @@ export class Agent {
|
|
|
674
563
|
* @param args The Step generated by the AI SDK.
|
|
675
564
|
*/
|
|
676
565
|
async saveStep(ctx, args) {
|
|
677
|
-
const messages = await serializeNewMessagesInStep(ctx, this.component, args.step, {
|
|
678
|
-
provider: args.provider ?? this.options.
|
|
679
|
-
model: args.model ?? this.options.
|
|
566
|
+
const { messages } = await serializeNewMessagesInStep(ctx, this.component, args.step, {
|
|
567
|
+
provider: args.provider ?? getProviderName(this.options.languageModel),
|
|
568
|
+
model: args.model ?? getModelName(this.options.languageModel),
|
|
680
569
|
});
|
|
681
|
-
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) =>
|
|
682
|
-
|
|
570
|
+
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
|
|
571
|
+
return ctx.runMutation(this.component.messages.addMessages, {
|
|
683
572
|
userId: args.userId,
|
|
684
573
|
threadId: args.threadId,
|
|
685
574
|
agentName: this.options.name,
|
|
@@ -688,7 +577,6 @@ export class Agent {
|
|
|
688
577
|
embeddings,
|
|
689
578
|
failPendingSteps: false,
|
|
690
579
|
});
|
|
691
|
-
return saved;
|
|
692
580
|
}
|
|
693
581
|
/**
|
|
694
582
|
* Manually save the result of a generateObject call to the thread.
|
|
@@ -699,11 +587,15 @@ export class Agent {
|
|
|
699
587
|
*/
|
|
700
588
|
async saveObject(ctx, args) {
|
|
701
589
|
const { messages } = await serializeObjectResult(ctx, this.component, args.result, {
|
|
702
|
-
model: args.model ??
|
|
703
|
-
|
|
590
|
+
model: args.model ??
|
|
591
|
+
args.metadata?.model ??
|
|
592
|
+
getModelName(this.options.languageModel),
|
|
593
|
+
provider: args.provider ??
|
|
594
|
+
args.metadata?.provider ??
|
|
595
|
+
getProviderName(this.options.languageModel),
|
|
704
596
|
});
|
|
705
|
-
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) =>
|
|
706
|
-
|
|
597
|
+
const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
|
|
598
|
+
return ctx.runMutation(this.component.messages.addMessages, {
|
|
707
599
|
userId: args.userId,
|
|
708
600
|
threadId: args.threadId,
|
|
709
601
|
promptMessageId: args.promptMessageId,
|
|
@@ -711,7 +603,6 @@ export class Agent {
|
|
|
711
603
|
messages,
|
|
712
604
|
embeddings,
|
|
713
605
|
agentName: this.options.name,
|
|
714
|
-
pending: false,
|
|
715
606
|
});
|
|
716
607
|
}
|
|
717
608
|
/**
|
|
@@ -722,19 +613,11 @@ export class Agent {
|
|
|
722
613
|
* @param args What message to save. Generally the parent message sent into
|
|
723
614
|
* the generateText call.
|
|
724
615
|
*/
|
|
725
|
-
async
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
await ctx.runMutation(this.component.messages.rollbackMessage, {
|
|
734
|
-
messageId: args.messageId,
|
|
735
|
-
error: result.error,
|
|
736
|
-
});
|
|
737
|
-
}
|
|
616
|
+
async finalizeMessage(ctx, args) {
|
|
617
|
+
await ctx.runMutation(this.component.messages.finalizeMessage, {
|
|
618
|
+
messageId: args.messageId,
|
|
619
|
+
result: args.result,
|
|
620
|
+
});
|
|
738
621
|
}
|
|
739
622
|
/**
|
|
740
623
|
* Update a message by its id.
|
|
@@ -847,206 +730,6 @@ export class Agent {
|
|
|
847
730
|
limit: args.pageSize,
|
|
848
731
|
});
|
|
849
732
|
}
|
|
850
|
-
async _saveMessagesAndFetchContext(ctx, args, { userId: argsUserId, threadId, contextOptions, storageOptions, }) {
|
|
851
|
-
// If only a promptMessageId is provided, this will be empty.
|
|
852
|
-
const messages = args.messages ?? [];
|
|
853
|
-
const prompt = !args.prompt
|
|
854
|
-
? []
|
|
855
|
-
: Array.isArray(args.prompt)
|
|
856
|
-
? args.prompt
|
|
857
|
-
: [{ role: "user", content: args.prompt }];
|
|
858
|
-
const userId = argsUserId ??
|
|
859
|
-
(threadId &&
|
|
860
|
-
(await ctx.runQuery(this.component.threads.getThread, { threadId }))
|
|
861
|
-
?.userId);
|
|
862
|
-
// If only a messageId is provided, this will add that message to the end.
|
|
863
|
-
const contextMessages = await this.fetchContextMessages(ctx, {
|
|
864
|
-
userId,
|
|
865
|
-
threadId,
|
|
866
|
-
upToAndIncludingMessageId: args.promptMessageId,
|
|
867
|
-
messages,
|
|
868
|
-
contextOptions,
|
|
869
|
-
});
|
|
870
|
-
// If it was a promptMessageId, pop it off context messages
|
|
871
|
-
// and add to the end of messages.
|
|
872
|
-
const promptMessageIndex = args.promptMessageId
|
|
873
|
-
? contextMessages.findIndex((m) => m._id === args.promptMessageId)
|
|
874
|
-
: -1;
|
|
875
|
-
const promptMessage = promptMessageIndex !== -1
|
|
876
|
-
? contextMessages.splice(promptMessageIndex, 1)[0]
|
|
877
|
-
: undefined;
|
|
878
|
-
let messageId = promptMessage?._id;
|
|
879
|
-
let order = promptMessage?.order;
|
|
880
|
-
let stepOrder = promptMessage?.stepOrder;
|
|
881
|
-
if (threadId &&
|
|
882
|
-
messages.length + prompt.length &&
|
|
883
|
-
storageOptions?.saveMessages !== "none" &&
|
|
884
|
-
// If it was a promptMessageId, we don't want to save it again.
|
|
885
|
-
(!args.promptMessageId || storageOptions?.saveMessages === "all")) {
|
|
886
|
-
const saveAll = storageOptions?.saveMessages === "all";
|
|
887
|
-
const coreMessages = [...messages, ...prompt];
|
|
888
|
-
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
889
|
-
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
890
|
-
metadata[metadata.length - 1] = { id: args.id };
|
|
891
|
-
const saved = await this.saveMessages(ctx, {
|
|
892
|
-
threadId,
|
|
893
|
-
userId,
|
|
894
|
-
messages: toSave,
|
|
895
|
-
metadata,
|
|
896
|
-
failPendingSteps: true,
|
|
897
|
-
});
|
|
898
|
-
messageId = saved.lastMessageId;
|
|
899
|
-
order = saved.messages.at(-1)?.order;
|
|
900
|
-
stepOrder = saved.messages.at(-1)?.stepOrder;
|
|
901
|
-
}
|
|
902
|
-
if (promptMessage?.message) {
|
|
903
|
-
if (!args.prompt) {
|
|
904
|
-
// If they override the prompt, we skip the existing prompt message.
|
|
905
|
-
messages.push(deserializeMessage(promptMessage.message));
|
|
906
|
-
}
|
|
907
|
-
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
908
|
-
// embeddings yet. This can happen if the message was saved in a mutation
|
|
909
|
-
// where the LLM is not available.
|
|
910
|
-
if (!promptMessage.embeddingId && this.options.textEmbedding) {
|
|
911
|
-
await this._generateAndSaveEmbeddings(ctx, [promptMessage]);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
const prePrompt = contextMessages.map((m) => deserializeMessage(m.message));
|
|
915
|
-
let existingResponses = [];
|
|
916
|
-
if (promptMessageIndex !== -1) {
|
|
917
|
-
// pull any messages that already responded to the prompt off
|
|
918
|
-
// and add them after the prompt
|
|
919
|
-
existingResponses = prePrompt.splice(promptMessageIndex);
|
|
920
|
-
}
|
|
921
|
-
let processedMessages = [
|
|
922
|
-
...prePrompt,
|
|
923
|
-
...messages,
|
|
924
|
-
...prompt,
|
|
925
|
-
...existingResponses,
|
|
926
|
-
];
|
|
927
|
-
if (promptMessageIndex === -1) {
|
|
928
|
-
processedMessages.push(...prompt);
|
|
929
|
-
}
|
|
930
|
-
else {
|
|
931
|
-
// We add the prompt where the prompt message was
|
|
932
|
-
processedMessages.splice(promptMessageIndex, 0, ...prompt);
|
|
933
|
-
}
|
|
934
|
-
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
935
|
-
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
936
|
-
processedMessages = await this._inlineMessagesFiles(processedMessages);
|
|
937
|
-
}
|
|
938
|
-
const { prompt: _, model, ...rest } = args;
|
|
939
|
-
return {
|
|
940
|
-
args: {
|
|
941
|
-
...rest,
|
|
942
|
-
maxRetries: args.maxRetries ?? this.options.maxRetries,
|
|
943
|
-
model: model ?? this.options.chat,
|
|
944
|
-
system: args.system ?? this.options.instructions,
|
|
945
|
-
messages: processedMessages,
|
|
946
|
-
},
|
|
947
|
-
userId,
|
|
948
|
-
messageId,
|
|
949
|
-
order,
|
|
950
|
-
stepOrder,
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
async doEmbed(ctx, options) {
|
|
954
|
-
const embeddingModel = this.options.textEmbedding;
|
|
955
|
-
assert(embeddingModel, "a textEmbedding model is required to be set on the Agent that you're doing vector search with");
|
|
956
|
-
const result = await embedMany({
|
|
957
|
-
model: embeddingModel,
|
|
958
|
-
values: options.values,
|
|
959
|
-
abortSignal: options.abortSignal,
|
|
960
|
-
headers: options.headers,
|
|
961
|
-
maxRetries: this.options.maxRetries,
|
|
962
|
-
});
|
|
963
|
-
if (this.options.usageHandler && result.usage) {
|
|
964
|
-
await this.options.usageHandler(ctx, {
|
|
965
|
-
userId: options.userId,
|
|
966
|
-
threadId: options.threadId,
|
|
967
|
-
agentName: this.options.name,
|
|
968
|
-
model: embeddingModel.modelId,
|
|
969
|
-
provider: embeddingModel.provider,
|
|
970
|
-
providerMetadata: undefined,
|
|
971
|
-
usage: {
|
|
972
|
-
inputTokens: result.usage.tokens,
|
|
973
|
-
outputTokens: 0,
|
|
974
|
-
totalTokens: result.usage.tokens,
|
|
975
|
-
},
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
return { embeddings: result.embeddings };
|
|
979
|
-
}
|
|
980
|
-
/**
|
|
981
|
-
* Process messages to inline file and image URLs that point to localhost
|
|
982
|
-
* by converting them to base64. This solves the problem of LLMs not being
|
|
983
|
-
* able to access localhost URLs.
|
|
984
|
-
*/
|
|
985
|
-
async _inlineMessagesFiles(messages) {
|
|
986
|
-
// Process each message to convert localhost URLs to base64
|
|
987
|
-
return Promise.all(messages.map(async (message) => {
|
|
988
|
-
if ((message.role !== "user" && message.role !== "assistant") ||
|
|
989
|
-
typeof message.content === "string" ||
|
|
990
|
-
!Array.isArray(message.content)) {
|
|
991
|
-
return message;
|
|
992
|
-
}
|
|
993
|
-
const processedContent = await Promise.all(message.content.map(async (part) => {
|
|
994
|
-
if (part.type === "image" && part.image instanceof URL) {
|
|
995
|
-
assert(message.role === "user", "Images can only be in user messages");
|
|
996
|
-
if (this._isLocalhostUrl(part.image)) {
|
|
997
|
-
const imageData = await this._downloadFile(part.image);
|
|
998
|
-
return {
|
|
999
|
-
...part,
|
|
1000
|
-
image: imageData,
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
// Handle file parts
|
|
1005
|
-
if (part.type === "file" && part.data instanceof URL) {
|
|
1006
|
-
if (this._isLocalhostUrl(part.data)) {
|
|
1007
|
-
const fileData = await this._downloadFile(part.data);
|
|
1008
|
-
return {
|
|
1009
|
-
...part,
|
|
1010
|
-
data: fileData,
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
return part;
|
|
1015
|
-
}));
|
|
1016
|
-
if (message.role === "user") {
|
|
1017
|
-
return {
|
|
1018
|
-
...message,
|
|
1019
|
-
content: processedContent,
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
else {
|
|
1023
|
-
return {
|
|
1024
|
-
...message,
|
|
1025
|
-
content: processedContent,
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
}));
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Check if a URL points to localhost
|
|
1032
|
-
*/
|
|
1033
|
-
_isLocalhostUrl(url) {
|
|
1034
|
-
return (url.hostname === "localhost" ||
|
|
1035
|
-
url.hostname === "127.0.0.1" ||
|
|
1036
|
-
url.hostname === "::1" ||
|
|
1037
|
-
url.hostname === "0.0.0.0");
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Download a file from a URL
|
|
1041
|
-
*/
|
|
1042
|
-
async _downloadFile(url) {
|
|
1043
|
-
// Fetch the file
|
|
1044
|
-
const response = await fetch(url);
|
|
1045
|
-
if (!response.ok) {
|
|
1046
|
-
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
1047
|
-
}
|
|
1048
|
-
return await response.arrayBuffer();
|
|
1049
|
-
}
|
|
1050
733
|
/**
|
|
1051
734
|
* WORKFLOW UTILITIES
|
|
1052
735
|
*/
|
|
@@ -1087,28 +770,26 @@ export class Agent {
|
|
|
1087
770
|
* @param spec Configuration for the agent acting as an action, including
|
|
1088
771
|
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
|
|
1089
772
|
*/
|
|
1090
|
-
asTextAction(spec) {
|
|
1091
|
-
const stopWhen = spec?.stopWhen ?? this.options.stopWhen;
|
|
773
|
+
asTextAction(spec, overrides) {
|
|
1092
774
|
return internalActionGeneric({
|
|
1093
775
|
args: vTextArgs,
|
|
1094
776
|
handler: async (ctx_, args) => {
|
|
1095
|
-
const stream = args.stream === true ? spec?.stream || true : spec?.stream ?? false;
|
|
1096
|
-
const
|
|
777
|
+
const stream = args.stream === true ? spec?.stream || true : (spec?.stream ?? false);
|
|
778
|
+
const { userId, threadId, prompt, messages, maxSteps, ...rest } = args;
|
|
779
|
+
const targetArgs = { userId, threadId };
|
|
1097
780
|
const llmArgs = {
|
|
1098
|
-
stopWhen,
|
|
1099
|
-
...
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
: args.prompt,
|
|
781
|
+
stopWhen: spec?.stopWhen,
|
|
782
|
+
...overrides,
|
|
783
|
+
...omit(rest, ["storageOptions", "contextOptions", "stream"]),
|
|
784
|
+
messages: messages?.map(toModelMessage),
|
|
785
|
+
prompt: Array.isArray(prompt) ? prompt.map(toModelMessage) : prompt,
|
|
1104
786
|
toolChoice: args.toolChoice,
|
|
1105
787
|
};
|
|
1106
|
-
if (
|
|
1107
|
-
llmArgs.stopWhen = stepCountIs(
|
|
788
|
+
if (maxSteps) {
|
|
789
|
+
llmArgs.stopWhen = stepCountIs(maxSteps);
|
|
1108
790
|
}
|
|
1109
791
|
const opts = {
|
|
1110
|
-
...
|
|
1111
|
-
...(spec && pick(spec, ["contextOptions", "storageOptions"])),
|
|
792
|
+
...pick(spec, ["contextOptions", "storageOptions"]),
|
|
1112
793
|
...pick(args, ["contextOptions", "storageOptions"]),
|
|
1113
794
|
saveStreamDeltas: stream,
|
|
1114
795
|
};
|
|
@@ -1116,26 +797,26 @@ export class Agent {
|
|
|
1116
797
|
? { ...ctx_, ...spec.customCtx(ctx_, targetArgs, llmArgs) }
|
|
1117
798
|
: ctx_);
|
|
1118
799
|
if (stream) {
|
|
1119
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1120
800
|
const result = await this.streamText(ctx, targetArgs, llmArgs, opts);
|
|
1121
801
|
await result.consumeStream();
|
|
1122
802
|
return {
|
|
1123
803
|
text: await result.text,
|
|
1124
|
-
|
|
804
|
+
promptMessageId: result.promptMessageId,
|
|
1125
805
|
order: result.order,
|
|
1126
806
|
finishReason: await result.finishReason,
|
|
1127
|
-
warnings: result.warnings,
|
|
807
|
+
warnings: await result.warnings,
|
|
808
|
+
savedMessageIds: result.savedMessages?.map((m) => m._id) ?? [],
|
|
1128
809
|
};
|
|
1129
810
|
}
|
|
1130
811
|
else {
|
|
1131
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1132
812
|
const res = await this.generateText(ctx, targetArgs, llmArgs, opts);
|
|
1133
813
|
return {
|
|
1134
814
|
text: res.text,
|
|
1135
|
-
|
|
815
|
+
promptMessageId: res.promptMessageId,
|
|
1136
816
|
order: res.order,
|
|
1137
817
|
finishReason: res.finishReason,
|
|
1138
818
|
warnings: res.warnings,
|
|
819
|
+
savedMessageIds: res.savedMessages?.map((m) => m._id) ?? [],
|
|
1139
820
|
};
|
|
1140
821
|
}
|
|
1141
822
|
},
|
|
@@ -1148,45 +829,43 @@ export class Agent {
|
|
|
1148
829
|
* the normal parameters to {@link generateObject}, plus {@link ContextOptions}
|
|
1149
830
|
* and stopWhen.
|
|
1150
831
|
*/
|
|
1151
|
-
asObjectAction(
|
|
832
|
+
asObjectAction(objectArgs, options) {
|
|
1152
833
|
return internalActionGeneric({
|
|
1153
834
|
args: vSafeObjectArgs,
|
|
1154
|
-
handler: async (
|
|
1155
|
-
const { userId, threadId, ...rest } = args;
|
|
835
|
+
handler: async (ctx_, args) => {
|
|
836
|
+
const { userId, threadId, callSettings, ...rest } = args;
|
|
1156
837
|
const overrides = pick(rest, ["contextOptions", "storageOptions"]);
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
...
|
|
1160
|
-
|
|
838
|
+
const targetArgs = { userId, threadId };
|
|
839
|
+
const llmArgs = {
|
|
840
|
+
...objectArgs,
|
|
841
|
+
...callSettings,
|
|
842
|
+
...omit(rest, ["storageOptions", "contextOptions"]),
|
|
843
|
+
messages: args.messages?.map(toModelMessage),
|
|
844
|
+
prompt: Array.isArray(args.prompt)
|
|
845
|
+
? args.prompt.map(toModelMessage)
|
|
846
|
+
: args.prompt,
|
|
847
|
+
};
|
|
848
|
+
const ctx = (options?.customCtx
|
|
849
|
+
? { ...ctx_, ...options.customCtx(ctx_, targetArgs, llmArgs) }
|
|
850
|
+
: ctx_);
|
|
851
|
+
const value = await this.generateObject(ctx, targetArgs, llmArgs, {
|
|
852
|
+
...this.options,
|
|
853
|
+
...options,
|
|
854
|
+
...overrides,
|
|
855
|
+
});
|
|
1161
856
|
return {
|
|
1162
|
-
object: value.object,
|
|
1163
|
-
|
|
857
|
+
object: convexToJson(value.object),
|
|
858
|
+
promptMessageId: value.promptMessageId,
|
|
1164
859
|
order: value.order,
|
|
1165
860
|
finishReason: value.finishReason,
|
|
1166
861
|
warnings: value.warnings,
|
|
862
|
+
savedMessageIds: value.savedMessages?.map((m) => m._id) ?? [],
|
|
1167
863
|
};
|
|
1168
864
|
},
|
|
1169
865
|
});
|
|
1170
866
|
}
|
|
1171
867
|
/**
|
|
1172
|
-
*
|
|
1173
|
-
* Useful as a step in Workflows, e.g.
|
|
1174
|
-
* ```ts
|
|
1175
|
-
* const saveMessages = agent.asSaveMessagesMutation();
|
|
1176
|
-
*
|
|
1177
|
-
* const myWorkflow = workflow.define({
|
|
1178
|
-
* args: {...},
|
|
1179
|
-
* handler: async (step, args) => {
|
|
1180
|
-
* // do things to create (but not save)messages
|
|
1181
|
-
* const { messageIds } = await step.runMutation(internal.foo.saveMessages, {
|
|
1182
|
-
* threadId: args.threadId,
|
|
1183
|
-
* messages: args.messages,
|
|
1184
|
-
* });
|
|
1185
|
-
* // ...
|
|
1186
|
-
* },
|
|
1187
|
-
* })
|
|
1188
|
-
* ```
|
|
1189
|
-
* @returns A mutation that can be used to save messages to the thread.
|
|
868
|
+
* @deprecated Use {@link saveMessages} directly instead.
|
|
1190
869
|
*/
|
|
1191
870
|
asSaveMessagesMutation() {
|
|
1192
871
|
return internalMutationGeneric({
|
|
@@ -1195,116 +874,22 @@ export class Agent {
|
|
|
1195
874
|
userId: v.optional(v.string()),
|
|
1196
875
|
promptMessageId: v.optional(v.string()),
|
|
1197
876
|
messages: v.array(vMessageWithMetadata),
|
|
1198
|
-
pending: v.optional(v.boolean()),
|
|
1199
877
|
failPendingSteps: v.optional(v.boolean()),
|
|
878
|
+
embeddings: v.optional(vMessageEmbeddings),
|
|
1200
879
|
},
|
|
1201
880
|
handler: async (ctx, args) => {
|
|
1202
|
-
const {
|
|
881
|
+
const { messages } = await this.saveMessages(ctx, {
|
|
1203
882
|
...args,
|
|
1204
|
-
messages: args.messages.map((m) =>
|
|
883
|
+
messages: args.messages.map((m) => toModelMessage(m.message)),
|
|
1205
884
|
metadata: args.messages.map(({ message: _, ...m }) => m),
|
|
885
|
+
skipEmbeddings: true,
|
|
1206
886
|
});
|
|
1207
887
|
return {
|
|
1208
|
-
lastMessageId,
|
|
1209
|
-
|
|
888
|
+
lastMessageId: messages.at(-1)._id,
|
|
889
|
+
messages: messages.map((m) => pick(m, ["_id", "order", "stepOrder"])),
|
|
1210
890
|
};
|
|
1211
891
|
},
|
|
1212
892
|
});
|
|
1213
893
|
}
|
|
1214
894
|
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Create a thread to store messages with an Agent.
|
|
1217
|
-
* @param ctx The context from a mutation or action.
|
|
1218
|
-
* @param component The Agent component, usually `components.agent`.
|
|
1219
|
-
* @param args The associated thread metadata.
|
|
1220
|
-
* @returns The id of the created thread.
|
|
1221
|
-
*/
|
|
1222
|
-
export async function createThread(ctx, component, args) {
|
|
1223
|
-
const { _id: threadId } = await ctx.runMutation(component.threads.createThread, {
|
|
1224
|
-
userId: args?.userId ?? undefined,
|
|
1225
|
-
title: args?.title,
|
|
1226
|
-
summary: args?.summary,
|
|
1227
|
-
});
|
|
1228
|
-
return threadId;
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Get the metadata for a thread.
|
|
1232
|
-
* @param ctx A ctx object from a query, mutation, or action.
|
|
1233
|
-
* @param args.threadId The thread to get the metadata for.
|
|
1234
|
-
* @returns The metadata for the thread.
|
|
1235
|
-
*/
|
|
1236
|
-
export async function getThreadMetadata(ctx, component, args) {
|
|
1237
|
-
const thread = await ctx.runQuery(component.threads.getThread, {
|
|
1238
|
-
threadId: args.threadId,
|
|
1239
|
-
});
|
|
1240
|
-
if (!thread) {
|
|
1241
|
-
throw new Error("Thread not found");
|
|
1242
|
-
}
|
|
1243
|
-
return thread;
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Explicitly save messages associated with the thread (& user if provided)
|
|
1247
|
-
*/
|
|
1248
|
-
export async function saveMessages(ctx, component, args) {
|
|
1249
|
-
let embeddings;
|
|
1250
|
-
if (args.embeddings) {
|
|
1251
|
-
const dimension = args.embeddings.vectors.find((v) => v !== null)?.length;
|
|
1252
|
-
if (dimension) {
|
|
1253
|
-
validateVectorDimension(dimension);
|
|
1254
|
-
embeddings = {
|
|
1255
|
-
model: args.embeddings.model,
|
|
1256
|
-
dimension,
|
|
1257
|
-
vectors: args.embeddings.vectors,
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
const result = await ctx.runMutation(component.messages.addMessages, {
|
|
1262
|
-
threadId: args.threadId,
|
|
1263
|
-
userId: args.userId ?? undefined,
|
|
1264
|
-
agentName: args.agentName,
|
|
1265
|
-
promptMessageId: args.promptMessageId,
|
|
1266
|
-
embeddings,
|
|
1267
|
-
messages: await Promise.all(args.messages.map(async (m, i) => {
|
|
1268
|
-
const { message, fileIds } = await serializeMessage(ctx, component, m);
|
|
1269
|
-
return {
|
|
1270
|
-
...args.metadata?.[i],
|
|
1271
|
-
message,
|
|
1272
|
-
fileIds,
|
|
1273
|
-
};
|
|
1274
|
-
})),
|
|
1275
|
-
failPendingSteps: args.failPendingSteps ?? false,
|
|
1276
|
-
pending: args.pending ?? false,
|
|
1277
|
-
});
|
|
1278
|
-
return {
|
|
1279
|
-
lastMessageId: result.messages.at(-1)._id,
|
|
1280
|
-
messages: result.messages,
|
|
1281
|
-
};
|
|
1282
|
-
}
|
|
1283
|
-
/**
|
|
1284
|
-
* Save a message to the thread.
|
|
1285
|
-
* @param ctx A ctx object from a mutation or action.
|
|
1286
|
-
* @param args The message and what to associate it with (user / thread)
|
|
1287
|
-
* You can pass extra metadata alongside the message, e.g. associated fileIds.
|
|
1288
|
-
* @returns The messageId of the saved message.
|
|
1289
|
-
*/
|
|
1290
|
-
export async function saveMessage(ctx, component, args) {
|
|
1291
|
-
let embeddings;
|
|
1292
|
-
if (args.embedding && args.embedding.vector) {
|
|
1293
|
-
embeddings = {
|
|
1294
|
-
model: args.embedding.model,
|
|
1295
|
-
vectors: [args.embedding.vector],
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
const { lastMessageId, messages } = await saveMessages(ctx, component, {
|
|
1299
|
-
threadId: args.threadId,
|
|
1300
|
-
userId: args.userId ?? undefined,
|
|
1301
|
-
agentName: args.agentName,
|
|
1302
|
-
messages: args.prompt !== undefined
|
|
1303
|
-
? [{ role: "user", content: args.prompt }]
|
|
1304
|
-
: [args.message],
|
|
1305
|
-
metadata: args.metadata ? [args.metadata] : undefined,
|
|
1306
|
-
embeddings,
|
|
1307
|
-
});
|
|
1308
|
-
return { messageId: lastMessageId, message: messages.at(-1) };
|
|
1309
|
-
}
|
|
1310
895
|
//# sourceMappingURL=index.js.map
|