@convex-dev/agent 0.2.5-alpha.0 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +13 -19
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +49 -49
- package/dist/client/index.js.map +1 -1
- package/dist/client/mockModel.d.ts +30 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +153 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/streaming.d.ts +2 -1
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +10 -8
- package/dist/client/streaming.js.map +1 -1
- package/dist/component/messages.js +1 -1
- package/dist/component/messages.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +83 -80
- package/src/client/mockModel.ts +195 -0
- package/src/client/streaming.ts +8 -7
- package/src/component/messages.ts +1 -1
package/src/client/index.ts
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
type ProviderMetadata,
|
|
60
60
|
type StreamArgs,
|
|
61
61
|
} from "../validators.js";
|
|
62
|
-
import {
|
|
62
|
+
import { wrapTools, type ToolCtx } from "./createTool.js";
|
|
63
63
|
import {
|
|
64
64
|
listMessages,
|
|
65
65
|
saveMessages,
|
|
@@ -164,7 +164,8 @@ export {
|
|
|
164
164
|
updateThreadMetadata,
|
|
165
165
|
searchThreadTitles,
|
|
166
166
|
} from "./threads.js";
|
|
167
|
-
export {
|
|
167
|
+
export { extractText, isTool, sorted } from "../shared.js";
|
|
168
|
+
export { createTool } from "./createTool.js";
|
|
168
169
|
export type {
|
|
169
170
|
AgentComponent,
|
|
170
171
|
ContextOptions,
|
|
@@ -178,9 +179,10 @@ export type {
|
|
|
178
179
|
ThreadDoc,
|
|
179
180
|
UsageHandler,
|
|
180
181
|
};
|
|
182
|
+
export { mockModel } from "./mockModel.js";
|
|
181
183
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
// 10k characters should be more than enough for most cases, and stays under
|
|
185
|
+
// the 8k token limit for some models.
|
|
184
186
|
const MAX_EMBEDDING_TEXT_LENGTH = 10_000;
|
|
185
187
|
|
|
186
188
|
export type Config = {
|
|
@@ -429,7 +431,12 @@ export class Agent<
|
|
|
429
431
|
};
|
|
430
432
|
}
|
|
431
433
|
|
|
432
|
-
async start<
|
|
434
|
+
async start<
|
|
435
|
+
TOOLS extends ToolSet | undefined,
|
|
436
|
+
T extends {
|
|
437
|
+
_internal?: { generateId?: IdGenerator };
|
|
438
|
+
},
|
|
439
|
+
>(
|
|
433
440
|
ctx: ActionCtx & CustomCtx,
|
|
434
441
|
/**
|
|
435
442
|
* These are the arguments you'll pass to the LLM call such as
|
|
@@ -482,9 +489,6 @@ export class Agent<
|
|
|
482
489
|
* aborted, it will trigger this signal when detected.
|
|
483
490
|
*/
|
|
484
491
|
abortSignal?: AbortSignal;
|
|
485
|
-
// We optimistically override the generateId function to use the pending
|
|
486
|
-
// message id.
|
|
487
|
-
_internal?: { generateId?: IdGenerator };
|
|
488
492
|
stopWhen?:
|
|
489
493
|
| StopCondition<TOOLS extends undefined ? AgentTools : TOOLS>
|
|
490
494
|
| Array<StopCondition<TOOLS extends undefined ? AgentTools : TOOLS>>;
|
|
@@ -495,7 +499,6 @@ export class Agent<
|
|
|
495
499
|
system?: string;
|
|
496
500
|
model: LanguageModel;
|
|
497
501
|
messages: ModelMessage[];
|
|
498
|
-
// abortSignal?: AbortSignal;
|
|
499
502
|
tools?: TOOLS extends undefined ? AgentTools : TOOLS;
|
|
500
503
|
} & CallSettings;
|
|
501
504
|
order: number;
|
|
@@ -513,23 +516,17 @@ export class Agent<
|
|
|
513
516
|
getSavedMessages: () => MessageDoc[];
|
|
514
517
|
}> {
|
|
515
518
|
const { threadId, ...opts } = { ...this.options, ...options };
|
|
516
|
-
const context = await this._saveMessagesAndFetchContext(ctx,
|
|
519
|
+
const context = await this._saveMessagesAndFetchContext(ctx, {
|
|
517
520
|
userId: options?.userId,
|
|
518
521
|
threadId: options?.threadId,
|
|
522
|
+
messages: args.messages,
|
|
523
|
+
prompt: args.prompt,
|
|
524
|
+
promptMessageId: args.promptMessageId,
|
|
519
525
|
...opts,
|
|
520
526
|
});
|
|
521
527
|
let pendingMessageId = context.pendingMessageId;
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
const messages = context.savedMessages ?? [];
|
|
525
|
-
if (pendingMessageId) {
|
|
526
|
-
if (!aiArgs._internal?.generateId) {
|
|
527
|
-
aiArgs._internal = {
|
|
528
|
-
...aiArgs._internal,
|
|
529
|
-
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
}
|
|
528
|
+
const { messages, promptMessageId, order, stepOrder, userId } = context;
|
|
529
|
+
const savedMessages = context.savedMessages ?? [];
|
|
533
530
|
const toolCtx = {
|
|
534
531
|
...(ctx as UserActionCtx & CustomCtx),
|
|
535
532
|
userId,
|
|
@@ -553,10 +550,9 @@ export class Agent<
|
|
|
553
550
|
});
|
|
554
551
|
}
|
|
555
552
|
};
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
aiArgs.abortSignal.addEventListener(
|
|
553
|
+
if (args.abortSignal) {
|
|
554
|
+
const abortSignal = args.abortSignal;
|
|
555
|
+
abortSignal.addEventListener(
|
|
560
556
|
"abort",
|
|
561
557
|
async () => {
|
|
562
558
|
await fail(abortSignal.reason ?? "Aborted");
|
|
@@ -564,21 +560,39 @@ export class Agent<
|
|
|
564
560
|
{ once: true },
|
|
565
561
|
);
|
|
566
562
|
}
|
|
563
|
+
const aiArgs = {
|
|
564
|
+
...this.options.callSettings,
|
|
565
|
+
...this.options.providerOptions,
|
|
566
|
+
...omit(args, ["messages", "prompt", "promptMessageId"]),
|
|
567
|
+
model: args.model ?? this.options.languageModel,
|
|
568
|
+
system: args.system ?? this.options.instructions,
|
|
569
|
+
messages,
|
|
570
|
+
stopWhen:
|
|
571
|
+
args.stopWhen ??
|
|
572
|
+
this.options.stopWhen ??
|
|
573
|
+
stepCountIs(this.options.maxSteps ?? 1),
|
|
574
|
+
tools,
|
|
575
|
+
} as T & {
|
|
576
|
+
model: LanguageModel;
|
|
577
|
+
messages: ModelMessage[];
|
|
578
|
+
tools?: TOOLS extends undefined ? AgentTools : TOOLS;
|
|
579
|
+
} & CallSettings;
|
|
580
|
+
if (pendingMessageId) {
|
|
581
|
+
if (!aiArgs._internal?.generateId) {
|
|
582
|
+
aiArgs._internal = {
|
|
583
|
+
...aiArgs._internal,
|
|
584
|
+
generateId: () => pendingMessageId ?? crypto.randomUUID(),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
let activeModel = aiArgs.model;
|
|
567
589
|
return {
|
|
568
|
-
args:
|
|
569
|
-
stopWhen:
|
|
570
|
-
args.stopWhen ??
|
|
571
|
-
this.options.stopWhen ??
|
|
572
|
-
stepCountIs(this.options.maxSteps ?? 1),
|
|
573
|
-
...aiArgs,
|
|
574
|
-
tools,
|
|
575
|
-
// abortSignal: abortController.signal,
|
|
576
|
-
},
|
|
590
|
+
args: aiArgs,
|
|
577
591
|
order: order ?? 0,
|
|
578
592
|
stepOrder: stepOrder ?? 0,
|
|
579
593
|
userId,
|
|
580
594
|
promptMessageId,
|
|
581
|
-
getSavedMessages: () =>
|
|
595
|
+
getSavedMessages: () => savedMessages,
|
|
582
596
|
updateModel: (model: LanguageModel | undefined) => {
|
|
583
597
|
if (model) {
|
|
584
598
|
activeModel = model;
|
|
@@ -640,18 +654,18 @@ export class Agent<
|
|
|
640
654
|
if (createPendingMessage) {
|
|
641
655
|
if (lastMessage.status === "failed") {
|
|
642
656
|
pendingMessageId = undefined;
|
|
643
|
-
|
|
657
|
+
savedMessages.push(...saved.messages);
|
|
644
658
|
await fail(
|
|
645
659
|
lastMessage.error ??
|
|
646
660
|
"Aborting - the pending message was marked as failed",
|
|
647
661
|
);
|
|
648
662
|
} else {
|
|
649
663
|
pendingMessageId = lastMessage._id;
|
|
650
|
-
|
|
664
|
+
savedMessages.push(...saved.messages.slice(0, -1));
|
|
651
665
|
}
|
|
652
666
|
} else {
|
|
653
667
|
pendingMessageId = undefined;
|
|
654
|
-
|
|
668
|
+
savedMessages.push(...saved.messages);
|
|
655
669
|
}
|
|
656
670
|
}
|
|
657
671
|
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
@@ -796,17 +810,23 @@ export class Agent<
|
|
|
796
810
|
const opts = { ...this.options, ...options };
|
|
797
811
|
const streamer =
|
|
798
812
|
threadId && opts.saveStreamDeltas
|
|
799
|
-
? new DeltaStreamer(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
813
|
+
? new DeltaStreamer(
|
|
814
|
+
this.component,
|
|
815
|
+
ctx,
|
|
816
|
+
opts.saveStreamDeltas,
|
|
817
|
+
call.fail,
|
|
818
|
+
{
|
|
819
|
+
threadId,
|
|
820
|
+
userId,
|
|
821
|
+
agentName: this.options.name,
|
|
822
|
+
model: getModelName(args.model),
|
|
823
|
+
provider: getProviderName(args.model),
|
|
824
|
+
providerOptions: args.providerOptions,
|
|
825
|
+
order,
|
|
826
|
+
stepOrder,
|
|
827
|
+
abortSignal: args.abortSignal,
|
|
828
|
+
},
|
|
829
|
+
)
|
|
810
830
|
: undefined;
|
|
811
831
|
|
|
812
832
|
const result = streamText({
|
|
@@ -1652,30 +1672,23 @@ export class Agent<
|
|
|
1652
1672
|
});
|
|
1653
1673
|
}
|
|
1654
1674
|
|
|
1655
|
-
async _saveMessagesAndFetchContext
|
|
1656
|
-
T extends {
|
|
1657
|
-
prompt?: string | (ModelMessage | Message)[];
|
|
1658
|
-
messages?: (ModelMessage | Message)[];
|
|
1659
|
-
system?: string;
|
|
1660
|
-
promptMessageId?: string;
|
|
1661
|
-
pendingMessageId?: string;
|
|
1662
|
-
model?: LanguageModel;
|
|
1663
|
-
},
|
|
1664
|
-
>(
|
|
1675
|
+
async _saveMessagesAndFetchContext(
|
|
1665
1676
|
ctx: RunActionCtx,
|
|
1666
|
-
args: T,
|
|
1667
1677
|
{
|
|
1668
1678
|
userId: argsUserId,
|
|
1669
1679
|
threadId,
|
|
1670
1680
|
contextOptions,
|
|
1671
1681
|
storageOptions,
|
|
1682
|
+
...args
|
|
1672
1683
|
}: {
|
|
1684
|
+
prompt: string | (ModelMessage | Message)[] | undefined;
|
|
1685
|
+
messages: (ModelMessage | Message)[] | undefined;
|
|
1686
|
+
promptMessageId: string | undefined;
|
|
1673
1687
|
userId: string | null | undefined;
|
|
1674
1688
|
threadId: string | undefined;
|
|
1675
1689
|
} & Options,
|
|
1676
1690
|
): Promise<{
|
|
1677
|
-
|
|
1678
|
-
CallSettings;
|
|
1691
|
+
messages: ModelMessage[];
|
|
1679
1692
|
userId: string | undefined;
|
|
1680
1693
|
promptMessageId: string | undefined;
|
|
1681
1694
|
pendingMessageId: string | undefined;
|
|
@@ -1685,7 +1698,7 @@ export class Agent<
|
|
|
1685
1698
|
}> {
|
|
1686
1699
|
// If only a promptMessageId is provided, this will be empty.
|
|
1687
1700
|
const messages: (ModelMessage | Message)[] = args.messages ?? [];
|
|
1688
|
-
const
|
|
1701
|
+
const promptArray: ModelMessage[] = !args.prompt
|
|
1689
1702
|
? []
|
|
1690
1703
|
: Array.isArray(args.prompt)
|
|
1691
1704
|
? args.prompt.map((p) => deserializeMessage(p))
|
|
@@ -1722,14 +1735,14 @@ export class Agent<
|
|
|
1722
1735
|
if (threadId && storageOptions?.saveMessages !== "none") {
|
|
1723
1736
|
let saved: { messages: MessageDoc[] };
|
|
1724
1737
|
if (
|
|
1725
|
-
messages.length +
|
|
1738
|
+
messages.length + promptArray.length &&
|
|
1726
1739
|
// If it was a promptMessageId, we don't want to save it again.
|
|
1727
1740
|
(!args.promptMessageId || storageOptions?.saveMessages === "all")
|
|
1728
1741
|
) {
|
|
1729
1742
|
const saveAll = storageOptions?.saveMessages === "all";
|
|
1730
1743
|
const coreMessages: (ModelMessage | Message)[] = [
|
|
1731
1744
|
...messages,
|
|
1732
|
-
...
|
|
1745
|
+
...promptArray,
|
|
1733
1746
|
];
|
|
1734
1747
|
const toSave = saveAll ? coreMessages : coreMessages.slice(-1);
|
|
1735
1748
|
const metadata = Array.from({ length: toSave.length }, () => ({}));
|
|
@@ -1738,8 +1751,8 @@ export class Agent<
|
|
|
1738
1751
|
userId,
|
|
1739
1752
|
messages: [...toSave, { role: "assistant", content: [] }],
|
|
1740
1753
|
metadata: [...metadata, { status: "pending" }],
|
|
1741
|
-
|
|
1742
|
-
|
|
1754
|
+
// TODO: sanity check
|
|
1755
|
+
failPendingSteps: !!args.promptMessageId,
|
|
1743
1756
|
});
|
|
1744
1757
|
promptMessageId = saved.messages.at(-2)!._id;
|
|
1745
1758
|
} else {
|
|
@@ -1748,8 +1761,7 @@ export class Agent<
|
|
|
1748
1761
|
userId,
|
|
1749
1762
|
messages: [{ role: "assistant", content: [] }],
|
|
1750
1763
|
metadata: [{ status: "pending" }],
|
|
1751
|
-
failPendingSteps: !!args.
|
|
1752
|
-
pendingMessageId: args.pendingMessageId,
|
|
1764
|
+
failPendingSteps: !!args.promptMessageId,
|
|
1753
1765
|
});
|
|
1754
1766
|
}
|
|
1755
1767
|
pendingMessageId = saved.messages.at(-1)!._id;
|
|
@@ -1783,7 +1795,7 @@ export class Agent<
|
|
|
1783
1795
|
let processedMessages: ModelMessage[] = [
|
|
1784
1796
|
...prePrompt,
|
|
1785
1797
|
...messages,
|
|
1786
|
-
...
|
|
1798
|
+
...promptArray,
|
|
1787
1799
|
...existingResponses,
|
|
1788
1800
|
].map((m) => deserializeMessage(m));
|
|
1789
1801
|
|
|
@@ -1792,17 +1804,8 @@ export class Agent<
|
|
|
1792
1804
|
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
1793
1805
|
}
|
|
1794
1806
|
|
|
1795
|
-
const { prompt: _, model, ...rest } = args;
|
|
1796
1807
|
return {
|
|
1797
|
-
|
|
1798
|
-
...this.options.callSettings,
|
|
1799
|
-
...this.options.providerOptions,
|
|
1800
|
-
...rest,
|
|
1801
|
-
model: model ?? this.options.languageModel,
|
|
1802
|
-
system: args.system ?? this.options.instructions,
|
|
1803
|
-
messages: processedMessages,
|
|
1804
|
-
} as Extract<T, { model: LanguageModel; messages: ModelMessage[] }> &
|
|
1805
|
-
CallSettings,
|
|
1808
|
+
messages: processedMessages,
|
|
1806
1809
|
userId,
|
|
1807
1810
|
promptMessageId,
|
|
1808
1811
|
pendingMessageId,
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV2,
|
|
3
|
+
LanguageModelV2StreamPart,
|
|
4
|
+
} from "@ai-sdk/provider";
|
|
5
|
+
import type { ReasoningPart, TextPart } from "@ai-sdk/provider-utils";
|
|
6
|
+
import { simulateReadableStream } from "ai";
|
|
7
|
+
|
|
8
|
+
const longDefaultText = `
|
|
9
|
+
A A A A A A A A A A A A A A A
|
|
10
|
+
B B B B B B B B B B B B B B B
|
|
11
|
+
C C C C C C C C C C C C C C C
|
|
12
|
+
D D D D D D D D D D D D D D D
|
|
13
|
+
`;
|
|
14
|
+
const defaultUsage = { outputTokens: 10, inputTokens: 3, totalTokens: 13 };
|
|
15
|
+
|
|
16
|
+
export type MockModelArgs = {
|
|
17
|
+
provider?: LanguageModelV2["provider"];
|
|
18
|
+
modelId?: LanguageModelV2["modelId"];
|
|
19
|
+
supportedUrls?:
|
|
20
|
+
| LanguageModelV2["supportedUrls"]
|
|
21
|
+
| (() => LanguageModelV2["supportedUrls"]);
|
|
22
|
+
chunkDelayInMs?: number;
|
|
23
|
+
initialDelayInMs?: number;
|
|
24
|
+
// provide either content or doGenerate & doStream
|
|
25
|
+
content?: (TextPart | ReasoningPart)[];
|
|
26
|
+
doGenerate?: LanguageModelV2["doGenerate"];
|
|
27
|
+
doStream?: LanguageModelV2["doStream"];
|
|
28
|
+
fail?:
|
|
29
|
+
| boolean
|
|
30
|
+
| {
|
|
31
|
+
probability?: number;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function mockModel(args?: MockModelArgs): LanguageModelV2 {
|
|
37
|
+
return new MockLanguageModel(args ?? {});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class MockLanguageModel implements LanguageModelV2 {
|
|
41
|
+
readonly specificationVersion = "v2";
|
|
42
|
+
|
|
43
|
+
private _supportedUrls: () => LanguageModelV2["supportedUrls"];
|
|
44
|
+
|
|
45
|
+
readonly provider: LanguageModelV2["provider"];
|
|
46
|
+
readonly modelId: LanguageModelV2["modelId"];
|
|
47
|
+
|
|
48
|
+
doGenerate: LanguageModelV2["doGenerate"];
|
|
49
|
+
doStream: LanguageModelV2["doStream"];
|
|
50
|
+
|
|
51
|
+
doGenerateCalls: Parameters<LanguageModelV2["doGenerate"]>[0][] = [];
|
|
52
|
+
doStreamCalls: Parameters<LanguageModelV2["doStream"]>[0][] = [];
|
|
53
|
+
|
|
54
|
+
constructor(args: MockModelArgs) {
|
|
55
|
+
this.provider = args.provider || "mock-provider";
|
|
56
|
+
this.modelId = args.modelId || "mock-model-id";
|
|
57
|
+
const {
|
|
58
|
+
content = [{ type: "text", text: longDefaultText }],
|
|
59
|
+
chunkDelayInMs = 200,
|
|
60
|
+
initialDelayInMs = 1000,
|
|
61
|
+
supportedUrls = {},
|
|
62
|
+
} = args;
|
|
63
|
+
const fail =
|
|
64
|
+
args.fail &&
|
|
65
|
+
(args.fail === true ||
|
|
66
|
+
!args.fail.probability ||
|
|
67
|
+
Math.random() < args.fail.probability);
|
|
68
|
+
const error =
|
|
69
|
+
(typeof args.fail === "object" && args.fail.error) ||
|
|
70
|
+
"Mock error message";
|
|
71
|
+
|
|
72
|
+
const chunks: LanguageModelV2StreamPart[] = [
|
|
73
|
+
{ type: "stream-start", warnings: [] },
|
|
74
|
+
];
|
|
75
|
+
chunks.push(
|
|
76
|
+
...content.flatMap((c, ci) => {
|
|
77
|
+
const deltas = c.text.split(" ");
|
|
78
|
+
let parts: LanguageModelV2StreamPart[] = [];
|
|
79
|
+
if (c.type === "reasoning") {
|
|
80
|
+
parts.push({
|
|
81
|
+
type: "reasoning-start",
|
|
82
|
+
id: `${ci}-reasoning-start`,
|
|
83
|
+
});
|
|
84
|
+
parts.push(
|
|
85
|
+
...deltas.map(
|
|
86
|
+
(delta, di) =>
|
|
87
|
+
({
|
|
88
|
+
type: "reasoning-delta",
|
|
89
|
+
delta,
|
|
90
|
+
id: `${ci}-reasoning-${di}`,
|
|
91
|
+
providerMetadata: {
|
|
92
|
+
mockProvider: { mock: { reasoningDetails: null } },
|
|
93
|
+
},
|
|
94
|
+
}) satisfies LanguageModelV2StreamPart,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
parts.push({
|
|
98
|
+
type: "reasoning-end",
|
|
99
|
+
id: `${ci}-reasoning-end`,
|
|
100
|
+
});
|
|
101
|
+
} else if (c.type === "text") {
|
|
102
|
+
parts.push({
|
|
103
|
+
type: "text-start",
|
|
104
|
+
id: `${ci}-text-start`,
|
|
105
|
+
});
|
|
106
|
+
parts = deltas.map((delta, di) => ({
|
|
107
|
+
type: "text-delta",
|
|
108
|
+
delta,
|
|
109
|
+
id: `${ci}-text-${di}`,
|
|
110
|
+
}));
|
|
111
|
+
parts.push({
|
|
112
|
+
type: "text-end",
|
|
113
|
+
id: `${ci}-text-end`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return parts;
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
if (fail) {
|
|
120
|
+
chunks.push({
|
|
121
|
+
type: "error",
|
|
122
|
+
error,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
chunks.push({
|
|
126
|
+
type: "finish",
|
|
127
|
+
finishReason: fail ? "error" : "stop",
|
|
128
|
+
usage: defaultUsage,
|
|
129
|
+
providerMetadata: {
|
|
130
|
+
mockProvider: { mock: "mock metadata" },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
this.doGenerate = async (options) => {
|
|
134
|
+
this.doGenerateCalls.push(options);
|
|
135
|
+
|
|
136
|
+
if (fail) {
|
|
137
|
+
throw new Error(error);
|
|
138
|
+
}
|
|
139
|
+
if (typeof args.doGenerate === "function") {
|
|
140
|
+
return args.doGenerate(options);
|
|
141
|
+
} else if (Array.isArray(args.doGenerate)) {
|
|
142
|
+
return args.doGenerate[this.doGenerateCalls.length];
|
|
143
|
+
} else if (content) {
|
|
144
|
+
return {
|
|
145
|
+
content,
|
|
146
|
+
finishReason: "stop",
|
|
147
|
+
usage: defaultUsage,
|
|
148
|
+
providerMetadata: { mockProvider: { mock: "mock metadata" } },
|
|
149
|
+
warnings: [],
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error("Unexpected: no content or doGenerate");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
this._supportedUrls =
|
|
156
|
+
typeof supportedUrls === "function"
|
|
157
|
+
? supportedUrls
|
|
158
|
+
: async () => supportedUrls;
|
|
159
|
+
this.doStream = async (options) => {
|
|
160
|
+
this.doStreamCalls.push(options);
|
|
161
|
+
|
|
162
|
+
if (typeof args.doStream === "function") {
|
|
163
|
+
return args.doStream(options);
|
|
164
|
+
} else if (Array.isArray(args.doStream)) {
|
|
165
|
+
return args.doStream[this.doStreamCalls.length];
|
|
166
|
+
} else if (content) {
|
|
167
|
+
const stream = simulateReadableStream({
|
|
168
|
+
chunks,
|
|
169
|
+
initialDelayInMs,
|
|
170
|
+
chunkDelayInMs,
|
|
171
|
+
});
|
|
172
|
+
if (options.abortSignal) {
|
|
173
|
+
throw new Error("abortSignal in mock model");
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
stream,
|
|
177
|
+
request: { body: {} },
|
|
178
|
+
response: { headers: {} },
|
|
179
|
+
};
|
|
180
|
+
} else if (args.doStream) {
|
|
181
|
+
return args.doStream;
|
|
182
|
+
} else {
|
|
183
|
+
throw new Error("Provide either content or doStream");
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
this._supportedUrls =
|
|
187
|
+
typeof supportedUrls === "function"
|
|
188
|
+
? supportedUrls
|
|
189
|
+
: async () => supportedUrls;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get supportedUrls() {
|
|
193
|
+
return this._supportedUrls();
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/client/streaming.ts
CHANGED
|
@@ -197,6 +197,7 @@ export class DeltaStreamer {
|
|
|
197
197
|
public readonly component: AgentComponent,
|
|
198
198
|
public readonly ctx: RunActionCtx,
|
|
199
199
|
options: true | StreamingOptions,
|
|
200
|
+
private onAsyncAbort: (reason: string) => Promise<void>,
|
|
200
201
|
public readonly metadata: {
|
|
201
202
|
threadId: string;
|
|
202
203
|
userId?: string;
|
|
@@ -210,21 +211,22 @@ export class DeltaStreamer {
|
|
|
210
211
|
},
|
|
211
212
|
) {
|
|
212
213
|
this.options =
|
|
213
|
-
|
|
214
|
+
options === true
|
|
214
215
|
? DEFAULT_STREAMING_OPTIONS
|
|
215
216
|
: { ...DEFAULT_STREAMING_OPTIONS, ...options };
|
|
216
217
|
this.#nextParts = [];
|
|
217
218
|
this.abortController = new AbortController();
|
|
218
219
|
if (metadata.abortSignal) {
|
|
219
220
|
metadata.abortSignal.addEventListener("abort", async () => {
|
|
221
|
+
if (this.abortController.signal.aborted) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
220
224
|
if (this.streamId) {
|
|
221
225
|
this.abortController.abort();
|
|
222
|
-
const finalDelta = this.#createDelta();
|
|
223
226
|
await this.#ongoingWrite;
|
|
224
227
|
await this.ctx.runMutation(this.component.streams.abort, {
|
|
225
228
|
streamId: this.streamId,
|
|
226
229
|
reason: "abortSignal",
|
|
227
|
-
finalDelta,
|
|
228
230
|
});
|
|
229
231
|
}
|
|
230
232
|
});
|
|
@@ -265,9 +267,12 @@ export class DeltaStreamer {
|
|
|
265
267
|
delta,
|
|
266
268
|
);
|
|
267
269
|
if (!success) {
|
|
270
|
+
await this.onAsyncAbort("async abort");
|
|
268
271
|
this.abortController.abort();
|
|
272
|
+
return;
|
|
269
273
|
}
|
|
270
274
|
} catch (e) {
|
|
275
|
+
await this.onAsyncAbort(e instanceof Error ? e.message : "unknown error");
|
|
271
276
|
this.abortController.abort();
|
|
272
277
|
throw e;
|
|
273
278
|
}
|
|
@@ -302,11 +307,9 @@ export class DeltaStreamer {
|
|
|
302
307
|
if (!this.streamId) {
|
|
303
308
|
return;
|
|
304
309
|
}
|
|
305
|
-
const finalDelta = this.#createDelta();
|
|
306
310
|
await this.#ongoingWrite;
|
|
307
311
|
await this.ctx.runMutation(this.component.streams.finish, {
|
|
308
312
|
streamId: this.streamId,
|
|
309
|
-
finalDelta,
|
|
310
313
|
});
|
|
311
314
|
}
|
|
312
315
|
|
|
@@ -318,12 +321,10 @@ export class DeltaStreamer {
|
|
|
318
321
|
if (!this.streamId) {
|
|
319
322
|
return;
|
|
320
323
|
}
|
|
321
|
-
const finalDelta = this.#createDelta();
|
|
322
324
|
await this.#ongoingWrite;
|
|
323
325
|
await this.ctx.runMutation(this.component.streams.abort, {
|
|
324
326
|
streamId: this.streamId,
|
|
325
327
|
reason,
|
|
326
|
-
finalDelta,
|
|
327
328
|
});
|
|
328
329
|
}
|
|
329
330
|
}
|
|
@@ -351,7 +351,7 @@ export const finalizeMessage = mutation({
|
|
|
351
351
|
return;
|
|
352
352
|
}
|
|
353
353
|
// See if we can add any in-progress data
|
|
354
|
-
if (message.message
|
|
354
|
+
if (!message.message?.content.length) {
|
|
355
355
|
const messages = await getStreamingMessagesWithMetadata(
|
|
356
356
|
ctx,
|
|
357
357
|
message,
|