@convex-dev/agent 0.2.11-alpha.2 → 0.2.11-alpha.4
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/README.md +2 -2
- package/dist/UIMessages.d.ts +1 -0
- package/dist/UIMessages.d.ts.map +1 -1
- package/dist/UIMessages.js +51 -0
- package/dist/UIMessages.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +20 -20
- package/dist/client/index.d.ts +28 -107
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +17 -25
- package/dist/client/index.js.map +1 -1
- package/dist/client/threads.d.ts +4 -4
- package/dist/client/types.d.ts +61 -169
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/files.d.ts +4 -4
- package/dist/component/messages.d.ts +9 -9
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js.map +1 -1
- package/dist/component/streams.d.ts +2 -6
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +4 -4
- package/dist/component/users.d.ts +4 -4
- package/dist/deltas.d.ts +0 -1
- package/dist/deltas.d.ts.map +1 -1
- package/dist/deltas.js +0 -51
- package/dist/deltas.js.map +1 -1
- package/dist/react/useUIMessages.d.ts.map +1 -1
- package/dist/react/useUIMessages.js +1 -2
- package/dist/react/useUIMessages.js.map +1 -1
- package/package.json +61 -20
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +64 -0
- package/src/client/index.ts +47 -123
- package/src/client/types.ts +85 -192
- package/src/component/messages.ts +16 -2
- package/src/component/streams.ts +8 -1
- package/src/deltas.test.ts +0 -272
- package/src/deltas.ts +0 -63
- package/src/react/useUIMessages.ts +5 -2
- package/src/test.ts +17 -0
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
package/src/deltas.test.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
blankUIMessage,
|
|
4
|
-
combineUIMessages,
|
|
5
4
|
deriveUIMessagesFromTextStreamParts,
|
|
6
5
|
updateFromTextStreamParts,
|
|
7
6
|
updateFromUIMessageChunks,
|
|
@@ -535,274 +534,3 @@ describe("mergeDeltas", () => {
|
|
|
535
534
|
]);
|
|
536
535
|
});
|
|
537
536
|
});
|
|
538
|
-
|
|
539
|
-
describe("combineUIMessages", () => {
|
|
540
|
-
it("combines messages spanning two pages correctly", () => {
|
|
541
|
-
const message1 = {
|
|
542
|
-
id: "msg1",
|
|
543
|
-
key: "msg1-key",
|
|
544
|
-
_creationTime: Date.now(),
|
|
545
|
-
order: 1,
|
|
546
|
-
stepOrder: 1,
|
|
547
|
-
status: "success" as const,
|
|
548
|
-
role: "assistant" as const,
|
|
549
|
-
text: "",
|
|
550
|
-
parts: [
|
|
551
|
-
{
|
|
552
|
-
type: "dynamic-tool" as const,
|
|
553
|
-
state: "input-available" as const,
|
|
554
|
-
toolCallId: "call_123",
|
|
555
|
-
toolName: "calculator",
|
|
556
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
557
|
-
},
|
|
558
|
-
],
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
const message2 = {
|
|
562
|
-
id: "msg2",
|
|
563
|
-
key: "msg2-key",
|
|
564
|
-
_creationTime: Date.now() + 1,
|
|
565
|
-
order: 1,
|
|
566
|
-
stepOrder: 2,
|
|
567
|
-
status: "success" as const,
|
|
568
|
-
role: "assistant" as const,
|
|
569
|
-
text: "The result is 5.",
|
|
570
|
-
parts: [
|
|
571
|
-
{
|
|
572
|
-
type: "tool-calculator" as const,
|
|
573
|
-
state: "output-available" as const,
|
|
574
|
-
toolCallId: "call_123",
|
|
575
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
576
|
-
output: { result: 5 },
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
type: "text" as const,
|
|
580
|
-
text: "The result is 5.",
|
|
581
|
-
state: "done" as const,
|
|
582
|
-
},
|
|
583
|
-
],
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
const combined = combineUIMessages([message1, message2]);
|
|
587
|
-
|
|
588
|
-
expect(combined).toHaveLength(1);
|
|
589
|
-
expect(combined[0].role).toBe("assistant");
|
|
590
|
-
expect(combined[0].text).toBe("The result is 5.");
|
|
591
|
-
expect(combined[0].parts).toHaveLength(2);
|
|
592
|
-
|
|
593
|
-
const toolPart = combined[0].parts.find(
|
|
594
|
-
(p) => p.type === "tool-calculator",
|
|
595
|
-
);
|
|
596
|
-
expect(toolPart).toMatchObject({
|
|
597
|
-
type: "tool-calculator",
|
|
598
|
-
state: "output-available",
|
|
599
|
-
toolCallId: "call_123",
|
|
600
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
601
|
-
output: { result: 5 },
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
const textPart = combined[0].parts.find((p) => p.type === "text");
|
|
605
|
-
expect(textPart).toMatchObject({
|
|
606
|
-
type: "text",
|
|
607
|
-
text: "The result is 5.",
|
|
608
|
-
state: "done",
|
|
609
|
-
});
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it("preserves separate messages with different roles", () => {
|
|
613
|
-
const userMessage = {
|
|
614
|
-
id: "user1",
|
|
615
|
-
key: "user1-key",
|
|
616
|
-
_creationTime: Date.now(),
|
|
617
|
-
order: 1,
|
|
618
|
-
stepOrder: 0,
|
|
619
|
-
status: "success" as const,
|
|
620
|
-
role: "user" as const,
|
|
621
|
-
text: "Calculate 2 + 3",
|
|
622
|
-
parts: [{ type: "text" as const, text: "Calculate 2 + 3" }],
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
const assistantMessage = {
|
|
626
|
-
id: "assistant1",
|
|
627
|
-
key: "assistant1-key",
|
|
628
|
-
_creationTime: Date.now() + 1,
|
|
629
|
-
order: 2,
|
|
630
|
-
stepOrder: 0,
|
|
631
|
-
status: "success" as const,
|
|
632
|
-
role: "assistant" as const,
|
|
633
|
-
text: "The result is 5.",
|
|
634
|
-
parts: [
|
|
635
|
-
{
|
|
636
|
-
type: "text" as const,
|
|
637
|
-
text: "The result is 5.",
|
|
638
|
-
state: "done" as const,
|
|
639
|
-
},
|
|
640
|
-
],
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
const combined = combineUIMessages([userMessage, assistantMessage]);
|
|
644
|
-
|
|
645
|
-
expect(combined).toHaveLength(2);
|
|
646
|
-
expect(combined[0]).toEqual(userMessage);
|
|
647
|
-
expect(combined[1]).toEqual(assistantMessage);
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
it("combines multiple tool calls across pages", () => {
|
|
651
|
-
const message1 = {
|
|
652
|
-
id: "msg1",
|
|
653
|
-
key: "msg1-key",
|
|
654
|
-
_creationTime: Date.now(),
|
|
655
|
-
order: 1,
|
|
656
|
-
stepOrder: 1,
|
|
657
|
-
status: "success" as const,
|
|
658
|
-
role: "assistant" as const,
|
|
659
|
-
text: "",
|
|
660
|
-
parts: [
|
|
661
|
-
{
|
|
662
|
-
type: "dynamic-tool" as const,
|
|
663
|
-
state: "input-available" as const,
|
|
664
|
-
toolCallId: "call_1",
|
|
665
|
-
toolName: "calculator",
|
|
666
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
667
|
-
},
|
|
668
|
-
{
|
|
669
|
-
type: "dynamic-tool" as const,
|
|
670
|
-
state: "input-available" as const,
|
|
671
|
-
toolCallId: "call_2",
|
|
672
|
-
toolName: "formatter",
|
|
673
|
-
input: { text: "result" },
|
|
674
|
-
},
|
|
675
|
-
],
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
const message2 = {
|
|
679
|
-
id: "msg2",
|
|
680
|
-
key: "msg2-key",
|
|
681
|
-
_creationTime: Date.now() + 1,
|
|
682
|
-
order: 1,
|
|
683
|
-
stepOrder: 2,
|
|
684
|
-
status: "success" as const,
|
|
685
|
-
role: "assistant" as const,
|
|
686
|
-
text: "The formatted result is: 5",
|
|
687
|
-
parts: [
|
|
688
|
-
{
|
|
689
|
-
type: "tool-calculator" as const,
|
|
690
|
-
state: "output-available" as const,
|
|
691
|
-
toolCallId: "call_1",
|
|
692
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
693
|
-
output: { result: 5 },
|
|
694
|
-
},
|
|
695
|
-
{
|
|
696
|
-
type: "tool-formatter" as const,
|
|
697
|
-
state: "output-available" as const,
|
|
698
|
-
toolCallId: "call_2",
|
|
699
|
-
input: { text: "result" },
|
|
700
|
-
output: { formatted: "The formatted result is: 5" },
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
type: "text" as const,
|
|
704
|
-
text: "The formatted result is: 5",
|
|
705
|
-
state: "done" as const,
|
|
706
|
-
},
|
|
707
|
-
],
|
|
708
|
-
};
|
|
709
|
-
|
|
710
|
-
const combined = combineUIMessages([message1, message2]);
|
|
711
|
-
|
|
712
|
-
expect(combined).toHaveLength(1);
|
|
713
|
-
expect(combined[0].role).toBe("assistant");
|
|
714
|
-
expect(combined[0].text).toBe("The formatted result is: 5");
|
|
715
|
-
expect(combined[0].parts).toHaveLength(3);
|
|
716
|
-
|
|
717
|
-
const calculatorPart = combined[0].parts.find(
|
|
718
|
-
(p) =>
|
|
719
|
-
p.type === "tool-calculator" &&
|
|
720
|
-
"toolCallId" in p &&
|
|
721
|
-
p.toolCallId === "call_1",
|
|
722
|
-
);
|
|
723
|
-
expect(calculatorPart).toMatchObject({
|
|
724
|
-
type: "tool-calculator",
|
|
725
|
-
state: "output-available",
|
|
726
|
-
toolCallId: "call_1",
|
|
727
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
728
|
-
output: { result: 5 },
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
const formatterPart = combined[0].parts.find(
|
|
732
|
-
(p) =>
|
|
733
|
-
p.type === "tool-formatter" &&
|
|
734
|
-
"toolCallId" in p &&
|
|
735
|
-
p.toolCallId === "call_2",
|
|
736
|
-
);
|
|
737
|
-
expect(formatterPart).toMatchObject({
|
|
738
|
-
type: "tool-formatter",
|
|
739
|
-
state: "output-available",
|
|
740
|
-
toolCallId: "call_2",
|
|
741
|
-
input: { text: "result" },
|
|
742
|
-
output: { formatted: "The formatted result is: 5" },
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it("handles tool call without corresponding output", () => {
|
|
747
|
-
const message1 = {
|
|
748
|
-
id: "msg1",
|
|
749
|
-
key: "msg1-key",
|
|
750
|
-
_creationTime: Date.now(),
|
|
751
|
-
order: 1,
|
|
752
|
-
stepOrder: 1,
|
|
753
|
-
status: "success" as const,
|
|
754
|
-
role: "assistant" as const,
|
|
755
|
-
text: "",
|
|
756
|
-
parts: [
|
|
757
|
-
{
|
|
758
|
-
type: "dynamic-tool" as const,
|
|
759
|
-
state: "input-available" as const,
|
|
760
|
-
toolCallId: "call_orphan",
|
|
761
|
-
toolName: "calculator",
|
|
762
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
763
|
-
},
|
|
764
|
-
],
|
|
765
|
-
};
|
|
766
|
-
|
|
767
|
-
const message2 = {
|
|
768
|
-
id: "msg2",
|
|
769
|
-
key: "msg2-key",
|
|
770
|
-
_creationTime: Date.now() + 1,
|
|
771
|
-
order: 1,
|
|
772
|
-
stepOrder: 2,
|
|
773
|
-
status: "success" as const,
|
|
774
|
-
role: "assistant" as const,
|
|
775
|
-
text: "Still processing...",
|
|
776
|
-
parts: [
|
|
777
|
-
{
|
|
778
|
-
type: "text" as const,
|
|
779
|
-
text: "Still processing...",
|
|
780
|
-
state: "done" as const,
|
|
781
|
-
},
|
|
782
|
-
],
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
const combined = combineUIMessages([message1, message2]);
|
|
786
|
-
|
|
787
|
-
expect(combined).toHaveLength(1);
|
|
788
|
-
expect(combined[0].role).toBe("assistant");
|
|
789
|
-
expect(combined[0].text).toBe("Still processing...");
|
|
790
|
-
expect(combined[0].parts).toHaveLength(2);
|
|
791
|
-
|
|
792
|
-
const toolPart = combined[0].parts.find((p) => p.type === "dynamic-tool");
|
|
793
|
-
expect(toolPart).toMatchObject({
|
|
794
|
-
type: "dynamic-tool",
|
|
795
|
-
state: "input-available",
|
|
796
|
-
toolCallId: "call_orphan",
|
|
797
|
-
toolName: "calculator",
|
|
798
|
-
input: { operation: "add", a: 2, b: 3 },
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
const textPart = combined[0].parts.find((p) => p.type === "text");
|
|
802
|
-
expect(textPart).toMatchObject({
|
|
803
|
-
type: "text",
|
|
804
|
-
text: "Still processing...",
|
|
805
|
-
state: "done",
|
|
806
|
-
});
|
|
807
|
-
});
|
|
808
|
-
});
|
package/src/deltas.ts
CHANGED
|
@@ -529,66 +529,3 @@ function mergeProviderMetadata(
|
|
|
529
529
|
}
|
|
530
530
|
return merged;
|
|
531
531
|
}
|
|
532
|
-
|
|
533
|
-
export function combineUIMessages(messages: UIMessage[]): UIMessage[] {
|
|
534
|
-
const combined = messages.reduce((acc, message) => {
|
|
535
|
-
if (!acc.length) {
|
|
536
|
-
return [message];
|
|
537
|
-
}
|
|
538
|
-
const previous = acc.at(-1)!;
|
|
539
|
-
if (
|
|
540
|
-
message.order !== previous.order ||
|
|
541
|
-
previous.role !== message.role ||
|
|
542
|
-
message.role !== "assistant"
|
|
543
|
-
) {
|
|
544
|
-
acc.push(message);
|
|
545
|
-
return acc;
|
|
546
|
-
}
|
|
547
|
-
// We will replace it with a combined message
|
|
548
|
-
acc.pop();
|
|
549
|
-
const newParts = [...previous.parts];
|
|
550
|
-
for (const part of message.parts) {
|
|
551
|
-
const toolCallId = getToolCallId(part);
|
|
552
|
-
if (!toolCallId) {
|
|
553
|
-
newParts.push(part);
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
const previousPartIndex = newParts.findIndex(
|
|
557
|
-
(p) => getToolCallId(p) === toolCallId,
|
|
558
|
-
);
|
|
559
|
-
const previousPart = newParts.splice(previousPartIndex, 1)[0];
|
|
560
|
-
if (!previousPart) {
|
|
561
|
-
newParts.push(part);
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
newParts.push(mergeParts(previousPart, part));
|
|
565
|
-
}
|
|
566
|
-
acc.push({
|
|
567
|
-
...previous,
|
|
568
|
-
...pick(message, ["status", "metadata", "agentName"]),
|
|
569
|
-
parts: newParts,
|
|
570
|
-
text: joinText(newParts),
|
|
571
|
-
});
|
|
572
|
-
return acc;
|
|
573
|
-
}, [] as UIMessage[]);
|
|
574
|
-
return combined;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function getToolCallId(
|
|
578
|
-
part: UIMessage["parts"][number] & { toolCallId?: string },
|
|
579
|
-
) {
|
|
580
|
-
return part.toolCallId;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function mergeParts(
|
|
584
|
-
previousPart: UIMessage["parts"][number],
|
|
585
|
-
part: UIMessage["parts"][number],
|
|
586
|
-
): UIMessage["parts"][number] {
|
|
587
|
-
const merged: Record<string, unknown> = { ...previousPart };
|
|
588
|
-
for (const [key, value] of Object.entries(part)) {
|
|
589
|
-
if (value !== undefined) {
|
|
590
|
-
merged[key] = value;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return merged as ToolUIPart | DynamicToolUIPart;
|
|
594
|
-
}
|
|
@@ -19,10 +19,13 @@ import { useMemo } from "react";
|
|
|
19
19
|
import type { SyncStreamsReturnValue } from "../client/types.js";
|
|
20
20
|
import type { StreamArgs } from "../validators.js";
|
|
21
21
|
import type { StreamQuery } from "./types.js";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
type UIMessage,
|
|
24
|
+
type UIStatus,
|
|
25
|
+
combineUIMessages,
|
|
26
|
+
} from "../UIMessages.js";
|
|
23
27
|
import { sorted } from "../shared.js";
|
|
24
28
|
import { useStreamingUIMessages } from "./useStreamingUIMessages.js";
|
|
25
|
-
import { combineUIMessages } from "../deltas.js";
|
|
26
29
|
|
|
27
30
|
export type UIMessageLike = {
|
|
28
31
|
order: number;
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TestConvex } from "convex-test";
|
|
2
|
+
import type { GenericSchema, SchemaDefinition } from "convex/server";
|
|
3
|
+
import schema from "./component/schema.js";
|
|
4
|
+
const modules = import.meta.glob("./component/**/*.ts");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register the component with the test convex instance.
|
|
8
|
+
* @param t - The test convex instance, e.g. from calling `convexTest`.
|
|
9
|
+
* @param name - The name of the component, as registered in convex.config.ts.
|
|
10
|
+
*/
|
|
11
|
+
function register(
|
|
12
|
+
t: TestConvex<SchemaDefinition<GenericSchema, boolean>>,
|
|
13
|
+
name: string = "agent",
|
|
14
|
+
) {
|
|
15
|
+
t.registerComponent(name, schema, modules);
|
|
16
|
+
}
|
|
17
|
+
export default { register, schema, modules };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=_ignore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_ignore.d.ts","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_ignore.js","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":";AAAA,kEAAkE"}
|