@convex-dev/agent 0.1.18 → 0.2.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 (122) hide show
  1. package/dist/client/createTool.d.ts +30 -11
  2. package/dist/client/createTool.d.ts.map +1 -1
  3. package/dist/client/createTool.js +24 -10
  4. package/dist/client/createTool.js.map +1 -1
  5. package/dist/client/defaultComponent.d.ts +11 -0
  6. package/dist/client/defaultComponent.d.ts.map +1 -0
  7. package/dist/client/defaultComponent.js +7 -0
  8. package/dist/client/defaultComponent.js.map +1 -0
  9. package/dist/client/definePlaygroundAPI.d.ts +68 -23
  10. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  11. package/dist/client/definePlaygroundAPI.js +7 -20
  12. package/dist/client/definePlaygroundAPI.js.map +1 -1
  13. package/dist/client/files.d.ts +14 -4
  14. package/dist/client/files.d.ts.map +1 -1
  15. package/dist/client/files.js +66 -11
  16. package/dist/client/files.js.map +1 -1
  17. package/dist/client/index.d.ts +323 -780
  18. package/dist/client/index.d.ts.map +1 -1
  19. package/dist/client/index.js +445 -543
  20. package/dist/client/index.js.map +1 -1
  21. package/dist/client/messages.d.ts +256 -0
  22. package/dist/client/messages.d.ts.map +1 -0
  23. package/dist/client/messages.js +97 -0
  24. package/dist/client/messages.js.map +1 -0
  25. package/dist/client/search.d.ts +35 -12
  26. package/dist/client/search.d.ts.map +1 -1
  27. package/dist/client/search.js +21 -4
  28. package/dist/client/search.js.map +1 -1
  29. package/dist/client/streaming.d.ts +21 -21
  30. package/dist/client/streaming.d.ts.map +1 -1
  31. package/dist/client/streaming.js +35 -40
  32. package/dist/client/streaming.js.map +1 -1
  33. package/dist/client/threads.d.ts +46 -0
  34. package/dist/client/threads.d.ts.map +1 -0
  35. package/dist/client/threads.js +49 -0
  36. package/dist/client/threads.js.map +1 -0
  37. package/dist/client/types.d.ts +159 -75
  38. package/dist/client/types.d.ts.map +1 -1
  39. package/dist/component/_generated/api.d.ts +261 -209
  40. package/dist/component/messages.d.ts +314 -105
  41. package/dist/component/messages.d.ts.map +1 -1
  42. package/dist/component/messages.js +141 -83
  43. package/dist/component/messages.js.map +1 -1
  44. package/dist/component/schema.d.ts +631 -870
  45. package/dist/component/schema.d.ts.map +1 -1
  46. package/dist/component/schema.js +11 -16
  47. package/dist/component/schema.js.map +1 -1
  48. package/dist/component/streams.d.ts +29 -149
  49. package/dist/component/streams.d.ts.map +1 -1
  50. package/dist/component/streams.js +104 -60
  51. package/dist/component/streams.js.map +1 -1
  52. package/dist/component/threads.d.ts +4 -4
  53. package/dist/component/users.d.ts +3 -3
  54. package/dist/mapping.d.ts +15 -16
  55. package/dist/mapping.d.ts.map +1 -1
  56. package/dist/mapping.js +208 -62
  57. package/dist/mapping.js.map +1 -1
  58. package/dist/parts.d.ts +3 -0
  59. package/dist/parts.d.ts.map +1 -0
  60. package/dist/parts.js +34 -0
  61. package/dist/parts.js.map +1 -0
  62. package/dist/react/SmoothText.d.ts +5 -0
  63. package/dist/react/SmoothText.d.ts.map +1 -0
  64. package/dist/react/SmoothText.js +6 -0
  65. package/dist/react/SmoothText.js.map +1 -0
  66. package/dist/react/deltas.d.ts +3 -5
  67. package/dist/react/deltas.d.ts.map +1 -1
  68. package/dist/react/deltas.js +89 -53
  69. package/dist/react/deltas.js.map +1 -1
  70. package/dist/react/index.d.ts +1 -0
  71. package/dist/react/index.d.ts.map +1 -1
  72. package/dist/react/index.js +31 -11
  73. package/dist/react/index.js.map +1 -1
  74. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  75. package/dist/react/optimisticallySendMessage.js +2 -8
  76. package/dist/react/optimisticallySendMessage.js.map +1 -1
  77. package/dist/react/toUIMessages.d.ts +5 -4
  78. package/dist/react/toUIMessages.d.ts.map +1 -1
  79. package/dist/react/toUIMessages.js +103 -40
  80. package/dist/react/toUIMessages.js.map +1 -1
  81. package/dist/react/useSmoothText.d.ts +13 -12
  82. package/dist/react/useSmoothText.d.ts.map +1 -1
  83. package/dist/react/useSmoothText.js +1 -1
  84. package/dist/react/useSmoothText.js.map +1 -1
  85. package/dist/validators.d.ts +751 -1464
  86. package/dist/validators.d.ts.map +1 -1
  87. package/dist/validators.js +39 -80
  88. package/dist/validators.js.map +1 -1
  89. package/package.json +34 -30
  90. package/src/client/createTool.ts +66 -40
  91. package/src/client/defaultComponent.ts +17 -0
  92. package/src/client/definePlaygroundAPI.ts +17 -31
  93. package/src/client/files.ts +94 -17
  94. package/src/client/index.test.ts +28 -38
  95. package/src/client/index.ts +785 -977
  96. package/src/client/messages.ts +205 -0
  97. package/src/client/search.ts +37 -10
  98. package/src/client/streaming.ts +53 -56
  99. package/src/client/threads.ts +78 -0
  100. package/src/client/types.ts +239 -131
  101. package/src/component/_generated/api.d.ts +261 -209
  102. package/src/component/messages.test.ts +182 -40
  103. package/src/component/messages.ts +165 -108
  104. package/src/component/schema.ts +11 -17
  105. package/src/component/streams.ts +180 -69
  106. package/src/mapping.ts +253 -96
  107. package/src/parts.ts +39 -0
  108. package/src/react/SmoothText.tsx +9 -0
  109. package/src/react/deltas.test.ts +38 -42
  110. package/src/react/deltas.ts +116 -65
  111. package/src/react/index.ts +35 -13
  112. package/src/react/optimisticallySendMessage.ts +2 -8
  113. package/src/react/toUIMessages.test.ts +154 -36
  114. package/src/react/toUIMessages.ts +136 -57
  115. package/src/react/useSmoothText.ts +14 -15
  116. package/src/validators.test.ts +1 -117
  117. package/src/validators.ts +57 -107
  118. package/dist/client/listMessages.d.ts +0 -22
  119. package/dist/client/listMessages.d.ts.map +0 -1
  120. package/dist/client/listMessages.js +0 -25
  121. package/dist/client/listMessages.js.map +0 -1
  122. package/src/client/listMessages.ts +0 -38
