@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.0

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 (233) hide show
  1. package/README.md +32 -27
  2. package/dist/UIMessages.d.ts +46 -0
  3. package/dist/UIMessages.d.ts.map +1 -0
  4. package/dist/UIMessages.js +546 -0
  5. package/dist/UIMessages.js.map +1 -0
  6. package/dist/client/createTool.d.ts +129 -27
  7. package/dist/client/createTool.d.ts.map +1 -1
  8. package/dist/client/createTool.js +66 -12
  9. package/dist/client/createTool.js.map +1 -1
  10. package/dist/client/defaultComponent.d.ts +11 -0
  11. package/dist/client/defaultComponent.d.ts.map +1 -0
  12. package/dist/client/defaultComponent.js +7 -0
  13. package/dist/client/defaultComponent.js.map +1 -0
  14. package/dist/client/definePlaygroundAPI.d.ts +1323 -192
  15. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  16. package/dist/client/definePlaygroundAPI.js +52 -28
  17. package/dist/client/definePlaygroundAPI.js.map +1 -1
  18. package/dist/client/files.d.ts +20 -7
  19. package/dist/client/files.d.ts.map +1 -1
  20. package/dist/client/files.js +68 -11
  21. package/dist/client/files.js.map +1 -1
  22. package/dist/client/index.d.ts +1056 -965
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +242 -748
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/client/messages.d.ts +461 -0
  27. package/dist/client/messages.d.ts.map +1 -0
  28. package/dist/client/messages.js +106 -0
  29. package/dist/client/messages.js.map +1 -0
  30. package/dist/client/mockModel.d.ts +42 -0
  31. package/dist/client/mockModel.d.ts.map +1 -0
  32. package/dist/client/mockModel.js +175 -0
  33. package/dist/client/mockModel.js.map +1 -0
  34. package/dist/client/saveInputMessages.d.ts +20 -0
  35. package/dist/client/saveInputMessages.d.ts.map +1 -0
  36. package/dist/client/saveInputMessages.js +58 -0
  37. package/dist/client/saveInputMessages.js.map +1 -0
  38. package/dist/client/search.d.ts +346 -35
  39. package/dist/client/search.d.ts.map +1 -1
  40. package/dist/client/search.js +350 -39
  41. package/dist/client/search.js.map +1 -1
  42. package/dist/client/start.d.ts +84 -0
  43. package/dist/client/start.d.ts.map +1 -0
  44. package/dist/client/start.js +171 -0
  45. package/dist/client/start.js.map +1 -0
  46. package/dist/client/streamText.d.ts +46 -0
  47. package/dist/client/streamText.d.ts.map +1 -0
  48. package/dist/client/streamText.js +93 -0
  49. package/dist/client/streamText.js.map +1 -0
  50. package/dist/client/streaming.d.ts +3705 -32
  51. package/dist/client/streaming.d.ts.map +1 -1
  52. package/dist/client/streaming.js +141 -59
  53. package/dist/client/streaming.js.map +1 -1
  54. package/dist/client/threads.d.ts +46 -0
  55. package/dist/client/threads.d.ts.map +1 -0
  56. package/dist/client/threads.js +49 -0
  57. package/dist/client/threads.js.map +1 -0
  58. package/dist/client/types.d.ts +265 -128
  59. package/dist/client/types.d.ts.map +1 -1
  60. package/dist/client/utils.d.ts +4 -0
  61. package/dist/client/utils.d.ts.map +1 -0
  62. package/dist/client/utils.js +21 -0
  63. package/dist/client/utils.js.map +1 -0
  64. package/dist/component/_generated/api.d.ts +24 -2178
  65. package/dist/component/_generated/api.d.ts.map +1 -1
  66. package/dist/component/_generated/api.js +10 -1
  67. package/dist/component/_generated/api.js.map +1 -1
  68. package/dist/component/_generated/component.d.ts +3119 -0
  69. package/dist/component/_generated/component.d.ts.map +1 -0
  70. package/dist/component/_generated/component.js +11 -0
  71. package/dist/component/_generated/component.js.map +1 -0
  72. package/dist/component/_generated/dataModel.d.ts +4 -18
  73. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  74. package/dist/component/_generated/dataModel.js +11 -0
  75. package/dist/component/_generated/dataModel.js.map +1 -0
  76. package/dist/component/_generated/server.d.ts +10 -38
  77. package/dist/component/_generated/server.d.ts.map +1 -1
  78. package/dist/component/_generated/server.js +9 -5
  79. package/dist/component/_generated/server.js.map +1 -1
  80. package/dist/component/files.d.ts +16 -10
  81. package/dist/component/files.d.ts.map +1 -1
  82. package/dist/component/files.js +10 -2
  83. package/dist/component/files.js.map +1 -1
  84. package/dist/component/messages.d.ts +2553 -342
  85. package/dist/component/messages.d.ts.map +1 -1
  86. package/dist/component/messages.js +387 -154
  87. package/dist/component/messages.js.map +1 -1
  88. package/dist/component/schema.d.ts +5697 -3584
  89. package/dist/component/schema.d.ts.map +1 -1
  90. package/dist/component/schema.js +18 -41
  91. package/dist/component/schema.js.map +1 -1
  92. package/dist/component/streams.d.ts +35 -335
  93. package/dist/component/streams.d.ts.map +1 -1
  94. package/dist/component/streams.js +114 -73
  95. package/dist/component/streams.js.map +1 -1
  96. package/dist/component/threads.d.ts +16 -16
  97. package/dist/component/users.d.ts +4 -4
  98. package/dist/component/vector/index.d.ts +1 -1
  99. package/dist/component/vector/index.d.ts.map +1 -1
  100. package/dist/component/vector/index.js +1 -3
  101. package/dist/component/vector/index.js.map +1 -1
  102. package/dist/deltas.d.ts +43 -0
  103. package/dist/deltas.d.ts.map +1 -0
  104. package/dist/deltas.js +447 -0
  105. package/dist/deltas.js.map +1 -0
  106. package/dist/mapping.d.ts +20 -20
  107. package/dist/mapping.d.ts.map +1 -1
  108. package/dist/mapping.js +313 -96
  109. package/dist/mapping.js.map +1 -1
  110. package/dist/react/SmoothText.d.ts +5 -0
  111. package/dist/react/SmoothText.d.ts.map +1 -0
  112. package/dist/react/SmoothText.js +6 -0
  113. package/dist/react/SmoothText.js.map +1 -0
  114. package/dist/react/index.d.ts +5 -77
  115. package/dist/react/index.d.ts.map +1 -1
  116. package/dist/react/index.js +6 -160
  117. package/dist/react/index.js.map +1 -1
  118. package/dist/react/optimisticallySendMessage.d.ts +36 -3
  119. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  120. package/dist/react/optimisticallySendMessage.js +35 -9
  121. package/dist/react/optimisticallySendMessage.js.map +1 -1
  122. package/dist/react/types.d.ts +4 -18
  123. package/dist/react/types.d.ts.map +1 -1
  124. package/dist/react/useDeltaStreams.d.ts +10 -0
  125. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  126. package/dist/react/useDeltaStreams.js +101 -0
  127. package/dist/react/useDeltaStreams.js.map +1 -0
  128. package/dist/react/useSmoothText.d.ts +13 -12
  129. package/dist/react/useSmoothText.d.ts.map +1 -1
  130. package/dist/react/useSmoothText.js +32 -15
  131. package/dist/react/useSmoothText.js.map +1 -1
  132. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  133. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  134. package/dist/react/useStreamingUIMessages.js +92 -0
  135. package/dist/react/useStreamingUIMessages.js.map +1 -0
  136. package/dist/react/useThreadMessages.d.ts +104 -0
  137. package/dist/react/useThreadMessages.d.ts.map +1 -0
  138. package/dist/react/useThreadMessages.js +148 -0
  139. package/dist/react/useThreadMessages.js.map +1 -0
  140. package/dist/react/useUIMessages.d.ts +96 -0
  141. package/dist/react/useUIMessages.d.ts.map +1 -0
  142. package/dist/react/useUIMessages.js +108 -0
  143. package/dist/react/useUIMessages.js.map +1 -0
  144. package/dist/shared.d.ts +20 -4
  145. package/dist/shared.d.ts.map +1 -1
  146. package/dist/shared.js +45 -8
  147. package/dist/shared.js.map +1 -1
  148. package/dist/validators.d.ts +22981 -5666
  149. package/dist/validators.d.ts.map +1 -1
  150. package/dist/validators.js +245 -137
  151. package/dist/validators.js.map +1 -1
  152. package/package.json +98 -50
  153. package/src/UIMessages.combineUIMessages.test.ts +239 -0
  154. package/src/UIMessages.test.ts +273 -0
  155. package/src/UIMessages.ts +739 -0
  156. package/src/client/createTool.ts +293 -76
  157. package/src/client/defaultComponent.ts +17 -0
  158. package/src/client/definePlaygroundAPI.ts +67 -31
  159. package/src/client/files.ts +100 -20
  160. package/src/client/index.test.ts +40 -85
  161. package/src/client/index.ts +520 -1290
  162. package/src/client/messages.ts +237 -0
  163. package/src/client/mockModel.ts +245 -0
  164. package/src/client/saveInputMessages.test.ts +583 -0
  165. package/src/client/saveInputMessages.ts +101 -0
  166. package/src/client/search.test.ts +1207 -0
  167. package/src/client/search.ts +577 -70
  168. package/src/client/start.ts +310 -0
  169. package/src/client/streamText.ts +163 -0
  170. package/src/client/streaming.test.ts +186 -0
  171. package/src/client/streaming.ts +219 -97
  172. package/src/client/threads.ts +83 -0
  173. package/src/client/types.ts +368 -219
  174. package/src/client/utils.ts +27 -0
  175. package/src/component/_generated/api.ts +64 -0
  176. package/src/component/_generated/component.ts +4913 -0
  177. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  178. package/src/component/files.ts +11 -2
  179. package/src/component/messages.test.ts +195 -51
  180. package/src/component/messages.ts +490 -201
  181. package/src/component/schema.ts +20 -46
  182. package/src/component/setup.test.ts +7 -0
  183. package/src/component/streams.ts +184 -83
  184. package/src/component/users.test.ts +0 -1
  185. package/src/component/vector/index.ts +1 -3
  186. package/src/deltas.test.ts +626 -0
  187. package/src/deltas.ts +570 -0
  188. package/src/fromUIMessages.test.ts +497 -0
  189. package/src/mapping.test.ts +103 -6
  190. package/src/mapping.ts +422 -161
  191. package/src/react/SmoothText.tsx +9 -0
  192. package/src/react/index.ts +10 -230
  193. package/src/react/optimisticallySendMessage.ts +55 -12
  194. package/src/react/types.ts +6 -39
  195. package/src/react/useDeltaStreams.ts +154 -0
  196. package/src/react/useSmoothText.ts +56 -36
  197. package/src/react/useStreamingUIMessages.ts +143 -0
  198. package/src/react/useThreadMessages.ts +262 -0
  199. package/src/react/useUIMessages.test.ts +255 -0
  200. package/src/react/useUIMessages.ts +195 -0
  201. package/src/shared.ts +88 -12
  202. package/src/test.ts +18 -0
  203. package/src/toUIMessages.test.ts +1269 -0
  204. package/src/validators.test.ts +18 -19
  205. package/src/validators.ts +325 -185
  206. package/dist/client/_generated/_ignore.d.ts +0 -1
  207. package/dist/client/_generated/_ignore.d.ts.map +0 -1
  208. package/dist/client/_generated/_ignore.js +0 -3
  209. package/dist/client/_generated/_ignore.js.map +0 -1
  210. package/dist/client/listMessages.d.ts +0 -22
  211. package/dist/client/listMessages.d.ts.map +0 -1
  212. package/dist/client/listMessages.js +0 -25
  213. package/dist/client/listMessages.js.map +0 -1
  214. package/dist/package.json +0 -3
  215. package/dist/react/deltas.d.ts +0 -26
  216. package/dist/react/deltas.d.ts.map +0 -1
  217. package/dist/react/deltas.js +0 -384
  218. package/dist/react/deltas.js.map +0 -1
  219. package/dist/react/toUIMessages.d.ts +0 -15
  220. package/dist/react/toUIMessages.d.ts.map +0 -1
  221. package/dist/react/toUIMessages.js +0 -211
  222. package/dist/react/toUIMessages.js.map +0 -1
  223. package/src/client/listMessages.ts +0 -38
  224. package/src/component/_generated/api.d.ts +0 -2202
  225. package/src/component/_generated/api.js +0 -23
  226. package/src/component/_generated/server.js +0 -90
  227. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  228. package/src/react/deltas.test.ts +0 -315
  229. package/src/react/deltas.ts +0 -478
  230. package/src/react/toUIMessages.test.ts +0 -420
  231. package/src/react/toUIMessages.ts +0 -253
  232. package/src/vitest.config.ts +0 -7
  233. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -1,420 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { toUIMessages } from "./toUIMessages.js";
