@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
package/dist/mapping.js CHANGED
@@ -1,38 +1,84 @@
1
1
  import {} from "ai";
2
+ import { vMessageWithMetadata, vToolResultOutput, vToolApprovalRequest, vToolApprovalResponse, } from "./validators.js";
2
3
  import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
3
- import { omit } from "convex-helpers";
4
- export async function serializeMessage(ctx, component, messageWithId) {
5
- const message = "id" in messageWithId ? omit(messageWithId, ["id"]) : messageWithId;
4
+ import { convertUint8ArrayToBase64, } from "@ai-sdk/provider-utils";
5
+ import { parse, validate } from "convex-helpers/validators";
6
+ import { getModelName, getProviderName, } from "./shared.js";
7
+ export async function serializeMessage(ctx, component, message) {
6
8
  const { content, fileIds } = await serializeContent(ctx, component, message.content);
7
9
  return {
8
10
  message: {
9
- ...message,
11
+ role: message.role,
10
12
  content,
13
+ ...(message.providerOptions
14
+ ? { providerOptions: message.providerOptions }
15
+ : {}),
11
16
  },
12
17
  fileIds,
13
18
  };
14
19
  }
15
- export function deserializeMessage(message) {
20
+ // Similar to serializeMessage, but doesn't save any files and is looser
21
+ // For use on the frontend / in synchronous environments.
22
+ export function fromModelMessage(message) {
23
+ const content = fromModelMessageContent(message.content);
24
+ return {
25
+ role: message.role,
26
+ content,
27
+ ...(message.providerOptions
28
+ ? { providerOptions: message.providerOptions }
29
+ : {}),
30
+ };
31
+ }
32
+ export async function serializeOrThrow(message) {
33
+ const { content } = await serializeContent({}, {}, message.content);
34
+ return {
35
+ role: message.role,
36
+ content,
37
+ ...(message.providerOptions
38
+ ? { providerOptions: message.providerOptions }
39
+ : {}),
40
+ };
41
+ }
42
+ export function toModelMessage(message) {
16
43
  return {
17
44
  ...message,
18
- content: deserializeContent(message.content),
45
+ content: toModelMessageContent(message.content),
19
46
  };
20
47
  }
48
+ export function docsToModelMessages(messages) {
49
+ return messages
50
+ .map((m) => m.message)
51
+ .filter((m) => !!m)
52
+ .filter((m) => !!m.content.length)
53
+ .map(toModelMessage);
54
+ }
21
55
  export function serializeUsage(usage) {
22
56
  return {
23
57
  promptTokens: usage.inputTokens ?? 0,
24
58
  completionTokens: usage.outputTokens ?? 0,
25
59
  totalTokens: usage.totalTokens ?? 0,
26
60
  reasoningTokens: usage.reasoningTokens,
61
+ cachedInputTokens: usage.cachedInputTokens,
27
62
  };
28
63
  }
29
- export function deserializeUsage(usage) {
64
+ export function toModelMessageUsage(usage) {
30
65
  return {
31
66
  inputTokens: usage.promptTokens,
32
67
  outputTokens: usage.completionTokens,
33
68
  totalTokens: usage.totalTokens,
34
69
  reasoningTokens: usage.reasoningTokens,
35
70
  cachedInputTokens: usage.cachedInputTokens,
71
+ // These detail fields are required by LanguageModelUsage type but we don't
72
+ // have the granular data, so we provide empty objects with undefined values.
73
+ inputTokenDetails: {
74
+ cacheReadTokens: undefined,
75
+ cacheWriteTokens: undefined,
76
+ noCacheTokens: undefined,
77
+ },
78
+ outputTokenDetails: {
79
+ textTokens: undefined,
80
+ reasoningTokens: undefined,
81
+ },
36
82
  };
37
83
  }
38
84
  export function serializeWarnings(warnings) {
@@ -40,25 +86,38 @@ export function serializeWarnings(warnings) {
40
86
  return undefined;
41
87
  }
42
88
  return warnings.map((warning) => {
43
- if (warning.type !== "unsupported-setting") {
44
- return warning;
89
+ if (warning.type === "compatibility") {
90
+ return {
91
+ type: "unsupported-setting",
92
+ setting: warning.feature,
93
+ details: warning.details,
94
+ };
45
95
  }
46
- return {
47
- ...warning,
48
- setting: warning.setting.toString(),
49
- };
96
+ return warning;
50
97
  });
51
98
  }
52
- export function deserializeWarnings(warnings) {
53
- // We don't need to do anythign here for now
54
- return warnings;
99
+ export function toModelMessageWarnings(warnings) {
100
+ if (!warnings) {
101
+ return undefined;
102
+ }
103
+ return warnings.map((warning) => {
104
+ if (warning.type === "unsupported-setting") {
105
+ return {
106
+ type: "compatibility",
107
+ feature: warning.setting,
108
+ details: warning.details,
109
+ };
110
+ }
111
+ return warning;
112
+ });
55
113
  }
56
- export async function serializeNewMessagesInStep(ctx, component, step, metadata) {
114
+ export async function serializeNewMessagesInStep(ctx, component, step, model) {
57
115
  // If there are tool results, there's another message with the tool results
58
- // ref: https://github.com/vercel/ai/blob/main/packages/ai/core/generate-text/to-response-messages.ts
116
+ // ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
117
+ const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
59
118
  const assistantFields = {
60
- model: metadata.model,
61
- provider: metadata.provider,
119
+ model: model ? getModelName(model) : undefined,
120
+ provider: model ? getProviderName(model) : undefined,
62
121
  providerMetadata: step.providerMetadata,
63
122
  reasoning: step.reasoningText,
64
123
  reasoningDetails: step.reasoning,
@@ -66,28 +125,26 @@ export async function serializeNewMessagesInStep(ctx, component, step, metadata)
66
125
  warnings: serializeWarnings(step.warnings),
67
126
  finishReason: step.finishReason,
68
127
  // Only store the sources on one message
69
- sources: step.toolResults.length === 0 ? step.sources : undefined,
70
- };
71
- const toolFields = {
72
- sources: step.sources,
128
+ sources: hasToolMessage ? undefined : step.sources,
73
129
  };
74
- const messages = await Promise.all((step.toolResults.length > 0
130
+ const toolFields = { sources: step.sources };
131
+ const messages = await Promise.all((hasToolMessage
75
132
  ? step.response.messages.slice(-2)
76
- : step.response.messages.slice(-1)).map(async (messageWithId) => {
77
- const { message, fileIds } = await serializeMessage(ctx, component, messageWithId);
78
- return {
133
+ : step.content.length
134
+ ? step.response.messages.slice(-1)
135
+ : [{ role: "assistant", content: [] }]).map(async (msg) => {
136
+ const { message, fileIds } = await serializeMessage(ctx, component, msg);
137
+ return parse(vMessageWithMetadata, {
79
138
  message,
80
- // Let's not store the ID by default here. It's being generated internally
81
- // and not referenced elsewhere that we know of.
82
- // id: message.id,
83
139
  ...(message.role === "tool" ? toolFields : assistantFields),
84
140
  text: step.text,
85
141
  fileIds,
86
- };
142
+ });
87
143
  }));
88
- return messages;
144
+ // TODO: capture step.files separately?
145
+ return { messages };
89
146
  }
90
- export async function serializeObjectResult(ctx, component, result, metadata) {
147
+ export async function serializeObjectResult(ctx, component, result, model) {
91
148
  const text = JSON.stringify(result.object);
92
149
  const { message, fileIds } = await serializeMessage(ctx, component, {
93
150
  role: "assistant",
@@ -97,9 +154,8 @@ export async function serializeObjectResult(ctx, component, result, metadata) {
97
154
  messages: [
98
155
  {
99
156
  message,
100
- id: result.response.id,
101
- model: metadata.model,
102
- provider: metadata.provider,
157
+ model: model ? getModelName(model) : undefined,
158
+ provider: model ? getProviderName(model) : undefined,
103
159
  providerMetadata: result.providerMetadata,
104
160
  finishReason: result.finishReason,
105
161
  text,
@@ -110,27 +166,34 @@ export async function serializeObjectResult(ctx, component, result, metadata) {
110
166
  ],
111
167
  };
112
168
  }
169
+ function getMimeOrMediaType(part) {
170
+ if ("mediaType" in part) {
171
+ return part.mediaType;
172
+ }
173
+ if ("mimeType" in part) {
174
+ return part.mimeType;
175
+ }
176
+ return undefined;
177
+ }
113
178
  export async function serializeContent(ctx, component, content) {
114
179
  if (typeof content === "string") {
115
180
  return { content };
116
181
  }
117
182
  const fileIds = [];
118
- function getMimeType(part) {
119
- if ("mediaType" in part) {
120
- return part.mediaType;
183
+ const serialized = await Promise.all(content.map(async (part) => {
184
+ const metadata = {};
185
+ if ("providerOptions" in part) {
186
+ metadata.providerOptions = part.providerOptions;
121
187
  }
122
- if ("mimeType" in part) {
123
- return part.mimeType;
188
+ if ("providerMetadata" in part) {
189
+ metadata.providerMetadata = part.providerMetadata;
124
190
  }
125
- return undefined;
126
- }
127
- const serialized = await Promise.all(content.map(async (part) => {
128
191
  switch (part.type) {
129
192
  case "text": {
130
193
  return {
131
194
  type: part.type,
132
195
  text: part.text,
133
- providerOptions: part.providerOptions,
196
+ ...metadata,
134
197
  };
135
198
  }
136
199
  case "image": {
@@ -138,22 +201,22 @@ export async function serializeContent(ctx, component, content) {
138
201
  if (image instanceof ArrayBuffer &&
139
202
  image.byteLength > MAX_FILE_SIZE) {
140
203
  const { file } = await storeFile(ctx, component, new Blob([image], {
141
- type: getMimeType(part) || guessMimeType(image),
204
+ type: getMimeOrMediaType(part) || guessMimeType(image),
142
205
  }));
143
206
  image = file.url;
144
207
  fileIds.push(file.fileId);
145
208
  }
146
209
  return {
147
210
  type: part.type,
148
- mimeType: getMimeType(part),
149
- providerOptions: part.providerOptions,
211
+ mediaType: getMimeOrMediaType(part),
212
+ ...metadata,
150
213
  image,
151
214
  };
152
215
  }
153
216
  case "file": {
154
217
  let data = serializeDataOrUrl(part.data);
155
218
  if (data instanceof ArrayBuffer && data.byteLength > MAX_FILE_SIZE) {
156
- const { file } = await storeFile(ctx, component, new Blob([data], { type: getMimeType(part) }));
219
+ const { file } = await storeFile(ctx, component, new Blob([data], { type: getMimeOrMediaType(part) }));
157
220
  data = file.url;
158
221
  fileIds.push(file.fileId);
159
222
  }
@@ -161,116 +224,270 @@ export async function serializeContent(ctx, component, content) {
161
224
  type: part.type,
162
225
  data,
163
226
  filename: part.filename,
164
- mimeType: getMimeType(part),
165
- providerOptions: part.providerOptions,
227
+ mediaType: getMimeOrMediaType(part),
228
+ ...metadata,
166
229
  };
167
230
  }
168
231
  case "tool-call": {
169
- const args = "input" in part ? part.input : part.args;
232
+ // Handle legacy data where only args field exists
233
+ const input = part.input ?? part?.args ?? {};
170
234
  return {
171
235
  type: part.type,
172
- args: args ?? null,
236
+ input,
237
+ /** @deprecated Use `input` instead. */
238
+ args: input,
173
239
  toolCallId: part.toolCallId,
174
240
  toolName: part.toolName,
175
- providerOptions: part.providerOptions,
176
241
  providerExecuted: part.providerExecuted,
242
+ ...metadata,
177
243
  };
178
244
  }
179
245
  case "tool-result": {
180
- const result = "output" in part ? part.output : part.result;
181
- return {
182
- type: part.type,
183
- result: result ?? null,
184
- toolCallId: part.toolCallId,
185
- toolName: part.toolName,
186
- providerOptions: part.providerOptions,
187
- };
246
+ return normalizeToolResult(part, metadata);
188
247
  }
189
248
  case "reasoning": {
190
249
  return {
191
250
  type: part.type,
192
251
  text: part.text,
193
- providerOptions: part.providerOptions,
252
+ ...metadata,
194
253
  };
195
254
  }
255
+ // Not in current generation output, but could be in historical messages
196
256
  case "redacted-reasoning": {
197
257
  return {
198
258
  type: part.type,
199
259
  data: part.data,
200
- providerOptions: part.providerOptions,
260
+ ...metadata,
201
261
  };
202
262
  }
203
- default:
263
+ case "source": {
204
264
  return part;
265
+ }
266
+ case "tool-approval-request": {
267
+ return {
268
+ type: part.type,
269
+ approvalId: part.approvalId,
270
+ toolCallId: part.toolCallId,
271
+ ...metadata,
272
+ };
273
+ }
274
+ case "tool-approval-response": {
275
+ return {
276
+ type: part.type,
277
+ approvalId: part.approvalId,
278
+ approved: part.approved,
279
+ reason: part.reason,
280
+ providerExecuted: part.providerExecuted,
281
+ ...metadata,
282
+ };
283
+ }
284
+ default:
285
+ return null;
205
286
  }
206
287
  }));
207
288
  return {
208
- content: serialized,
289
+ content: serialized.filter((p) => p !== null),
209
290
  fileIds: fileIds.length > 0 ? fileIds : undefined,
210
291
  };
211
292
  }
212
- export function deserializeContent(content) {
293
+ export function fromModelMessageContent(content) {
213
294
  if (typeof content === "string") {
214
295
  return content;
215
296
  }
216
- return content.map((part) => {
297
+ return content
298
+ .map((part) => {
299
+ const metadata = {};
300
+ if ("providerOptions" in part) {
301
+ metadata.providerOptions = part.providerOptions;
302
+ }
303
+ if ("providerMetadata" in part) {
304
+ metadata.providerMetadata = part.providerMetadata;
305
+ }
217
306
  switch (part.type) {
218
307
  case "text":
219
- return {
220
- type: part.type,
221
- text: part.text,
222
- providerOptions: part.providerOptions,
223
- };
308
+ return part;
224
309
  case "image":
225
310
  return {
226
311
  type: part.type,
227
- image: deserializeUrl(part.image),
228
- mediaType: part.mimeType,
229
- providerOptions: part.providerOptions,
312
+ mediaType: getMimeOrMediaType(part),
313
+ ...metadata,
314
+ image: serializeDataOrUrl(part.image),
230
315
  };
231
316
  case "file":
232
317
  return {
233
318
  type: part.type,
234
- data: deserializeUrl(part.data),
319
+ data: serializeDataOrUrl(part.data),
235
320
  filename: part.filename,
236
- mediaType: part.mimeType,
237
- providerOptions: part.providerOptions,
321
+ mediaType: getMimeOrMediaType(part),
322
+ ...metadata,
238
323
  };
239
324
  case "tool-call":
325
+ // Handle legacy data where only args field exists
240
326
  return {
241
327
  type: part.type,
242
- input: part.args ?? null,
243
- providerExecuted: part.providerExecuted,
328
+ input: part.input ?? part?.args ?? {},
329
+ /** @deprecated Use `input` instead. */
330
+ args: part.input ?? part?.args ?? {},
244
331
  toolCallId: part.toolCallId,
245
332
  toolName: part.toolName,
246
- providerOptions: part.providerOptions,
333
+ providerExecuted: part.providerExecuted,
334
+ ...metadata,
247
335
  };
248
336
  case "tool-result":
337
+ return normalizeToolResult(part, metadata);
338
+ case "reasoning":
339
+ return {
340
+ type: part.type,
341
+ text: part.text,
342
+ ...metadata,
343
+ };
344
+ case "tool-approval-request":
249
345
  return {
250
346
  type: part.type,
251
- output: part.result ?? null,
347
+ approvalId: part.approvalId,
348
+ toolCallId: part.toolCallId,
349
+ ...metadata,
350
+ };
351
+ case "tool-approval-response":
352
+ return {
353
+ type: part.type,
354
+ approvalId: part.approvalId,
355
+ approved: part.approved,
356
+ reason: part.reason,
357
+ providerExecuted: part.providerExecuted,
358
+ ...metadata,
359
+ };
360
+ // Not in current generation output, but could be in historical messages
361
+ default:
362
+ return null;
363
+ }
364
+ })
365
+ .filter((p) => p !== null);
366
+ }
367
+ export function toModelMessageContent(content) {
368
+ if (typeof content === "string") {
369
+ return content;
370
+ }
371
+ return content
372
+ .map((part) => {
373
+ const metadata = {};
374
+ if ("providerOptions" in part) {
375
+ metadata.providerOptions = part.providerOptions;
376
+ }
377
+ if ("providerMetadata" in part) {
378
+ metadata.providerMetadata = part.providerMetadata;
379
+ }
380
+ switch (part.type) {
381
+ case "text":
382
+ return {
383
+ type: part.type,
384
+ text: part.text,
385
+ ...metadata,
386
+ };
387
+ case "image":
388
+ return {
389
+ type: part.type,
390
+ image: toModelMessageDataOrUrl(part.image),
391
+ mediaType: getMimeOrMediaType(part),
392
+ ...metadata,
393
+ };
394
+ case "file":
395
+ return {
396
+ type: part.type,
397
+ data: toModelMessageDataOrUrl(part.data),
398
+ filename: part.filename,
399
+ mediaType: getMimeOrMediaType(part),
400
+ ...metadata,
401
+ };
402
+ case "tool-call": {
403
+ // Handle legacy data where only args field exists
404
+ const input = part.input ?? part?.args ?? {};
405
+ return {
406
+ type: part.type,
407
+ input,
252
408
  toolCallId: part.toolCallId,
253
409
  toolName: part.toolName,
254
- providerOptions: part.providerOptions,
410
+ providerExecuted: part.providerExecuted,
411
+ ...metadata,
255
412
  };
413
+ }
414
+ case "tool-result": {
415
+ return normalizeToolResult(part, metadata);
416
+ }
256
417
  case "reasoning":
257
418
  return {
258
419
  type: part.type,
259
420
  text: part.text,
260
- providerMetadata: part.providerOptions,
261
- state: part.state,
421
+ ...metadata,
262
422
  };
263
423
  case "redacted-reasoning":
264
424
  // TODO: should we just drop this?
265
425
  return {
266
426
  type: "reasoning",
267
- text: part.data,
268
- providerMetadata: part.providerOptions,
427
+ text: "",
428
+ ...metadata,
429
+ providerOptions: metadata.providerOptions
430
+ ? {
431
+ ...Object.fromEntries(Object.entries(metadata.providerOptions ?? {}).map(([key, value]) => [
432
+ key,
433
+ { ...value, redactedData: part.data },
434
+ ])),
435
+ }
436
+ : undefined,
269
437
  };
270
- default:
438
+ case "source":
271
439
  return part;
440
+ case "tool-approval-request":
441
+ return {
442
+ type: part.type,
443
+ approvalId: part.approvalId,
444
+ toolCallId: part.toolCallId,
445
+ ...metadata,
446
+ };
447
+ case "tool-approval-response":
448
+ return {
449
+ type: part.type,
450
+ approvalId: part.approvalId,
451
+ approved: part.approved,
452
+ reason: part.reason,
453
+ providerExecuted: part.providerExecuted,
454
+ ...metadata,
455
+ };
456
+ default:
457
+ return null;
272
458
  }
273
- });
459
+ })
460
+ .filter((p) => p !== null);
461
+ }
462
+ export function normalizeToolOutput(result) {
463
+ if (typeof result === "string") {
464
+ return {
465
+ type: "text",
466
+ value: result,
467
+ };
468
+ }
469
+ if (validate(vToolResultOutput, result)) {
470
+ return result;
471
+ }
472
+ return {
473
+ type: "json",
474
+ value: result ?? null,
475
+ };
476
+ }
477
+ function normalizeToolResult(part, metadata) {
478
+ return {
479
+ type: part.type,
480
+ output: part.output
481
+ ? validate(vToolResultOutput, part.output)
482
+ ? part.output
483
+ : normalizeToolOutput(JSON.stringify(part.output))
484
+ : normalizeToolOutput("result" in part ? part.result : undefined),
485
+ toolCallId: part.toolCallId,
486
+ toolName: part.toolName,
487
+ // Preserve isError flag for error reporting
488
+ ...("isError" in part && part.isError ? { isError: true } : {}),
489
+ ...metadata,
490
+ };
274
491
  }
275
492
  /**
276
493
  * Return a best-guess MIME type based on the magic-number signature
@@ -362,7 +579,10 @@ export function serializeDataOrUrl(dataOrUrl) {
362
579
  }
363
580
  return dataOrUrl.buffer.slice(dataOrUrl.byteOffset, dataOrUrl.byteOffset + dataOrUrl.byteLength);
364
581
  }
365
- export function deserializeUrl(urlOrString) {
582
+ export function toModelMessageDataOrUrl(urlOrString) {
583
+ if (urlOrString instanceof URL) {
584
+ return urlOrString;
585
+ }
366
586
  if (typeof urlOrString === "string") {
367
587
  if (urlOrString.startsWith("http://") ||
368
588
  urlOrString.startsWith("https://")) {
@@ -375,7 +595,7 @@ export function deserializeUrl(urlOrString) {
375
595
  export function toUIFilePart(part) {
376
596
  const dataOrUrl = part.type === "image" ? part.image : part.data;
377
597
  const url = dataOrUrl instanceof ArrayBuffer
378
- ? encodeBase64(dataOrUrl)
598
+ ? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
379
599
  : dataOrUrl.toString();
380
600
  return {
381
601
  type: "file",
@@ -385,9 +605,6 @@ export function toUIFilePart(part) {
385
605
  providerMetadata: part.providerOptions,
386
606
  };
387
607
  }
388
- function encodeBase64(data) {
389
- return Buffer.from(data).toString("base64");
390
- }
391
608
  // Currently unused
392
609
  // export function toModelMessages(args: {
393
610
  // messages?: ModelMessage[] | AIMessageWithoutId[];