@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +153 -0
- package/README.md +32 -27
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +546 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +126 -27
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +67 -12
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/defaultComponent.d.ts +11 -0
- package/dist/client/defaultComponent.d.ts.map +1 -0
- package/dist/client/defaultComponent.js +7 -0
- package/dist/client/defaultComponent.js.map +1 -0
- package/dist/client/definePlaygroundAPI.d.ts +1335 -204
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +52 -28
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +20 -7
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +68 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +1116 -978
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +332 -747
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +461 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +106 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/mockModel.d.ts +42 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +182 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +58 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +350 -39
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +350 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +84 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +185 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streamText.d.ts +46 -0
- package/dist/client/streamText.d.ts.map +1 -0
- package/dist/client/streamText.js +117 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3716 -32
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +161 -59
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/threads.d.ts +46 -0
- package/dist/client/threads.d.ts.map +1 -0
- package/dist/client/threads.js +49 -0
- package/dist/client/threads.js.map +1 -0
- package/dist/client/types.d.ts +266 -128
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.d.ts +4 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +21 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +24 -2178
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js +10 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3120 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +4 -18
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +9 -5
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/files.d.ts +16 -10
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +2578 -366
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +397 -154
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +5697 -3584
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +18 -41
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +39 -339
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +114 -73
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +13 -13
- package/dist/component/users.d.ts +7 -7
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/component/vector/index.d.ts.map +1 -1
- package/dist/component/vector/index.js +1 -3
- package/dist/component/vector/index.js.map +1 -1
- package/dist/deltas.d.ts +43 -0
- package/dist/deltas.d.ts.map +1 -0
- package/dist/deltas.js +446 -0
- package/dist/deltas.js.map +1 -0
- package/dist/mapping.d.ts +38 -20
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +365 -97
- package/dist/mapping.js.map +1 -1
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/index.d.ts +5 -77
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -160
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts +36 -3
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +35 -9
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/types.d.ts +4 -18
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.d.ts +10 -0
- package/dist/react/useDeltaStreams.d.ts.map +1 -0
- package/dist/react/useDeltaStreams.js +106 -0
- package/dist/react/useDeltaStreams.js.map +1 -0
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +32 -15
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/react/useStreamingUIMessages.d.ts +22 -0
- package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
- package/dist/react/useStreamingUIMessages.js +92 -0
- package/dist/react/useStreamingUIMessages.js.map +1 -0
- package/dist/react/useThreadMessages.d.ts +104 -0
- package/dist/react/useThreadMessages.d.ts.map +1 -0
- package/dist/react/useThreadMessages.js +148 -0
- package/dist/react/useThreadMessages.js.map +1 -0
- package/dist/react/useUIMessages.d.ts +96 -0
- package/dist/react/useUIMessages.d.ts.map +1 -0
- package/dist/react/useUIMessages.js +108 -0
- package/dist/react/useUIMessages.js.map +1 -0
- package/dist/shared.d.ts +20 -4
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +45 -8
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +22981 -5666
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +245 -137
- package/dist/validators.js.map +1 -1
- package/package.json +101 -51
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +739 -0
- package/src/client/approval.test.ts +350 -0
- package/src/client/createTool.ts +291 -76
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +67 -31
- package/src/client/files.ts +100 -20
- package/src/client/index.test.ts +40 -85
- package/src/client/index.ts +638 -1289
- package/src/client/messages.ts +237 -0
- package/src/client/mockModel.ts +252 -0
- package/src/client/saveInputMessages.test.ts +583 -0
- package/src/client/saveInputMessages.ts +101 -0
- package/src/client/search.test.ts +1207 -0
- package/src/client/search.ts +581 -70
- package/src/client/start.ts +327 -0
- package/src/client/streamText.ts +187 -0
- package/src/client/streaming.test.ts +186 -0
- package/src/client/streaming.ts +241 -97
- package/src/client/threads.ts +83 -0
- package/src/client/types.ts +370 -219
- package/src/client/utils.ts +27 -0
- package/src/component/_generated/api.ts +64 -0
- package/src/component/_generated/component.ts +4902 -0
- package/src/component/_generated/{server.d.ts → server.ts} +33 -21
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +195 -51
- package/src/component/messages.ts +500 -201
- package/src/component/schema.ts +20 -46
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +184 -83
- package/src/component/users.test.ts +0 -1
- package/src/component/vector/index.ts +1 -3
- package/src/deltas.test.ts +626 -0
- package/src/deltas.ts +569 -0
- package/src/fromUIMessages.test.ts +497 -0
- package/src/mapping.test.ts +180 -6
- package/src/mapping.ts +479 -162
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/index.ts +10 -230
- package/src/react/optimisticallySendMessage.ts +55 -12
- package/src/react/types.ts +6 -39
- package/src/react/useDeltaStreams.ts +160 -0
- package/src/react/useSmoothText.ts +56 -36
- package/src/react/useStreamingUIMessages.ts +143 -0
- package/src/react/useThreadMessages.ts +262 -0
- package/src/react/useUIMessages.test.ts +255 -0
- package/src/react/useUIMessages.ts +195 -0
- package/src/shared.ts +88 -12
- package/src/test.ts +18 -0
- package/src/toUIMessages.test.ts +1269 -0
- package/src/validators.test.ts +18 -19
- package/src/validators.ts +325 -185
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- package/dist/client/listMessages.d.ts +0 -22
- package/dist/client/listMessages.d.ts.map +0 -1
- package/dist/client/listMessages.js +0 -25
- package/dist/client/listMessages.js.map +0 -1
- package/dist/package.json +0 -3
- package/dist/react/deltas.d.ts +0 -26
- package/dist/react/deltas.d.ts.map +0 -1
- package/dist/react/deltas.js +0 -384
- package/dist/react/deltas.js.map +0 -1
- package/dist/react/toUIMessages.d.ts +0 -15
- package/dist/react/toUIMessages.d.ts.map +0 -1
- package/dist/react/toUIMessages.js +0 -211
- package/dist/react/toUIMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
- package/src/component/_generated/api.d.ts +0 -2202
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/src/react/deltas.test.ts +0 -315
- package/src/react/deltas.ts +0 -478
- package/src/react/toUIMessages.test.ts +0 -420
- package/src/react/toUIMessages.ts +0 -253
- package/src/vitest.config.ts +0 -7
- /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { MessageDoc } from "./client/index.js";
|
|
3
|
+
import type { UIMessage } from "./UIMessages.js";
|
|
4
|
+
import { fromUIMessages, toUIMessages } from "./UIMessages.js";
|
|
5
|
+
|
|
6
|
+
// Helper to create a base message doc
|
|
7
|
+
function baseMessageDoc<T = unknown>(
|
|
8
|
+
overrides: Partial<MessageDoc & { streaming?: boolean; metadata?: T }> = {},
|
|
9
|
+
): MessageDoc & { streaming?: boolean; metadata?: T } {
|
|
10
|
+
return {
|
|
11
|
+
_id: "msg1",
|
|
12
|
+
_creationTime: Date.now(),
|
|
13
|
+
order: 0,
|
|
14
|
+
stepOrder: 0,
|
|
15
|
+
status: "success",
|
|
16
|
+
threadId: "thread1",
|
|
17
|
+
tool: false,
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("fromUIMessages round-trip tests", () => {
|
|
23
|
+
it("preserves essential data for simple user message", async () => {
|
|
24
|
+
const originalMessages = [
|
|
25
|
+
baseMessageDoc({
|
|
26
|
+
message: {
|
|
27
|
+
role: "user",
|
|
28
|
+
content: "Hello world!",
|
|
29
|
+
},
|
|
30
|
+
text: "Hello world!",
|
|
31
|
+
}),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
35
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
36
|
+
threadId: "thread1",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(uiMessages).toHaveLength(1);
|
|
40
|
+
expect(uiMessages[0].role).toBe("user");
|
|
41
|
+
expect(uiMessages[0].text).toBe("Hello world!");
|
|
42
|
+
|
|
43
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
44
|
+
expect(backToMessageDocs[0].text).toBe("Hello world!");
|
|
45
|
+
expect(backToMessageDocs[0].threadId).toBe("thread1");
|
|
46
|
+
|
|
47
|
+
// Content gets normalized to array format
|
|
48
|
+
expect(Array.isArray(backToMessageDocs[0].message?.content)).toBe(true);
|
|
49
|
+
if (Array.isArray(backToMessageDocs[0].message?.content)) {
|
|
50
|
+
expect(backToMessageDocs[0].message.content[0]).toMatchObject({
|
|
51
|
+
type: "text",
|
|
52
|
+
text: "Hello world!",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("preserves essential data for assistant message", async () => {
|
|
58
|
+
const originalMessages = [
|
|
59
|
+
baseMessageDoc({
|
|
60
|
+
message: {
|
|
61
|
+
role: "assistant",
|
|
62
|
+
content: "Hi there! How can I help?",
|
|
63
|
+
},
|
|
64
|
+
text: "Hi there! How can I help?",
|
|
65
|
+
}),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
69
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
70
|
+
threadId: "thread1",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(uiMessages).toHaveLength(1);
|
|
74
|
+
expect(uiMessages[0].role).toBe("assistant");
|
|
75
|
+
expect(uiMessages[0].text).toBe("Hi there! How can I help?");
|
|
76
|
+
|
|
77
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
78
|
+
expect(backToMessageDocs[0].text).toBe("Hi there! How can I help?");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("preserves system messages correctly", async () => {
|
|
82
|
+
const originalMessages = [
|
|
83
|
+
baseMessageDoc({
|
|
84
|
+
message: {
|
|
85
|
+
role: "system",
|
|
86
|
+
content: "You are a helpful assistant.",
|
|
87
|
+
},
|
|
88
|
+
text: "You are a helpful assistant.",
|
|
89
|
+
}),
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
93
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
94
|
+
threadId: "thread1",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(uiMessages).toHaveLength(1);
|
|
98
|
+
expect(uiMessages[0].role).toBe("system");
|
|
99
|
+
expect(uiMessages[0].text).toBe("You are a helpful assistant.");
|
|
100
|
+
|
|
101
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
102
|
+
expect(backToMessageDocs[0].text).toBe("You are a helpful assistant.");
|
|
103
|
+
expect(backToMessageDocs[0].message?.role).toBe("system");
|
|
104
|
+
|
|
105
|
+
// System content stays as string
|
|
106
|
+
expect(backToMessageDocs[0].message?.content).toBe(
|
|
107
|
+
"You are a helpful assistant.",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("preserves reasoning in assistant messages", async () => {
|
|
112
|
+
const originalMessages = [
|
|
113
|
+
baseMessageDoc({
|
|
114
|
+
message: {
|
|
115
|
+
role: "assistant",
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: "reasoning",
|
|
119
|
+
text: "Let me think about this...",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: "Here's my response.",
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
text: "Here's my response.",
|
|
128
|
+
reasoning: "Let me think about this...",
|
|
129
|
+
}),
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
133
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
134
|
+
threadId: "thread1",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(uiMessages).toHaveLength(1);
|
|
138
|
+
expect(uiMessages[0].text).toBe("Here's my response.");
|
|
139
|
+
|
|
140
|
+
// Check that reasoning parts are preserved in UI message
|
|
141
|
+
const reasoningParts = uiMessages[0].parts.filter(
|
|
142
|
+
(part) => part.type === "reasoning",
|
|
143
|
+
);
|
|
144
|
+
expect(reasoningParts).toHaveLength(1);
|
|
145
|
+
expect(reasoningParts[0]).toMatchObject({
|
|
146
|
+
type: "reasoning",
|
|
147
|
+
text: "Let me think about this...",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
151
|
+
expect(backToMessageDocs[0].text).toBe("Here's my response.");
|
|
152
|
+
expect(backToMessageDocs[0].reasoning).toBe("Let me think about this...");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("handles tool calls and groups them correctly", async () => {
|
|
156
|
+
// Tool calls get grouped into single UI message but expanded back to multiple message docs
|
|
157
|
+
const originalMessages = [
|
|
158
|
+
baseMessageDoc({
|
|
159
|
+
_id: "msg1",
|
|
160
|
+
order: 1,
|
|
161
|
+
stepOrder: 1,
|
|
162
|
+
message: {
|
|
163
|
+
role: "assistant",
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: "tool-call",
|
|
167
|
+
toolName: "calculator",
|
|
168
|
+
toolCallId: "call1",
|
|
169
|
+
input: { operation: "add", a: 2, b: 3 },
|
|
170
|
+
args: { operation: "add", a: 2, b: 3 },
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
tool: true,
|
|
175
|
+
}),
|
|
176
|
+
baseMessageDoc({
|
|
177
|
+
_id: "msg2",
|
|
178
|
+
order: 1,
|
|
179
|
+
stepOrder: 2,
|
|
180
|
+
message: {
|
|
181
|
+
role: "tool",
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "tool-result",
|
|
185
|
+
toolCallId: "call1",
|
|
186
|
+
toolName: "calculator",
|
|
187
|
+
output: {
|
|
188
|
+
type: "json",
|
|
189
|
+
value: { result: 5 },
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
tool: true,
|
|
195
|
+
}),
|
|
196
|
+
];
|
|
197
|
+
const toTest = [originalMessages, [...originalMessages].reverse()];
|
|
198
|
+
for (const messages of toTest) {
|
|
199
|
+
const uiMessages = toUIMessages(messages);
|
|
200
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
201
|
+
threadId: "thread1",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Should be grouped into single UI message
|
|
205
|
+
expect(uiMessages).toHaveLength(1);
|
|
206
|
+
const uiMessage = uiMessages[0];
|
|
207
|
+
expect(uiMessage.role).toBe("assistant");
|
|
208
|
+
expect(uiMessage.id).toBe("msg1");
|
|
209
|
+
|
|
210
|
+
// Check tool parts exist
|
|
211
|
+
const toolParts = uiMessage.parts.filter(
|
|
212
|
+
(part) => part.type === "tool-calculator",
|
|
213
|
+
);
|
|
214
|
+
expect(toolParts).toHaveLength(1);
|
|
215
|
+
expect(toolParts[0]).toMatchObject({
|
|
216
|
+
type: "tool-calculator",
|
|
217
|
+
toolCallId: "call1",
|
|
218
|
+
state: "output-available",
|
|
219
|
+
input: { operation: "add", a: 2, b: 3 },
|
|
220
|
+
output: { result: 5 },
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Should expand back to multiple message docs
|
|
224
|
+
expect(backToMessageDocs.length).toBeGreaterThanOrEqual(1);
|
|
225
|
+
|
|
226
|
+
// Check that tool information is preserved
|
|
227
|
+
const toolMessages = backToMessageDocs.filter((msg) => msg.tool);
|
|
228
|
+
expect(toolMessages.length).toBeGreaterThan(0);
|
|
229
|
+
expect(toolMessages[0].stepOrder).toBe(1);
|
|
230
|
+
expect(toolMessages[1].stepOrder).toBe(2);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("preserves file attachments in user messages", async () => {
|
|
235
|
+
const originalMessages = [
|
|
236
|
+
baseMessageDoc({
|
|
237
|
+
message: {
|
|
238
|
+
role: "user",
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: "What's in this image?",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
type: "file",
|
|
246
|
+
mimeType: "image/png",
|
|
247
|
+
data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
text: "What's in this image?",
|
|
252
|
+
}),
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
256
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
257
|
+
threadId: "thread1",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(uiMessages).toHaveLength(1);
|
|
261
|
+
expect(uiMessages[0].role).toBe("user");
|
|
262
|
+
expect(uiMessages[0].text).toBe("What's in this image?");
|
|
263
|
+
|
|
264
|
+
// Check file parts exist in UI message
|
|
265
|
+
const fileParts = uiMessages[0].parts.filter(
|
|
266
|
+
(part) => part.type === "file",
|
|
267
|
+
);
|
|
268
|
+
expect(fileParts).toHaveLength(1);
|
|
269
|
+
|
|
270
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
271
|
+
expect(backToMessageDocs[0].text).toBe("What's in this image?");
|
|
272
|
+
|
|
273
|
+
// Check file content is preserved
|
|
274
|
+
const content = backToMessageDocs[0].message?.content;
|
|
275
|
+
expect(Array.isArray(content)).toBe(true);
|
|
276
|
+
if (Array.isArray(content)) {
|
|
277
|
+
const fileContent = content.find((c) => c.type === "file");
|
|
278
|
+
expect(fileContent).toBeDefined();
|
|
279
|
+
expect(fileContent).toMatchObject({
|
|
280
|
+
type: "file",
|
|
281
|
+
mediaType: "image/png",
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("preserves sources correctly", async () => {
|
|
287
|
+
const originalMessages = [
|
|
288
|
+
baseMessageDoc({
|
|
289
|
+
message: {
|
|
290
|
+
role: "assistant",
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: "I found some relevant sources.",
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
text: "I found some relevant sources.",
|
|
299
|
+
sources: [
|
|
300
|
+
{
|
|
301
|
+
type: "source",
|
|
302
|
+
sourceType: "url",
|
|
303
|
+
id: "source1",
|
|
304
|
+
url: "https://example.com",
|
|
305
|
+
title: "Example Source",
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
type: "source",
|
|
309
|
+
sourceType: "document",
|
|
310
|
+
id: "source2",
|
|
311
|
+
mediaType: "application/pdf",
|
|
312
|
+
title: "Document Source",
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
}),
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
319
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
320
|
+
threadId: "thread1",
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
expect(uiMessages).toHaveLength(1);
|
|
324
|
+
|
|
325
|
+
// Check source parts exist in UI message
|
|
326
|
+
const sourceParts = uiMessages[0].parts.filter(
|
|
327
|
+
(part) => part.type === "source-url" || part.type === "source-document",
|
|
328
|
+
);
|
|
329
|
+
expect(sourceParts).toHaveLength(2);
|
|
330
|
+
|
|
331
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
332
|
+
expect(backToMessageDocs[0].sources).toHaveLength(2);
|
|
333
|
+
expect(backToMessageDocs[0].sources![0]).toMatchObject({
|
|
334
|
+
type: "source",
|
|
335
|
+
sourceType: "url",
|
|
336
|
+
id: "source1",
|
|
337
|
+
url: "https://example.com",
|
|
338
|
+
title: "Example Source",
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("preserves metadata when provided", async () => {
|
|
343
|
+
const testMetadata = {
|
|
344
|
+
customField: "customValue",
|
|
345
|
+
timestamp: Date.now(),
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const originalMessages = [
|
|
349
|
+
baseMessageDoc({
|
|
350
|
+
message: {
|
|
351
|
+
role: "user",
|
|
352
|
+
content: "Test message",
|
|
353
|
+
},
|
|
354
|
+
text: "Test message",
|
|
355
|
+
metadata: testMetadata,
|
|
356
|
+
}),
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
360
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
361
|
+
threadId: "thread1",
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(uiMessages).toHaveLength(1);
|
|
365
|
+
expect(uiMessages[0].metadata).toEqual(testMetadata);
|
|
366
|
+
|
|
367
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
368
|
+
expect(backToMessageDocs[0].metadata).toEqual(testMetadata);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("handles streaming status correctly", async () => {
|
|
372
|
+
const originalMessages = [
|
|
373
|
+
baseMessageDoc({
|
|
374
|
+
message: {
|
|
375
|
+
role: "assistant",
|
|
376
|
+
content: "Streaming response...",
|
|
377
|
+
},
|
|
378
|
+
text: "Streaming response...",
|
|
379
|
+
streaming: true,
|
|
380
|
+
status: "pending",
|
|
381
|
+
}),
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
const uiMessages = toUIMessages(originalMessages);
|
|
385
|
+
const backToMessageDocs = await fromUIMessages(uiMessages, {
|
|
386
|
+
threadId: "thread1",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
expect(uiMessages).toHaveLength(1);
|
|
390
|
+
expect(uiMessages[0].status).toBe("streaming");
|
|
391
|
+
|
|
392
|
+
expect(backToMessageDocs).toHaveLength(1);
|
|
393
|
+
expect(backToMessageDocs[0].streaming).toBe(true);
|
|
394
|
+
expect(backToMessageDocs[0].status).toBe("pending");
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("fromUIMessages functionality tests", () => {
|
|
399
|
+
it("handles empty messages array", async () => {
|
|
400
|
+
const uiMessages: UIMessage[] = [];
|
|
401
|
+
const result = await fromUIMessages(uiMessages, { threadId: "thread1" });
|
|
402
|
+
expect(result).toHaveLength(0);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("correctly assigns thread ID", async () => {
|
|
406
|
+
const uiMessage: UIMessage = {
|
|
407
|
+
id: "test-id",
|
|
408
|
+
_creationTime: Date.now(),
|
|
409
|
+
order: 0,
|
|
410
|
+
stepOrder: 0,
|
|
411
|
+
status: "success",
|
|
412
|
+
key: "test-key",
|
|
413
|
+
text: "Hello",
|
|
414
|
+
role: "user",
|
|
415
|
+
parts: [{ type: "text", text: "Hello" }],
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const result = await fromUIMessages([uiMessage], {
|
|
419
|
+
threadId: "custom-thread-id",
|
|
420
|
+
});
|
|
421
|
+
expect(result).toHaveLength(1);
|
|
422
|
+
expect(result[0].threadId).toBe("custom-thread-id");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("correctly determines tool status", async () => {
|
|
426
|
+
const toolUIMessage: UIMessage = {
|
|
427
|
+
id: "tool-id",
|
|
428
|
+
_creationTime: Date.now(),
|
|
429
|
+
order: 0,
|
|
430
|
+
stepOrder: 0,
|
|
431
|
+
status: "success",
|
|
432
|
+
key: "tool-key",
|
|
433
|
+
text: "",
|
|
434
|
+
role: "assistant",
|
|
435
|
+
parts: [
|
|
436
|
+
{
|
|
437
|
+
type: "tool-calculator",
|
|
438
|
+
toolCallId: "call1",
|
|
439
|
+
input: { a: 1, b: 2 },
|
|
440
|
+
state: "output-available",
|
|
441
|
+
output: { result: 3 },
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const result = await fromUIMessages([toolUIMessage], {
|
|
447
|
+
threadId: "thread1",
|
|
448
|
+
});
|
|
449
|
+
expect(result.length).toBeGreaterThan(0);
|
|
450
|
+
|
|
451
|
+
// Should have tool messages
|
|
452
|
+
const toolMessages = result.filter((msg) => msg.tool);
|
|
453
|
+
expect(toolMessages.length).toBeGreaterThan(0);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it("handles tool calls without responses", async () => {
|
|
457
|
+
const toolUIMessage: UIMessage = {
|
|
458
|
+
id: "tool-id",
|
|
459
|
+
_creationTime: Date.now(),
|
|
460
|
+
order: 0,
|
|
461
|
+
stepOrder: 0,
|
|
462
|
+
status: "success",
|
|
463
|
+
key: "tool-key",
|
|
464
|
+
text: "",
|
|
465
|
+
role: "assistant",
|
|
466
|
+
parts: [
|
|
467
|
+
{ type: "text", text: "Tool call" },
|
|
468
|
+
{
|
|
469
|
+
type: "tool-calculator",
|
|
470
|
+
toolCallId: "call1",
|
|
471
|
+
input: { a: 1, b: 2 },
|
|
472
|
+
state: "input-available",
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
const result = await fromUIMessages([toolUIMessage], {
|
|
478
|
+
threadId: "thread1",
|
|
479
|
+
});
|
|
480
|
+
expect(result.length).toBeGreaterThan(0);
|
|
481
|
+
|
|
482
|
+
// Should have tool messages
|
|
483
|
+
const toolMessages = result.filter((msg) => msg.tool);
|
|
484
|
+
expect(toolMessages.length).toBe(1);
|
|
485
|
+
expect(toolMessages[0].message?.role).toBe("assistant");
|
|
486
|
+
expect(toolMessages[0].message?.content[0]).toMatchObject({
|
|
487
|
+
type: "text",
|
|
488
|
+
text: "Tool call",
|
|
489
|
+
});
|
|
490
|
+
expect(toolMessages[0].message?.content[1]).toMatchObject({
|
|
491
|
+
args: { a: 1, b: 2 },
|
|
492
|
+
toolCallId: "call1",
|
|
493
|
+
toolName: "calculator",
|
|
494
|
+
type: "tool-call",
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|