@convex-dev/agent 0.6.3 → 0.7.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 (246) hide show
  1. package/MIGRATION.md +60 -116
  2. package/dist/client/messages.d.ts +1 -398
  3. package/dist/client/messages.d.ts.map +1 -1
  4. package/dist/client/messages.js +3 -47
  5. package/dist/client/messages.js.map +1 -1
  6. package/dist/client/types.d.ts +1 -455
  7. package/dist/client/types.d.ts.map +1 -1
  8. package/dist/component/_generated/component.d.ts +805 -45
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/apiKeys.js +5 -5
  11. package/dist/component/apiKeys.js.map +1 -1
  12. package/dist/component/files.d.ts.map +1 -1
  13. package/dist/component/files.js +13 -11
  14. package/dist/component/files.js.map +1 -1
  15. package/dist/component/messages.d.ts +1252 -201
  16. package/dist/component/messages.d.ts.map +1 -1
  17. package/dist/component/messages.js +83 -47
  18. package/dist/component/messages.js.map +1 -1
  19. package/dist/component/schema.d.ts +2593 -332
  20. package/dist/component/schema.d.ts.map +1 -1
  21. package/dist/component/streams.d.ts +7 -1
  22. package/dist/component/streams.d.ts.map +1 -1
  23. package/dist/component/streams.js +46 -47
  24. package/dist/component/streams.js.map +1 -1
  25. package/dist/component/threads.d.ts.map +1 -1
  26. package/dist/component/threads.js +8 -9
  27. package/dist/component/threads.js.map +1 -1
  28. package/dist/component/users.js +2 -2
  29. package/dist/component/users.js.map +1 -1
  30. package/dist/component/vector/index.d.ts.map +1 -1
  31. package/dist/component/vector/index.js +14 -8
  32. package/dist/component/vector/index.js.map +1 -1
  33. package/dist/react/index.d.ts +5 -5
  34. package/dist/react/index.d.ts.map +1 -1
  35. package/dist/react/index.js +5 -5
  36. package/dist/react/index.js.map +1 -1
  37. package/dist/shared.d.ts +19 -7
  38. package/dist/shared.d.ts.map +1 -1
  39. package/dist/shared.js +4 -4
  40. package/dist/shared.js.map +1 -1
  41. package/dist/streaming/materializePersistedUIMessageChunks.d.ts +16 -0
  42. package/dist/streaming/materializePersistedUIMessageChunks.d.ts.map +1 -0
  43. package/dist/streaming/materializePersistedUIMessageChunks.js +302 -0
  44. package/dist/streaming/materializePersistedUIMessageChunks.js.map +1 -0
  45. package/dist/streaming/persistedUIMessageChunks.d.ts +106 -0
  46. package/dist/streaming/persistedUIMessageChunks.d.ts.map +1 -0
  47. package/dist/streaming/persistedUIMessageChunks.js +475 -0
  48. package/dist/streaming/persistedUIMessageChunks.js.map +1 -0
  49. package/dist/validators.d.ts +10026 -1055
  50. package/dist/validators.d.ts.map +1 -1
  51. package/dist/validators.js +71 -9
  52. package/dist/validators.js.map +1 -1
  53. package/dist/{UIMessages.d.ts → vercel/UIMessages.d.ts} +1 -1
  54. package/dist/vercel/UIMessages.d.ts.map +1 -0
  55. package/dist/{UIMessages.js → vercel/UIMessages.js} +63 -1
  56. package/dist/vercel/UIMessages.js.map +1 -0
  57. package/dist/{client → vercel/client}/createTool.d.ts +23 -16
  58. package/dist/vercel/client/createTool.d.ts.map +1 -0
  59. package/dist/{client → vercel/client}/createTool.js +2 -0
  60. package/dist/vercel/client/createTool.js.map +1 -0
  61. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts +593 -134
  62. package/dist/{client → vercel/client}/definePlaygroundAPI.d.ts.map +1 -1
  63. package/dist/{client → vercel/client}/definePlaygroundAPI.js +13 -9
  64. package/dist/vercel/client/definePlaygroundAPI.js.map +1 -0
  65. package/dist/{client → vercel/client}/files.d.ts +2 -2
  66. package/dist/vercel/client/files.d.ts.map +1 -0
  67. package/dist/vercel/client/files.js.map +1 -0
  68. package/dist/vercel/client/messages.d.ts +542 -0
  69. package/dist/vercel/client/messages.d.ts.map +1 -0
  70. package/dist/vercel/client/messages.js +66 -0
  71. package/dist/vercel/client/messages.js.map +1 -0
  72. package/dist/{client → vercel/client}/mockModel.d.ts +17 -17
  73. package/dist/vercel/client/mockModel.d.ts.map +1 -0
  74. package/dist/{client → vercel/client}/mockModel.js +12 -13
  75. package/dist/vercel/client/mockModel.js.map +1 -0
  76. package/dist/{client → vercel/client}/saveInputMessages.d.ts +2 -2
  77. package/dist/vercel/client/saveInputMessages.d.ts.map +1 -0
  78. package/dist/vercel/client/saveInputMessages.js.map +1 -0
  79. package/dist/{client → vercel/client}/search.d.ts +119 -22
  80. package/dist/vercel/client/search.d.ts.map +1 -0
  81. package/dist/{client → vercel/client}/search.js +2 -2
  82. package/dist/vercel/client/search.js.map +1 -0
  83. package/dist/{client → vercel/client}/start.d.ts +26 -11
  84. package/dist/vercel/client/start.d.ts.map +1 -0
  85. package/dist/{client → vercel/client}/start.js +64 -22
  86. package/dist/vercel/client/start.js.map +1 -0
  87. package/dist/{client → vercel/client}/streamText.d.ts +13 -12
  88. package/dist/vercel/client/streamText.d.ts.map +1 -0
  89. package/dist/{client → vercel/client}/streamText.js +43 -9
  90. package/dist/vercel/client/streamText.js.map +1 -0
  91. package/dist/{client → vercel/client}/streaming.d.ts +1410 -183
  92. package/dist/vercel/client/streaming.d.ts.map +1 -0
  93. package/dist/{client → vercel/client}/streaming.js +120 -52
  94. package/dist/vercel/client/streaming.js.map +1 -0
  95. package/dist/vercel/client/types.d.ts +456 -0
  96. package/dist/vercel/client/types.d.ts.map +1 -0
  97. package/dist/vercel/client/types.js +3 -0
  98. package/dist/vercel/client/types.js.map +1 -0
  99. package/dist/{client → vercel/client}/utils.d.ts +3 -2
  100. package/dist/vercel/client/utils.d.ts.map +1 -0
  101. package/dist/{client → vercel/client}/utils.js +2 -2
  102. package/dist/vercel/client/utils.js.map +1 -0
  103. package/dist/vercel/deltas.d.ts +31 -0
  104. package/dist/vercel/deltas.d.ts.map +1 -0
  105. package/dist/vercel/deltas.js +525 -0
  106. package/dist/vercel/deltas.js.map +1 -0
  107. package/dist/{client → vercel}/index.d.ts +484 -97
  108. package/dist/vercel/index.d.ts.map +1 -0
  109. package/dist/{client → vercel}/index.js +65 -38
  110. package/dist/vercel/index.js.map +1 -0
  111. package/dist/{mapping.d.ts → vercel/mapping.d.ts} +5 -13
  112. package/dist/vercel/mapping.d.ts.map +1 -0
  113. package/dist/{mapping.js → vercel/mapping.js} +411 -112
  114. package/dist/vercel/mapping.js.map +1 -0
  115. package/dist/{react → vercel/react}/optimisticallySendMessage.d.ts +2 -2
  116. package/dist/vercel/react/optimisticallySendMessage.d.ts.map +1 -0
  117. package/dist/vercel/react/optimisticallySendMessage.js.map +1 -0
  118. package/dist/{react → vercel/react}/types.d.ts +1 -1
  119. package/dist/vercel/react/types.d.ts.map +1 -0
  120. package/dist/{react → vercel/react}/types.js.map +1 -1
  121. package/dist/{react → vercel/react}/useDeltaStreams.d.ts +1 -1
  122. package/dist/vercel/react/useDeltaStreams.d.ts.map +1 -0
  123. package/dist/{react → vercel/react}/useDeltaStreams.js +1 -1
  124. package/dist/vercel/react/useDeltaStreams.js.map +1 -0
  125. package/dist/{react → vercel/react}/useStreamingUIMessages.d.ts +1 -1
  126. package/dist/vercel/react/useStreamingUIMessages.d.ts.map +1 -0
  127. package/dist/vercel/react/useStreamingUIMessages.js +112 -0
  128. package/dist/vercel/react/useStreamingUIMessages.js.map +1 -0
  129. package/dist/{react → vercel/react}/useThreadMessages.d.ts +1 -1
  130. package/dist/vercel/react/useThreadMessages.d.ts.map +1 -0
  131. package/dist/{react → vercel/react}/useThreadMessages.js +1 -1
  132. package/dist/vercel/react/useThreadMessages.js.map +1 -0
  133. package/dist/{react → vercel/react}/useUIMessages.d.ts +2 -1
  134. package/dist/vercel/react/useUIMessages.d.ts.map +1 -0
  135. package/dist/{react → vercel/react}/useUIMessages.js +8 -4
  136. package/dist/vercel/react/useUIMessages.js.map +1 -0
  137. package/package.json +30 -22
  138. package/src/client/messages.ts +5 -107
  139. package/src/client/types.ts +0 -621
  140. package/src/component/_generated/component.ts +1089 -48
  141. package/src/component/apiKeys.ts +5 -5
  142. package/src/component/files.test.ts +1 -1
  143. package/src/component/files.ts +14 -12
  144. package/src/component/messages.test.ts +273 -1
  145. package/src/component/messages.ts +99 -57
  146. package/src/component/streams.ts +80 -64
  147. package/src/component/threads.ts +12 -9
  148. package/src/component/users.ts +2 -2
  149. package/src/component/vector/index.ts +14 -7
  150. package/src/eslintConfig.test.ts +44 -0
  151. package/src/react/index.ts +8 -5
  152. package/src/shared.ts +26 -36
  153. package/src/streaming/materializePersistedUIMessageChunks.test.ts +513 -0
  154. package/src/streaming/materializePersistedUIMessageChunks.ts +398 -0
  155. package/src/streaming/persistedUIMessageChunks.test.ts +205 -0
  156. package/src/streaming/persistedUIMessageChunks.ts +658 -0
  157. package/src/validators.test.ts +4 -1
  158. package/src/validators.ts +89 -8
  159. package/src/{UIMessages.combineUIMessages.test.ts → vercel/UIMessages.combineUIMessages.test.ts} +4 -1
  160. package/src/{UIMessages.ts → vercel/UIMessages.ts} +96 -5
  161. package/src/{client → vercel/client}/approval.test.ts +48 -19
  162. package/src/{client → vercel/client}/createTool.ts +63 -23
  163. package/src/{client → vercel/client}/definePlaygroundAPI.ts +44 -29
  164. package/src/{client → vercel/client}/files.ts +2 -2
  165. package/src/{client → vercel/client}/index.test.ts +162 -3
  166. package/src/vercel/client/messages.ts +182 -0
  167. package/src/{client → vercel/client}/mockModel.ts +38 -39
  168. package/src/{client → vercel/client}/saveInputMessages.test.ts +1 -1
  169. package/src/{client → vercel/client}/saveInputMessages.ts +7 -4
  170. package/src/{client → vercel/client}/search.test.ts +3 -3
  171. package/src/{client → vercel/client}/search.ts +4 -4
  172. package/src/{client → vercel/client}/setup.test.ts +6 -3
  173. package/src/vercel/client/start.test.ts +14 -0
  174. package/src/{client → vercel/client}/start.ts +147 -36
  175. package/src/vercel/client/streamText.test.ts +223 -0
  176. package/src/{client → vercel/client}/streamText.ts +102 -31
  177. package/src/{client → vercel/client}/streaming.integration.test.ts +40 -115
  178. package/src/{client → vercel/client}/streaming.test.ts +178 -4
  179. package/src/{client → vercel/client}/streaming.ts +114 -52
  180. package/src/vercel/client/types.test.ts +13 -0
  181. package/src/vercel/client/types.ts +679 -0
  182. package/src/{client → vercel/client}/utils.ts +13 -7
  183. package/src/vercel/deltas.test.ts +898 -0
  184. package/src/vercel/deltas.ts +633 -0
  185. package/src/{fromUIMessages.test.ts → vercel/fromUIMessages.test.ts} +17 -1
  186. package/src/{client → vercel}/index.ts +169 -62
  187. package/src/vercel/mapping.test.ts +965 -0
  188. package/src/{mapping.ts → vercel/mapping.ts} +501 -131
  189. package/src/{react → vercel/react}/optimisticallySendMessage.ts +2 -2
  190. package/src/{react → vercel/react}/types.ts +1 -1
  191. package/src/{react → vercel/react}/useDeltaStreams.ts +6 -2
  192. package/src/{react → vercel/react}/useStreamingUIMessages.ts +67 -35
  193. package/src/{react → vercel/react}/useThreadMessages.ts +2 -2
  194. package/src/{react → vercel/react}/useUIMessages.test.ts +80 -1
  195. package/src/{react → vercel/react}/useUIMessages.ts +13 -5
  196. package/src/{toUIMessages.test.ts → vercel/toUIMessages.test.ts} +49 -2
  197. package/dist/UIMessages.d.ts.map +0 -1
  198. package/dist/UIMessages.js.map +0 -1
  199. package/dist/client/createTool.d.ts.map +0 -1
  200. package/dist/client/createTool.js.map +0 -1
  201. package/dist/client/definePlaygroundAPI.js.map +0 -1
  202. package/dist/client/files.d.ts.map +0 -1
  203. package/dist/client/files.js.map +0 -1
  204. package/dist/client/index.d.ts.map +0 -1
  205. package/dist/client/index.js.map +0 -1
  206. package/dist/client/mockModel.d.ts.map +0 -1
  207. package/dist/client/mockModel.js.map +0 -1
  208. package/dist/client/saveInputMessages.d.ts.map +0 -1
  209. package/dist/client/saveInputMessages.js.map +0 -1
  210. package/dist/client/search.d.ts.map +0 -1
  211. package/dist/client/search.js.map +0 -1
  212. package/dist/client/start.d.ts.map +0 -1
  213. package/dist/client/start.js.map +0 -1
  214. package/dist/client/streamText.d.ts.map +0 -1
  215. package/dist/client/streamText.js.map +0 -1
  216. package/dist/client/streaming.d.ts.map +0 -1
  217. package/dist/client/streaming.js.map +0 -1
  218. package/dist/client/utils.d.ts.map +0 -1
  219. package/dist/client/utils.js.map +0 -1
  220. package/dist/deltas.d.ts +0 -43
  221. package/dist/deltas.d.ts.map +0 -1
  222. package/dist/deltas.js +0 -446
  223. package/dist/deltas.js.map +0 -1
  224. package/dist/mapping.d.ts.map +0 -1
  225. package/dist/mapping.js.map +0 -1
  226. package/dist/react/optimisticallySendMessage.d.ts.map +0 -1
  227. package/dist/react/optimisticallySendMessage.js.map +0 -1
  228. package/dist/react/types.d.ts.map +0 -1
  229. package/dist/react/useDeltaStreams.d.ts.map +0 -1
  230. package/dist/react/useDeltaStreams.js.map +0 -1
  231. package/dist/react/useStreamingUIMessages.d.ts.map +0 -1
  232. package/dist/react/useStreamingUIMessages.js +0 -92
  233. package/dist/react/useStreamingUIMessages.js.map +0 -1
  234. package/dist/react/useThreadMessages.d.ts.map +0 -1
  235. package/dist/react/useThreadMessages.js.map +0 -1
  236. package/dist/react/useUIMessages.d.ts.map +0 -1
  237. package/dist/react/useUIMessages.js.map +0 -1
  238. package/src/client/streamText.test.ts +0 -102
  239. package/src/deltas.test.ts +0 -626
  240. package/src/deltas.ts +0 -569
  241. package/src/mapping.test.ts +0 -402
  242. /package/dist/{client → vercel/client}/files.js +0 -0
  243. /package/dist/{client → vercel/client}/saveInputMessages.js +0 -0
  244. /package/dist/{react → vercel/react}/optimisticallySendMessage.js +0 -0
  245. /package/dist/{react → vercel/react}/types.js +0 -0
  246. /package/src/{UIMessages.test.ts → vercel/UIMessages.test.ts} +0 -0
