@convex-dev/agent 0.1.10 → 0.1.12

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.
Files changed (138) hide show
  1. package/dist/esm/client/index.d.ts +2 -0
  2. package/dist/esm/client/index.d.ts.map +1 -1
  3. package/dist/esm/client/index.js +35 -26
  4. package/dist/esm/client/index.js.map +1 -1
  5. package/dist/esm/mapping.d.ts +1 -0
  6. package/dist/esm/mapping.d.ts.map +1 -1
  7. package/dist/esm/mapping.js +2 -2
  8. package/dist/esm/mapping.js.map +1 -1
  9. package/dist/esm/react/useSmoothText.d.ts +7 -1
  10. package/dist/esm/react/useSmoothText.d.ts.map +1 -1
  11. package/dist/esm/react/useSmoothText.js +5 -5
  12. package/dist/esm/react/useSmoothText.js.map +1 -1
  13. package/dist/esm.tsbuildinfo +1 -1
  14. package/package.json +24 -53
  15. package/src/client/index.ts +39 -29
  16. package/src/mapping.ts +9 -2
  17. package/src/react/useSmoothText.ts +11 -4
  18. package/dist/commonjs/client/_generated/_ignore.d.ts +0 -1
  19. package/dist/commonjs/client/_generated/_ignore.d.ts.map +0 -1
  20. package/dist/commonjs/client/_generated/_ignore.js +0 -3
  21. package/dist/commonjs/client/_generated/_ignore.js.map +0 -1
  22. package/dist/commonjs/client/createTool.d.ts +0 -47
  23. package/dist/commonjs/client/createTool.d.ts.map +0 -1
  24. package/dist/commonjs/client/createTool.js +0 -49
  25. package/dist/commonjs/client/createTool.js.map +0 -1
  26. package/dist/commonjs/client/files.d.ts +0 -56
  27. package/dist/commonjs/client/files.d.ts.map +0 -1
  28. package/dist/commonjs/client/files.js +0 -129
  29. package/dist/commonjs/client/files.js.map +0 -1
  30. package/dist/commonjs/client/index.d.ts +0 -1355
  31. package/dist/commonjs/client/index.d.ts.map +0 -1
  32. package/dist/commonjs/client/index.js +0 -1169
  33. package/dist/commonjs/client/index.js.map +0 -1
  34. package/dist/commonjs/client/streaming.d.ts +0 -57
  35. package/dist/commonjs/client/streaming.d.ts.map +0 -1
  36. package/dist/commonjs/client/streaming.js +0 -148
  37. package/dist/commonjs/client/streaming.js.map +0 -1
  38. package/dist/commonjs/client/types.d.ts +0 -352
  39. package/dist/commonjs/client/types.d.ts.map +0 -1
  40. package/dist/commonjs/client/types.js +0 -3
  41. package/dist/commonjs/client/types.js.map +0 -1
  42. package/dist/commonjs/component/_generated/api.d.ts +0 -1938
  43. package/dist/commonjs/component/_generated/api.d.ts.map +0 -1
  44. package/dist/commonjs/component/_generated/api.js +0 -25
  45. package/dist/commonjs/component/_generated/api.js.map +0 -1
  46. package/dist/commonjs/component/_generated/dataModel.d.ts +0 -60
  47. package/dist/commonjs/component/_generated/server.d.ts +0 -149
  48. package/dist/commonjs/component/_generated/server.d.ts.map +0 -1
  49. package/dist/commonjs/component/_generated/server.js +0 -77
  50. package/dist/commonjs/component/_generated/server.js.map +0 -1
  51. package/dist/commonjs/component/apiKeys.d.ts +0 -11
  52. package/dist/commonjs/component/apiKeys.d.ts.map +0 -1
  53. package/dist/commonjs/component/apiKeys.js +0 -72
  54. package/dist/commonjs/component/apiKeys.js.map +0 -1
  55. package/dist/commonjs/component/convex.config.d.ts +0 -3
  56. package/dist/commonjs/component/convex.config.d.ts.map +0 -1
  57. package/dist/commonjs/component/convex.config.js +0 -5
  58. package/dist/commonjs/component/convex.config.js.map +0 -1
  59. package/dist/commonjs/component/files.d.ts +0 -90
  60. package/dist/commonjs/component/files.d.ts.map +0 -1
  61. package/dist/commonjs/component/files.js +0 -157
  62. package/dist/commonjs/component/files.js.map +0 -1
  63. package/dist/commonjs/component/messages.d.ts +0 -1479
  64. package/dist/commonjs/component/messages.d.ts.map +0 -1
  65. package/dist/commonjs/component/messages.js +0 -483
  66. package/dist/commonjs/component/messages.js.map +0 -1
  67. package/dist/commonjs/component/schema.d.ts +0 -4582
  68. package/dist/commonjs/component/schema.d.ts.map +0 -1
  69. package/dist/commonjs/component/schema.js +0 -167
  70. package/dist/commonjs/component/schema.js.map +0 -1
  71. package/dist/commonjs/component/streams.d.ts +0 -242
  72. package/dist/commonjs/component/streams.d.ts.map +0 -1
  73. package/dist/commonjs/component/streams.js +0 -323
  74. package/dist/commonjs/component/streams.js.map +0 -1
  75. package/dist/commonjs/component/threads.d.ts +0 -101
  76. package/dist/commonjs/component/threads.d.ts.map +0 -1
  77. package/dist/commonjs/component/threads.js +0 -188
  78. package/dist/commonjs/component/threads.js.map +0 -1
  79. package/dist/commonjs/component/users.d.ts +0 -52
  80. package/dist/commonjs/component/users.d.ts.map +0 -1
  81. package/dist/commonjs/component/users.js +0 -233
  82. package/dist/commonjs/component/users.js.map +0 -1
  83. package/dist/commonjs/component/vector/index.d.ts +0 -61
  84. package/dist/commonjs/component/vector/index.d.ts.map +0 -1
  85. package/dist/commonjs/component/vector/index.js +0 -153
  86. package/dist/commonjs/component/vector/index.js.map +0 -1
  87. package/dist/commonjs/component/vector/tables.d.ts +0 -58
  88. package/dist/commonjs/component/vector/tables.d.ts.map +0 -1
  89. package/dist/commonjs/component/vector/tables.js +0 -62
  90. package/dist/commonjs/component/vector/tables.js.map +0 -1
  91. package/dist/commonjs/mapping.d.ts +0 -50
  92. package/dist/commonjs/mapping.d.ts.map +0 -1
  93. package/dist/commonjs/mapping.js +0 -272
  94. package/dist/commonjs/mapping.js.map +0 -1
  95. package/dist/commonjs/package.json +0 -3
  96. package/dist/commonjs/react/deltas.d.ts +0 -29
  97. package/dist/commonjs/react/deltas.d.ts.map +0 -1
  98. package/dist/commonjs/react/deltas.js +0 -274
  99. package/dist/commonjs/react/deltas.js.map +0 -1
  100. package/dist/commonjs/react/index.d.ts +0 -95
  101. package/dist/commonjs/react/index.d.ts.map +0 -1
  102. package/dist/commonjs/react/index.js +0 -219
  103. package/dist/commonjs/react/index.js.map +0 -1
  104. package/dist/commonjs/react/optimisticallySendMessage.d.ts +0 -8
  105. package/dist/commonjs/react/optimisticallySendMessage.d.ts.map +0 -1
  106. package/dist/commonjs/react/optimisticallySendMessage.js +0 -43
  107. package/dist/commonjs/react/optimisticallySendMessage.js.map +0 -1
  108. package/dist/commonjs/react/toUIMessages.d.ts +0 -13
  109. package/dist/commonjs/react/toUIMessages.d.ts.map +0 -1
  110. package/dist/commonjs/react/toUIMessages.js +0 -149
  111. package/dist/commonjs/react/toUIMessages.js.map +0 -1
  112. package/dist/commonjs/react/types.d.ts +0 -26
  113. package/dist/commonjs/react/types.d.ts.map +0 -1
  114. package/dist/commonjs/react/types.js +0 -3
  115. package/dist/commonjs/react/types.js.map +0 -1
  116. package/dist/commonjs/react/useSmoothText.d.ts +0 -20
  117. package/dist/commonjs/react/useSmoothText.d.ts.map +0 -1
  118. package/dist/commonjs/react/useSmoothText.js +0 -54
  119. package/dist/commonjs/react/useSmoothText.js.map +0 -1
  120. package/dist/commonjs/shared.d.ts +0 -10
  121. package/dist/commonjs/shared.d.ts.map +0 -1
  122. package/dist/commonjs/shared.js +0 -35
  123. package/dist/commonjs/shared.js.map +0 -1
  124. package/dist/commonjs/validators.d.ts +0 -5716
  125. package/dist/commonjs/validators.d.ts.map +0 -1
  126. package/dist/commonjs/validators.js +0 -319
  127. package/dist/commonjs/validators.js.map +0 -1
  128. package/dist/commonjs/vitest.config.d.ts +0 -3
  129. package/dist/commonjs/vitest.config.d.ts.map +0 -1
  130. package/dist/commonjs/vitest.config.js +0 -9
  131. package/dist/commonjs/vitest.config.js.map +0 -1
  132. package/dist/commonjs.tsbuildinfo +0 -1
  133. package/dist/esm/vitest.config.d.ts +0 -3
  134. package/dist/esm/vitest.config.d.ts.map +0 -1
  135. package/dist/esm/vitest.config.js +0 -7
  136. package/dist/esm/vitest.config.js.map +0 -1
  137. package/react/package.json +0 -5
  138. package/validators/package.json +0 -5
