@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.
- package/dist/client/createTool.d.ts +30 -11
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +24 -10
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/defaultComponent.d.ts +11 -0
- package/dist/client/defaultComponent.d.ts.map +1 -0
- package/dist/client/defaultComponent.js +7 -0
- package/dist/client/defaultComponent.js.map +1 -0
- package/dist/client/definePlaygroundAPI.d.ts +68 -23
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +7 -20
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +14 -4
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +66 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +323 -780
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +445 -543
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +256 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +97 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/search.d.ts +35 -12
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +21 -4
- package/dist/client/search.js.map +1 -1
- package/dist/client/streaming.d.ts +21 -21
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +35 -40
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/threads.d.ts +46 -0
- package/dist/client/threads.d.ts.map +1 -0
- package/dist/client/threads.js +49 -0
- package/dist/client/threads.js.map +1 -0
- package/dist/client/types.d.ts +159 -75
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +261 -209
- package/dist/component/messages.d.ts +314 -105
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +141 -83
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +631 -870
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +11 -16
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +29 -149
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +104 -60
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +4 -4
- package/dist/component/users.d.ts +3 -3
- package/dist/mapping.d.ts +15 -16
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +208 -62
- package/dist/mapping.js.map +1 -1
- package/dist/parts.d.ts +3 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +34 -0
- package/dist/parts.js.map +1 -0
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/deltas.d.ts +3 -5
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +89 -53
- package/dist/react/deltas.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +31 -11
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +2 -8
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/toUIMessages.d.ts +5 -4
- package/dist/react/toUIMessages.d.ts.map +1 -1
- package/dist/react/toUIMessages.js +103 -40
- package/dist/react/toUIMessages.js.map +1 -1
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +1 -1
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/validators.d.ts +751 -1464
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +39 -80
- package/dist/validators.js.map +1 -1
- package/package.json +34 -30
- package/src/client/createTool.ts +66 -40
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +17 -31
- package/src/client/files.ts +94 -17
- package/src/client/index.test.ts +28 -38
- package/src/client/index.ts +785 -977
- package/src/client/messages.ts +205 -0
- package/src/client/search.ts +37 -10
- package/src/client/streaming.ts +53 -56
- package/src/client/threads.ts +78 -0
- package/src/client/types.ts +239 -131
- package/src/component/_generated/api.d.ts +261 -209
- package/src/component/messages.test.ts +182 -40
- package/src/component/messages.ts +165 -108
- package/src/component/schema.ts +11 -17
- package/src/component/streams.ts +180 -69
- package/src/mapping.ts +253 -96
- package/src/parts.ts +39 -0
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/deltas.test.ts +38 -42
- package/src/react/deltas.ts +116 -65
- package/src/react/index.ts +35 -13
- package/src/react/optimisticallySendMessage.ts +2 -8
- package/src/react/toUIMessages.test.ts +154 -36
- package/src/react/toUIMessages.ts +136 -57
- package/src/react/useSmoothText.ts +14 -15
- package/src/validators.test.ts +1 -117
- package/src/validators.ts +57 -107
- package/dist/client/listMessages.d.ts +0 -22
- package/dist/client/listMessages.d.ts.map +0 -1
- package/dist/client/listMessages.js +0 -25
- package/dist/client/listMessages.js.map +0 -1
- 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
|
-
|
|
4
|
-
coreMessageSchema,
|
|
5
|
-
type Message as AIMessage,
|
|
2
|
+
type UIMessage as AIMessage,
|
|
6
3
|
type AssistantContent,
|
|
7
|
-
type
|
|
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 {
|
|
18
|
-
|
|
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 =
|
|
53
|
+
export type SerializedContent = Message["content"];
|
|
38
54
|
|
|
39
|
-
export type SerializedMessage =
|
|
55
|
+
export type SerializedMessage = Message;
|
|
40
56
|
|
|
41
57
|
export async function serializeMessage(
|
|
42
58
|
ctx: ActionCtx | RunMutationCtx,
|
|
43
59
|
component: AgentComponent,
|
|
44
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
83
|
-
reasoningDetails: step.
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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 {
|
|
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
|
|
270
|
+
new Blob([data], { type: getMimeType(part) }),
|
|
184
271
|
);
|
|
185
272
|
data = file.url;
|
|
186
273
|
fileIds.push(file.fileId);
|
|
187
274
|
}
|
|
188
|
-
return {
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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 =
|
|
327
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
+
}
|