@@ -26,7 +26,9 @@ import {
26
26
  type MessageWithMetadata,
27
27
  type Usage,
28
28
  type vFilePart,
29
+ type vCustomContentPart,
29
30
  type vImagePart,
31
+ type vReasoningFilePart,
30
32
  type vReasoningPart,
31
33
  type vRedactedReasoningPart,
32
34
  type vTextPart,
@@ -37,22 +39,27 @@ import {
37
39
  type MessageDoc,
38
40
  vToolApprovalRequest,
39
41
  vToolApprovalResponse,
40
- } from "./validators.js";
42
+ vProviderReference,
43
+ } from "../validators.js";
41
44
  import type { ActionCtx, AgentComponent } from "./client/types.js";
42
45
  import type { MutationCtx } from "./client/types.js";
43
46
  import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
44
47
  import type { Infer } from "convex/values";
45
48
  import {
46
49
  convertUint8ArrayToBase64,
50
+ type FileData,
47
51
  type ProviderOptions,
52
+ type ProviderReference,
53
+ type ReasoningFilePart,
48
54
  type ReasoningPart,
55
+ type ToolResultOutput,
49
56
  } from "@ai-sdk/provider-utils";
50
57
  import { parse, validate } from "convex-helpers/validators";
51
58
  import {
52
59
  getModelName,
53
60
  getProviderName,
54
61
  type ModelOrMetadata,
55
- } from "./shared.js";
62
+ } from "../shared.js";
56
63
  export type AIMessageWithoutId = Omit<AIMessage, "id">;
