@convex-dev/agent 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +634 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +167 -0
- package/dist/client/createTool.d.ts.map +1 -0
- package/dist/client/createTool.js +116 -0
- package/dist/client/createTool.js.map +1 -0
- 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 +1725 -0
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -0
- package/dist/client/definePlaygroundAPI.js +271 -0
- package/dist/client/definePlaygroundAPI.js.map +1 -0
- package/dist/client/files.d.ts +69 -0
- package/dist/client/files.d.ts.map +1 -0
- package/dist/client/files.js +181 -0
- package/dist/client/files.js.map +1 -0
- package/dist/client/index.d.ts +2093 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +933 -0
- package/dist/client/index.js.map +1 -0
- 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 +493 -0
- package/dist/client/search.d.ts.map +1 -0
- package/dist/client/search.js +435 -0
- package/dist/client/search.js.map +1 -0
- 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 +145 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3778 -0
- package/dist/client/streaming.d.ts.map +1 -0
- package/dist/client/streaming.js +344 -0
- package/dist/client/streaming.js.map +1 -0
- 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 +461 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/utils.d.ts +12 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +35 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +48 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- 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 +46 -0
- 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 +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/apiKeys.d.ts +11 -0
- package/dist/component/apiKeys.d.ts.map +1 -0
- package/dist/component/apiKeys.js +69 -0
- package/dist/component/apiKeys.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/files.d.ts +97 -0
- package/dist/component/files.d.ts.map +1 -0
- package/dist/component/files.js +190 -0
- package/dist/component/files.js.map +1 -0
- package/dist/component/messages.d.ts +3851 -0
- package/dist/component/messages.d.ts.map +1 -0
- package/dist/component/messages.js +757 -0
- package/dist/component/messages.js.map +1 -0
- package/dist/component/schema.d.ts +8029 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +147 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/streams.d.ts +128 -0
- package/dist/component/streams.d.ts.map +1 -0
- package/dist/component/streams.js +413 -0
- package/dist/component/streams.js.map +1 -0
- package/dist/component/threads.d.ts +115 -0
- package/dist/component/threads.d.ts.map +1 -0
- package/dist/component/threads.js +208 -0
- package/dist/component/threads.js.map +1 -0
- package/dist/component/users.d.ts +52 -0
- package/dist/component/users.d.ts.map +1 -0
- package/dist/component/users.js +229 -0
- package/dist/component/users.js.map +1 -0
- package/dist/component/vector/index.d.ts +61 -0
- package/dist/component/vector/index.d.ts.map +1 -0
- package/dist/component/vector/index.js +146 -0
- package/dist/component/vector/index.js.map +1 -0
- package/dist/component/vector/tables.d.ts +58 -0
- package/dist/component/vector/tables.d.ts.map +1 -0
- package/dist/component/vector/tables.js +56 -0
- package/dist/component/vector/tables.js.map +1 -0
- 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 +76 -0
- package/dist/mapping.d.ts.map +1 -0
- package/dist/mapping.js +720 -0
- package/dist/mapping.js.map +1 -0
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/index.d.ts +25 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +70 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/optimisticallySendMessage.d.ts +42 -0
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -0
- package/dist/react/optimisticallySendMessage.js +74 -0
- package/dist/react/optimisticallySendMessage.js.map +1 -0
- package/dist/react/types.d.ts +12 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +2 -0
- package/dist/react/types.js.map +1 -0
- 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 +27 -0
- package/dist/react/useSmoothText.d.ts.map +1 -0
- package/dist/react/useSmoothText.js +68 -0
- package/dist/react/useSmoothText.js.map +1 -0
- 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 +26 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +67 -0
- package/dist/shared.js.map +1 -0
- package/dist/validators.d.ts +24516 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +475 -0
- package/dist/validators.js.map +1 -0
- package/package.json +44 -50
- package/src/UIMessages.ts +0 -9
- package/src/client/approval.test.ts +144 -0
- package/src/client/index.ts +26 -19
- package/src/client/search.ts +0 -29
- package/src/client/setup.test.ts +3 -5
- package/src/client/start.ts +2 -2
- package/src/client/streamText.test.ts +102 -0
- package/src/client/streamText.ts +35 -9
- package/src/client/streaming.ts +44 -23
- package/src/client/utils.ts +22 -1
- package/src/component/_generated/dataModel.ts +1 -1
- package/src/component/_generated/server.ts +0 -5
- package/src/mapping.ts +38 -22
- package/src/test.ts +3 -4
package/src/client/streamText.ts
CHANGED
|
@@ -80,9 +80,21 @@ export async function streamText<
|
|
|
80
80
|
|
|
81
81
|
const steps: StepResult<TOOLS>[] = [];
|
|
82
82
|
|
|
83
|
-
// Track the final step for atomic save with stream finish (issue #181)
|
|
83
|
+
// Track the final step for atomic save with stream finish (issue #181).
|
|
84
|
+
// Only used when streamText awaits stream consumption itself; the
|
|
85
|
+
// `returnImmediately` path saves inline instead (see onStepFinish below).
|
|
84
86
|
let pendingFinalStep: StepResult<TOOLS> | undefined;
|
|
85
87
|
|
|
88
|
+
// Whether streamText will await stream consumption before returning.
|
|
89
|
+
// When false (saveStreamDeltas.returnImmediately === true), we cannot
|
|
90
|
+
// defer the final-step save to a post-await block — the function has
|
|
91
|
+
// already returned by the time onStepFinish fires. See issue #265.
|
|
92
|
+
const willAwaitStream =
|
|
93
|
+
Boolean(threadId) &&
|
|
94
|
+
(options.saveStreamDeltas === true ||
|
|
95
|
+
(typeof options.saveStreamDeltas === "object" &&
|
|
96
|
+
!options.saveStreamDeltas.returnImmediately));
|
|
97
|
+
|
|
86
98
|
const streamer =
|
|
87
99
|
threadId && options.saveStreamDeltas
|
|
88
100
|
? new DeltaStreamer(
|
|
@@ -142,10 +154,19 @@ export async function streamText<
|
|
|
142
154
|
steps.push(step);
|
|
143
155
|
const createPendingMessage = await willContinue(steps, args.stopWhen);
|
|
144
156
|
if (!createPendingMessage && streamer) {
|
|
145
|
-
//
|
|
146
|
-
// Defer saving until stream consumption completes for atomic finish (issue #181).
|
|
157
|
+
// Final step with streaming enabled.
|
|
147
158
|
streamer.markFinishedExternally();
|
|
148
|
-
|
|
159
|
+
if (willAwaitStream) {
|
|
160
|
+
// We're about to `await stream` below — defer the save so it
|
|
161
|
+
// happens atomically with stream finish (issue #181).
|
|
162
|
+
pendingFinalStep = step;
|
|
163
|
+
} else {
|
|
164
|
+
// returnImmediately path: streamText is about to return without
|
|
165
|
+
// awaiting consumption, so the deferred-save block below won't
|
|
166
|
+
// see this step. Save inline now (issue #265).
|
|
167
|
+
const finishStreamId = await streamer.getOrCreateStreamId();
|
|
168
|
+
await call.save({ step }, false, finishStreamId);
|
|
169
|
+
}
|
|
149
170
|
} else {
|
|
150
171
|
await call.save({ step }, createPendingMessage);
|
|
151
172
|
}
|
|
@@ -155,11 +176,7 @@ export async function streamText<
|
|
|
155
176
|
const stream = streamer?.consumeStream(
|
|
156
177
|
result.toUIMessageStream<AIUIMessage<TOOLS>>(),
|
|
157
178
|
);
|
|
158
|
-
if (
|
|
159
|
-
(typeof options?.saveStreamDeltas === "object" &&
|
|
160
|
-
!options.saveStreamDeltas.returnImmediately) ||
|
|
161
|
-
options?.saveStreamDeltas === true
|
|
162
|
-
) {
|
|
179
|
+
if (willAwaitStream) {
|
|
163
180
|
try {
|
|
164
181
|
await stream;
|
|
165
182
|
await result.consumeStream();
|
|
@@ -168,6 +185,15 @@ export async function streamText<
|
|
|
168
185
|
// finish() was never called, leaving the streaming message stuck in
|
|
169
186
|
// "streaming" state. Clean it up by marking it as aborted.
|
|
170
187
|
await streamer?.fail(e instanceof Error ? e.message : String(e));
|
|
188
|
+
// Save the deferred final step if it was already generated but not yet persisted
|
|
189
|
+
if (pendingFinalStep) {
|
|
190
|
+
try {
|
|
191
|
+
await call.save({ step: pendingFinalStep }, false);
|
|
192
|
+
} catch (saveError) {
|
|
193
|
+
console.error("Failed to save deferred final step:", saveError);
|
|
194
|
+
}
|
|
195
|
+
pendingFinalStep = undefined;
|
|
196
|
+
}
|
|
171
197
|
throw e;
|
|
172
198
|
}
|
|
173
199
|
}
|
package/src/client/streaming.ts
CHANGED
|
@@ -244,16 +244,25 @@ export class DeltaStreamer<T> {
|
|
|
244
244
|
this.abortController = new AbortController();
|
|
245
245
|
if (config.abortSignal) {
|
|
246
246
|
config.abortSignal.addEventListener("abort", async () => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
try {
|
|
248
|
+
if (this.abortController.signal.aborted) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
251
|
this.abortController.abort();
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
// Wait for in-flight stream creation before trying to abort it
|
|
253
|
+
if (this.#creatingStreamIdPromise) {
|
|
254
|
+
await this.#creatingStreamIdPromise;
|
|
255
|
+
}
|
|
256
|
+
if (this.streamId) {
|
|
257
|
+
await this.#ongoingWrite;
|
|
258
|
+
await this.ctx.runMutation(this.component.streams.abort, {
|
|
259
|
+
streamId: this.streamId,
|
|
260
|
+
reason: "abortSignal",
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
// Best-effort cleanup — the stream will be garbage-collected
|
|
265
|
+
// by the 10-minute timeout if this fails.
|
|
257
266
|
}
|
|
258
267
|
});
|
|
259
268
|
}
|
|
@@ -278,6 +287,13 @@ export class DeltaStreamer<T> {
|
|
|
278
287
|
if (this.abortController.signal.aborted) {
|
|
279
288
|
return;
|
|
280
289
|
}
|
|
290
|
+
// Once the stream has been finished externally (e.g. by the inline
|
|
291
|
+
// save in streamText's onStepFinish for the returnImmediately path),
|
|
292
|
+
// the stream record is already "finished" in the DB. Late deltas
|
|
293
|
+
// would be silently dropped by streams.addDelta — skip the work.
|
|
294
|
+
if (this.#finishedExternally) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
281
297
|
await this.getStreamId();
|
|
282
298
|
this.#nextParts.push(...parts);
|
|
283
299
|
if (
|
|
@@ -293,7 +309,9 @@ export class DeltaStreamer<T> {
|
|
|
293
309
|
await this.addParts([chunk]);
|
|
294
310
|
}
|
|
295
311
|
// Skip finish if it will be handled externally (atomically with message save)
|
|
296
|
-
// or if the stream was aborted (e.g., due to a failed delta write)
|
|
312
|
+
// or if the stream was aborted (e.g., due to a failed delta write).
|
|
313
|
+
// Aborted streams are cleaned up via streams.abort (called by the abort
|
|
314
|
+
// signal handler), so we don't need to call finish() for them.
|
|
297
315
|
if (!this.#finishedExternally && !this.abortController.signal.aborted) {
|
|
298
316
|
await this.finish();
|
|
299
317
|
}
|
|
@@ -331,6 +349,13 @@ export class DeltaStreamer<T> {
|
|
|
331
349
|
delta,
|
|
332
350
|
);
|
|
333
351
|
if (!success) {
|
|
352
|
+
// An in-flight #sendDelta started before markFinishedExternally()
|
|
353
|
+
// will get `success === false` because the stream row is already
|
|
354
|
+
// "finished". That's a benign late-write miss, not a failure —
|
|
355
|
+
// don't convert it into an abort.
|
|
356
|
+
if (this.#finishedExternally) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
334
359
|
await this.config.onAsyncAbort("async abort");
|
|
335
360
|
this.abortController.abort();
|
|
336
361
|
return;
|
|
@@ -376,10 +401,7 @@ export class DeltaStreamer<T> {
|
|
|
376
401
|
return;
|
|
377
402
|
}
|
|
378
403
|
await this.#ongoingWrite;
|
|
379
|
-
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
await this.#sendDelta();
|
|
404
|
+
await this.#sendDelta(); // #sendDelta checks aborted internally
|
|
383
405
|
if (this.abortController.signal.aborted) {
|
|
384
406
|
return;
|
|
385
407
|
}
|
|
@@ -439,16 +461,15 @@ export function compressTextStreamParts(
|
|
|
439
461
|
} else {
|
|
440
462
|
compressed.push(part);
|
|
441
463
|
}
|
|
464
|
+
} else if (part.type === "file") {
|
|
465
|
+
compressed.push({
|
|
466
|
+
type: "file",
|
|
467
|
+
file: {
|
|
468
|
+
...part.file,
|
|
469
|
+
uint8Array: undefined as unknown as Uint8Array,
|
|
470
|
+
},
|
|
471
|
+
});
|
|
442
472
|
} else {
|
|
443
|
-
if (part.type === "file") {
|
|
444
|
-
compressed.push({
|
|
445
|
-
type: "file",
|
|
446
|
-
file: {
|
|
447
|
-
...part.file,
|
|
448
|
-
uint8Array: undefined as unknown as Uint8Array,
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
473
|
compressed.push(part);
|
|
453
474
|
}
|
|
454
475
|
}
|
package/src/client/utils.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import type { StepResult, StopCondition } from "ai";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A stop condition that only matches tool calls which completed
|
|
5
|
+
* successfully (i.e. produced a `tool-result`, not a `tool-error`).
|
|
6
|
+
*
|
|
7
|
+
* Use this instead of the AI SDK's `hasToolCall` when you want the
|
|
8
|
+
* agent to retry on argument validation failures rather than stopping.
|
|
9
|
+
*/
|
|
10
|
+
export function hasSuccessfulToolCall(toolName: string): StopCondition<any> {
|
|
11
|
+
return ({ steps }) =>
|
|
12
|
+
steps[steps.length - 1]?.toolResults?.some(
|
|
13
|
+
(result) => result.toolName === toolName,
|
|
14
|
+
) ?? false;
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
export async function willContinue(
|
|
4
18
|
steps: StepResult<any>[],
|
|
5
19
|
|
|
@@ -9,8 +23,15 @@ export async function willContinue(
|
|
|
9
23
|
// we aren't doing another round after a tool result
|
|
10
24
|
// TODO: whether to handle continuing after too much context used..
|
|
11
25
|
if (step.finishReason !== "tool-calls") return false;
|
|
26
|
+
// Count both successful results and errors as completed outputs.
|
|
27
|
+
// In AI SDK v6, failed tool calls produce tool-error content parts
|
|
28
|
+
// instead of tool-result, so only checking toolResults misses them.
|
|
29
|
+
const completedOutputs =
|
|
30
|
+
step.content?.filter(
|
|
31
|
+
(p) => p.type === "tool-result" || p.type === "tool-error",
|
|
32
|
+
).length ?? step.toolResults.length;
|
|
12
33
|
// we don't have a tool result, so we'll wait for more
|
|
13
|
-
if (step.toolCalls.length >
|
|
34
|
+
if (step.toolCalls.length > completedOutputs) return false;
|
|
14
35
|
if (Array.isArray(stopWhen)) {
|
|
15
36
|
return (await Promise.all(stopWhen.map(async (s) => s({ steps })))).every(
|
|
16
37
|
(stop) => !stop,
|
|
@@ -38,7 +38,7 @@ export type Doc<TableName extends TableNames> = DocumentByName<
|
|
|
38
38
|
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
39
39
|
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
40
40
|
*
|
|
41
|
-
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
|
41
|
+
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
|
|
42
42
|
*
|
|
43
43
|
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
44
44
|
* strings when type checking.
|
|
@@ -107,11 +107,6 @@ export const internalAction: ActionBuilder<DataModel, "internal"> =
|
|
|
107
107
|
*/
|
|
108
108
|
export const httpAction: HttpActionBuilder = httpActionGeneric;
|
|
109
109
|
|
|
110
|
-
type GenericCtx =
|
|
111
|
-
| GenericActionCtx<DataModel>
|
|
112
|
-
| GenericMutationCtx<DataModel>
|
|
113
|
-
| GenericQueryCtx<DataModel>;
|
|
114
|
-
|
|
115
110
|
/**
|
|
116
111
|
* A set of services for use within Convex query functions.
|
|
117
112
|
*
|
package/src/mapping.ts
CHANGED
|
@@ -297,21 +297,49 @@ export function toModelMessageWarnings(
|
|
|
297
297
|
}) as any;
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Serialize explicitly provided response messages for a step.
|
|
302
|
+
* Used by the streaming/generation loop where the caller tracks which
|
|
303
|
+
* messages are new via slicing.
|
|
304
|
+
*/
|
|
305
|
+
export async function serializeResponseMessages<TOOLS extends ToolSet>(
|
|
306
|
+
ctx: ActionCtx,
|
|
307
|
+
component: AgentComponent,
|
|
308
|
+
step: StepResult<TOOLS>,
|
|
309
|
+
model: ModelOrMetadata | undefined,
|
|
310
|
+
responseMessages: ModelMessage[],
|
|
311
|
+
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
312
|
+
return serializeStepMessages(ctx, component, step, model, responseMessages);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Serialize the new messages from a step using a heuristic to determine
|
|
317
|
+
* which response messages are new (last 1-2 messages).
|
|
318
|
+
*/
|
|
300
319
|
export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
301
320
|
ctx: ActionCtx,
|
|
302
321
|
component: AgentComponent,
|
|
303
322
|
step: StepResult<TOOLS>,
|
|
304
323
|
model: ModelOrMetadata | undefined,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
324
|
+
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
325
|
+
const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
|
|
326
|
+
let messagesToSerialize: ModelMessage[];
|
|
327
|
+
if (hasToolMessage) {
|
|
328
|
+
messagesToSerialize = step.response.messages.slice(-2);
|
|
329
|
+
} else if (step.content.length) {
|
|
330
|
+
messagesToSerialize = step.response.messages.slice(-1);
|
|
331
|
+
} else {
|
|
332
|
+
messagesToSerialize = [{ role: "assistant" as const, content: [] }];
|
|
333
|
+
}
|
|
334
|
+
return serializeStepMessages(ctx, component, step, model, messagesToSerialize);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function serializeStepMessages<TOOLS extends ToolSet>(
|
|
338
|
+
ctx: ActionCtx,
|
|
339
|
+
component: AgentComponent,
|
|
340
|
+
step: StepResult<TOOLS>,
|
|
341
|
+
model: ModelOrMetadata | undefined,
|
|
342
|
+
messagesToSerialize: ModelMessage[],
|
|
315
343
|
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
316
344
|
// If there are tool results, there's another message with the tool results
|
|
317
345
|
// ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
|
|
@@ -330,18 +358,6 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
330
358
|
} satisfies Omit<MessageWithMetadata, "message" | "text" | "fileIds">;
|
|
331
359
|
const toolFields = { sources: step.sources };
|
|
332
360
|
|
|
333
|
-
// Determine which messages to serialize for this step
|
|
334
|
-
let messagesToSerialize: ModelMessage[];
|
|
335
|
-
if (newResponseMessages) {
|
|
336
|
-
messagesToSerialize = newResponseMessages;
|
|
337
|
-
} else if (hasToolMessage) {
|
|
338
|
-
messagesToSerialize = step.response.messages.slice(-2);
|
|
339
|
-
} else if (step.content.length) {
|
|
340
|
-
messagesToSerialize = step.response.messages.slice(-1);
|
|
341
|
-
} else {
|
|
342
|
-
messagesToSerialize = [{ role: "assistant" as const, content: [] }];
|
|
343
|
-
}
|
|
344
|
-
|
|
345
361
|
const messages: MessageWithMetadata[] = await Promise.all(
|
|
346
362
|
messagesToSerialize.map(async (msg): Promise<MessageWithMetadata> => {
|
|
347
363
|
const { message, fileIds } = await serializeMessage(ctx, component, msg);
|
package/src/test.ts
CHANGED
|
@@ -9,10 +9,9 @@ const modules = import.meta.glob("./component/**/*.ts");
|
|
|
9
9
|
* @param t - The test convex instance, e.g. from calling `convexTest`.
|
|
10
10
|
* @param name - The name of the component, as registered in convex.config.ts.
|
|
11
11
|
*/
|
|
12
|
-
export function register
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
) {
|
|
12
|
+
export function register<
|
|
13
|
+
Schema extends SchemaDefinition<GenericSchema, boolean>,
|
|
14
|
+
>(t: TestConvex<Schema>, name: string = "agent") {
|
|
16
15
|
t.registerComponent(name, schema, modules);
|
|
17
16
|
}
|
|
18
17
|
export default { register, schema, modules };
|