@@ -1,1169 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Agent = exports.isTool = exports.extractText = exports.createTool = exports.vUserMessage = exports.vUsage = exports.vToolMessage = exports.vSystemMessage = exports.vStreamArgs = exports.vStorageOptions = exports.vProviderMetadata = exports.vPaginationResult = exports.vMessage = exports.vContextOptions = exports.vAssistantMessage = exports.vThreadDoc = exports.vMessageDoc = exports.serializeDataOrUrl = exports.getFile = exports.storeFile = void 0;
4
- exports.filterOutOrphanedToolMessages = filterOutOrphanedToolMessages;
5
- const ai_1 = require("ai");
6
- const convex_helpers_1 = require("convex-helpers");
7
- const server_1 = require("convex/server");
8
- const values_1 = require("convex/values");
9
- const tables_js_1 = require("../component/vector/tables.js");
10
- const mapping_js_1 = require("../mapping.js");
11
- const shared_js_1 = require("../shared.js");
12
- Object.defineProperty(exports, "extractText", { enumerable: true, get: function () { return shared_js_1.extractText; } });
13
- Object.defineProperty(exports, "isTool", { enumerable: true, get: function () { return shared_js_1.isTool; } });
14
- const validators_js_1 = require("../validators.js");
15
- const createTool_js_1 = require("./createTool.js");
16
- Object.defineProperty(exports, "createTool", { enumerable: true, get: function () { return createTool_js_1.createTool; } });
17
- const streaming_js_1 = require("./streaming.js");
18
- var files_js_1 = require("./files.js");
19
- Object.defineProperty(exports, "storeFile", { enumerable: true, get: function () { return files_js_1.storeFile; } });
20
- Object.defineProperty(exports, "getFile", { enumerable: true, get: function () { return files_js_1.getFile; } });
21
- var mapping_js_2 = require("../mapping.js");
22
- Object.defineProperty(exports, "serializeDataOrUrl", { enumerable: true, get: function () { return mapping_js_2.serializeDataOrUrl; } });
23
- var schema_js_1 = require("../component/schema.js");
24
- Object.defineProperty(exports, "vMessageDoc", { enumerable: true, get: function () { return schema_js_1.vMessageDoc; } });
25
- Object.defineProperty(exports, "vThreadDoc", { enumerable: true, get: function () { return schema_js_1.vThreadDoc; } });
26
- var validators_js_2 = require("../validators.js");
27
- Object.defineProperty(exports, "vAssistantMessage", { enumerable: true, get: function () { return validators_js_2.vAssistantMessage; } });
28
- Object.defineProperty(exports, "vContextOptions", { enumerable: true, get: function () { return validators_js_2.vContextOptions; } });
29
- Object.defineProperty(exports, "vMessage", { enumerable: true, get: function () { return validators_js_2.vMessage; } });
30
- Object.defineProperty(exports, "vPaginationResult", { enumerable: true, get: function () { return validators_js_2.vPaginationResult; } });
31
- Object.defineProperty(exports, "vProviderMetadata", { enumerable: true, get: function () { return validators_js_2.vProviderMetadata; } });
32
- Object.defineProperty(exports, "vStorageOptions", { enumerable: true, get: function () { return validators_js_2.vStorageOptions; } });
33
- Object.defineProperty(exports, "vStreamArgs", { enumerable: true, get: function () { return validators_js_2.vStreamArgs; } });
34
- Object.defineProperty(exports, "vSystemMessage", { enumerable: true, get: function () { return validators_js_2.vSystemMessage; } });
35
- Object.defineProperty(exports, "vToolMessage", { enumerable: true, get: function () { return validators_js_2.vToolMessage; } });
36
- Object.defineProperty(exports, "vUsage", { enumerable: true, get: function () { return validators_js_2.vUsage; } });
37
- Object.defineProperty(exports, "vUserMessage", { enumerable: true, get: function () { return validators_js_2.vUserMessage; } });
38
- class Agent {
39
- component;
40
- options;
41
- constructor(component, options) {
42
- this.component = component;
43
- this.options = options;
44
- }
45
- async createThread(ctx, args) {
46
- const threadDoc = await ctx.runMutation(this.component.threads.createThread, {
47
- userId: args?.userId,
48
- title: args?.title,
49
- summary: args?.summary,
50
- });
51
- if (!("runAction" in ctx)) {
52
- return { threadId: threadDoc._id };
53
- }
54
- const { thread } = await this.continueThread(ctx, {
55
- threadId: threadDoc._id,
56
- userId: args?.userId,
57
- usageHandler: args?.usageHandler,
58
- tools: args?.tools,
59
- });
60
- return {
61
- threadId: threadDoc._id,
62
- thread,
63
- };
64
- }
65
- /**
66
- * Continues a thread using this agent. Note: threads can be continued
67
- * by different agents. This is a convenience around calling the various
68
- * generate and stream functions with explicit userId and threadId parameters.
69
- * @param ctx The ctx object passed to the action handler
70
- * @param { threadId, userId }: the thread and user to associate the messages with.
71
- * @returns Functions bound to the userId and threadId on a `{thread}` object.
72
- */
73
- async continueThread(ctx, args) {
74
- return {
75
- thread: {
76
- threadId: args.threadId,
77
- getMetadata: this.getThreadMetadata.bind(this, ctx, {
78
- threadId: args.threadId,
79
- }),
80
- updateMetadata: (patch) => ctx.runMutation(this.component.threads.updateThread, {
81
- threadId: args.threadId,
82
- patch,
83
- }),
84
- generateText: this.generateText.bind(this, ctx, args),
85
- streamText: this.streamText.bind(this, ctx, args),
86
- generateObject: this.generateObject.bind(this, ctx, args),
87
- streamObject: this.streamObject.bind(this, ctx, args),
88
- },
89
- };
90
- }
91
- /**
92
- * This behaves like {@link generateText} from the "ai" package except that
93
- * it add context based on the userId and threadId and saves the input and
94
- * resulting messages to the thread, if specified.
95
- * Use {@link continueThread} to get a version of this function already scoped
96
- * to a thread (and optionally userId).
97
- * @param ctx The context passed from the action function calling this.
98
- * @param { userId, threadId }: The user and thread to associate the message with
99
- * @param args The arguments to the generateText function, along with extra controls
100
- * for the {@link ContextOptions} and {@link StorageOptions}.
101
- * @returns The result of the generateText function.
102
- */
103
- async generateText(ctx, { userId: argsUserId, threadId, usageHandler, tools: threadTools, }, args, options) {
104
- const context = await this._saveMessagesAndFetchContext(ctx, args, {
105
- userId: argsUserId,
106
- threadId,
107
- ...options,
108
- });
109
- const { args: aiArgs, messageId, userId } = context;
110
- const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
111
- const tools = (0, createTool_js_1.wrapTools)(toolCtx, args.tools ?? threadTools ?? this.options.tools);
112
- const saveOutputMessages = this._shouldSaveOutputMessages(options?.storageOptions);
113
- const trackUsage = usageHandler ?? this.options.usageHandler;
114
- try {
115
- const result = (await (0, ai_1.generateText)({
116
- // Can be overridden
117
- maxSteps: this.options.maxSteps,
118
- ...aiArgs,
119
- tools,
120
- onStepFinish: async (step) => {
121
- if (threadId && messageId && saveOutputMessages) {
122
- await this.saveStep(ctx, {
123
- userId,
124
- threadId,
125
- promptMessageId: messageId,
126
- step,
127
- });
128
- }
129
- if (this.options.rawRequestResponseHandler) {
130
- await this.options.rawRequestResponseHandler(ctx, {
131
- userId,
132
- threadId,
133
- agentName: this.options.name,
134
- request: step.request,
135
- response: step.response,
136
- });
137
- }
138
- if (trackUsage && step.usage) {
139
- await trackUsage(ctx, {
140
- userId,
141
- threadId,
142
- agentName: this.options.name,
143
- model: aiArgs.model.modelId,
144
- provider: aiArgs.model.provider,
145
- usage: step.usage,
146
- providerMetadata: step.providerMetadata,
147
- });
148
- }
149
- return args.onStepFinish?.(step);
150
- },
151
- }));
152
- result.messageId = messageId;
153
- return result;
154
- }
155
- catch (error) {
156
- if (threadId && messageId) {
157
- console.error("RollbackMessage", messageId);
158
- await ctx.runMutation(this.component.messages.rollbackMessage, {
159
- messageId,
160
- error: error.message,
161
- });
162
- }
163
- throw error;
164
- }
165
- }
166
- /**
167
- * This behaves like {@link streamText} from the "ai" package except that
168
- * it add context based on the userId and threadId and saves the input and
169
- * resulting messages to the thread, if specified.
170
- * Use {@link continueThread} to get a version of this function already scoped
171
- * to a thread (and optionally userId).
172
- */
173
- async streamText(ctx, { userId: argsUserId, threadId, usageHandler,
174
- /**
175
- * @deprecated Pass `tools` in the next parameter instead.
176
- * This is only intended to pass through thread-default tools.
177
- */
178
- tools: threadTools, },
179
- /**
180
- * The arguments to the streamText function, similar to the ai `streamText` function.
181
- */
182
- args,
183
- /**
184
- * The {@link ContextOptions} and {@link StorageOptions}
185
- * options to use for fetching contextual messages and saving input/output messages.
186
- */
187
- options) {
188
- const context = await this._saveMessagesAndFetchContext(ctx, args, {
189
- userId: argsUserId,
190
- threadId,
191
- ...options,
192
- });
193
- const { args: aiArgs, messageId, order, stepOrder, userId } = context;
194
- const toolCtx = { ...ctx, userId, threadId, messageId, agent: this };
195
- const tools = (0, createTool_js_1.wrapTools)(toolCtx, args.tools ?? threadTools ?? this.options.tools);
196
- const saveOutputMessages = this._shouldSaveOutputMessages(options?.storageOptions);
197
- const trackUsage = usageHandler ?? this.options.usageHandler;
198
- const streamer = threadId && options?.saveStreamDeltas
199
- ? new streaming_js_1.DeltaStreamer(this.component, ctx, options.saveStreamDeltas, {
200
- threadId,
201
- userId,
202
- agentName: this.options.name,
203
- model: aiArgs.model.modelId,
204
- provider: aiArgs.model.provider,
205
- providerOptions: aiArgs.providerOptions,
206
- order,
207
- stepOrder,
208
- abortSignal: aiArgs.abortSignal,
209
- })
210
- : undefined;
211
- const result = (0, ai_1.streamText)({
212
- // Can be overridden
213
- maxSteps: this.options.maxSteps,
214
- ...aiArgs,
215
- tools,
216
- abortSignal: streamer?.abortController.signal ?? aiArgs.abortSignal,
217
- experimental_transform: (0, streaming_js_1.mergeTransforms)(options?.saveStreamDeltas, args.experimental_transform),
218
- onChunk: async (event) => {
219
- await streamer?.addParts([event.chunk]);
220
- // console.log("onChunk", chunk);
221
- return args.onChunk?.(event);
222
- },
223
- onError: async (error) => {
224
- console.error("onError", error);
225
- if (threadId && messageId && saveOutputMessages) {
226
- await ctx.runMutation(this.component.messages.rollbackMessage, {
227
- messageId,
228
- error: error.error.message,
229
- });
230
- }
231
- return args.onError?.(error);
232
- },
233
- onStepFinish: async (step) => {
234
- // console.log("onStepFinish", step);
235
- // TODO: compare delta to the output. internally drop the deltas when committing
236
- if (threadId && messageId) {
237
- const saved = await this.saveStep(ctx, {
238
- userId,
239
- threadId,
240
- promptMessageId: messageId,
241
- step,
242
- });
243
- // TODO: figure out pending/not
244
- await streamer?.finish(saved.messages);
245
- }
246
- if (this.options.rawRequestResponseHandler) {
247
- await this.options.rawRequestResponseHandler(ctx, {
248
- userId,
249
- threadId,
250
- agentName: this.options.name,
251
- request: step.request,
252
- response: step.response,
253
- });
254
- }
255
- if (trackUsage && step.usage) {
256
- await trackUsage(ctx, {
257
- userId,
258
- threadId,
259
- agentName: this.options.name,
260
- model: aiArgs.model.modelId,
261
- provider: aiArgs.model.provider,
262
- usage: step.usage,
263
- providerMetadata: step.providerMetadata,
264
- });
265
- }
266
- return args.onStepFinish?.(step);
267
- },
268
- });
269
- result.messageId = messageId;
270
- return result;
271
- }
272
- /**
273
- * This behaves like {@link generateObject} from the "ai" package except that
274
- * it add context based on the userId and threadId and saves the input and
275
- * resulting messages to the thread, if specified.
276
- * Use {@link continueThread} to get a version of this function already scoped
277
- * to a thread (and optionally userId).
278
- */
279
- async generateObject(ctx, { userId: argsUserId, threadId, usageHandler, },
280
- /**
281
- * The arguments to the generateObject function, similar to the ai.generateObject function.
282
- */
283
- args,
284
- /**
285
- * The {@link ContextOptions} and {@link StorageOptions}
286
- * options to use for fetching contextual messages and saving input/output messages.
287
- */
288
- options) {
289
- const context = await this._saveMessagesAndFetchContext(ctx, args, {
290
- userId: argsUserId,
291
- threadId,
292
- ...options,
293
- });
294
- const { args: aiArgs, messageId, userId } = context;
295
- const trackUsage = usageHandler ?? this.options.usageHandler;
296
- const saveOutputMessages = this._shouldSaveOutputMessages(options?.storageOptions);
297
- try {
298
- const result = (await (0, ai_1.generateObject)(
299
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
300
- aiArgs));
301
- if (threadId && messageId && saveOutputMessages) {
302
- await this.saveObject(ctx, {
303
- threadId,
304
- promptMessageId: messageId,
305
- result,
306
- userId,
307
- });
308
- }
309
- result.messageId = messageId;
310
- if (this.options.rawRequestResponseHandler) {
311
- await this.options.rawRequestResponseHandler(ctx, {
312
- userId,
313
- threadId,
314
- agentName: this.options.name,
315
- request: result.request,
316
- response: result.response,
317
- });
318
- }
319
- if (trackUsage && result.usage) {
320
- await trackUsage(ctx, {
321
- userId,
322
- threadId,
323
- agentName: this.options.name,
324
- model: aiArgs.model.modelId,
325
- provider: aiArgs.model.provider,
326
- usage: result.usage,
327
- providerMetadata: result.providerMetadata,
328
- });
329
- }
330
- return result;
331
- }
332
- catch (error) {
333
- if (threadId && messageId) {
334
- await ctx.runMutation(this.component.messages.rollbackMessage, {
335
- messageId,
336
- error: error.message,
337
- });
338
- }
339
- throw error;
340
- }
341
- }
342
- /**
343
- * This behaves like `streamObject` from the "ai" package except that
344
- * it add context based on the userId and threadId and saves the input and
345
- * resulting messages to the thread, if specified.
346
- * Use {@link continueThread} to get a version of this function already scoped
347
- * to a thread (and optionally userId).
348
- */
349
- async streamObject(ctx, { userId: argsUserId, threadId, usageHandler, },
350
- /**
351
- * The arguments to the streamObject function, similar to the ai `streamObject` function.
352
- */
353
- args,
354
- /**
355
- * The {@link ContextOptions} and {@link StorageOptions}
356
- * options to use for fetching contextual messages and saving input/output messages.
357
- */
358
- options) {
359
- // TODO: unify all this shared code between all the generate* and stream* functions
360
- const context = await this._saveMessagesAndFetchContext(ctx, args, {
361
- userId: argsUserId,
362
- threadId,
363
- ...options,
364
- });
365
- const { args: aiArgs, messageId, userId } = context;
366
- const trackUsage = usageHandler ?? this.options.usageHandler;
367
- const saveOutputMessages = this._shouldSaveOutputMessages(options?.storageOptions);
368
- const stream = (0, ai_1.streamObject)({
369
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
370
- ...aiArgs,
371
- onError: async (error) => {
372
- console.error("onError", error);
373
- return args.onError?.(error);
374
- },
375
- onFinish: async (result) => {
376
- if (threadId && messageId && saveOutputMessages) {
377
- await this.saveObject(ctx, {
378
- userId,
379
- threadId,
380
- promptMessageId: messageId,
381
- result: {
382
- object: result.object,
383
- finishReason: "stop",
384
- usage: result.usage,
385
- warnings: result.warnings,
386
- request: await stream.request,
387
- response: result.response,
388
- providerMetadata: result.providerMetadata,
389
- experimental_providerMetadata: result.experimental_providerMetadata,
390
- logprobs: undefined,
391
- toJsonResponse: stream.toTextStreamResponse,
392
- },
393
- });
394
- }
395
- if (trackUsage && result.usage) {
396
- await trackUsage(ctx, {
397
- userId,
398
- threadId,
399
- agentName: this.options.name,
400
- model: aiArgs.model.modelId,
401
- provider: aiArgs.model.provider,
402
- usage: result.usage,
403
- providerMetadata: result.providerMetadata,
404
- });
405
- }
406
- if (this.options.rawRequestResponseHandler) {
407
- await this.options.rawRequestResponseHandler(ctx, {
408
- userId,
409
- threadId,
410
- agentName: this.options.name,
411
- request: await stream.request,
412
- response: result.response,
413
- });
414
- }
415
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
416
- return args.onFinish?.(result);
417
- },
418
- });
419
- stream.messageId = messageId;
420
- return stream;
421
- }
422
- /**
423
- * Save a message to the thread.
424
- * @param ctx A ctx object from a mutation or action.
425
- * @param args The message and what to associate it with (user / thread)
426
- * You can pass extra metadata alongside the message, e.g. associated fileIds.
427
- * @returns The messageId of the saved message.
428
- */
429
- async saveMessage(ctx, args) {
430
- const { lastMessageId, messages } = await this.saveMessages(ctx, {
431
- threadId: args.threadId,
432
- userId: args.userId,
433
- messages: args.prompt !== undefined
434
- ? [{ role: "user", content: args.prompt }]
435
- : [args.message],
436
- metadata: args.metadata ? [args.metadata] : undefined,
437
- skipEmbeddings: args.skipEmbeddings,
438
- });
439
- return { messageId: lastMessageId, message: messages.at(-1) };
440
- }
441
- /**
442
- * Explicitly save messages associated with the thread (& user if provided)
443
- * @param ctx The ctx parameter to a mutation or action.
444
- * @param args The messages and context to save
445
- * @returns
446
- */
447
- async saveMessages(ctx, args) {
448
- let embeddings;
449
- if (args.skipEmbeddings || !("runAction" in ctx)) {
450
- embeddings = undefined;
451
- if (!args.skipEmbeddings && this.options.textEmbedding) {
452
- console.warn("You're trying to save messages and generate embeddings, but you're in a mutation. " +
453
- "Pass `skipEmbeddings: true` to skip generating embeddings in the mutation and skip this warning. " +
454
- "They will be generated lazily when you generate or stream text / objects. " +
455
- "You can explicitly generate them asynchronously by using the scheduler to run an action later that calls `agent.generateAndSaveEmbeddings`.");
456
- }
457
- }
458
- else {
459
- embeddings = await this.generateEmbeddings(ctx, {
460
- userId: args.userId,
461
- threadId: args.threadId,
462
- }, args.messages);
463
- }
464
- const result = await ctx.runMutation(this.component.messages.addMessages, {
465
- threadId: args.threadId,
466
- userId: args.userId,
467
- agentName: this.options.name,
468
- promptMessageId: args.promptMessageId,
469
- embeddings,
470
- messages: await Promise.all(args.messages.map(async (m, i) => {
471
- const { message, fileIds } = await (0, mapping_js_1.serializeMessage)(ctx, this.component, m);
472
- return {
473
- ...args.metadata?.[i],
474
- message,
475
- fileIds,
476
- };
477
- })),
478
- failPendingSteps: args.failPendingSteps ?? false,
479
- pending: args.pending ?? false,
480
- });
481
- return {
482
- lastMessageId: result.messages.at(-1)._id,
483
- messages: result.messages,
484
- };
485
- }
486
- /**
487
- * List messages from a thread.
488
- * @param ctx A ctx object from a query, mutation, or action.
489
- * @param args.threadId The thread to list messages from.
490
- * @param args.paginationOpts Pagination options (e.g. via usePaginatedQuery).
491
- * @param args.excludeToolMessages Whether to exclude tool messages.
492
- * False by default.
493
- * @param args.statuses What statuses to include. All by default.
494
- * @returns The MessageDoc's in a format compatible with usePaginatedQuery.
495
- */
496
- async listMessages(ctx, args) {
497
- if (args.paginationOpts.numItems === 0) {
498
- return {
499
- page: [],
500
- isDone: true,
501
- continueCursor: args.paginationOpts.cursor ?? "",
502
- };
503
- }
504
- return ctx.runQuery(this.component.messages.listMessagesByThreadId, {
505
- order: "desc",
506
- ...args,
507
- });
508
- }
509
- /**
510
- * A function that handles fetching stream deltas, used with the React hooks
511
- * `useThreadMessages` or `useStreamingThreadMessages`.
512
- * @param ctx A ctx object from a query, mutation, or action.
513
- * @param args.threadId The thread to sync streams for.
514
- * @param args.streamArgs The stream arguments with per-stream cursors.
515
- * @returns The deltas for each stream from their existing cursor.
516
- */
517
- async syncStreams(ctx, args) {
518
- if (!args.streamArgs)
519
- return undefined;
520
- if (args.streamArgs.kind === "list") {
521
- return {
522
- kind: "list",
523
- messages: await ctx.runQuery(this.component.streams.list, {
524
- threadId: args.threadId,
525
- }),
526
- };
527
- }
528
- else {
529
- return {
530
- kind: "deltas",
531
- deltas: await ctx.runQuery(this.component.streams.listDeltas, {
532
- threadId: args.threadId,
533
- cursors: args.streamArgs.cursors,
534
- }),
535
- };
536
- }
537
- }
538
- /**
539
- * Fetch the context messages for a thread.
540
- * @param ctx Either a query, mutation, or action ctx.
541
- * If it is not an action context, you can't do text or
542
- * vector search.
543
- * @param args The associated thread, user, message
544
- * @returns
545
- */
546
- async fetchContextMessages(ctx, args) {
547
- (0, convex_helpers_1.assert)(args.userId || args.threadId, "Specify userId or threadId");
548
- // Fetch the latest messages from the thread
549
- let included;
550
- const opts = this._mergedContextOptions(args.contextOptions);
551
- const contextMessages = [];
552
- if (args.threadId &&
553
- (opts.recentMessages !== 0 || args.upToAndIncludingMessageId)) {
554
- const { page } = await ctx.runQuery(this.component.messages.listMessagesByThreadId, {
555
- threadId: args.threadId,
556
- excludeToolMessages: opts.excludeToolMessages,
557
- paginationOpts: {
558
- numItems: opts.recentMessages ?? shared_js_1.DEFAULT_RECENT_MESSAGES,
559
- cursor: null,
560
- },
561
- upToAndIncludingMessageId: args.upToAndIncludingMessageId,
562
- order: "desc",
563
- statuses: ["success"],
564
- });
565
- included = new Set(page.map((m) => m._id));
566
- contextMessages.push(
567
- // Reverse since we fetched in descending order
568
- ...page.reverse());
569
- }
570
- if (opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) {
571
- const targetMessage = contextMessages.find((m) => m._id === args.upToAndIncludingMessageId)?.message;
572
- const messagesToSearch = targetMessage
573
- ? [targetMessage, ...args.messages]
574
- : args.messages;
575
- if (!("runAction" in ctx)) {
576
- throw new Error("searchUserMessages only works in an action");
577
- }
578
- const searchMessages = await ctx.runAction(this.component.messages.searchMessages, {
579
- searchAllMessagesForUserId: opts?.searchOtherThreads
580
- ? args.userId ??
581
- (args.threadId &&
582
- (await ctx.runQuery(this.component.threads.getThread, {
583
- threadId: args.threadId,
584
- }))?.userId)
585
- : undefined,
586
- threadId: args.threadId,
587
- beforeMessageId: args.upToAndIncludingMessageId,
588
- ...(await this._searchOptionsWithEmbeddingAndDefaults(ctx, { userId: args.userId, threadId: args.threadId }, opts, messagesToSearch)),
589
- });
590
- // TODO: track what messages we used for context
591
- contextMessages.unshift(...searchMessages.filter((m) => !included?.has(m._id)));
592
- }
593
- // Ensure we don't include tool messages without a corresponding tool call
594
- return filterOutOrphanedToolMessages(contextMessages.sort((a, b) =>
595
- // Sort the raw MessageDocs by order and stepOrder
596
- a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order));
597
- }
598
- /**
599
- * Get the metadata for a thread.
600
- * @param ctx A ctx object from a query, mutation, or action.
601
- * @param args.threadId The thread to get the metadata for.
602
- * @returns The metadata for the thread.
603
- */
604
- async getThreadMetadata(ctx, args) {
605
- const thread = await ctx.runQuery(this.component.threads.getThread, {
606
- threadId: args.threadId,
607
- });
608
- if (!thread) {
609
- throw new Error("Thread not found");
610
- }
611
- return thread;
612
- }
613
- /**
614
- * Update the metadata for a thread.
615
- * @param ctx A ctx object from a mutation or action.
616
- * @param args.threadId The thread to update the metadata for.
617
- * @param args.patch The patch to apply to the thread.
618
- * @returns The updated thread metadata.
619
- */
620
- async updateThreadMetadata(ctx, args) {
621
- const thread = await ctx.runMutation(this.component.threads.updateThread, args);
622
- return thread;
623
- }
624
- /**
625
- * Get the embeddings for a set of messages.
626
- * @param messages The messages to get the embeddings for.
627
- * @returns The embeddings for the messages.
628
- */
629
- async generateEmbeddings(ctx, { userId, threadId, }, messages) {
630
- if (!this.options.textEmbedding) {
631
- return undefined;
632
- }
633
- let embeddings;
634
- const messageTexts = messages.map((m) => !(0, shared_js_1.isTool)(m) && (0, shared_js_1.extractText)(m));
635
- // Find the indexes of the messages that have text.
636
- const textIndexes = messageTexts
637
- .map((t, i) => (t ? i : undefined))
638
- .filter((i) => i !== undefined);
639
- if (textIndexes.length === 0) {
640
- return undefined;
641
- }
642
- // Then embed those messages.
643
- const textEmbeddings = await this.doEmbed(ctx, {
644
- userId,
645
- threadId,
646
- values: messageTexts.filter((t) => !!t),
647
- });
648
- // TODO: record usage of embeddings
649
- // Then assemble the embeddings into a single array with nulls for the messages without text.
650
- const embeddingsOrNull = Array(messages.length).fill(null);
651
- textIndexes.forEach((i, j) => {
652
- embeddingsOrNull[i] = textEmbeddings.embeddings[j];
653
- });
654
- if (textEmbeddings.embeddings.length > 0) {
655
- const dimension = textEmbeddings.embeddings[0].length;
656
- (0, tables_js_1.validateVectorDimension)(dimension);
657
- embeddings = {
658
- vectors: embeddingsOrNull,
659
- dimension,
660
- model: this.options.textEmbedding.modelId,
661
- };
662
- }
663
- return embeddings;
664
- }
665
- /**
666
- * Generate embeddings for a set of messages, and save them to the database.
667
- * It will not generate or save embeddings for messages that already have an
668
- * embedding.
669
- * @param ctx The ctx parameter to an action.
670
- * @param args The messageIds to generate embeddings for.
671
- */
672
- async generateAndSaveEmbeddings(ctx, args) {
673
- const messages = (await ctx.runQuery(this.component.messages.getMessagesByIds, {
674
- messageIds: args.messageIds,
675
- })).filter((m) => m !== null);
676
- if (messages.length !== args.messageIds.length) {
677
- throw new Error("Some messages were not found: " +
678
- args.messageIds
679
- .filter((id) => !messages.some((m) => m?._id === id))
680
- .join(", "));
681
- }
682
- if (messages.some((m) => !m.message)) {
683
- throw new Error("Some messages don't have a message: " +
684
- args.messageIds
685
- .map((id, i) => (!messages[i].message ? id : undefined))
686
- .filter((id) => id !== undefined)
687
- .join(", "));
688
- }
689
- const messagesMissingEmbeddings = messages.filter((m) => !m.embeddingId);
690
- if (messagesMissingEmbeddings.length === 0) {
691
- return;
692
- }
693
- const embeddings = await this.generateEmbeddings(ctx, {
694
- userId: messagesMissingEmbeddings[0].userId,
695
- threadId: messagesMissingEmbeddings[0].threadId,
696
- }, messagesMissingEmbeddings.map((m) => m.message));
697
- if (!embeddings) {
698
- if (!this.options.textEmbedding) {
699
- throw new Error("No embeddings were generated for the messages. You must pass a textEmbedding model to the agent constructor.");
700
- }
701
- throw new Error("No embeddings were generated for these messages: " +
702
- messagesMissingEmbeddings.map((m) => m._id).join(", "));
703
- }
704
- await ctx.runMutation(this.component.vector.index.insertBatch, {
705
- vectorDimension: embeddings.dimension,
706
- vectors: messagesMissingEmbeddings
707
- .map((m, i) => ({
708
- messageId: m._id,
709
- model: embeddings.model,
710
- table: "messages",
711
- userId: m.userId,
712
- threadId: m.threadId,
713
- vector: embeddings.vectors[i],
714
- }))
715
- .filter((v) => v.vector !== null),
716
- });
717
- }
718
- /**
719
- * Explicitly save a "step" created by the AI SDK.
720
- * @param ctx The ctx argument to a mutation or action.
721
- * @param args The Step generated by the AI SDK.
722
- */
723
- async saveStep(ctx, args) {
724
- const messages = await (0, mapping_js_1.serializeNewMessagesInStep)(ctx, this.component, args.step, {
725
- provider: args.provider ?? this.options.chat.provider,
726
- model: args.model ?? this.options.chat.modelId,
727
- });
728
- const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
729
- const saved = await ctx.runMutation(this.component.messages.addMessages, {
730
- userId: args.userId,
731
- threadId: args.threadId,
732
- agentName: this.options.name,
733
- promptMessageId: args.promptMessageId,
734
- messages,
735
- embeddings,
736
- failPendingSteps: false,
737
- });
738
- return saved;
739
- }
740
- /**
741
- * Manually save the result of a generateObject call to the thread.
742
- * This happens automatically when using {@link generateObject} or {@link streamObject}
743
- * from the `thread` object created by {@link continueThread} or {@link createThread}.
744
- * @param ctx The context passed from the mutation or action function calling this.
745
- * @param args The arguments to the saveObject function.
746
- */
747
- async saveObject(ctx, args) {
748
- const { messages } = await (0, mapping_js_1.serializeObjectResult)(ctx, this.component, args.result, {
749
- model: this.options.chat.modelId,
750
- provider: this.options.chat.provider,
751
- });
752
- const embeddings = await this.generateEmbeddings(ctx, { userId: args.userId, threadId: args.threadId }, messages.map((m) => m.message));
753
- await ctx.runMutation(this.component.messages.addMessages, {
754
- userId: args.userId,
755
- threadId: args.threadId,
756
- promptMessageId: args.promptMessageId,
757
- failPendingSteps: false,
758
- messages,
759
- embeddings,
760
- agentName: this.options.name,
761
- pending: false,
762
- });
763
- }
764
- /**
765
- * Commit or rollback a message that was pending.
766
- * This is done automatically when saving messages by default.
767
- * If creating pending messages, you can call this when the full "transaction" is done.
768
- * @param ctx The ctx argument to your mutation or action.
769
- * @param args What message to save. Generally the parent message sent into
770
- * the generateText call.
771
- */
772
- async completeMessage(ctx, args) {
773
- const result = args.result;
774
- if (result.kind === "success") {
775
- await ctx.runMutation(this.component.messages.commitMessage, {
776
- messageId: args.messageId,
777
- });
778
- }
779
- else {
780
- await ctx.runMutation(this.component.messages.rollbackMessage, {
781
- messageId: args.messageId,
782
- error: result.error,
783
- });
784
- }
785
- }
786
- async _saveMessagesAndFetchContext(ctx, args, { userId: argsUserId, threadId, contextOptions, storageOptions, }) {
787
- contextOptions ||= this.options.contextOptions;
788
- storageOptions ||= this.options.storageOptions;
789
- // If only a messageId is provided, this will be empty.
790
- const messages = args.promptMessageId
791
- ? []
792
- : (0, mapping_js_1.promptOrMessagesToCoreMessages)(args);
793
- const userId = argsUserId ??
794
- (threadId &&
795
- (await ctx.runQuery(this.component.threads.getThread, { threadId }))
796
- ?.userId);
797
- (0, convex_helpers_1.assert)(!args.promptMessageId || !(args.prompt || args.messages), "you can't specify a prompt or message if you specify a promptMessageId");
798
- // If only a messageId is provided, this will add that message to the end.
799
- const contextMessages = await this.fetchContextMessages(ctx, {
800
- userId,
801
- threadId,
802
- upToAndIncludingMessageId: args.promptMessageId,
803
- messages,
804
- contextOptions,
805
- });
806
- // Lazily generate embeddings for the prompt message, if it doesn't have
807
- // embeddings yet. This can happen if the message was saved in a mutation
808
- // where the LLM is not available.
809
- if (args.promptMessageId &&
810
- !contextMessages.at(-1)?.embeddingId &&
811
- this.options.textEmbedding) {
812
- await this.generateAndSaveEmbeddings(ctx, {
813
- messageIds: [args.promptMessageId],
814
- });
815
- }
816
- let messageId = args.promptMessageId;
817
- let order = args.promptMessageId
818
- ? contextMessages.at(-1)?.order
819
- : undefined;
820
- let stepOrder = args.promptMessageId
821
- ? contextMessages.at(-1)?.stepOrder
822
- : undefined;
823
- if (threadId &&
824
- messages.length &&
825
- storageOptions?.saveMessages !== "none" &&
826
- storageOptions?.saveAnyInputMessages !== false) {
827
- const saveAll = storageOptions?.saveMessages === "all";
828
- const coreMessages = saveAll ? messages : messages.slice(-1);
829
- const saved = await this.saveMessages(ctx, {
830
- threadId,
831
- userId,
832
- messages: coreMessages,
833
- metadata: coreMessages.map((_, i) => i === coreMessages.length - 1 ? { id: args.id } : {}),
834
- pending: true,
835
- failPendingSteps: true,
836
- });
837
- messageId = saved.lastMessageId;
838
- order = saved.messages.at(-1)?.order;
839
- stepOrder = saved.messages.at(-1)?.stepOrder;
840
- }
841
- let processedMessages = [
842
- ...contextMessages.map((m) => (0, mapping_js_1.deserializeMessage)(m.message)),
843
- ...messages,
844
- ];
845
- // Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
846
- if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
847
- processedMessages = await this._inlineMessagesFiles(processedMessages);
848
- }
849
- const { prompt: _, model, ...rest } = args;
850
- return {
851
- args: {
852
- ...rest,
853
- maxRetries: args.maxRetries ?? this.options.maxRetries,
854
- model: model ?? this.options.chat,
855
- system: args.system ?? this.options.instructions,
856
- messages: processedMessages,
857
- },
858
- userId,
859
- messageId,
860
- order,
861
- stepOrder,
862
- };
863
- }
864
- _shouldSaveOutputMessages(storageOpts) {
865
- const opts = storageOpts ?? this.options.storageOptions;
866
- return opts?.saveOutputMessages !== false && opts?.saveMessages !== "none";
867
- }
868
- _mergedContextOptions(opts) {
869
- const searchOptions = {
870
- ...this.options.contextOptions?.searchOptions,
871
- ...opts?.searchOptions,
872
- };
873
- return {
874
- ...this.options.contextOptions,
875
- ...opts,
876
- searchOptions: searchOptions.limit
877
- ? searchOptions
878
- : undefined,
879
- };
880
- }
881
- async _searchOptionsWithEmbeddingAndDefaults(ctx, { userId, threadId }, contextOptions, messages) {
882
- (0, convex_helpers_1.assert)(contextOptions.searchOptions?.textSearch ||
883
- contextOptions.searchOptions?.vectorSearch, "searchOptions is required");
884
- (0, convex_helpers_1.assert)(messages.length > 0, "Core messages cannot be empty");
885
- const text = (0, shared_js_1.extractText)(messages.at(-1));
886
- const search = {
887
- limit: contextOptions.searchOptions?.limit ?? 10,
888
- messageRange: {
889
- ...shared_js_1.DEFAULT_MESSAGE_RANGE,
890
- ...contextOptions.searchOptions?.messageRange,
891
- },
892
- text: (0, shared_js_1.extractText)(messages.at(-1)),
893
- };
894
- if (contextOptions.searchOptions?.vectorSearch &&
895
- text &&
896
- this.options.textEmbedding) {
897
- search.vector = (await this.doEmbed(ctx, {
898
- threadId,
899
- userId,
900
- values: [text],
901
- })).embeddings[0];
902
- search.vectorModel = this.options.textEmbedding.modelId;
903
- }
904
- return search;
905
- }
906
- async doEmbed(ctx, options) {
907
- const embedding = this.options.textEmbedding;
908
- (0, convex_helpers_1.assert)(embedding, "textEmbedding is required");
909
- const result = await embedding.doEmbed({
910
- values: options.values,
911
- abortSignal: options.abortSignal,
912
- headers: options.headers,
913
- });
914
- if (this.options.usageHandler && result.usage) {
915
- await this.options.usageHandler(ctx, {
916
- userId: options.userId,
917
- threadId: options.threadId,
918
- agentName: this.options.name,
919
- model: embedding.modelId,
920
- provider: embedding.provider,
921
- providerMetadata: result.rawResponse
922
- ? { [embedding.provider]: result.rawResponse }
923
- : undefined,
924
- usage: {
925
- promptTokens: result.usage.tokens,
926
- completionTokens: 0,
927
- totalTokens: result.usage.tokens,
928
- },
929
- });
930
- }
931
- return { embeddings: result.embeddings };
932
- }
933
- /**
934
- * Process messages to inline file and image URLs that point to localhost
935
- * by converting them to base64. This solves the problem of LLMs not being
936
- * able to access localhost URLs.
937
- */
938
- async _inlineMessagesFiles(messages) {
939
- // Process each message to convert localhost URLs to base64
940
- return Promise.all(messages.map(async (message) => {
941
- if (message.role !== "user" ||
942
- typeof message.content === "string" ||
943
- !Array.isArray(message.content)) {
944
- return message;
945
- }
946
- const processedContent = await Promise.all(message.content.map(async (part) => {
947
- if (part.type === "image" && part.image instanceof URL) {
948
- if (this._isLocalhostUrl(part.image)) {
949
- const imageData = await this._downloadFile(part.image);
950
- return {
951
- ...part,
952
- image: imageData,
953
- };
954
- }
955
- }
956
- // Handle file parts
957
- if (part.type === "file" && part.data instanceof URL) {
958
- if (this._isLocalhostUrl(part.data)) {
959
- const fileData = await this._downloadFile(part.data);
960
- return {
961
- ...part,
962
- data: fileData,
963
- };
964
- }
965
- }
966
- return part;
967
- }));
968
- return {
969
- ...message,
970
- content: processedContent,
971
- };
972
- }));
973
- }
974
- /**
975
- * Check if a URL points to localhost
976
- */
977
- _isLocalhostUrl(url) {
978
- return (url.hostname === "localhost" ||
979
- url.hostname === "127.0.0.1" ||
980
- url.hostname === "::1" ||
981
- url.hostname === "0.0.0.0");
982
- }
983
- /**
984
- * Download a file from a URL
985
- */
986
- async _downloadFile(url) {
987
- // Fetch the file
988
- const response = await fetch(url);
989
- if (!response.ok) {
990
- throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
991
- }
992
- return await response.arrayBuffer();
993
- }
994
- /**
995
- * WORKFLOW UTILITIES
996
- */
997
- /**
998
- * Create a mutation that creates a thread so you can call it from a Workflow.
999
- * e.g.
1000
- * ```ts
1001
- * // in convex/foo.ts
1002
- * export const createThread = weatherAgent.createThreadMutation();
1003
- *
1004
- * const workflow = new WorkflowManager(components.workflow);
1005
- * export const myWorkflow = workflow.define({
1006
- * args: {},
1007
- * handler: async (step) => {
1008
- * const { threadId } = await step.runMutation(internal.foo.createThread);
1009
- * // use the threadId to generate text, object, etc.
1010
- * },
1011
- * });
1012
- * ```
1013
- * @returns A mutation that creates a thread.
1014
- */
1015
- createThreadMutation() {
1016
- return (0, server_1.internalMutationGeneric)({
1017
- args: {
1018
- userId: values_1.v.optional(values_1.v.string()),
1019
- title: values_1.v.optional(values_1.v.string()),
1020
- summary: values_1.v.optional(values_1.v.string()),
1021
- },
1022
- handler: async (ctx, args) => {
1023
- const { threadId } = await this.createThread(ctx, args);
1024
- return { threadId };
1025
- },
1026
- });
1027
- }
1028
- /**
1029
- * Create an action out of this agent so you can call it from workflows or other actions
1030
- * without a wrapping function.
1031
- * @param spec Configuration for the agent acting as an action, including
1032
- * {@link ContextOptions}, {@link StorageOptions}, and maxSteps.
1033
- */
1034
- asTextAction(spec) {
1035
- const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
1036
- return (0, server_1.internalActionGeneric)({
1037
- args: validators_js_1.vTextArgs,
1038
- handler: async (ctx, args) => {
1039
- const { contextOptions, storageOptions, ...rest } = args;
1040
- const stream = args.stream === true ? spec?.stream || true : spec?.stream ?? false;
1041
- const targetArgs = { userId: args.userId, threadId: args.threadId };
1042
- const llmArgs = { maxSteps, ...rest };
1043
- const opts = {
1044
- contextOptions: contextOptions ??
1045
- spec?.contextOptions ??
1046
- this.options.contextOptions,
1047
- storageOptions: storageOptions ??
1048
- spec?.storageOptions ??
1049
- this.options.storageOptions,
1050
- saveStreamDeltas: stream,
1051
- };
1052
- if (stream) {
1053
- const result = await this.streamText(ctx, targetArgs, llmArgs, opts);
1054
- await result.consumeStream();
1055
- return {
1056
- text: await result.text,
1057
- finishReason: await result.finishReason,
1058
- messageId: result.messageId,
1059
- };
1060
- }
1061
- else {
1062
- const { text, messageId, finishReason } = await this.generateText(ctx, targetArgs, llmArgs, opts);
1063
- return { text, messageId, finishReason };
1064
- }
1065
- },
1066
- });
1067
- }
1068
- /**
1069
- * Create an action that generates an object out of this agent so you can call
1070
- * it from workflows or other actions without a wrapping function.
1071
- * @param spec Configuration for the agent acting as an action, including
1072
- * the normal parameters to {@link generateObject}, plus {@link ContextOptions}
1073
- * and maxSteps.
1074
- */
1075
- asObjectAction(spec, options) {
1076
- const maxSteps = spec?.maxSteps ?? this.options.maxSteps;
1077
- return (0, server_1.internalActionGeneric)({
1078
- args: validators_js_1.vSafeObjectArgs,
1079
- handler: async (ctx, args) => {
1080
- const { contextOptions, storageOptions, ...rest } = args;
1081
- const value = await this.generateObject(ctx, { userId: args.userId, threadId: args.threadId }, {
1082
- ...spec,
1083
- maxSteps,
1084
- ...rest,
1085
- }, {
1086
- contextOptions: contextOptions ??
1087
- options?.contextOptions ??
1088
- this.options.contextOptions,
1089
- storageOptions: storageOptions ??
1090
- options?.storageOptions ??
1091
- this.options.storageOptions,
1092
- });
1093
- return { object: value.object };
1094
- },
1095
- });
1096
- }
1097
- /**
1098
- * Save messages to the thread.
1099
- * Useful as a step in Workflows, e.g.
1100
- * ```ts
1101
- * const saveMessages = agent.asSaveMessagesMutation();
1102
- *
1103
- * const myWorkflow = workflow.define({
1104
- * args: {...},
1105
- * handler: async (step, args) => {
1106
- * // do things to create (but not save)messages
1107
- * const { messageIds } = await step.runMutation(internal.foo.saveMessages, {
1108
- * threadId: args.threadId,
1109
- * messages: args.messages,
1110
- * });
1111
- * // ...
1112
- * },
1113
- * })
1114
- * ```
1115
- * @returns A mutation that can be used to save messages to the thread.
1116
- */
1117
- asSaveMessagesMutation() {
1118
- return (0, server_1.internalMutationGeneric)({
1119
- args: {
1120
- threadId: values_1.v.string(),
1121
- userId: values_1.v.optional(values_1.v.string()),
1122
- promptMessageId: values_1.v.optional(values_1.v.string()),
1123
- messages: values_1.v.array(validators_js_1.vMessageWithMetadata),
1124
- pending: values_1.v.optional(values_1.v.boolean()),
1125
- failPendingSteps: values_1.v.optional(values_1.v.boolean()),
1126
- },
1127
- handler: async (ctx, args) => {
1128
- const { lastMessageId, messages } = await this.saveMessages(ctx, {
1129
- ...args,
1130
- messages: args.messages.map((m) => m.message),
1131
- metadata: args.messages.map(({ message: _, ...m }) => m),
1132
- });
1133
- return {
1134
- lastMessageId,
1135
- messageIds: messages.map((m) => m._id),
1136
- };
1137
- },
1138
- });
1139
- }
1140
- }
1141
- exports.Agent = Agent;
1142
- function filterOutOrphanedToolMessages(docs) {
1143
- const toolCallIds = new Set();
1144
- const result = [];
1145
- for (const doc of docs) {
1146
- if (doc.message?.role === "assistant" &&
1147
- Array.isArray(doc.message.content)) {
1148
- for (const content of doc.message.content) {
1149
- if (content.type === "tool-call") {
1150
- toolCallIds.add(content.toolCallId);
1151
- }
1152
- }
1153
- result.push(doc);
1154
- }
1155
- else if (doc.message?.role === "tool") {
1156
- if (doc.message.content.every((c) => toolCallIds.has(c.toolCallId))) {
1157
- result.push(doc);
1158
- }
1159
- else {
1160
- console.debug("Filtering out orphaned tool message", doc);
1161
- }
1162
- }
1163
- else {
1164
- result.push(doc);
1165
- }
1166
- }
1167
- return result;
1168
- }
1169
- //# sourceMappingURL=index.js.map