@convex-dev/agent 0.6.0-alpha.1 → 0.6.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 (181) hide show
  1. package/package.json +1 -1
  2. package/src/UIMessages.ts +135 -0
  3. package/src/client/index.ts +56 -14
  4. package/src/client/search.test.ts +4 -5
  5. package/src/client/search.ts +45 -4
  6. package/src/client/streaming.integration.test.ts +1206 -0
  7. package/src/client/streaming.ts +9 -2
  8. package/src/mapping.test.ts +136 -71
  9. package/src/mapping.ts +81 -28
  10. package/dist/UIMessages.d.ts +0 -46
  11. package/dist/UIMessages.d.ts.map +0 -1
  12. package/dist/UIMessages.js +0 -546
  13. package/dist/UIMessages.js.map +0 -1
  14. package/dist/client/createTool.d.ts +0 -167
  15. package/dist/client/createTool.d.ts.map +0 -1
  16. package/dist/client/createTool.js +0 -116
  17. package/dist/client/createTool.js.map +0 -1
  18. package/dist/client/defaultComponent.d.ts +0 -11
  19. package/dist/client/defaultComponent.d.ts.map +0 -1
  20. package/dist/client/defaultComponent.js +0 -7
  21. package/dist/client/defaultComponent.js.map +0 -1
  22. package/dist/client/definePlaygroundAPI.d.ts +0 -1725
  23. package/dist/client/definePlaygroundAPI.d.ts.map +0 -1
  24. package/dist/client/definePlaygroundAPI.js +0 -271
  25. package/dist/client/definePlaygroundAPI.js.map +0 -1
  26. package/dist/client/files.d.ts +0 -69
  27. package/dist/client/files.d.ts.map +0 -1
  28. package/dist/client/files.js +0 -181
  29. package/dist/client/files.js.map +0 -1
  30. package/dist/client/index.d.ts +0 -2091
  31. package/dist/client/index.d.ts.map +0 -1
  32. package/dist/client/index.js +0 -895
  33. package/dist/client/index.js.map +0 -1
  34. package/dist/client/messages.d.ts +0 -461
  35. package/dist/client/messages.d.ts.map +0 -1
  36. package/dist/client/messages.js +0 -106
  37. package/dist/client/messages.js.map +0 -1
  38. package/dist/client/mockModel.d.ts +0 -42
  39. package/dist/client/mockModel.d.ts.map +0 -1
  40. package/dist/client/mockModel.js +0 -182
  41. package/dist/client/mockModel.js.map +0 -1
  42. package/dist/client/saveInputMessages.d.ts +0 -20
  43. package/dist/client/saveInputMessages.d.ts.map +0 -1
  44. package/dist/client/saveInputMessages.js +0 -58
  45. package/dist/client/saveInputMessages.js.map +0 -1
  46. package/dist/client/search.d.ts +0 -493
  47. package/dist/client/search.d.ts.map +0 -1
  48. package/dist/client/search.js +0 -425
  49. package/dist/client/search.js.map +0 -1
  50. package/dist/client/start.d.ts +0 -84
  51. package/dist/client/start.d.ts.map +0 -1
  52. package/dist/client/start.js +0 -185
  53. package/dist/client/start.js.map +0 -1
  54. package/dist/client/streamText.d.ts +0 -46
  55. package/dist/client/streamText.d.ts.map +0 -1
  56. package/dist/client/streamText.js +0 -117
  57. package/dist/client/streamText.js.map +0 -1
  58. package/dist/client/streaming.d.ts +0 -3778
  59. package/dist/client/streaming.d.ts.map +0 -1
  60. package/dist/client/streaming.js +0 -314
  61. package/dist/client/streaming.js.map +0 -1
  62. package/dist/client/threads.d.ts +0 -46
  63. package/dist/client/threads.d.ts.map +0 -1
  64. package/dist/client/threads.js +0 -49
  65. package/dist/client/threads.js.map +0 -1
  66. package/dist/client/types.d.ts +0 -461
  67. package/dist/client/types.d.ts.map +0 -1
  68. package/dist/client/types.js +0 -2
  69. package/dist/client/types.js.map +0 -1
  70. package/dist/client/utils.d.ts +0 -4
  71. package/dist/client/utils.d.ts.map +0 -1
  72. package/dist/client/utils.js +0 -21
  73. package/dist/client/utils.js.map +0 -1
  74. package/dist/component/_generated/api.d.ts +0 -48
  75. package/dist/component/_generated/api.d.ts.map +0 -1
  76. package/dist/component/_generated/api.js +0 -31
  77. package/dist/component/_generated/api.js.map +0 -1
  78. package/dist/component/_generated/component.d.ts +0 -3120
  79. package/dist/component/_generated/component.d.ts.map +0 -1
  80. package/dist/component/_generated/component.js +0 -11
  81. package/dist/component/_generated/component.js.map +0 -1
  82. package/dist/component/_generated/dataModel.d.ts +0 -46
  83. package/dist/component/_generated/dataModel.d.ts.map +0 -1
  84. package/dist/component/_generated/dataModel.js +0 -11
  85. package/dist/component/_generated/dataModel.js.map +0 -1
  86. package/dist/component/_generated/server.d.ts +0 -121
  87. package/dist/component/_generated/server.d.ts.map +0 -1
  88. package/dist/component/_generated/server.js +0 -78
  89. package/dist/component/_generated/server.js.map +0 -1
  90. package/dist/component/apiKeys.d.ts +0 -11
  91. package/dist/component/apiKeys.d.ts.map +0 -1
  92. package/dist/component/apiKeys.js +0 -69
  93. package/dist/component/apiKeys.js.map +0 -1
  94. package/dist/component/convex.config.d.ts +0 -3
  95. package/dist/component/convex.config.d.ts.map +0 -1
  96. package/dist/component/convex.config.js +0 -3
  97. package/dist/component/convex.config.js.map +0 -1
  98. package/dist/component/files.d.ts +0 -97
  99. package/dist/component/files.d.ts.map +0 -1
  100. package/dist/component/files.js +0 -190
  101. package/dist/component/files.js.map +0 -1
  102. package/dist/component/messages.d.ts +0 -3851
  103. package/dist/component/messages.d.ts.map +0 -1
  104. package/dist/component/messages.js +0 -757
  105. package/dist/component/messages.js.map +0 -1
  106. package/dist/component/schema.d.ts +0 -8026
  107. package/dist/component/schema.d.ts.map +0 -1
  108. package/dist/component/schema.js +0 -147
  109. package/dist/component/schema.js.map +0 -1
  110. package/dist/component/streams.d.ts +0 -128
  111. package/dist/component/streams.d.ts.map +0 -1
  112. package/dist/component/streams.js +0 -413
  113. package/dist/component/streams.js.map +0 -1
  114. package/dist/component/threads.d.ts +0 -115
  115. package/dist/component/threads.d.ts.map +0 -1
  116. package/dist/component/threads.js +0 -208
  117. package/dist/component/threads.js.map +0 -1
  118. package/dist/component/users.d.ts +0 -52
  119. package/dist/component/users.d.ts.map +0 -1
  120. package/dist/component/users.js +0 -229
  121. package/dist/component/users.js.map +0 -1
  122. package/dist/component/vector/index.d.ts +0 -61
  123. package/dist/component/vector/index.d.ts.map +0 -1
  124. package/dist/component/vector/index.js +0 -146
  125. package/dist/component/vector/index.js.map +0 -1
  126. package/dist/component/vector/tables.d.ts +0 -58
  127. package/dist/component/vector/tables.d.ts.map +0 -1
  128. package/dist/component/vector/tables.js +0 -56
  129. package/dist/component/vector/tables.js.map +0 -1
  130. package/dist/deltas.d.ts +0 -43
  131. package/dist/deltas.d.ts.map +0 -1
  132. package/dist/deltas.js +0 -446
  133. package/dist/deltas.js.map +0 -1
  134. package/dist/mapping.d.ts +0 -72
  135. package/dist/mapping.d.ts.map +0 -1
  136. package/dist/mapping.js +0 -677
  137. package/dist/mapping.js.map +0 -1
  138. package/dist/react/SmoothText.d.ts +0 -5
  139. package/dist/react/SmoothText.d.ts.map +0 -1
  140. package/dist/react/SmoothText.js +0 -6
  141. package/dist/react/SmoothText.js.map +0 -1
  142. package/dist/react/index.d.ts +0 -25
  143. package/dist/react/index.d.ts.map +0 -1
  144. package/dist/react/index.js +0 -70
  145. package/dist/react/index.js.map +0 -1
  146. package/dist/react/optimisticallySendMessage.d.ts +0 -42
  147. package/dist/react/optimisticallySendMessage.d.ts.map +0 -1
  148. package/dist/react/optimisticallySendMessage.js +0 -74
  149. package/dist/react/optimisticallySendMessage.js.map +0 -1
  150. package/dist/react/types.d.ts +0 -12
  151. package/dist/react/types.d.ts.map +0 -1
  152. package/dist/react/types.js +0 -2
  153. package/dist/react/types.js.map +0 -1
  154. package/dist/react/useDeltaStreams.d.ts +0 -10
  155. package/dist/react/useDeltaStreams.d.ts.map +0 -1
  156. package/dist/react/useDeltaStreams.js +0 -106
  157. package/dist/react/useDeltaStreams.js.map +0 -1
  158. package/dist/react/useSmoothText.d.ts +0 -27
  159. package/dist/react/useSmoothText.d.ts.map +0 -1
  160. package/dist/react/useSmoothText.js +0 -68
  161. package/dist/react/useSmoothText.js.map +0 -1
  162. package/dist/react/useStreamingUIMessages.d.ts +0 -22
  163. package/dist/react/useStreamingUIMessages.d.ts.map +0 -1
  164. package/dist/react/useStreamingUIMessages.js +0 -92
  165. package/dist/react/useStreamingUIMessages.js.map +0 -1
  166. package/dist/react/useThreadMessages.d.ts +0 -104
  167. package/dist/react/useThreadMessages.d.ts.map +0 -1
  168. package/dist/react/useThreadMessages.js +0 -148
  169. package/dist/react/useThreadMessages.js.map +0 -1
  170. package/dist/react/useUIMessages.d.ts +0 -96
  171. package/dist/react/useUIMessages.d.ts.map +0 -1
  172. package/dist/react/useUIMessages.js +0 -108
  173. package/dist/react/useUIMessages.js.map +0 -1
  174. package/dist/shared.d.ts +0 -26
  175. package/dist/shared.d.ts.map +0 -1
  176. package/dist/shared.js +0 -67
  177. package/dist/shared.js.map +0 -1
  178. package/dist/validators.d.ts +0 -24516
  179. package/dist/validators.d.ts.map +0 -1
  180. package/dist/validators.js +0 -475
  181. package/dist/validators.js.map +0 -1