57
64
 
58
65
  export type SerializeUrlsAndUint8Arrays<T> = T extends URL
@@ -232,32 +239,15 @@ export function autoDenyUnresolvedApprovals(
232
239
 
233
240
  export function serializeUsage(usage: LanguageModelUsage): Usage {
234
241
  return {
235
- promptTokens: usage.inputTokens ?? 0,
236
- completionTokens: usage.outputTokens ?? 0,
237
- totalTokens: usage.totalTokens ?? 0,
238
- reasoningTokens: usage.reasoningTokens,
239
- cachedInputTokens: usage.cachedInputTokens,
240
- };
241
- }
242
-
243
- export function toModelMessageUsage(usage: Usage): LanguageModelUsage {
244
- return {
245
- inputTokens: usage.promptTokens,
246
- outputTokens: usage.completionTokens,
242
+ promptTokens: usage.inputTokens,
243
+ completionTokens: usage.outputTokens,
247
244
  totalTokens: usage.totalTokens,
248
- reasoningTokens: usage.reasoningTokens,
249
- cachedInputTokens: usage.cachedInputTokens,
250
- // These detail fields are required by LanguageModelUsage type but we don't
251
- // have the granular data, so we provide empty objects with undefined values.
252
- inputTokenDetails: {
253
- cacheReadTokens: undefined,
254
- cacheWriteTokens: undefined,
255
- noCacheTokens: undefined,
256
- },
257
- outputTokenDetails: {
258
- textTokens: undefined,
259
- reasoningTokens: undefined,
260
- },
245
+ reasoningTokens: usage.outputTokenDetails?.reasoningTokens,
246
+ cachedInputTokens: usage.inputTokenDetails?.cacheReadTokens,
247
+ nonCachedInputTokens: usage.inputTokenDetails?.noCacheTokens,
248
+ cacheWriteInputTokens: usage.inputTokenDetails?.cacheWriteTokens,
249
+ textOutputTokens: usage.outputTokenDetails?.textTokens,
250
+ raw: usage.raw,
261
251
  };
262
252
  }
263
253
 
@@ -268,33 +258,24 @@ export function serializeWarnings(
268
258
  return undefined;
269
259
  }
270
260
  return warnings.map((warning) => {
271
- if (warning.type === "compatibility") {
272
- return {
273
- type: "unsupported-setting",
274
- setting: warning.feature,
275
- details: warning.details,
276
- };
261
+ switch (warning.type) {
262
+ case "unsupported":
263
+ case "compatibility":
264
+ return {
265
+ type: warning.type,
266
+ feature: warning.feature,
267
+ details: warning.details,
268
+ };
269
+ case "deprecated":
270
+ return {
271
+ type: warning.type,
272
+ setting: warning.setting,
273
+ message: warning.message,
274
+ };
275
+ case "other":
276
+ return { type: warning.type, message: warning.message };
277
277
  }
278
- return warning;
279
- }) as any;
280
- }
281
-
282
- export function toModelMessageWarnings(
283
- warnings: MessageWithMetadata["warnings"],
284
- ): CallWarning[] | undefined {
285
- if (!warnings) {
286
- return undefined;
287
- }
288
- return warnings.map((warning) => {
289
- if (warning.type === "unsupported-setting") {
290
- return {
291
- type: "compatibility",
292
- feature: warning.setting,
293
- details: warning.details,
294
- };
295
- }
296
- return warning;
297
- }) as any;
278
+ });
298
279
  }