package/src/mapping.ts CHANGED
@@ -1,10 +1,7 @@
1
- import type { FileUIPart } from "@ai-sdk/ui-utils";
2
1
  import {
3
- convertToCoreMessages,
4
- coreMessageSchema,
5
- type Message as AIMessage,
2
+ type UIMessage as AIMessage,
6
3
  type AssistantContent,
7
- type CoreMessage,
4
+ type ModelMessage,
8
5
  type DataContent,
9
6
  type FilePart,
10
7
  type GenerateObjectResult,
@@ -13,13 +10,32 @@ import {
13
10
  type ToolContent,
14
11
  type ToolSet,
15
12
  type UserContent,
13
+ type FileUIPart,
14
+ type LanguageModelUsage,
15
+ type CallWarning,
16
+ type TextPart,
17
+ type ToolCallPart,
18
+ type ToolResultPart,
19
+ type ReasoningUIPart,
16
20
  } from "ai";
17
- import { assert } from "convex-helpers";
18
- import type { MessageWithMetadata } from "./validators.js";
21
+ import type {
22
+ Message,
23
+ MessageWithMetadata,
24
+ Usage,
25
+ vContent,
26
+ vFilePart,
27
+ vImagePart,
28
+ vReasoningPart,
29
+ vRedactedReasoningPart,
30
+ vTextPart,
31
+ vToolCallPart,
32
+ vToolResultPart,
33
+ } from "./validators.js";
19
34
  import type { ActionCtx, AgentComponent } from "./client/types.js";
20
35
  import type { RunMutationCtx } from "./client/types.js";
21
36
  import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
22
-
37
+ import type { Infer } from "convex/values";
38
+ import { convertUint8ArrayToBase64 } from "@ai-sdk/provider-utils";
23
39
  export type AIMessageWithoutId = Omit<AIMessage, "id">;
24
40
 
25
41
  export type SerializeUrlsAndUint8Arrays<T> = T extends URL
@@ -34,16 +50,15 @@ export type SerializeUrlsAndUint8Arrays<T> = T extends URL
34
50
  : T;
35
51
 
36
52
  export type Content = UserContent | AssistantContent | ToolContent;
37
- export type SerializedContent = SerializeUrlsAndUint8Arrays<Content>;
53
+ export type SerializedContent = Message["content"];
38
54
 
39
- export type SerializedMessage = SerializeUrlsAndUint8Arrays<CoreMessage>;
55
+ export type SerializedMessage = Message;
40
56
 
41
57
  export async function serializeMessage(
42
58
  ctx: ActionCtx | RunMutationCtx,
43
59
  component: AgentComponent,
44
- messageWithId: CoreMessage & { id?: string },
60
+ message: ModelMessage | Message,
45
61
  ): Promise<{ message: SerializedMessage; fileIds?: string[] }> {
46
- const { id: _, experimental_providerMetadata, ...message } = messageWithId;
47
62
  const { content, fileIds } = await serializeContent(
48
63
  ctx,
49
64
  component,
@@ -51,20 +66,80 @@ export async function serializeMessage(
51
66
  );
52
67
  return {
53
68
  message: {
54
- // for backwards compatibility
55
- providerOptions: experimental_providerMetadata,
56
- ...message,
69
+ role: message.role,
57
70
  content,
71
+ ...(message.providerOptions
72
+ ? { providerOptions: message.providerOptions }
73
+ : {}),
58
74
  } as SerializedMessage,
59
75
  fileIds,
60
76
  };
61
77
  }
62
78
 
63
- export function deserializeMessage(message: SerializedMessage): CoreMessage {
79
+ export async function serializeOrThrow(
80
+ message: ModelMessage | Message,
81
+ ): Promise<SerializedMessage> {
82
+ const { content } = await serializeContent(
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ {} as any,
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ {} as any,
87
+ message.content,
88
+ );
89
+ return {
90
+ role: message.role,
91
+ content,
92
+ ...(message.providerOptions
93
+ ? { providerOptions: message.providerOptions }
94
+ : {}),
95
+ } as SerializedMessage;
96
+ }
97
+
98
+ export function deserializeMessage(message: SerializedMessage): ModelMessage {
64
99
  return {
65
100
  ...message,
66
101
  content: deserializeContent(message.content),
67
- } as CoreMessage;
102
+ } as ModelMessage;
103
+ }
104
+
105
+ export function serializeUsage(usage: LanguageModelUsage): Usage {
106
+ return {
107
+ promptTokens: usage.inputTokens ?? 0,
108
+ completionTokens: usage.outputTokens ?? 0,
109
+ totalTokens: usage.totalTokens ?? 0,
110
+ reasoningTokens: usage.reasoningTokens,
111
+ };
112
+ }
113
+
114
+ export function deserializeUsage(usage: Usage): LanguageModelUsage {
115
+ return {
116
+ inputTokens: usage.promptTokens,
117
+ outputTokens: usage.completionTokens,
118
+ totalTokens: usage.totalTokens,
119
+ reasoningTokens: usage.reasoningTokens,
120
+ cachedInputTokens: usage.cachedInputTokens,
121
+ };
122
+ }
123
+
124
+ export function serializeWarnings(
125
+ warnings: CallWarning[] | undefined,
126
+ ): MessageWithMetadata["warnings"] {
127
+ if (!warnings) {
128
+ return undefined;
129
+ }
130
+ return warnings.map((warning) => {
131
+ if (warning.type !== "unsupported-setting") {
132
+ return warning;
133
+ }
134
+ return { ...warning, setting: warning.setting.toString() };
135
+ });
136
+ }
137
+
138
+ export function deserializeWarnings(
139
+ warnings: MessageWithMetadata["warnings"],
140
+ ): CallWarning[] | undefined {
141
+ // We don't need to do anythign here for now
142
+ return warnings;
68
143
  }
69
144
 
70
145
  export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
@@ -72,45 +147,38 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
72
147
  component: AgentComponent,
73
148
  step: StepResult<TOOLS>,
74
149
  metadata: { model: string; provider: string },
75
- ): Promise<MessageWithMetadata[]> {
150
+ ): Promise<{ messages: MessageWithMetadata[] }> {
76
151
  // If there are tool results, there's another message with the tool results
77
152
  // ref: https://github.com/vercel/ai/blob/main/packages/ai/core/generate-text/to-response-messages.ts
78
153
  const assistantFields = {
79
154
  model: metadata.model,
80
155
  provider: metadata.provider,
81
156
  providerMetadata: step.providerMetadata,
82
- reasoning: step.reasoning,
83
- reasoningDetails: step.reasoningDetails,
84
- usage: step.usage,
85
- warnings: step.warnings,
157
+ reasoning: step.reasoningText,
158
+ reasoningDetails: step.reasoning,
159
+ usage: serializeUsage(step.usage),
160
+ warnings: serializeWarnings(step.warnings),
86
161
  finishReason: step.finishReason,
87
- sources: step.stepType === "tool-result" ? undefined : step.sources,
88
- };
89
- const toolFields = {
90
- sources: step.sources,
91
- };
162
+ // Only store the sources on one message
163
+ sources: step.toolResults.length === 0 ? step.sources : undefined,
164
+ } satisfies Omit<MessageWithMetadata, "message" | "text" | "fileIds">;
165
+ const toolFields = { sources: step.sources };
92
166
  const messages: MessageWithMetadata[] = await Promise.all(
93
167
  (step.toolResults.length > 0
94
168
  ? step.response.messages.slice(-2)
95
169
  : step.response.messages.slice(-1)
96
- ).map(async (messageWithId): Promise<MessageWithMetadata> => {
97
- const { message, fileIds } = await serializeMessage(
98
- ctx,
99
- component,
100
- messageWithId,
101
- );
170
+ ).map(async (msg): Promise<MessageWithMetadata> => {
171
+ const { message, fileIds } = await serializeMessage(ctx, component, msg);
102
172
  return {
103
173
  message,
104
- // Let's not store the ID by default here. It's being generated internally
105
- // and not referenced elsewhere that we know of.
106
- // id: message.id,
107
174
  ...(message.role === "tool" ? toolFields : assistantFields),
108
175
  text: step.text,
109
176
  fileIds,
110
177
  };
111
178
  }),
112
179
  );
113
- return messages;
180
+ // TODO: capture step.files separately?
181
+ return { messages };
114
182
  }
115
183
 
116
184
  export async function serializeObjectResult(
@@ -129,14 +197,13 @@ export async function serializeObjectResult(
129
197
  messages: [
130
198
  {
131
199
  message,
132
- id: result.response.id,
133
200
  model: metadata.model,
134
201
  provider: metadata.provider,
135
202
  providerMetadata: result.providerMetadata,
136
203
  finishReason: result.finishReason,
137
204
  text,
138
- usage: result.usage,
139
- warnings: result.warnings,
205
+ usage: serializeUsage(result.usage),
206
+ warnings: serializeWarnings(result.warnings),
140
207
  fileIds,
141
208
  },
142
209
  ],
@@ -146,16 +213,31 @@ export async function serializeObjectResult(
146
213
  export async function serializeContent(
147
214
  ctx: ActionCtx | RunMutationCtx,
148
215
  component: AgentComponent,
149
- content: Content,
216
+ content: Content | Message["content"],
150
217
  ): Promise<{ content: SerializedContent; fileIds?: string[] }> {
151
218
  if (typeof content === "string") {
152
219
  return { content };
153
220
  }
154
221
  const fileIds: string[] = [];
222
+ function getMimeType(part: { mediaType?: string; mimeType?: string }) {
223
+ if ("mediaType" in part) {
224
+ return part.mediaType;
225
+ }
226
+ if ("mimeType" in part) {
227
+ return part.mimeType;
228
+ }
229
+ return undefined;
230
+ }
155
231
  const serialized = await Promise.all(
156
- content.map(async ({ experimental_providerMetadata, ...rest }) => {
157
- const part = { providerOptions: experimental_providerMetadata, ...rest };
232
+ content.map(async (part) => {
158
233
  switch (part.type) {
234
+ case "text": {
235
+ return {
236
+ type: part.type,
237
+ text: part.text,
238
+ providerOptions: part.providerOptions,
239
+ } satisfies Infer<typeof vTextPart>;
240
+ }
159
241
  case "image": {
160
242
  let image = serializeDataOrUrl(part.image);
161
243
  if (
@@ -166,13 +248,18 @@ export async function serializeContent(
166
248
  ctx,
167
249
  component,
168
250
  new Blob([image], {
169
- type: part.mimeType || guessMimeType(image),
251
+ type: getMimeType(part) || guessMimeType(image),
170
252
  }),
171
253
  );
172
254
  image = file.url;
173
255
  fileIds.push(file.fileId);
174
256
  }
175
- return { ...part, image };
257
+ return {
258
+ type: part.type,
259
+ mimeType: getMimeType(part),
260
+ providerOptions: part.providerOptions,
261
+ image,
262
+ } satisfies Infer<typeof vImagePart>;
176
263
  }
177
264
  case "file": {
178
265
  let data = serializeDataOrUrl(part.data);
@@ -180,18 +267,57 @@ export async function serializeContent(
180
267
  const { file } = await storeFile(
181
268
  ctx,
182
269
  component,
183
- new Blob([data], { type: part.mimeType }),
270
+ new Blob([data], { type: getMimeType(part) }),
184
271
  );
185
272
  data = file.url;
186
273
  fileIds.push(file.fileId);
187
274
  }
188
- return { ...part, data };
275
+ return {
276
+ type: part.type,
277
+ data,
278
+ filename: part.filename,
279
+ mimeType: getMimeType(part)!,
280
+ providerOptions: part.providerOptions,
281
+ } satisfies Infer<typeof vFilePart>;
282
+ }
283
+ case "tool-call": {
284
+ const args = "input" in part ? part.input : part.args;
285
+ return {
286
+ type: part.type,
287
+ args: args ?? null,
288
+ toolCallId: part.toolCallId,
289
+ toolName: part.toolName,
290
+ providerOptions: part.providerOptions,
291
+ providerExecuted: part.providerExecuted,
292
+ } satisfies Infer<typeof vToolCallPart>;
189
293
  }
190
294
  case "tool-result": {
191
- return { ...part, result: part.result ?? null };
295
+ const result = "output" in part ? part.output : part.result;
296
+ return {
297
+ type: part.type,
298
+ result: result ?? null,
299
+ toolCallId: part.toolCallId,
300
+ toolName: part.toolName,
301
+ providerOptions: part.providerOptions,
302
+ } satisfies Infer<typeof vToolResultPart>;
303
+ }
304
+ case "reasoning": {
305
+ return {
306
+ type: part.type,
307
+ text: part.text,
308
+ providerOptions: part.providerOptions,
309
+ } satisfies Infer<typeof vReasoningPart>;
310
+ }
311
+ // Not in current generation output, but could be in historical messages
312
+ case "redacted-reasoning": {
313
+ return {
314
+ type: part.type,
315
+ data: part.data,
316
+ providerOptions: part.providerOptions,
317
+ } satisfies Infer<typeof vRedactedReasoningPart>;
192
318
  }
193
319
  default:
194
- return part;
320
+ return part satisfies Infer<typeof vContent>;
195
321
  }
196
322
  }),
197
323
  );
@@ -207,12 +333,60 @@ export function deserializeContent(content: SerializedContent): Content {
207
333
  }
208
334
  return content.map((part) => {
209
335
  switch (part.type) {
336
+ case "text":
337
+ return {
338
+ type: part.type,
339
+ text: part.text,
340
+ providerOptions: part.providerOptions,
341
+ } satisfies TextPart;
210
342
  case "image":
211
- return { ...part, image: deserializeUrl(part.image) };
343
+ return {
344
+ type: part.type,
345
+ image: deserializeUrl(part.image),
346
+ mediaType: part.mimeType,
347
+ providerOptions: part.providerOptions,
348
+ } satisfies ImagePart;
212
349
  case "file":
213
- return { ...part, data: deserializeUrl(part.data) };
350
+ return {
351
+ type: part.type,
352
+ data: deserializeUrl(part.data),
353
+ filename: part.filename,
354
+ mediaType: part.mimeType,
355
+ providerOptions: part.providerOptions,
356
+ } satisfies FilePart;
357
+ case "tool-call":
358
+ return {
359
+ type: part.type,
360
+ input: part.args ?? null,
361
+ providerExecuted: part.providerExecuted,
362
+ toolCallId: part.toolCallId,
363
+ toolName: part.toolName,
364
+ providerOptions: part.providerOptions,
365
+ } satisfies ToolCallPart;
366
+ case "tool-result":
367
+ return {
368
+ type: part.type,
369
+ output: part.result ?? null,
370
+ toolCallId: part.toolCallId,
371
+ toolName: part.toolName,
372
+ providerOptions: part.providerOptions,
373
+ } satisfies ToolResultPart;
374
+ case "reasoning":
375
+ return {
376
+ type: part.type,
377
+ text: part.text,
378
+ providerMetadata: part.providerOptions,
379
+ state: part.state,
380
+ } satisfies ReasoningUIPart;
381
+ case "redacted-reasoning":
382
+ // TODO: should we just drop this?
383
+ return {
384
+ type: "reasoning",
385
+ text: part.data,
386
+ providerMetadata: part.providerOptions,
387
+ } satisfies ReasoningUIPart;
214
388
  default:
215
- return part;
389
+ return part satisfies Content;
216
390
  }
217
391
  }) as Content;
218
392
  }
@@ -323,53 +497,36 @@ export function deserializeUrl(
323
497
  }
324
498
 
325
499
  export function toUIFilePart(part: ImagePart | FilePart): FileUIPart {
326
- const dataOrUrl = serializeDataOrUrl(
327
- part.type === "image" ? part.image : part.data,
328
- );
500
+ const dataOrUrl = part.type === "image" ? part.image : part.data;
501
+ const url =
502
+ dataOrUrl instanceof ArrayBuffer
503
+ ? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
504
+ : dataOrUrl.toString();
329
505
 
330
506
  return {
331
507
  type: "file",
332
- data:
333
- dataOrUrl instanceof ArrayBuffer ? encodeBase64(dataOrUrl) : dataOrUrl,
334
- mimeType: part.mimeType ?? guessMimeType(dataOrUrl),
508
+ mediaType: part.mediaType!,
509
+ filename: part.type === "file" ? part.filename : undefined,
510
+ url,
511
+ providerMetadata: part.providerOptions,
335
512
  };
336
513
  }
337
514
 
338
- function encodeBase64(data: ArrayBuffer): string {
339
- return Buffer.from(data).toString("base64");
340
- }
341
-
342
- export function promptOrMessagesToCoreMessages(args: {
343
- prompt?: string;
344
- messages?: CoreMessage[] | AIMessageWithoutId[];
345
- promptMessageId?: string;
346
- }): CoreMessage[] {
347
- const messages: CoreMessage[] = [];
348
- assert(
349
- args.prompt || args.messages || args.promptMessageId,
350
- "messages or prompt or promptMessageId is required",
351
- );
352
- if (args.messages) {
353
- if (
354
- args.messages.some(
355
- (m) =>
356
- typeof m === "object" &&
357
- m !== null &&
358
- (m.role === "data" || // UI-only role
359
- "toolInvocations" in m || // UI-specific field
360
- "parts" in m || // UI-specific field
361
- "experimental_attachments" in m),
362
- )
363
- ) {
364
- messages.push(...convertToCoreMessages(args.messages as AIMessage[]));
365
- } else {
366
- messages.push(...coreMessageSchema.array().parse(args.messages));
367
- }
368
- }
369
- // If they specify both a promptMessageId and a prompt, we will replace the
370
- // promptMessageId message with the prompt later.
371
- if (args.prompt && !args.promptMessageId) {
372
- messages.push({ role: "user", content: args.prompt });
373
- }
374
- return messages;
375
- }
515
+ // Currently unused
516
+ // export function toModelMessages(args: {
517
+ // messages?: ModelMessage[] | AIMessageWithoutId[];
518
+ // }): ModelMessage[] {
519
+ // const messages: ModelMessage[] = [];
520
+ // if (args.messages) {
521
+ // if (
522
+ // args.messages.every(
523
+ // (m) => typeof m === "object" && m !== null && "parts" in m,
524
+ // )
525
+ // ) {
526
+ // messages.push(...convertToModelMessages(args.messages));
527
+ // } else {
528
+ // messages.push(...modelMessageSchema.array().parse(args.messages));
529
+ // }
530
+ // }
531
+ // return messages;
532
+ // }
package/src/parts.ts ADDED
@@ -0,0 +1,39 @@
1
+ import type { TextStreamPart, ToolSet } from "ai";
2
+
3
+ export function serializeTextStreamingPartsV5(
4
+ parts: TextStreamPart<ToolSet>[],
5
+ ): TextStreamPart<ToolSet>[] {
6
+ const compressed: TextStreamPart<ToolSet>[] = [];
7
+ for (const part of parts) {
8
+ const last = compressed.at(-1);
9
+ if (part.type === "text-delta" && last?.type === "text-delta") {
10
+ last.text += part.text;
11
+ } else if (
12
+ part.type === "reasoning-delta" &&
13
+ last?.type === "reasoning-delta"
14
+ ) {
15
+ last.text += part.text;
16
+ } else {
17
+ if (
18
+ part.type === "start-step" ||
19
+ part.type === "finish-step" ||
20
+ part.type === "start" ||
21
+ part.type === "finish"
22
+ ) {
23
+ continue;
24
+ }
25
+ if (part.type === "file") {
26
+ compressed.push({
27
+ type: "file",
28
+ file: {
29
+ mediaType: part.file.mediaType,
30
+ base64: part.file.base64,
31
+ uint8Array: new Uint8Array([]),
32
+ },
33
+ });
34
+ }
35
+ compressed.push(part);
36
+ }
37
+ }
38
+ return compressed;
39
+ }
@@ -0,0 +1,9 @@
1
+ import { useSmoothText, type SmoothTextOptions } from "./useSmoothText.js";
2
+
3
+ export function SmoothText({
4
+ text,
5
+ ...options
6
+ }: { text: string } & SmoothTextOptions) {
7
+ const [visibleText] = useSmoothText(text, options);
8
+ return visibleText;
9
+ }