package/dist/mapping.js DELETED
@@ -1,677 +0,0 @@
1
- import {} from "ai";
2
- import { vMessageWithMetadata, vToolResultOutput, vToolApprovalRequest, vToolApprovalResponse, } from "./validators.js";
3
- import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
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) {
8
- const { content, fileIds } = await serializeContent(ctx, component, message.content);
9
- return {
10
- message: {
11
- role: message.role,
12
- content,
13
- ...(message.providerOptions
14
- ? { providerOptions: message.providerOptions }
15
- : {}),
16
- },
17
- fileIds,
18
- };
19
- }
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) {
43
- return {
44
- ...message,
45
- content: toModelMessageContent(message.content),
46
- };
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
- }
55
- /**
56
- * Merge consecutive tool messages that contain `tool-approval-response` parts
57
- * into a single tool message. The AI SDK's `collectToolApprovals` only examines
58
- * the last tool message, so when multiple approvals are saved as separate
59
- * messages (e.g. approve tool A, then deny tool B), they must be combined
60
- * for the SDK to process them all.
61
- */
62
- export function mergeApprovalResponseMessages(messages) {
63
- const result = [];
64
- for (const msg of messages) {
65
- const prev = result.at(-1);
66
- if (msg.role === "tool" &&
67
- prev?.role === "tool" &&
68
- Array.isArray(msg.content) &&
69
- Array.isArray(prev.content) &&
70
- hasApprovalResponse(msg.content) &&
71
- hasApprovalResponse(prev.content)) {
72
- // Clone before merging to avoid mutating the original message's content array
73
- const cloned = { ...prev, content: [...prev.content] };
74
- result[result.length - 1] = cloned;
75
- cloned.content.push(...msg.content);
76
- }
77
- else {
78
- result.push(msg);
79
- }
80
- }
81
- return result;
82
- }
83
- function hasApprovalResponse(content) {
84
- return content.some((p) => p.type === "tool-approval-response");
85
- }
86
- export function serializeUsage(usage) {
87
- return {
88
- promptTokens: usage.inputTokens ?? 0,
89
- completionTokens: usage.outputTokens ?? 0,
90
- totalTokens: usage.totalTokens ?? 0,
91
- reasoningTokens: usage.reasoningTokens,
92
- cachedInputTokens: usage.cachedInputTokens,
93
- };
94
- }
95
- export function toModelMessageUsage(usage) {
96
- return {
97
- inputTokens: usage.promptTokens,
98
- outputTokens: usage.completionTokens,
99
- totalTokens: usage.totalTokens,
100
- reasoningTokens: usage.reasoningTokens,
101
- cachedInputTokens: usage.cachedInputTokens,
102
- // These detail fields are required by LanguageModelUsage type but we don't
103
- // have the granular data, so we provide empty objects with undefined values.
104
- inputTokenDetails: {
105
- cacheReadTokens: undefined,
106
- cacheWriteTokens: undefined,
107
- noCacheTokens: undefined,
108
- },
109
- outputTokenDetails: {
110
- textTokens: undefined,
111
- reasoningTokens: undefined,
112
- },
113
- };
114
- }
115
- export function serializeWarnings(warnings) {
116
- if (!warnings) {
117
- return undefined;
118
- }
119
- return warnings.map((warning) => {
120
- if (warning.type === "compatibility") {
121
- return {
122
- type: "unsupported-setting",
123
- setting: warning.feature,
124
- details: warning.details,
125
- };
126
- }
127
- return warning;
128
- });
129
- }
130
- export function toModelMessageWarnings(warnings) {
131
- if (!warnings) {
132
- return undefined;
133
- }
134
- return warnings.map((warning) => {
135
- if (warning.type === "unsupported-setting") {
136
- return {
137
- type: "compatibility",
138
- feature: warning.setting,
139
- details: warning.details,
140
- };
141
- }
142
- return warning;
143
- });
144
- }
145
- export async function serializeNewMessagesInStep(ctx, component, step, model,
146
- /**
147
- * If provided, these are the new response messages for this step
148
- * (pre-sliced by the caller). When not provided, falls back to the
149
- * existing heuristic of slicing the last 1-2 messages.
150
- *
151
- * This is needed for tool approval flows where the SDK adds extra
152
- * messages (e.g. approval tool-results) at the beginning of
153
- * responseMessages that the old slice(-1/-2) logic would miss.
154
- */
155
- newResponseMessages) {
156
- // If there are tool results, there's another message with the tool results
157
- // ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
158
- const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
159
- const assistantFields = {
160
- model: model ? getModelName(model) : undefined,
161
- provider: model ? getProviderName(model) : undefined,
162
- providerMetadata: step.providerMetadata,
163
- reasoning: step.reasoningText,
164
- reasoningDetails: step.reasoning,
165
- usage: serializeUsage(step.usage),
166
- warnings: serializeWarnings(step.warnings),
167
- finishReason: step.finishReason,
168
- // Only store the sources on one message
169
- sources: hasToolMessage ? undefined : step.sources,
170
- };
171
- const toolFields = { sources: step.sources };
172
- // Determine which messages to serialize for this step
173
- let messagesToSerialize;
174
- if (newResponseMessages) {
175
- messagesToSerialize = newResponseMessages;
176
- }
177
- else if (hasToolMessage) {
178
- messagesToSerialize = step.response.messages.slice(-2);
179
- }
180
- else if (step.content.length) {
181
- messagesToSerialize = step.response.messages.slice(-1);
182
- }
183
- else {
184
- messagesToSerialize = [{ role: "assistant", content: [] }];
185
- }
186
- const messages = await Promise.all(messagesToSerialize.map(async (msg) => {
187
- const { message, fileIds } = await serializeMessage(ctx, component, msg);
188
- return parse(vMessageWithMetadata, {
189
- message,
190
- ...(message.role === "tool" ? toolFields : assistantFields),
191
- text: step.text,
192
- fileIds,
193
- });
194
- }));
195
- // TODO: capture step.files separately?
196
- return { messages };
197
- }
198
- export async function serializeObjectResult(ctx, component, result, model) {
199
- const text = JSON.stringify(result.object);
200
- const { message, fileIds } = await serializeMessage(ctx, component, {
201
- role: "assistant",
202
- content: text,
203
- });
204
- return {
205
- messages: [
206
- {
207
- message,
208
- model: model ? getModelName(model) : undefined,
209
- provider: model ? getProviderName(model) : undefined,
210
- providerMetadata: result.providerMetadata,
211
- finishReason: result.finishReason,
212
- text,
213
- usage: serializeUsage(result.usage),
214
- warnings: serializeWarnings(result.warnings),
215
- fileIds,
216
- },
217
- ],
218
- };
219
- }
220
- function getMimeOrMediaType(part) {
221
- if ("mediaType" in part) {
222
- return part.mediaType;
223
- }
224
- if ("mimeType" in part) {
225
- return part.mimeType;
226
- }
227
- return undefined;
228
- }
229
- export async function serializeContent(ctx, component, content) {
230
- if (typeof content === "string") {
231
- return { content };
232
- }
233
- const fileIds = [];
234
- const serialized = await Promise.all(content.map(async (part) => {
235
- const metadata = {};
236
- if ("providerOptions" in part) {
237
- metadata.providerOptions = part.providerOptions;
238
- }
239
- if ("providerMetadata" in part) {
240
- metadata.providerMetadata = part.providerMetadata;
241
- }
242
- switch (part.type) {
243
- case "text": {
244
- return {
245
- type: part.type,
246
- text: part.text,
247
- ...metadata,
248
- };
249
- }
250
- case "image": {
251
- let image = serializeDataOrUrl(part.image);
252
- if (image instanceof ArrayBuffer &&
253
- image.byteLength > MAX_FILE_SIZE) {
254
- const { file } = await storeFile(ctx, component, new Blob([image], {
255
- type: getMimeOrMediaType(part) || guessMimeType(image),
256
- }));
257
- image = file.url;
258
- fileIds.push(file.fileId);
259
- }
260
- return {
261
- type: part.type,
262
- mediaType: getMimeOrMediaType(part),
263
- ...metadata,
264
- image,
265
- };
266
- }
267
- case "file": {
268
- let data = serializeDataOrUrl(part.data);
269
- if (data instanceof ArrayBuffer && data.byteLength > MAX_FILE_SIZE) {
270
- const { file } = await storeFile(ctx, component, new Blob([data], { type: getMimeOrMediaType(part) }));
271
- data = file.url;
272
- fileIds.push(file.fileId);
273
- }
274
- return {
275
- type: part.type,
276
- data,
277
- filename: part.filename,
278
- mediaType: getMimeOrMediaType(part),
279
- ...metadata,
280
- };
281
- }
282
- case "tool-call": {
283
- // Handle legacy data where only args field exists
284
- const input = part.input ?? part?.args ?? {};
285
- return {
286
- type: part.type,
287
- input,
288
- /** @deprecated Use `input` instead. */
289
- args: input,
290
- toolCallId: part.toolCallId,
291
- toolName: part.toolName,
292
- providerExecuted: part.providerExecuted,
293
- ...metadata,
294
- };
295
- }
296
- case "tool-result": {
297
- return normalizeToolResult(part, metadata);
298
- }
299
- case "reasoning": {
300
- return {
301
- type: part.type,
302
- text: part.text,
303
- ...metadata,
304
- };
305
- }
306
- // Not in current generation output, but could be in historical messages
307
- case "redacted-reasoning": {
308
- return {
309
- type: part.type,
310
- data: part.data,
311
- ...metadata,
312
- };
313
- }
314
- case "source": {
315
- return part;
316
- }
317
- case "tool-approval-request": {
318
- return {
319
- type: part.type,
320
- approvalId: part.approvalId,
321
- toolCallId: part.toolCallId,
322
- ...metadata,
323
- };
324
- }
325
- case "tool-approval-response": {
326
- return {
327
- type: part.type,
328
- approvalId: part.approvalId,
329
- approved: part.approved,
330
- reason: part.reason,
331
- providerExecuted: part.providerExecuted,
332
- ...metadata,
333
- };
334
- }
335
- default:
336
- return null;
337
- }
338
- }));
339
- return {
340
- content: serialized.filter((p) => p !== null),
341
- fileIds: fileIds.length > 0 ? fileIds : undefined,
342
- };
343
- }
344
- export function fromModelMessageContent(content) {
345
- if (typeof content === "string") {
346
- return content;
347
- }
348
- return content
349
- .map((part) => {
350
- const metadata = {};
351
- if ("providerOptions" in part) {
352
- metadata.providerOptions = part.providerOptions;
353
- }
354
- if ("providerMetadata" in part) {
355
- metadata.providerMetadata = part.providerMetadata;
356
- }
357
- switch (part.type) {
358
- case "text":
359
- return part;
360
- case "image":
361
- return {
362
- type: part.type,
363
- mediaType: getMimeOrMediaType(part),
364
- ...metadata,
365
- image: serializeDataOrUrl(part.image),
366
- };
367
- case "file":
368
- return {
369
- type: part.type,
370
- data: serializeDataOrUrl(part.data),
371
- filename: part.filename,
372
- mediaType: getMimeOrMediaType(part),
373
- ...metadata,
374
- };
375
- case "tool-call":
376
- // Handle legacy data where only args field exists
377
- return {
378
- type: part.type,
379
- input: part.input ?? part?.args ?? {},
380
- /** @deprecated Use `input` instead. */
381
- args: part.input ?? part?.args ?? {},
382
- toolCallId: part.toolCallId,
383
- toolName: part.toolName,
384
- providerExecuted: part.providerExecuted,
385
- ...metadata,
386
- };
387
- case "tool-result":
388
- return normalizeToolResult(part, metadata);
389
- case "reasoning":
390
- return {
391
- type: part.type,
392
- text: part.text,
393
- ...metadata,
394
- };
395
- case "tool-approval-request":
396
- return {
397
- type: part.type,
398
- approvalId: part.approvalId,
399
- toolCallId: part.toolCallId,
400
- ...metadata,
401
- };
402
- case "tool-approval-response":
403
- return {
404
- type: part.type,
405
- approvalId: part.approvalId,
406
- approved: part.approved,
407
- reason: part.reason,
408
- providerExecuted: part.providerExecuted,
409
- ...metadata,
410
- };
411
- // Not in current generation output, but could be in historical messages
412
- default:
413
- return null;
414
- }
415
- })
416
- .filter((p) => p !== null);
417
- }
418
- export function toModelMessageContent(content) {
419
- if (typeof content === "string") {
420
- return content;
421
- }
422
- return content
423
- .map((part) => {
424
- const metadata = {};
425
- if ("providerOptions" in part) {
426
- metadata.providerOptions = part.providerOptions;
427
- }
428
- if ("providerMetadata" in part) {
429
- metadata.providerMetadata = part.providerMetadata;
430
- }
431
- switch (part.type) {
432
- case "text":
433
- return {
434
- type: part.type,
435
- text: part.text,
436
- ...metadata,
437
- };
438
- case "image":
439
- return {
440
- type: part.type,
441
- image: toModelMessageDataOrUrl(part.image),
442
- mediaType: getMimeOrMediaType(part),
443
- ...metadata,
444
- };
445
- case "file":
446
- return {
447
- type: part.type,
448
- data: toModelMessageDataOrUrl(part.data),
449
- filename: part.filename,
450
- mediaType: getMimeOrMediaType(part),
451
- ...metadata,
452
- };
453
- case "tool-call": {
454
- // Handle legacy data where only args field exists
455
- const input = part.input ?? part?.args ?? {};
456
- return {
457
- type: part.type,
458
- input,
459
- toolCallId: part.toolCallId,
460
- toolName: part.toolName,
461
- providerExecuted: part.providerExecuted,
462
- ...metadata,
463
- };
464
- }
465
- case "tool-result": {
466
- return normalizeToolResult(part, metadata);
467
- }
468
- case "reasoning":
469
- return {
470
- type: part.type,
471
- text: part.text,
472
- ...metadata,
473
- };
474
- case "redacted-reasoning":
475
- // TODO: should we just drop this?
476
- return {
477
- type: "reasoning",
478
- text: "",
479
- ...metadata,
480
- providerOptions: metadata.providerOptions
481
- ? {
482
- ...Object.fromEntries(Object.entries(metadata.providerOptions ?? {}).map(([key, value]) => [
483
- key,
484
- { ...value, redactedData: part.data },
485
- ])),
486
- }
487
- : undefined,
488
- };
489
- case "source":
490
- return part;
491
- case "tool-approval-request":
492
- return {
493
- type: part.type,
494
- approvalId: part.approvalId,
495
- toolCallId: part.toolCallId,
496
- ...metadata,
497
- };
498
- case "tool-approval-response":
499
- return {
500
- type: part.type,
501
- approvalId: part.approvalId,
502
- approved: part.approved,
503
- reason: part.reason,
504
- providerExecuted: part.providerExecuted,
505
- ...metadata,
506
- };
507
- default:
508
- return null;
509
- }
510
- })
511
- .filter((p) => p !== null);
512
- }
513
- export function normalizeToolOutput(result) {
514
- if (typeof result === "string") {
515
- return {
516
- type: "text",
517
- value: result,
518
- };
519
- }
520
- if (validate(vToolResultOutput, result)) {
521
- return result;
522
- }
523
- return {
524
- type: "json",
525
- value: result ?? null,
526
- };
527
- }
528
- function normalizeToolResult(part, metadata) {
529
- return {
530
- type: part.type,
531
- output: part.output
532
- ? validate(vToolResultOutput, part.output)
533
- ? part.output
534
- : normalizeToolOutput(JSON.stringify(part.output))
535
- : normalizeToolOutput("result" in part ? part.result : undefined),
536
- toolCallId: part.toolCallId,
537
- toolName: part.toolName,
538
- // Preserve isError flag for error reporting
539
- ...("isError" in part && part.isError ? { isError: true } : {}),
540
- ...metadata,
541
- };
542
- }
543
- /**
544
- * Return a best-guess MIME type based on the magic-number signature
545
- * found at the start of an ArrayBuffer.
546
- *
547
- * @param buf – the source ArrayBuffer
548
- * @returns the detected MIME type, or `"application/octet-stream"` if unknown
549
- */
550
- export function guessMimeType(buf) {
551
- if (typeof buf === "string") {
552
- if (buf.match(/^data:\w+\/\w+;base64/)) {
553
- return buf.split(";")[0].split(":")[1];
554
- }
555
- return "text/plain";
556
- }
557
- if (buf.byteLength < 4)
558
- return "application/octet-stream";
559
- // Read the first 12 bytes (enough for all signatures below)
560
- const bytes = new Uint8Array(buf.slice(0, 12));
561
- const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
562
- // Helper so we can look at only the needed prefix
563
- const startsWith = (sig) => hex.startsWith(sig.toLowerCase());
564
- // --- image formats ---
565
- if (startsWith("89504e47"))
566
- return "image/png"; // PNG - 89 50 4E 47
567
- if (startsWith("ffd8ffdb") ||
568
- startsWith("ffd8ffe0") ||
569
- startsWith("ffd8ffee") ||
570
- startsWith("ffd8ffe1"))
571
- return "image/jpeg"; // JPEG
572
- if (startsWith("47494638"))
573
- return "image/gif"; // GIF
574
- if (startsWith("424d"))
575
- return "image/bmp"; // BMP
576
- if (startsWith("52494646") && hex.substr(16, 8) === "57454250")
577
- return "image/webp"; // WEBP (RIFF....WEBP)
578
- if (startsWith("49492a00"))
579
- return "image/tiff"; // TIFF
580
- // <svg in hex is 3c 3f 78 6d 6c
581
- if (startsWith("3c737667"))
582
- return "image/svg+xml"; // <svg
583
- if (startsWith("3c3f786d"))
584
- return "image/svg+xml"; // <?xm
585
- // --- audio/video ---
586
- if (startsWith("494433"))
587
- return "audio/mpeg"; // MP3 (ID3)
588
- if (startsWith("000001ba") || startsWith("000001b3"))
589
- return "video/mpeg"; // MPEG container
590
- if (startsWith("1a45dfa3"))
591
- return "video/webm"; // WEBM / Matroska
592
- if (startsWith("00000018") && hex.substr(16, 8) === "66747970")
593
- return "video/mp4"; // MP4
594
- if (startsWith("4f676753"))
595
- return "audio/ogg"; // OGG / Opus
596
- // --- documents & archives ---
597
- if (startsWith("25504446"))
598
- return "application/pdf"; // PDF
599
- if (startsWith("504b0304") ||
600
- startsWith("504b0506") ||
601
- startsWith("504b0708"))
602
- return "application/zip"; // ZIP / DOCX / PPTX / XLSX / EPUB
603
- if (startsWith("52617221"))
604
- return "application/x-rar-compressed"; // RAR
605
- if (startsWith("7f454c46"))
606
- return "application/x-elf"; // ELF binaries
607
- if (startsWith("1f8b08"))
608
- return "application/gzip"; // GZIP
609
- if (startsWith("425a68"))
610
- return "application/x-bzip2"; // BZIP2
611
- if (startsWith("3c3f786d6c"))
612
- return "application/xml"; // XML
613
- // Plain text, JSON and others are trickier—fallback:
614
- return "application/octet-stream";
615
- }
616
- /**
617
- * Serialize an AI SDK `DataContent` or `URL` to a Convex-serializable format.
618
- * @param dataOrUrl - The data or URL to serialize.
619
- * @returns The serialized data as an ArrayBuffer or the URL as a string.
620
- */
621
- export function serializeDataOrUrl(dataOrUrl) {
622
- if (typeof dataOrUrl === "string") {
623
- return dataOrUrl;
624
- }
625
- if (dataOrUrl instanceof ArrayBuffer) {
626
- return dataOrUrl; // Already an ArrayBuffer
627
- }
628
- if (dataOrUrl instanceof URL) {
629
- return dataOrUrl.toString();
630
- }
631
- return dataOrUrl.buffer.slice(dataOrUrl.byteOffset, dataOrUrl.byteOffset + dataOrUrl.byteLength);
632
- }
633
- export function toModelMessageDataOrUrl(urlOrString) {
634
- if (urlOrString instanceof URL) {
635
- return urlOrString;
636
- }
637
- if (typeof urlOrString === "string") {
638
- if (urlOrString.startsWith("http://") ||
639
- urlOrString.startsWith("https://")) {
640
- return new URL(urlOrString);
641
- }
642
- return urlOrString;
643
- }
644
- return urlOrString;
645
- }
646
- export function toUIFilePart(part) {
647
- const dataOrUrl = part.type === "image" ? part.image : part.data;
648
- const url = dataOrUrl instanceof ArrayBuffer
649
- ? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
650
- : dataOrUrl.toString();
651
- return {
652
- type: "file",
653
- mediaType: part.mediaType,
654
- filename: part.type === "file" ? part.filename : undefined,
655
- url,
656
- providerMetadata: part.providerOptions,
657
- };
658
- }
659
- // Currently unused
660
- // export function toModelMessages(args: {
661
- // messages?: ModelMessage[] | AIMessageWithoutId[];
662
- // }): ModelMessage[] {
663
- // const messages: ModelMessage[] = [];
664
- // if (args.messages) {
665
- // if (
666
- // args.messages.every(
667
- // (m) => typeof m === "object" && m !== null && "parts" in m,
668
- // )
669
- // ) {
670
- // messages.push(...convertToModelMessages(args.messages));
671
- // } else {
672
- // messages.push(...modelMessageSchema.array().parse(args.messages));
673
- // }
674
- // }
675
- // return messages;
676
- // }
677
- //# sourceMappingURL=mapping.js.map