299
280
 
300
281
  /**
@@ -312,28 +293,6 @@ export async function serializeResponseMessages<TOOLS extends ToolSet>(
312
293
  return serializeStepMessages(ctx, component, step, model, responseMessages);
313
294
  }
314
295
 
315
- /**
316
- * Serialize the new messages from a step using a heuristic to determine
317
- * which response messages are new (last 1-2 messages).
318
- */
319
- export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
320
- ctx: ActionCtx,
321
- component: AgentComponent,
322
- step: StepResult<TOOLS>,
323
- model: ModelOrMetadata | undefined,
324
- ): Promise<{ messages: MessageWithMetadata[] }> {
325
- const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
326
- let messagesToSerialize: ModelMessage[];
327
- if (hasToolMessage) {
328
- messagesToSerialize = step.response.messages.slice(-2);
329
- } else if (step.content.length) {
330
- messagesToSerialize = step.response.messages.slice(-1);
331
- } else {
332
- messagesToSerialize = [{ role: "assistant" as const, content: [] }];
333
- }
334
- return serializeStepMessages(ctx, component, step, model, messagesToSerialize);
335
- }
336
-
337
296
  async function serializeStepMessages<TOOLS extends ToolSet>(
338
297
  ctx: ActionCtx,
339
298
  component: AgentComponent,
@@ -344,12 +303,15 @@ async function serializeStepMessages<TOOLS extends ToolSet>(
344
303
  // If there are tool results, there's another message with the tool results
345
304
  // ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
346
305
  const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
306
+ const resolvedModel = step.model ?? model;
347
307
  const assistantFields = {
348
- model: model ? getModelName(model) : undefined,
349
- provider: model ? getProviderName(model) : undefined,
308
+ model: resolvedModel ? getModelName(resolvedModel) : undefined,
309
+ provider: resolvedModel ? getProviderName(resolvedModel) : undefined,
350
310
  providerMetadata: step.providerMetadata,
351
311
  reasoning: step.reasoningText,
352
- reasoningDetails: step.reasoning,
312
+ reasoningDetails: step.reasoning.filter(
313
+ (part) => part.type !== "reasoning-file",
314
+ ),
353
315
  usage: serializeUsage(step.usage),
354
316
  warnings: serializeWarnings(step.warnings),
355
317
  finishReason: step.finishReason,
@@ -369,7 +331,6 @@ async function serializeStepMessages<TOOLS extends ToolSet>(
369
331
  });
370
332
  }),
371
333
  );
372
- // TODO: capture step.files separately?
373
334
  return { messages };
374
335
  }
375
336
 
@@ -442,7 +403,9 @@ export async function serializeContent(
442
403
  } satisfies Infer<typeof vTextPart>;
443
404
  }