3
- import type { MessageDoc } from "../client/index.js";
4
- import { assert } from "convex-helpers";
5
-
6
- // Helper to create a base message doc
7
- function baseMessageDoc(overrides: Partial<MessageDoc> = {}): MessageDoc {
8
- return {
9
- _id: "msg1",
10
- _creationTime: Date.now(),
11
- order: 1,
12
- stepOrder: 0,
13
- status: "success",
14
- threadId: "thread1",
15
- tool: false,
16
- ...overrides,
17
- };
18
- }
19
-
20
- describe("toUIMessages", () => {
21
- it("handles user message", () => {
22
- const messages = [
23
- baseMessageDoc({
24
- message: {
25
- role: "user",
26
- content: "Hello!",
27
- },
28
- text: "Hello!",
29
- }),
30
- ];
31
- const uiMessages = toUIMessages(messages);
32
- expect(uiMessages).toHaveLength(1);
33
- expect(uiMessages[0].role).toBe("user");
34
- expect(uiMessages[0].text).toBe("Hello!");
35
- expect(uiMessages[0].parts[0]).toEqual({ type: "text", text: "Hello!" });
36
- });
37
-
38
- it("handles assistant message", () => {
39
- const messages = [
40
- baseMessageDoc({
41
- message: {
42
- role: "assistant",
43
- content: "Hi, how can I help?",
44
- },
45
- text: "Hi, how can I help?",
46
- }),
47
- ];
48
- const uiMessages = toUIMessages(messages);
49
- expect(uiMessages).toHaveLength(1);
50
- expect(uiMessages[0].role).toBe("assistant");
51
- expect(uiMessages[0].text).toBe("Hi, how can I help?");
52
- expect(uiMessages[0].parts[0]).toEqual({
53
- type: "text",
54
- text: "Hi, how can I help?",
55
- state: "done",
56
- });
57
- });
58
-
59
- it("handles multiple messages", () => {
60
- const messages = [
61
- baseMessageDoc({
62
- message: {
63
- role: "user",
64
- content: "Hello!",
65
- },
66
- text: "Hello!",
67
- }),
68
- baseMessageDoc({
69
- message: {
70
- role: "assistant",
71
- content: [
72
- {
73
- type: "reasoning",
74
- text: "I'm thinking...",
75
- },
76
- {
77
- type: "redacted-reasoning",
78
- data: "asdfasdfasdf",
79
- },
80
- {
81
- type: "text",
82
- text: "I'm thinking...",
83
- },
84
- {
85
- type: "file",
86
- mimeType: "text/plain",
87
- data: "https://example.com/file.txt",
88
- },
89
- {
90
- type: "tool-call",
91
- toolName: "myTool",
92
- toolCallId: "call1",
93
- args: "an arg",
94
- },
95
- ],
96
- },
97
- tool: true,
98
- reasoning: "I'm thinking...",
99
- text: "I'm thinking...",
100
- }),
101
- baseMessageDoc({
102
- message: {
103
- role: "tool",
104
- content: [
105
- {
106
- type: "tool-result",
107
- toolCallId: "call1",
108
- toolName: "myTool",
109
- result: "42",
110
- },
111
- ],
112
- },
113
- tool: true,
114
- }),
115
- ];
116
- const uiMessages = toUIMessages(messages);
117
- expect(uiMessages).toHaveLength(2);
118
- expect(uiMessages[0].role).toBe("user");
119
- expect(uiMessages[0].parts.filter((p) => p.type === "text")).toHaveLength(
120
- 1,
121
- );
122
- expect(uiMessages[1].role).toBe("assistant");
123
- expect(
124
- uiMessages[1].parts.filter((p) => p.type === "tool-myTool"),
125
- ).toHaveLength(1);
126
- expect(
127
- uiMessages[1].parts.filter((p) => p.type === "tool-myTool")[0],
128
- ).toMatchObject({
129
- type: "tool-myTool",
130
- toolCallId: "call1",
131
- state: "output-available",
132
- output: "42",
133
- });
134
- });
135
-
136
- it("handles multiple text and reasoning parts", () => {
137
- const messages = [
138
- baseMessageDoc({
139
- message: {
140
- role: "assistant",
141
- content: [
142
- {
143
- type: "reasoning",
144
- text: "I'm thinking...",
145
- },
146
- {
147
- type: "text",
148
- text: "Here's one idea.",
149
- },
150
- {
151
- type: "reasoning",
152
- text: "I'm thinking...",
153
- },
154
- {
155
- type: "text",
156
- text: "Here's another idea.",
157
- },
158
- ],
159
- },
160
- reasoning: "I'm thinking...I'm thinking...",
161
- text: "Here's one idea. Here's another idea.",
162
- }),
163
- ];
164
- const uiMessages = toUIMessages(messages);
165
- expect(uiMessages).toHaveLength(1);
166
- expect(uiMessages[0].role).toBe("assistant");
167
- expect(uiMessages[0].text).toBe("Here's one idea. Here's another idea.");
168
- expect(uiMessages[0].parts.filter((p) => p.type === "reasoning")).toEqual([
169
- {
170
- providerMetadata: undefined,
171
- state: undefined,
172
- text: "I'm thinking...",
173
- type: "reasoning",
174
- },
175
- {
176
- providerMetadata: undefined,
177
- state: undefined,
178
- text: "I'm thinking...",
179
- type: "reasoning",
180
- },
181
- ]);
182
- expect(uiMessages[0].parts[0].type).toBe("reasoning");
183
- assert(uiMessages[0].parts[0].type === "reasoning");
184
- expect(uiMessages[0].parts[0].text).toBe("I'm thinking...");
185
- expect(uiMessages[0].parts[1].type).toBe("text");
186
- assert(uiMessages[0].parts[1].type === "text");
187
- expect(uiMessages[0].parts[1].text).toBe("Here's one idea.");
188
- expect(uiMessages[0].parts[2].type).toBe("reasoning");
189
- assert(uiMessages[0].parts[2].type === "reasoning");
190
- expect(uiMessages[0].parts[2].text).toBe("I'm thinking...");
191
-
192
- expect(uiMessages[0].parts.filter((p) => p.type === "text")).toHaveLength(
193
- 2,
194
- );
195
- expect(uiMessages[0].parts.filter((p) => p.type === "text")[0].text).toBe(
196
- "Here's one idea.",
197
- );
198
- expect(uiMessages[0].parts.filter((p) => p.type === "text")[1].text).toBe(
199
- "Here's another idea.",
200
- );
201
- });
202
-
203
- it("handles system message", () => {
204
- const messages = [
205
- baseMessageDoc({
206
- message: {
207
- role: "system",
208
- content: "System message here",
209
- },
210
- text: "System message here",
211
- }),
212
- ];
213
- const uiMessages = toUIMessages(messages);
214
- expect(uiMessages).toHaveLength(1);
215
- expect(uiMessages[0].role).toBe("system");
216
- expect(uiMessages[0].text).toBe("System message here");
217
- expect(uiMessages[0].parts[0]).toEqual({
218
- type: "text",
219
- text: "System message here",
220
- state: "done",
221
- providerMetadata: undefined,
222
- });
223
- });
224
-
225
- it("handles wrapped JSON tool output", () => {
226
- const messages = [
227
- baseMessageDoc({
228
- message: {
229
- role: "assistant",
230
- content: [
231
- {
232
- type: "tool-call",
233
- toolName: "myTool",
234
- toolCallId: "call1",
235
- args: { query: "test" },
236
- },
237
- ],
238
- },
239
- tool: true,
240
- }),
241
- baseMessageDoc({
242
- message: {
243
- role: "tool",
244
- content: [
245
- {
246
- type: "tool-result",
247
- toolName: "myTool",
248
- toolCallId: "call1",
249
- result: {
250
- type: "json",
251
- value: { data: "wrapped result", success: true },
252
- },
253
- },
254
- ],
255
- },
256
- tool: true,
257
- }),
258
- ];
259
- const uiMessages = toUIMessages(messages);
260
- expect(uiMessages).toHaveLength(1);
261
- const toolPart = uiMessages[0].parts.find((p) => p.type === "tool-myTool");
262
- expect(toolPart).toMatchObject({
263
- type: "tool-myTool",
264
- toolCallId: "call1",
265
- state: "output-available",
266
- input: { query: "test" },
267
- output: { data: "wrapped result", success: true }, // Should be unwrapped
268
- });
269
- });
270
-
271
- it("handles tool call", () => {
272
- const messages = [
273
- baseMessageDoc({
274
- message: {
275
- role: "assistant",
276
- content: [
277
- {
278
- type: "tool-call",
279
- toolName: "myTool",
280
- toolCallId: "call1",
281
- args: "hi",
282
- },
283
- ],
284
- },
285
- text: "",
286
- }),
287
- ];
288
- const uiMessages = toUIMessages(messages);
289
- expect(uiMessages).toHaveLength(1);
290
- expect(uiMessages[0].role).toBe("assistant");
291
- expect(
292
- uiMessages[0].parts.filter((p) => p.type === "tool-myTool")[0],
293
- ).toMatchObject({
294
- type: "tool-myTool",
295
- toolCallId: "call1",
296
- input: "hi",
297
- state: "input-available",
298
- });
299
- });
300
-
301
- it("handles tool result", () => {
302
- const messages = [
303
- baseMessageDoc({
304
- tool: true,
305
- message: {
306
- role: "assistant",
307
- content: [
308
- {
309
- type: "tool-call",
310
- toolName: "myTool",
311
- toolCallId: "call1",
312
- args: "",
313
- },
314
- ],
315
- },
316
- text: "",
317
- }),
318
- baseMessageDoc({
319
- message: {
320
- role: "tool",
321
- content: [
322
- {
323
- type: "tool-result",
324
- toolCallId: "call1",
325
- toolName: "myTool",
326
- result: "42",
327
- },
328
- ],
329
- },
330
- text: "",
331
- }),
332
- ];
333
- const uiMessages = toUIMessages(messages);
334
- expect(uiMessages).toHaveLength(1);
335
- expect(uiMessages[0].role).toBe("assistant");
336
- // Should have a tool-invocation part
337
- expect(uiMessages[0].parts.some((p) => p.type === "tool-myTool")).toBe(
338
- true,
339
- );
340
- });
341
-
342
- it("does not duplicate text content", () => {
343
- const messages = [
344
- baseMessageDoc({
345
- message: {
346
- role: "assistant",
347
- content: "Hello!",
348
- },
349
- text: "Hello!",
350
- }),
351
- ];
352
- const uiMessages = toUIMessages(messages);
353
- // There should only be one text part
354
- const textParts = uiMessages[0].parts.filter((p) => p.type === "text");
355
- expect(textParts).toHaveLength(1);
356
- expect(textParts[0].text).toBe("Hello!");
357
- });
358
-
359
- // Add more tests for array content, tool calls, etc. as needed
360
-
361
- it("should update tool call state from input-available to output-available", () => {
362
- const messages = [
363
- baseMessageDoc({
364
- message: {
365
- role: "assistant",
366
- content: [
367
- {
368
- type: "tool-call",
369
- toolName: "calculator",
370
- toolCallId: "call1",
371
- args: { operation: "add", a: 1, b: 2 },
372
- },
373
- ],
374
- },
375
- tool: true,
376
- }),
377
- baseMessageDoc({
378
- message: {
379
- role: "tool",
380
- content: [
381
- {
382
- type: "tool-result",
383
- toolCallId: "call1",
384
- toolName: "calculator",
385
- result: { sum: 3 },
386
- },
387
- ],
388
- },
389
- tool: true,
390
- }),
391
- ];
392
-
393
- const uiMessages = toUIMessages(messages);
394
-
395
- // Should have one assistant message
396
- expect(uiMessages).toHaveLength(1);
397
- expect(uiMessages[0].role).toBe("assistant");
398
-
399
- // Should have a single tool-calculator part (not separate tool-call and tool-result parts)
400
- const toolParts = uiMessages[0].parts.filter(
401
- (p) => p.type === "tool-calculator",
402
- );
403
- expect(toolParts).toHaveLength(1);
404
-
405
- const toolPart = toolParts[0];
406
- expect(toolPart).toMatchObject({
407
- type: "tool-calculator",
408
- toolCallId: "call1",
409
- state: "output-available",
410
- input: { operation: "add", a: 1, b: 2 },
411
- output: { sum: 3 },
412
- });
413
-
414
- // Should NOT have a tool-call part (which is what currently happens)
415
- const toolCallParts = uiMessages[0].parts.filter(
416
- (p) => p.type === "tool-call",
417
- );
418
- expect(toolCallParts).toHaveLength(0);
419
- });
420
- });
@@ -1,253 +0,0 @@
1
- import type {
2
- UIMessage as AIUIMessage,
3
- DeepPartial,
4
- ReasoningUIPart,
5
- SourceDocumentUIPart,
6
- SourceUrlUIPart,
7
- StepStartUIPart,
8
- TextUIPart,
9
- ToolUIPart,
10
- UIDataTypes,
11
- UITools,
12
- } from "ai";
13
- import type { MessageDoc } from "../client/index.js";
14
- import { deserializeMessage, toUIFilePart } from "../mapping.js";
15
- import type { MessageStatus } from "../validators.js";
16
-
17
- export type UIMessage<
18
- METADATA = unknown,
19
- DATA_PARTS extends UIDataTypes = UIDataTypes,
20
- TOOLS extends UITools = UITools,
21
- > = AIUIMessage<METADATA, DATA_PARTS, TOOLS> & {
22
- key: string;
23
- order: number;
24
- stepOrder: number;
25
- status: "streaming" | MessageStatus;
26
- agentName?: string;
27
- text: string;
28
- };
29
-
30
- export function toUIMessages<
31
- METADATA = unknown,
32
- DATA_PARTS extends UIDataTypes = UIDataTypes,
33
- TOOLS extends UITools = UITools,
34
- >(
35
- messages: (MessageDoc & { streaming?: boolean })[],
36
- ): UIMessage<METADATA, DATA_PARTS, TOOLS>[] {
37
- const uiMessages: UIMessage<METADATA, DATA_PARTS, TOOLS>[] = [];
38
- let assistantMessage: UIMessage<METADATA, DATA_PARTS, TOOLS> | undefined;
39
- for (const message of messages) {
40
- const coreMessage = message.message && deserializeMessage(message.message);
41
- const text = message.text ?? "";
42
- const content = coreMessage?.content;
43
- const nonStringContent =
44
- content && typeof content !== "string" ? content : [];
45
- if (!coreMessage) continue;
46
- const common = {
47
- id: message._id,
48
- createdAt: new Date(message._creationTime),
49
- order: message.order,
50
- stepOrder: message.stepOrder,
51
- status: message.streaming ? ("streaming" as const) : message.status,
52
- key: `${message.threadId}-${message.order}-${message.stepOrder}`,
53
- text,
54
- };
55
- const partCommon = {
56
- state: message.streaming ? ("streaming" as const) : ("done" as const),
57
- ...(message.providerMetadata
58
- ? { providerMetadata: message.providerMetadata }
59
- : {}),
60
- };
61
- if (coreMessage.role === "system") {
62
- uiMessages.push({
63
- ...common,
64
- role: "system",
65
- agentName: message.agentName,
66
- parts: [{ type: "text", text, ...partCommon } satisfies TextUIPart],
67
- });
68
- } else if (coreMessage.role === "user") {
69
- const parts: UIMessage<METADATA, DATA_PARTS, TOOLS>["parts"] = [];
70
- if (text) {
71
- parts.push({ type: "text", text });
72
- }
73
- nonStringContent.forEach((contentPart) => {
74
- switch (contentPart.type) {
75
- case "file":
76
- case "image":
77
- parts.push(toUIFilePart(contentPart));
78
- break;
79
- }
80
- });
81
- uiMessages.push({
82
- ...common,
83
- role: "user",
84
- parts,
85
- });
86
- } else {
87
- if (coreMessage.role === "tool" && !assistantMessage) {
88
- console.warn(
89
- "Tool message without preceding assistant message.. skipping",
90
- message,
91
- );
92
- continue;
93
- }
94
- if (!assistantMessage) {
95
- assistantMessage = {
96
- ...common,
97
- role: "assistant",
98
- agentName: message.agentName,
99
- parts: [],
100
- };
101
- uiMessages.push(assistantMessage);
102
- } else {
103
- assistantMessage.status = message.streaming
104
- ? "streaming"
105
- : message.status;
106
- }
107
- // update it to the last message's id
108
- assistantMessage.id = message._id;
109
- if (
110
- message.reasoning &&
111
- !nonStringContent.some((c) => c.type === "reasoning")
112
- ) {
113
- assistantMessage.parts.push({
114
- type: "reasoning",
115
- text: message.reasoning,
116
- ...partCommon,
117
- } satisfies ReasoningUIPart);
118
- }
119
- if (message.text && !nonStringContent.length) {
120
- assistantMessage.parts.push({
121
- type: "text",
122
- text: message.text,
123
- ...partCommon,
124
- } satisfies TextUIPart);
125
- }
126
- for (const source of message.sources ?? []) {
127
- if (source.sourceType === "url") {
128
- assistantMessage.parts.push({
129
- type: "source-url",
130
- url: source.url!,
131
- sourceId: source.id,
132
- providerMetadata: message.providerMetadata,
133
- title: source.title,
134
- } satisfies SourceUrlUIPart);
135
- } else {
136
- assistantMessage.parts.push({
137
- type: "source-document",
138
- mediaType: source.mediaType,
139
- sourceId: source.id,
140
- title: source.title,
141
- filename: source.filename,
142
- providerMetadata: message.providerMetadata,
143
- } satisfies SourceDocumentUIPart);
144
- }
145
- }
146
- for (const contentPart of nonStringContent) {
147
- switch (contentPart.type) {
148
- case "text":
149
- assistantMessage.parts.push({
150
- ...partCommon,
151
- ...contentPart,
152
- } satisfies TextUIPart);
153
- break;
154
- case "reasoning":
155
- assistantMessage.parts.push({
156
- ...partCommon,
157
- ...contentPart,
158
- } satisfies ReasoningUIPart);
159
- break;
160
- case "file":
161
- case "image":
162
- assistantMessage.parts.push(toUIFilePart(contentPart));
163
- break;
164
- case "tool-call": {
165
- assistantMessage.parts.push({
166
- type: "step-start",
167
- } satisfies StepStartUIPart);
168
- const toolPart: ToolUIPart<TOOLS> = {
169
- type: `tool-${contentPart.toolName as keyof TOOLS & string}`,
170
- toolCallId: contentPart.toolCallId,
171
- input: contentPart.input as DeepPartial<
172
- TOOLS[keyof TOOLS & string]["input"]
173
- >,
174
- providerExecuted: contentPart.providerExecuted,
175
- ...(message.streaming
176
- ? { state: "input-streaming" }
177
- : {
178
- state: "input-available",
179
- callProviderMetadata: message.providerMetadata,
180
- }),
181
- };
182
- assistantMessage.parts.push(toolPart);
183
- break;
184
- }
185
- case "tool-result": {
186
- const call = assistantMessage.parts.find(
187
- (part) =>
188
- part.type === `tool-${contentPart.toolName}` &&
189
- "toolCallId" in part &&
190
- part.toolCallId === contentPart.toolCallId,
191
- ) as ToolUIPart | undefined;
192
- if (call) {
193
- if (message.error) {
194
- call.state = "output-error";
195
- call.errorText = message.error;
196
- call.output = contentPart.output;
197
- } else {
198
- call.state = "output-available";
199
- call.output =
200
- contentPart.output?.type === "json"
201
- ? contentPart.output.value
202
- : contentPart.output;
203
- // Technically we could pull this from the doc.message
204
- // but the ModelMessage doesn't have it
205
- // call.providerExecuted = contentPart.providerExecuted;
206
- }
207
- } else {
208
- console.warn(
209
- "Tool result without preceding tool call.. adding anyways",
210
- contentPart,
211
- );
212
- if (message.error) {
213
- assistantMessage.parts.push({
214
- type: `tool-${contentPart.toolName}`,
215
- toolCallId: contentPart.toolCallId,
216
- state: "output-error",
217
- input: undefined,
218
- errorText: message.error,
219
- // Technically we could pull this from the doc.message
220
- // but the ModelMessage doesn't have it
221
- // providerExecuted: contentPart.providerExecuted,
222
- callProviderMetadata: message.providerMetadata,
223
- } satisfies ToolUIPart<TOOLS>);
224
- } else {
225
- assistantMessage.parts.push({
226
- type: `tool-${contentPart.toolName}`,
227
- toolCallId: contentPart.toolCallId,
228
- state: "output-available",
229
- input: undefined,
230
- output:
231
- contentPart.output?.type === "json"
232
- ? contentPart.output.value
233
- : contentPart.output,
234
- callProviderMetadata: message.providerMetadata,
235
- } satisfies ToolUIPart<TOOLS>);
236
- }
237
- }
238
- break;
239
- }
240
- }
241
- }
242
- }
243
- if (
244
- !message.tool &&
245
- assistantMessage &&
246
- assistantMessage.parts.length > 0
247
- ) {
248
- // Reset it so the next set of tool calls will create a new assistant message
249
- assistantMessage = undefined;
250
- }
251
- }
252
- return uiMessages;
253
- }
@@ -1,7 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: "edge-runtime",
6
- },
7
- });