444
405
  case "image": {
445
- let image = serializeDataOrUrl(part.image);
406
+ let image =
407
+ toStoredProviderReference(part.image) ??
408
+ serializeDataOrUrl(part.image);
446
409
  if (
447
410
  image instanceof ArrayBuffer &&
448
411
  image.byteLength > MAX_FILE_SIZE
@@ -465,7 +428,9 @@ export async function serializeContent(
465
428
  } satisfies Infer<typeof vImagePart>;
466
429
  }
467
430
  case "file": {
468
- let data = serializeDataOrUrl(part.data);
431
+ let data =
432
+ toStoredProviderReference(part.data) ??
433
+ serializeDataOrUrl(part.data);
469
434
  if (data instanceof ArrayBuffer && data.byteLength > MAX_FILE_SIZE) {
470
435
  const { file } = await storeFile(
471
436
  ctx,
@@ -494,19 +459,41 @@ export async function serializeContent(
494
459
  toolCallId: part.toolCallId,
495
460
  toolName: part.toolName,
496
461
  providerExecuted: part.providerExecuted,
462
+ title: "title" in part ? part.title : undefined,
463
+ toolMetadata:
464
+ "toolMetadata" in part ? part.toolMetadata : undefined,
497
465
  ...metadata,
498
466
  } satisfies Infer<typeof vToolCallPart>;
499
467
  }
500
468
  case "tool-result": {
501
- return normalizeToolResult(part, metadata);
469
+ return serializeToolResult(part, metadata);
502
470
  }
503
471
  case "reasoning": {
504
472
  return {
505
473
  type: part.type,
506
474
  text: part.text,
475
+ signature:
476
+ "signature" in part && typeof part.signature === "string"
477
+ ? part.signature
478
+ : undefined,
507
479
  ...metadata,
508
480
  } satisfies Infer<typeof vReasoningPart>;
509
481
  }
482
+ case "reasoning-file": {
483
+ return {
484
+ type: part.type,
485
+ ...serializeReasoningFile(part),
486
+ mediaType: part.mediaType,
487
+ ...metadata,
488
+ } satisfies Infer<typeof vReasoningFilePart>;
489
+ }
490
+ case "custom": {
491
+ return {
492
+ type: part.type,
493
+ kind: part.kind,
494
+ ...metadata,
495
+ } satisfies Infer<typeof vCustomContentPart>;
496
+ }
510
497
  // Not in current generation output, but could be in historical messages
511
498
  case "redacted-reasoning": {
512
499
  return {
@@ -523,6 +510,8 @@ export async function serializeContent(
523
510
  type: part.type,
524
511
  approvalId: part.approvalId,
525
512
  toolCallId: part.toolCallId,
513
+ isAutomatic: "isAutomatic" in part ? part.isAutomatic : undefined,
514
+ signature: "signature" in part ? part.signature : undefined,
526
515
  ...metadata,
527
516
  } satisfies Infer<typeof vToolApprovalRequest>;
528
517
  }
@@ -571,12 +560,16 @@ export function fromModelMessageContent(content: Content): Message["content"] {
571
560
  type: part.type,
572
561
  mediaType: getMimeOrMediaType(part),
573
562
  ...metadata,
574
- image: serializeDataOrUrl(part.image),
563
+ image:
564
+ toStoredProviderReference(part.image) ??
565
+ serializeDataOrUrl(part.image),
575
566
  } satisfies Infer<typeof vImagePart>;
576
567
  case "file":
577
568
  return {
578
569
  type: part.type,
579
- data: serializeDataOrUrl(part.data),
570
+ data:
571
+ toStoredProviderReference(part.data) ??
572
+ serializeDataOrUrl(part.data),
580
573
  filename: part.filename,
581
574
  mediaType: getMimeOrMediaType(part)!,
582
575
  ...metadata,
@@ -594,18 +587,38 @@ export function fromModelMessageContent(content: Content): Message["content"] {
594
587
  ...metadata,
595
588
  } satisfies Infer<typeof vToolCallPart>;
596
589
  case "tool-result":
597
- return normalizeToolResult(part, metadata);
590
+ return serializeToolResult(part, metadata);
598
591
  case "reasoning":
599
592
  return {
600
593
  type: part.type,
601
594
  text: part.text,
595
+ signature:
596
+ "signature" in part && typeof part.signature === "string"
597
+ ? part.signature
598
+ : undefined,
602
599
  ...metadata,
603
600
  } satisfies Infer<typeof vReasoningPart>;
601
+ case "reasoning-file": {
602
+ return {
603
+ type: part.type,
604
+ ...serializeReasoningFile(part),
605
+ mediaType: part.mediaType,
606
+ ...metadata,
607
+ } satisfies Infer<typeof vReasoningFilePart>;
608
+ }
609
+ case "custom":
610
+ return {
611
+ type: part.type,
612
+ kind: part.kind,
613
+ ...metadata,
614
+ } satisfies Infer<typeof vCustomContentPart>;
604
615
  case "tool-approval-request":
605
616
  return {
606
617
  type: part.type,
607
618
  approvalId: part.approvalId,
608
619
  toolCallId: part.toolCallId,
620
+ isAutomatic: "isAutomatic" in part ? part.isAutomatic : undefined,
621
+ signature: "signature" in part ? part.signature : undefined,
609
622
  ...metadata,
610
623
  } satisfies Infer<typeof vToolApprovalRequest>;
611
624
  case "tool-approval-response":
@@ -653,14 +666,18 @@ export function toModelMessageContent(
653
666
  case "image":
654
667
  return {
655
668
  type: part.type,
656
- image: toModelMessageDataOrUrl(part.image),
669
+ image: (isStoredProviderReference(part.image)
670
+ ? part.image.reference
671
+ : toModelMessageDataOrUrl(part.image)) as ImagePart["image"],
657
672
  mediaType: getMimeOrMediaType(part),
658
673
  ...metadata,
659
674
  } satisfies ImagePart;
660
675
  case "file":
661
676
  return {
662
677
  type: part.type,
663
- data: toModelMessageDataOrUrl(part.data),
678
+ data: (isStoredProviderReference(part.data)
679
+ ? { type: "reference" as const, reference: part.data.reference }
680
+ : toModelMessageDataOrUrl(part.data)) as FilePart["data"],
664
681
  filename: part.filename,
665
682
  mediaType: getMimeOrMediaType(part)!,
666
683
  ...metadata,
@@ -678,7 +695,7 @@ export function toModelMessageContent(
678
695
  } satisfies ToolCallPart;
679
696
  }
680
697
  case "tool-result": {
681
- return normalizeToolResult(part, metadata);
698
+ return deserializeToolResult(part, metadata);
682
699
  }
683
700
  case "reasoning":
684
701
  return {
@@ -686,23 +703,32 @@ export function toModelMessageContent(
686
703
  text: part.text,
687
704
  ...metadata,
688
705
  } satisfies ReasoningPart;
706
+ case "reasoning-file":
707
+ return {
708
+ type: "reasoning-file",
709
+ data: deserializeReasoningFile(
710
+ part as Infer<typeof vReasoningFilePart>,
711
+ ),
712
+ mediaType: part.mediaType,
713
+ ...metadata,
714
+ } satisfies ReasoningFilePart;
715
+ case "custom":
716
+ return { type: "custom", kind: part.kind, ...metadata };
689
717
  case "redacted-reasoning":
690
- // TODO: should we just drop this?
718
+ // Legacy v5 part: round-trip the redacted payload via providerOptions.
691
719
  return {
692
720
  type: "reasoning",
693
721
  text: "",
694
722
  ...metadata,
695
723
  providerOptions: metadata.providerOptions
696
- ? {
697
- ...Object.fromEntries(
698
- Object.entries(metadata.providerOptions ?? {}).map(
699
- ([key, value]) => [
700
- key,
701
- { ...value, redactedData: part.data },
702
- ],
703
- ),
724
+ ? Object.fromEntries(
725
+ Object.entries(metadata.providerOptions ?? {}).map(
726
+ ([key, value]) => [
727
+ key,
728
+ { ...value, redactedData: part.data },
729
+ ],
704
730
  ),
705
- }
731
+ )
706
732
  : undefined,
707
733
  } satisfies ReasoningPart;
708
734
  case "source":
@@ -712,6 +738,8 @@ export function toModelMessageContent(
712
738
  type: part.type,
713
739
  approvalId: part.approvalId,
714
740
  toolCallId: part.toolCallId,
741
+ isAutomatic: "isAutomatic" in part ? part.isAutomatic : undefined,
742
+ signature: "signature" in part ? part.signature : undefined,
715
743
  ...metadata,
716
744
  } satisfies Infer<typeof vToolApprovalRequest>;
717
745
  case "tool-approval-response":
@@ -740,7 +768,7 @@ export function normalizeToolOutput(
740
768
  };
741
769
  }
742
770
  if (validate(vToolResultOutput, result)) {
743
- return result;
771
+ return result as unknown as ToolResultOutput;
744
772
  }
745
773
  return {
746
774
  type: "json",
@@ -748,28 +776,277 @@ export function normalizeToolOutput(
748
776
  };
749
777
  }
750
778
 
751
- function normalizeToolResult(
779
+ function serializeToolResult(
752
780
  part: ToolResultPart | Infer<typeof vToolResultPart>,
753
781
  metadata: {
754
782
  providerOptions?: ProviderOptions;
755
783
  providerMetadata?: ProviderMetadata;
756
784
  },
757
- ): ToolResultPart & Infer<typeof vToolResultPart> {
785
+ ): Infer<typeof vToolResultPart> {
758
786
  return {
759
787
  type: part.type,
760
- output: part.output
761
- ? validate(vToolResultOutput, part.output)
762
- ? (part.output as any)
763
- : normalizeToolOutput(JSON.stringify(part.output))
764
- : normalizeToolOutput("result" in part ? part.result : undefined),
788
+ output:
789
+ "output" in part && part.output !== undefined
790
+ ? serializeToolResultOutput(part.output)
791
+ : (normalizeToolOutput(
792
+ "result" in part ? part.result : undefined,
793
+ ) as Infer<typeof vToolResultOutput>),
765
794
  toolCallId: part.toolCallId,
766
795
  toolName: part.toolName,
796
+ ...("providerExecuted" in part
797
+ ? { providerExecuted: part.providerExecuted }
798
+ : {}),
767
799
  // Preserve isError flag for error reporting
768
800
  ...("isError" in part && part.isError ? { isError: true } : {}),
769
801
  ...metadata,
802
+ } as Infer<typeof vToolResultPart>;
803
+ }
804
+
805
+ function deserializeToolResult(
806
+ part: ToolResultPart | Infer<typeof vToolResultPart>,
807
+ metadata: {
808
+ providerOptions?: ProviderOptions;
809
+ providerMetadata?: ProviderMetadata;
810
+ },
811
+ ): ToolResultPart {
812
+ return {
813
+ type: part.type,
814
+ output:
815
+ "output" in part && part.output !== undefined
816
+ ? deserializeToolResultOutput(part.output)
817
+ : normalizeToolOutput("result" in part ? part.result : undefined),
818
+ toolCallId: part.toolCallId,
819
+ toolName: part.toolName,
820
+ ...("isError" in part && part.isError ? { isError: true } : {}),
821
+ ...metadata,
770
822
  } satisfies ToolResultPart;
771
823
  }
772
824
 
825
+ function serializeToolResultOutput(
826
+ output: unknown,
827
+ ): Infer<typeof vToolResultOutput> {
828
+ if (output === undefined) {
829
+ return normalizeToolOutput(undefined) as Infer<typeof vToolResultOutput>;
830
+ }
831
+ if (!isRecord(output) || typeof output.type !== "string") {
832
+ return normalizeToolOutput(output as JSONValue) as Infer<
833
+ typeof vToolResultOutput
834
+ >;
835
+ }
836
+ if (output.type !== "content") {
837
+ if (validate(vToolResultOutput, output)) {
838
+ return output as Infer<typeof vToolResultOutput>;
839
+ }
840
+ throw new Error(`Invalid AI SDK 7 tool-result output: ${output.type}`);
841
+ }
842
+ if (!Array.isArray(output.value)) {
843
+ throw new Error("Invalid AI SDK 7 tool-result content output");
844
+ }
845
+ const serialized = {
846
+ ...output,
847
+ value: output.value.map(serializeToolResultContentPart),
848
+ };
849
+ if (!validate(vToolResultOutput, serialized)) {
850
+ throw new Error("Invalid AI SDK 7 tool-result content part");
851
+ }
852
+ return serialized as Infer<typeof vToolResultOutput>;
853
+ }
854
+
855
+ function serializeToolResultContentPart(part: unknown): unknown {
856
+ if (!isRecord(part) || typeof part.type !== "string") {
857
+ throw new Error("Invalid AI SDK 7 tool-result content part");
858
+ }
859
+ if (part.type !== "file") return part;
860
+ if (!isRecord(part.data) || typeof part.data.type !== "string") {
861
+ throw new Error("Invalid AI SDK 7 tool-result file data");
862
+ }
863
+
864
+ const common = {
865
+ type: "file" as const,
866
+ mediaType: part.mediaType,
867
+ ...(typeof part.filename === "string" ? { filename: part.filename } : {}),
868
+ ...(isRecord(part.providerOptions)
869
+ ? { providerOptions: part.providerOptions }
870
+ : {}),
871
+ };
872
+ switch (part.data.type) {
873
+ case "data":
874
+ return {
875
+ ...common,
876
+ data: {
877
+ type: "data" as const,
878
+ data: serializeDataOrUrl(part.data.data as DataContent),
879
+ },
880
+ };
881
+ case "url": {
882
+ const { url } = part.data;
883
+ if (typeof url !== "string" && !(url instanceof URL)) {
884
+ throw new Error("Invalid AI SDK 7 tool-result file URL");
885
+ }
886
+ return { ...common, data: { type: "url" as const, url: url.toString() } };
887
+ }
888
+ case "text":
889
+ if (typeof part.data.text !== "string") {
890
+ throw new Error("Invalid AI SDK 7 tool-result file text");
891
+ }
892
+ return {
893
+ ...common,
894
+ data: { type: "text" as const, text: part.data.text },
895
+ };
896
+ case "reference":
897
+ if (!isProviderReference(part.data.reference)) {
898
+ throw new Error("Invalid AI SDK 7 tool-result file reference");
899
+ }
900
+ return {
901
+ ...common,
902
+ data: { type: "reference" as const, reference: part.data.reference },
903
+ };
904
+ default:
905
+ throw new Error(
906
+ `Invalid AI SDK 7 tool-result file data: ${part.data.type}`,
907
+ );
908
+ }
909
+ }
910
+
911
+ function deserializeToolResultOutput(output: unknown): ToolResultOutput {
912
+ if (!validate(vToolResultOutput, output)) {
913
+ return normalizeToolOutput(output as JSONValue);
914
+ }
915
+ const stored = output as Infer<typeof vToolResultOutput>;
916
+ if (stored.type !== "content") return stored as unknown as ToolResultOutput;
917
+ return {
918
+ type: "content",
919
+ value: stored.value.map(deserializeToolResultContentPart),
920
+ } as ToolResultOutput;
921
+ }
922
+
923
+ function deserializeToolResultContentPart(
924
+ part: Extract<
925
+ Infer<typeof vToolResultOutput>,
926
+ { type: "content" }
927
+ >["value"][number],
928
+ ) {
929
+ if (part.type === "file") {
930
+ return {
931
+ ...part,
932
+ data:
933
+ part.data.type === "url"
934
+ ? { type: "url" as const, url: new URL(part.data.url) }
935
+ : part.data,
936
+ };
937
+ }
938
+ switch (part.type) {
939
+ case "media":
940
+ return {
941
+ type: "file" as const,
942
+ data: { type: "data" as const, data: part.data },
943
+ mediaType: part.mediaType,
944
+ };
945
+ case "file-data":
946
+ return {
947
+ type: "file" as const,
948
+ data: { type: "data" as const, data: part.data },
949
+ mediaType: part.mediaType,
950
+ ...(part.filename !== undefined ? { filename: part.filename } : {}),
951
+ ...(part.providerOptions !== undefined
952
+ ? { providerOptions: part.providerOptions }
953
+ : {}),
954
+ };
955
+ case "file-url":
956
+ return {
957
+ type: "file" as const,
958
+ data: { type: "url" as const, url: new URL(part.url) },
959
+ mediaType: part.mediaType ?? "application/octet-stream",
960
+ ...(part.providerOptions !== undefined
961
+ ? { providerOptions: part.providerOptions }
962
+ : {}),
963
+ };
964
+ case "file-id":
965
+ case "image-file-id":
966
+ if (typeof part.fileId === "string") return part;
967
+ return {
968
+ type: "file" as const,
969
+ data: {
970
+ type: "reference" as const,
971
+ reference: part.fileId,
972
+ },
973
+ mediaType:
974
+ part.type === "image-file-id" ? "image" : "application/octet-stream",
975
+ ...(part.providerOptions !== undefined
976
+ ? { providerOptions: part.providerOptions }
977
+ : {}),
978
+ };
979
+ case "file-reference":
980
+ case "image-file-reference":
981
+ return {
982
+ type: "file" as const,
983
+ data: { type: "reference" as const, reference: part.providerReference },
984
+ mediaType:
985
+ part.type === "image-file-reference"
986
+ ? "image"
987
+ : "application/octet-stream",
988
+ ...(part.providerOptions !== undefined
989
+ ? { providerOptions: part.providerOptions }
990
+ : {}),
991
+ };
992
+ case "image-data":
993
+ return {
994
+ type: "file" as const,
995
+ data: { type: "data" as const, data: part.data },
996
+ mediaType: part.mediaType,
997
+ ...(part.providerOptions !== undefined
998
+ ? { providerOptions: part.providerOptions }
999
+ : {}),
1000
+ };
1001
+ case "image-url":
1002
+ return {
1003
+ type: "file" as const,
1004
+ data: { type: "url" as const, url: new URL(part.url) },
1005
+ mediaType: "image",
1006
+ ...(part.providerOptions !== undefined
1007
+ ? { providerOptions: part.providerOptions }
1008
+ : {}),
1009
+ };
1010
+ default:
1011
+ return part;
1012
+ }
1013
+ }
1014
+
1015
+ function serializeReasoningFile(
1016
+ part: ReasoningFilePart | Infer<typeof vReasoningFilePart>,
1017
+ ): Pick<Infer<typeof vReasoningFilePart>, "url" | "data"> {
1018
+ if ("url" in part && part.url !== undefined) {
1019
+ return { url: part.url.toString() };
1020
+ }
1021
+ if (!("data" in part) || part.data === undefined) {
1022
+ throw new Error("reasoning-file requires data or url");
1023
+ }
1024
+ const { data } = part;
1025
+ if (data instanceof URL) return { url: data.toString() };
1026
+ if (isRecord(data) && data.type === "url" && data.url instanceof URL) {
1027
+ return { url: data.url.toString() };
1028
+ }
1029
+ if (isRecord(data) && data.type === "data") {
1030
+ return { data: serializeDataOrUrl(data.data as DataContent) };
1031
+ }
1032
+ if (isRecord(data) && "type" in data) {
1033
+ throw new Error("Invalid AI SDK 7 reasoning-file data");
1034
+ }
1035
+ return { data: serializeDataOrUrl(data) };
1036
+ }
1037
+
1038
+ function deserializeReasoningFile(
1039
+ part: Infer<typeof vReasoningFilePart>,
1040
+ ): Extract<ReasoningFilePart["data"], { type: string }> {
1041
+ if (part.url !== undefined) {
1042
+ return { type: "url", url: new URL(part.url) };
1043
+ }
1044
+ if (part.data !== undefined) {
1045
+ return { type: "data", data: part.data };
1046
+ }
1047
+ throw new Error("reasoning-file requires data or url");
1048
+ }
1049
+
773
1050
  /**
774
1051
  * Return a best-guess MIME type based on the magic-number signature
775
1052
  * found at the start of an ArrayBuffer.
@@ -843,7 +1120,7 @@ export function guessMimeType(buf: ArrayBuffer | string): string {
843
1120
  * @returns The serialized data as an ArrayBuffer or the URL as a string.
844
1121
  */
845
1122
  export function serializeDataOrUrl(
846
- dataOrUrl: DataContent | URL,
1123
+ dataOrUrl: DataContent | URL | ProviderReference | FileData,
847
1124
  ): ArrayBuffer | string {
848
1125
  if (typeof dataOrUrl === "string") {
849
1126
  return dataOrUrl;
@@ -854,6 +1131,23 @@ export function serializeDataOrUrl(
854
1131
  if (dataOrUrl instanceof URL) {
855
1132
  return dataOrUrl.toString();
856
1133
  }
1134
+ if ("type" in dataOrUrl) {
1135
+ switch (dataOrUrl.type) {
1136
+ case "data":
1137
+ return serializeDataOrUrl(dataOrUrl.data);
1138
+ case "url":
1139
+ return dataOrUrl.url.toString();
1140
+ case "text":
1141
+ return convertUint8ArrayToBase64(
1142
+ new TextEncoder().encode(dataOrUrl.text),
1143
+ );
1144
+ case "reference":
1145
+ throw new Error("Provider references must be stored as references");
1146
+ }
1147
+ }
1148
+ if (!(dataOrUrl instanceof Uint8Array)) {
1149
+ throw new Error("Unsupported provider reference");
1150
+ }
857
1151
  return dataOrUrl.buffer.slice(
858
1152
  dataOrUrl.byteOffset,
859
1153
  dataOrUrl.byteOffset + dataOrUrl.byteLength,
@@ -861,8 +1155,14 @@ export function serializeDataOrUrl(
861
1155
  }
862
1156
 
863
1157
  export function toModelMessageDataOrUrl(
864
- urlOrString: string | ArrayBuffer | URL | DataContent,
865
- ): URL | DataContent {
1158
+ urlOrString:
1159
+ | string
1160
+ | ArrayBuffer
1161
+ | URL
1162
+ | DataContent
1163
+ | ProviderReference
1164
+ | FileData,
1165
+ ): URL | DataContent | ProviderReference | FileData {
866
1166
  if (urlOrString instanceof URL) {
867
1167
  return urlOrString;
868
1168
  }
@@ -878,37 +1178,107 @@ export function toModelMessageDataOrUrl(
878
1178
  return urlOrString;
879
1179
  }
880
1180
 
1181
+ type StoredProviderReference = Infer<typeof vProviderReference>;
1182
+
1183
+ function isRecord(value: unknown): value is Record<string, unknown> {
1184
+ return typeof value === "object" && value !== null;
1185
+ }
1186
+
1187
+ function isProviderReference(value: unknown): value is ProviderReference {
1188
+ return (
1189
+ isRecord(value) &&
1190
+ !(value instanceof ArrayBuffer) &&
1191
+ !(value instanceof Uint8Array) &&
1192
+ !(value instanceof URL) &&
1193
+ !("type" in value) &&
1194
+ Object.values(value).every((id) => typeof id === "string")
1195
+ );
1196
+ }
1197
+
1198
+ function isStoredProviderReference(
1199
+ value: unknown,
1200
+ ): value is StoredProviderReference {
1201
+ return (
1202
+ isRecord(value) &&
1203
+ value.type === "reference" &&
1204
+ isProviderReference(value.reference)
1205
+ );
1206
+ }
1207
+
1208
+ function toStoredProviderReference(
1209
+ value: unknown,
1210
+ ): StoredProviderReference | undefined {
1211
+ if (isStoredProviderReference(value)) return value;
1212
+ if (isProviderReference(value))
1213
+ return { type: "reference", reference: value };
1214
+ if (
1215
+ isRecord(value) &&
1216
+ value.type === "reference" &&
1217
+ isProviderReference(value.reference)
1218
+ ) {
1219
+ return { type: "reference", reference: value.reference };
1220
+ }
1221
+ return undefined;
1222
+ }
1223
+
1224
+ function isFileReference(
1225
+ value: unknown,
1226
+ ): value is { type: "reference"; reference: ProviderReference } {
1227
+ return (
1228
+ isRecord(value) &&
1229
+ value.type === "reference" &&
1230
+ isProviderReference(value.reference)
1231
+ );
1232
+ }
1233
+
881
1234
  export function toUIFilePart(part: ImagePart | FilePart): FileUIPart {
882
1235
  const dataOrUrl = part.type === "image" ? part.image : part.data;
883
- const url =
884
- dataOrUrl instanceof ArrayBuffer
885
- ? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
886
- : dataOrUrl.toString();
1236
+ const providerReference = isProviderReference(dataOrUrl)
1237
+ ? dataOrUrl
1238
+ : isFileReference(dataOrUrl)
1239
+ ? dataOrUrl.reference
1240
+ : undefined;
1241
+ const url = providerReference
1242
+ ? ""
1243
+ : toUIFileUrl(dataOrUrl, part.mediaType ?? "application/octet-stream");
887
1244
 
888
1245
  return {
889
1246
  type: "file",
890
1247
  mediaType: part.mediaType!,
891
1248
  filename: part.type === "file" ? part.filename : undefined,
892
1249
  url,
1250
+ ...(providerReference !== undefined ? { providerReference } : {}),
893
1251
  providerMetadata: part.providerOptions,
894
1252
  };
895
1253
  }
896
1254
 
897
- // Currently unused
898
- // export function toModelMessages(args: {
899
- // messages?: ModelMessage[] | AIMessageWithoutId[];
900
- // }): ModelMessage[] {
901
- // const messages: ModelMessage[] = [];
902
- // if (args.messages) {
903
- // if (
904
- // args.messages.every(
905
- // (m) => typeof m === "object" && m !== null && "parts" in m,
906
- // )
907
- // ) {
908
- // messages.push(...convertToModelMessages(args.messages));
909
- // } else {
910
- // messages.push(...modelMessageSchema.array().parse(args.messages));
911
- // }
912
- // }
913
- // return messages;
914
- // }
1255
+ function toUIFileUrl(data: unknown, mediaType: string): string {
1256
+ if (isFileReference(data)) return "";
1257
+ if (isRecord(data) && typeof data.type === "string") {
1258
+ switch (data.type) {
1259
+ case "url":
1260
+ return (data.url as URL).toString();
1261
+ case "data":
1262
+ return toUIFileDataUrl(data.data as DataContent, mediaType);
1263
+ case "text":
1264
+ return toUIFileDataUrl(
1265
+ new TextEncoder().encode(data.text as string),
1266
+ mediaType,
1267
+ );
1268
+ }
1269
+ }
1270
+ if (data instanceof URL) return data.toString();
1271
+ if (typeof data === "string") return data;
1272
+ return toUIFileDataUrl(data as DataContent, mediaType);
1273
+ }
1274
+
1275
+ function toUIFileDataUrl(data: DataContent, mediaType: string): string {
1276
+ if (typeof data === "string") {
1277
+ return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
1278
+ }
1279
+ const bytes =
1280
+ data instanceof ArrayBuffer
1281
+ ? new Uint8Array(data)
1282
+ : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1283
+ return `data:${mediaType};base64,${convertUint8ArrayToBase64(bytes)}`;
1284
+ }