@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.1
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/MIGRATION.md +153 -0
- package/README.md +32 -27
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +546 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +126 -27
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +67 -12
- 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 +1335 -204
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +52 -28
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +20 -7
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +68 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +1116 -978
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +332 -747
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +461 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +106 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/mockModel.d.ts +42 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +182 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +58 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +350 -39
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +350 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +84 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +185 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streamText.d.ts +46 -0
- package/dist/client/streamText.d.ts.map +1 -0
- package/dist/client/streamText.js +117 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3716 -32
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +161 -59
- 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 +266 -128
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.d.ts +4 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +21 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +24 -2178
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js +10 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3120 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +4 -18
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +9 -5
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/files.d.ts +16 -10
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +2578 -366
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +397 -154
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +5697 -3584
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +18 -41
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +39 -339
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +114 -73
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +13 -13
- package/dist/component/users.d.ts +7 -7
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/component/vector/index.d.ts.map +1 -1
- package/dist/component/vector/index.js +1 -3
- package/dist/component/vector/index.js.map +1 -1
- package/dist/deltas.d.ts +43 -0
- package/dist/deltas.d.ts.map +1 -0
- package/dist/deltas.js +446 -0
- package/dist/deltas.js.map +1 -0
- package/dist/mapping.d.ts +38 -20
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +365 -97
- package/dist/mapping.js.map +1 -1
- 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/index.d.ts +5 -77
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -160
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts +36 -3
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +35 -9
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/types.d.ts +4 -18
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.d.ts +10 -0
- package/dist/react/useDeltaStreams.d.ts.map +1 -0
- package/dist/react/useDeltaStreams.js +106 -0
- package/dist/react/useDeltaStreams.js.map +1 -0
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +32 -15
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/react/useStreamingUIMessages.d.ts +22 -0
- package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
- package/dist/react/useStreamingUIMessages.js +92 -0
- package/dist/react/useStreamingUIMessages.js.map +1 -0
- package/dist/react/useThreadMessages.d.ts +104 -0
- package/dist/react/useThreadMessages.d.ts.map +1 -0
- package/dist/react/useThreadMessages.js +148 -0
- package/dist/react/useThreadMessages.js.map +1 -0
- package/dist/react/useUIMessages.d.ts +96 -0
- package/dist/react/useUIMessages.d.ts.map +1 -0
- package/dist/react/useUIMessages.js +108 -0
- package/dist/react/useUIMessages.js.map +1 -0
- package/dist/shared.d.ts +20 -4
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +45 -8
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +22981 -5666
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +245 -137
- package/dist/validators.js.map +1 -1
- package/package.json +101 -51
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +739 -0
- package/src/client/approval.test.ts +350 -0
- package/src/client/createTool.ts +291 -76
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +67 -31
- package/src/client/files.ts +100 -20
- package/src/client/index.test.ts +40 -85
- package/src/client/index.ts +638 -1289
- package/src/client/messages.ts +237 -0
- package/src/client/mockModel.ts +252 -0
- package/src/client/saveInputMessages.test.ts +583 -0
- package/src/client/saveInputMessages.ts +101 -0
- package/src/client/search.test.ts +1207 -0
- package/src/client/search.ts +581 -70
- package/src/client/start.ts +327 -0
- package/src/client/streamText.ts +187 -0
- package/src/client/streaming.test.ts +186 -0
- package/src/client/streaming.ts +241 -97
- package/src/client/threads.ts +83 -0
- package/src/client/types.ts +370 -219
- package/src/client/utils.ts +27 -0
- package/src/component/_generated/api.ts +64 -0
- package/src/component/_generated/component.ts +4902 -0
- package/src/component/_generated/{server.d.ts → server.ts} +33 -21
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +195 -51
- package/src/component/messages.ts +500 -201
- package/src/component/schema.ts +20 -46
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +184 -83
- package/src/component/users.test.ts +0 -1
- package/src/component/vector/index.ts +1 -3
- package/src/deltas.test.ts +626 -0
- package/src/deltas.ts +569 -0
- package/src/fromUIMessages.test.ts +497 -0
- package/src/mapping.test.ts +180 -6
- package/src/mapping.ts +479 -162
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/index.ts +10 -230
- package/src/react/optimisticallySendMessage.ts +55 -12
- package/src/react/types.ts +6 -39
- package/src/react/useDeltaStreams.ts +160 -0
- package/src/react/useSmoothText.ts +56 -36
- package/src/react/useStreamingUIMessages.ts +143 -0
- package/src/react/useThreadMessages.ts +262 -0
- package/src/react/useUIMessages.test.ts +255 -0
- package/src/react/useUIMessages.ts +195 -0
- package/src/shared.ts +88 -12
- package/src/test.ts +18 -0
- package/src/toUIMessages.test.ts +1269 -0
- package/src/validators.test.ts +18 -19
- package/src/validators.ts +325 -185
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- 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/dist/package.json +0 -3
- package/dist/react/deltas.d.ts +0 -26
- package/dist/react/deltas.d.ts.map +0 -1
- package/dist/react/deltas.js +0 -384
- package/dist/react/deltas.js.map +0 -1
- package/dist/react/toUIMessages.d.ts +0 -15
- package/dist/react/toUIMessages.d.ts.map +0 -1
- package/dist/react/toUIMessages.js +0 -211
- package/dist/react/toUIMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
- package/src/component/_generated/api.d.ts +0 -2202
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/src/react/deltas.test.ts +0 -315
- package/src/react/deltas.ts +0 -478
- package/src/react/toUIMessages.test.ts +0 -420
- package/src/react/toUIMessages.ts +0 -253
- package/src/vitest.config.ts +0 -7
- /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
package/src/mapping.ts
CHANGED
|
@@ -16,27 +16,43 @@ import {
|
|
|
16
16
|
type TextPart,
|
|
17
17
|
type ToolCallPart,
|
|
18
18
|
type ToolResultPart,
|
|
19
|
-
type
|
|
19
|
+
type ProviderMetadata,
|
|
20
|
+
type JSONValue,
|
|
20
21
|
} from "ai";
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
import {
|
|
23
|
+
vMessageWithMetadata,
|
|
24
|
+
type vSourcePart,
|
|
25
|
+
type Message,
|
|
26
|
+
type MessageWithMetadata,
|
|
27
|
+
type Usage,
|
|
28
|
+
type vFilePart,
|
|
29
|
+
type vImagePart,
|
|
30
|
+
type vReasoningPart,
|
|
31
|
+
type vRedactedReasoningPart,
|
|
32
|
+
type vTextPart,
|
|
33
|
+
type vToolCallPart,
|
|
34
|
+
type vToolResultPart,
|
|
35
|
+
type SourcePart,
|
|
36
|
+
vToolResultOutput,
|
|
37
|
+
type MessageDoc,
|
|
38
|
+
vToolApprovalRequest,
|
|
39
|
+
vToolApprovalResponse,
|
|
33
40
|
} from "./validators.js";
|
|
34
41
|
import type { ActionCtx, AgentComponent } from "./client/types.js";
|
|
35
|
-
import type {
|
|
42
|
+
import type { MutationCtx } from "./client/types.js";
|
|
36
43
|
import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
|
|
37
44
|
import type { Infer } from "convex/values";
|
|
38
|
-
import {
|
|
39
|
-
|
|
45
|
+
import {
|
|
46
|
+
convertUint8ArrayToBase64,
|
|
47
|
+
type ProviderOptions,
|
|
48
|
+
type ReasoningPart,
|
|
49
|
+
} from "@ai-sdk/provider-utils";
|
|
50
|
+
import { parse, validate } from "convex-helpers/validators";
|
|
51
|
+
import {
|
|
52
|
+
getModelName,
|
|
53
|
+
getProviderName,
|
|
54
|
+
type ModelOrMetadata,
|
|
55
|
+
} from "./shared.js";
|
|
40
56
|
export type AIMessageWithoutId = Omit<AIMessage, "id">;
|
|
41
57
|
|
|
42
58
|
export type SerializeUrlsAndUint8Arrays<T> = T extends URL
|
|
@@ -45,8 +61,7 @@ export type SerializeUrlsAndUint8Arrays<T> = T extends URL
|
|
|
45
61
|
? ArrayBuffer
|
|
46
62
|
: T extends Array<infer Inner>
|
|
47
63
|
? Array<SerializeUrlsAndUint8Arrays<Inner>>
|
|
48
|
-
:
|
|
49
|
-
T extends Record<string, any>
|
|
64
|
+
: T extends Record<string, any>
|
|
50
65
|
? { [K in keyof T]: SerializeUrlsAndUint8Arrays<T[K]> }
|
|
51
66
|
: T;
|
|
52
67
|
|
|
@@ -56,12 +71,10 @@ export type SerializedContent = Message["content"];
|
|
|
56
71
|
export type SerializedMessage = Message;
|
|
57
72
|
|
|
58
73
|
export async function serializeMessage(
|
|
59
|
-
ctx: ActionCtx |
|
|
74
|
+
ctx: ActionCtx | MutationCtx,
|
|
60
75
|
component: AgentComponent,
|
|
61
|
-
|
|
76
|
+
message: ModelMessage | Message,
|
|
62
77
|
): Promise<{ message: SerializedMessage; fileIds?: string[] }> {
|
|
63
|
-
const message =
|
|
64
|
-
"id" in messageWithId ? omit(messageWithId, ["id"]) : messageWithId;
|
|
65
78
|
const { content, fileIds } = await serializeContent(
|
|
66
79
|
ctx,
|
|
67
80
|
component,
|
|
@@ -69,36 +82,129 @@ export async function serializeMessage(
|
|
|
69
82
|
);
|
|
70
83
|
return {
|
|
71
84
|
message: {
|
|
72
|
-
|
|
85
|
+
role: message.role,
|
|
73
86
|
content,
|
|
87
|
+
...(message.providerOptions
|
|
88
|
+
? { providerOptions: message.providerOptions }
|
|
89
|
+
: {}),
|
|
74
90
|
} as SerializedMessage,
|
|
75
91
|
fileIds,
|
|
76
92
|
};
|
|
77
93
|
}
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
// Similar to serializeMessage, but doesn't save any files and is looser
|
|
96
|
+
// For use on the frontend / in synchronous environments.
|
|
97
|
+
export function fromModelMessage(message: ModelMessage): Message {
|
|
98
|
+
const content = fromModelMessageContent(message.content);
|
|
99
|
+
return {
|
|
100
|
+
role: message.role,
|
|
101
|
+
content,
|
|
102
|
+
...(message.providerOptions
|
|
103
|
+
? { providerOptions: message.providerOptions }
|
|
104
|
+
: {}),
|
|
105
|
+
} as SerializedMessage;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function serializeOrThrow(
|
|
109
|
+
message: ModelMessage | Message,
|
|
110
|
+
): Promise<SerializedMessage> {
|
|
111
|
+
const { content } = await serializeContent(
|
|
112
|
+
{} as any,
|
|
113
|
+
{} as any,
|
|
114
|
+
message.content,
|
|
115
|
+
);
|
|
116
|
+
return {
|
|
117
|
+
role: message.role,
|
|
118
|
+
content,
|
|
119
|
+
...(message.providerOptions
|
|
120
|
+
? { providerOptions: message.providerOptions }
|
|
121
|
+
: {}),
|
|
122
|
+
} as SerializedMessage;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function toModelMessage(
|
|
126
|
+
message: SerializedMessage | ModelMessage,
|
|
127
|
+
): ModelMessage {
|
|
80
128
|
return {
|
|
81
129
|
...message,
|
|
82
|
-
content:
|
|
130
|
+
content: toModelMessageContent(message.content),
|
|
83
131
|
} as ModelMessage;
|
|
84
132
|
}
|
|
85
133
|
|
|
134
|
+
export function docsToModelMessages(messages: MessageDoc[]): ModelMessage[] {
|
|
135
|
+
return messages
|
|
136
|
+
.map((m) => m.message)
|
|
137
|
+
.filter((m) => !!m)
|
|
138
|
+
.filter((m) => !!m.content.length)
|
|
139
|
+
.map(toModelMessage);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Merge consecutive tool messages that contain `tool-approval-response` parts
|
|
144
|
+
* into a single tool message. The AI SDK's `collectToolApprovals` only examines
|
|
145
|
+
* the last tool message, so when multiple approvals are saved as separate
|
|
146
|
+
* messages (e.g. approve tool A, then deny tool B), they must be combined
|
|
147
|
+
* for the SDK to process them all.
|
|
148
|
+
*/
|
|
149
|
+
export function mergeApprovalResponseMessages(
|
|
150
|
+
messages: ModelMessage[],
|
|
151
|
+
): ModelMessage[] {
|
|
152
|
+
const result: ModelMessage[] = [];
|
|
153
|
+
for (const msg of messages) {
|
|
154
|
+
const prev = result.at(-1);
|
|
155
|
+
if (
|
|
156
|
+
msg.role === "tool" &&
|
|
157
|
+
prev?.role === "tool" &&
|
|
158
|
+
Array.isArray(msg.content) &&
|
|
159
|
+
Array.isArray(prev.content) &&
|
|
160
|
+
hasApprovalResponse(msg.content) &&
|
|
161
|
+
hasApprovalResponse(prev.content)
|
|
162
|
+
) {
|
|
163
|
+
// Clone before merging to avoid mutating the original message's content array
|
|
164
|
+
const cloned = { ...prev, content: [...(prev.content as any[])] };
|
|
165
|
+
result[result.length - 1] = cloned;
|
|
166
|
+
(cloned.content as any[]).push(...(msg.content as any[]));
|
|
167
|
+
} else {
|
|
168
|
+
result.push(msg);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function hasApprovalResponse(content: any[]): boolean {
|
|
175
|
+
return content.some(
|
|
176
|
+
(p: any) => p.type === "tool-approval-response",
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
86
180
|
export function serializeUsage(usage: LanguageModelUsage): Usage {
|
|
87
181
|
return {
|
|
88
182
|
promptTokens: usage.inputTokens ?? 0,
|
|
89
183
|
completionTokens: usage.outputTokens ?? 0,
|
|
90
184
|
totalTokens: usage.totalTokens ?? 0,
|
|
91
185
|
reasoningTokens: usage.reasoningTokens,
|
|
186
|
+
cachedInputTokens: usage.cachedInputTokens,
|
|
92
187
|
};
|
|
93
188
|
}
|
|
94
189
|
|
|
95
|
-
export function
|
|
190
|
+
export function toModelMessageUsage(usage: Usage): LanguageModelUsage {
|
|
96
191
|
return {
|
|
97
192
|
inputTokens: usage.promptTokens,
|
|
98
193
|
outputTokens: usage.completionTokens,
|
|
99
194
|
totalTokens: usage.totalTokens,
|
|
100
195
|
reasoningTokens: usage.reasoningTokens,
|
|
101
196
|
cachedInputTokens: usage.cachedInputTokens,
|
|
197
|
+
// These detail fields are required by LanguageModelUsage type but we don't
|
|
198
|
+
// have the granular data, so we provide empty objects with undefined values.
|
|
199
|
+
inputTokenDetails: {
|
|
200
|
+
cacheReadTokens: undefined,
|
|
201
|
+
cacheWriteTokens: undefined,
|
|
202
|
+
noCacheTokens: undefined,
|
|
203
|
+
},
|
|
204
|
+
outputTokenDetails: {
|
|
205
|
+
textTokens: undefined,
|
|
206
|
+
reasoningTokens: undefined,
|
|
207
|
+
},
|
|
102
208
|
};
|
|
103
209
|
}
|
|
104
210
|
|
|
@@ -109,34 +215,57 @@ export function serializeWarnings(
|
|
|
109
215
|
return undefined;
|
|
110
216
|
}
|
|
111
217
|
return warnings.map((warning) => {
|
|
112
|
-
if (warning.type
|
|
113
|
-
return
|
|
218
|
+
if (warning.type === "compatibility") {
|
|
219
|
+
return {
|
|
220
|
+
type: "unsupported-setting",
|
|
221
|
+
setting: warning.feature,
|
|
222
|
+
details: warning.details,
|
|
223
|
+
};
|
|
114
224
|
}
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
setting: warning.setting.toString(),
|
|
118
|
-
};
|
|
119
|
-
});
|
|
225
|
+
return warning;
|
|
226
|
+
}) as any;
|
|
120
227
|
}
|
|
121
228
|
|
|
122
|
-
export function
|
|
229
|
+
export function toModelMessageWarnings(
|
|
123
230
|
warnings: MessageWithMetadata["warnings"],
|
|
124
231
|
): CallWarning[] | undefined {
|
|
125
|
-
|
|
126
|
-
|
|
232
|
+
if (!warnings) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
return warnings.map((warning) => {
|
|
236
|
+
if (warning.type === "unsupported-setting") {
|
|
237
|
+
return {
|
|
238
|
+
type: "compatibility",
|
|
239
|
+
feature: warning.setting,
|
|
240
|
+
details: warning.details,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return warning;
|
|
244
|
+
}) as any;
|
|
127
245
|
}
|
|
128
246
|
|
|
129
247
|
export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
130
248
|
ctx: ActionCtx,
|
|
131
249
|
component: AgentComponent,
|
|
132
250
|
step: StepResult<TOOLS>,
|
|
133
|
-
|
|
134
|
-
|
|
251
|
+
model: ModelOrMetadata | undefined,
|
|
252
|
+
/**
|
|
253
|
+
* If provided, these are the new response messages for this step
|
|
254
|
+
* (pre-sliced by the caller). When not provided, falls back to the
|
|
255
|
+
* existing heuristic of slicing the last 1-2 messages.
|
|
256
|
+
*
|
|
257
|
+
* This is needed for tool approval flows where the SDK adds extra
|
|
258
|
+
* messages (e.g. approval tool-results) at the beginning of
|
|
259
|
+
* responseMessages that the old slice(-1/-2) logic would miss.
|
|
260
|
+
*/
|
|
261
|
+
newResponseMessages?: ModelMessage[],
|
|
262
|
+
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
135
263
|
// If there are tool results, there's another message with the tool results
|
|
136
|
-
// ref: https://github.com/vercel/ai/blob/main/packages/ai/
|
|
264
|
+
// ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
|
|
265
|
+
const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
|
|
137
266
|
const assistantFields = {
|
|
138
|
-
model:
|
|
139
|
-
provider:
|
|
267
|
+
model: model ? getModelName(model) : undefined,
|
|
268
|
+
provider: model ? getProviderName(model) : undefined,
|
|
140
269
|
providerMetadata: step.providerMetadata,
|
|
141
270
|
reasoning: step.reasoningText,
|
|
142
271
|
reasoningDetails: step.reasoning,
|
|
@@ -144,40 +273,42 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
144
273
|
warnings: serializeWarnings(step.warnings),
|
|
145
274
|
finishReason: step.finishReason,
|
|
146
275
|
// Only store the sources on one message
|
|
147
|
-
sources:
|
|
276
|
+
sources: hasToolMessage ? undefined : step.sources,
|
|
148
277
|
} satisfies Omit<MessageWithMetadata, "message" | "text" | "fileIds">;
|
|
149
|
-
const toolFields = {
|
|
150
|
-
|
|
151
|
-
|
|
278
|
+
const toolFields = { sources: step.sources };
|
|
279
|
+
|
|
280
|
+
// Determine which messages to serialize for this step
|
|
281
|
+
let messagesToSerialize: ModelMessage[];
|
|
282
|
+
if (newResponseMessages) {
|
|
283
|
+
messagesToSerialize = newResponseMessages;
|
|
284
|
+
} else if (hasToolMessage) {
|
|
285
|
+
messagesToSerialize = step.response.messages.slice(-2);
|
|
286
|
+
} else if (step.content.length) {
|
|
287
|
+
messagesToSerialize = step.response.messages.slice(-1);
|
|
288
|
+
} else {
|
|
289
|
+
messagesToSerialize = [{ role: "assistant" as const, content: [] }];
|
|
290
|
+
}
|
|
291
|
+
|
|
152
292
|
const messages: MessageWithMetadata[] = await Promise.all(
|
|
153
|
-
(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
).map(async (messageWithId): Promise<MessageWithMetadata> => {
|
|
157
|
-
const { message, fileIds } = await serializeMessage(
|
|
158
|
-
ctx,
|
|
159
|
-
component,
|
|
160
|
-
messageWithId,
|
|
161
|
-
);
|
|
162
|
-
return {
|
|
293
|
+
messagesToSerialize.map(async (msg): Promise<MessageWithMetadata> => {
|
|
294
|
+
const { message, fileIds } = await serializeMessage(ctx, component, msg);
|
|
295
|
+
return parse(vMessageWithMetadata, {
|
|
163
296
|
message,
|
|
164
|
-
// Let's not store the ID by default here. It's being generated internally
|
|
165
|
-
// and not referenced elsewhere that we know of.
|
|
166
|
-
// id: message.id,
|
|
167
297
|
...(message.role === "tool" ? toolFields : assistantFields),
|
|
168
298
|
text: step.text,
|
|
169
299
|
fileIds,
|
|
170
|
-
};
|
|
300
|
+
});
|
|
171
301
|
}),
|
|
172
302
|
);
|
|
173
|
-
|
|
303
|
+
// TODO: capture step.files separately?
|
|
304
|
+
return { messages };
|
|
174
305
|
}
|
|
175
306
|
|
|
176
307
|
export async function serializeObjectResult(
|
|
177
308
|
ctx: ActionCtx,
|
|
178
309
|
component: AgentComponent,
|
|
179
310
|
result: GenerateObjectResult<unknown>,
|
|
180
|
-
|
|
311
|
+
model: ModelOrMetadata | undefined,
|
|
181
312
|
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
182
313
|
const text = JSON.stringify(result.object);
|
|
183
314
|
|
|
@@ -189,9 +320,8 @@ export async function serializeObjectResult(
|
|
|
189
320
|
messages: [
|
|
190
321
|
{
|
|
191
322
|
message,
|
|
192
|
-
|
|
193
|
-
model:
|
|
194
|
-
provider: metadata.provider,
|
|
323
|
+
model: model ? getModelName(model) : undefined,
|
|
324
|
+
provider: model ? getProviderName(model) : undefined,
|
|
195
325
|
providerMetadata: result.providerMetadata,
|
|
196
326
|
finishReason: result.finishReason,
|
|
197
327
|
text,
|
|
@@ -203,8 +333,18 @@ export async function serializeObjectResult(
|
|
|
203
333
|
};
|
|
204
334
|
}
|
|
205
335
|
|
|
336
|
+
function getMimeOrMediaType(part: { mediaType?: string; mimeType?: string }) {
|
|
337
|
+
if ("mediaType" in part) {
|
|
338
|
+
return part.mediaType;
|
|
339
|
+
}
|
|
340
|
+
if ("mimeType" in part) {
|
|
341
|
+
return part.mimeType;
|
|
342
|
+
}
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
|
|
206
346
|
export async function serializeContent(
|
|
207
|
-
ctx: ActionCtx |
|
|
347
|
+
ctx: ActionCtx | MutationCtx,
|
|
208
348
|
component: AgentComponent,
|
|
209
349
|
content: Content | Message["content"],
|
|
210
350
|
): Promise<{ content: SerializedContent; fileIds?: string[] }> {
|
|
@@ -212,23 +352,24 @@ export async function serializeContent(
|
|
|
212
352
|
return { content };
|
|
213
353
|
}
|
|
214
354
|
const fileIds: string[] = [];
|
|
215
|
-
function getMimeType(part: { mediaType?: string; mimeType?: string }) {
|
|
216
|
-
if ("mediaType" in part) {
|
|
217
|
-
return part.mediaType;
|
|
218
|
-
}
|
|
219
|
-
if ("mimeType" in part) {
|
|
220
|
-
return part.mimeType;
|
|
221
|
-
}
|
|
222
|
-
return undefined;
|
|
223
|
-
}
|
|
224
355
|
const serialized = await Promise.all(
|
|
225
356
|
content.map(async (part) => {
|
|
357
|
+
const metadata: {
|
|
358
|
+
providerOptions?: ProviderOptions;
|
|
359
|
+
providerMetadata?: ProviderMetadata;
|
|
360
|
+
} = {};
|
|
361
|
+
if ("providerOptions" in part) {
|
|
362
|
+
metadata.providerOptions = part.providerOptions as ProviderOptions;
|
|
363
|
+
}
|
|
364
|
+
if ("providerMetadata" in part) {
|
|
365
|
+
metadata.providerMetadata = part.providerMetadata as ProviderMetadata;
|
|
366
|
+
}
|
|
226
367
|
switch (part.type) {
|
|
227
368
|
case "text": {
|
|
228
369
|
return {
|
|
229
370
|
type: part.type,
|
|
230
371
|
text: part.text,
|
|
231
|
-
|
|
372
|
+
...metadata,
|
|
232
373
|
} satisfies Infer<typeof vTextPart>;
|
|
233
374
|
}
|
|
234
375
|
case "image": {
|
|
@@ -241,7 +382,7 @@ export async function serializeContent(
|
|
|
241
382
|
ctx,
|
|
242
383
|
component,
|
|
243
384
|
new Blob([image], {
|
|
244
|
-
type:
|
|
385
|
+
type: getMimeOrMediaType(part) || guessMimeType(image),
|
|
245
386
|
}),
|
|
246
387
|
);
|
|
247
388
|
image = file.url;
|
|
@@ -249,8 +390,8 @@ export async function serializeContent(
|
|
|
249
390
|
}
|
|
250
391
|
return {
|
|
251
392
|
type: part.type,
|
|
252
|
-
|
|
253
|
-
|
|
393
|
+
mediaType: getMimeOrMediaType(part),
|
|
394
|
+
...metadata,
|
|
254
395
|
image,
|
|
255
396
|
} satisfies Infer<typeof vImagePart>;
|
|
256
397
|
}
|
|
@@ -260,7 +401,7 @@ export async function serializeContent(
|
|
|
260
401
|
const { file } = await storeFile(
|
|
261
402
|
ctx,
|
|
262
403
|
component,
|
|
263
|
-
new Blob([data], { type:
|
|
404
|
+
new Blob([data], { type: getMimeOrMediaType(part) }),
|
|
264
405
|
);
|
|
265
406
|
data = file.url;
|
|
266
407
|
fileIds.push(file.fileId);
|
|
@@ -269,118 +410,295 @@ export async function serializeContent(
|
|
|
269
410
|
type: part.type,
|
|
270
411
|
data,
|
|
271
412
|
filename: part.filename,
|
|
272
|
-
|
|
273
|
-
|
|
413
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
414
|
+
...metadata,
|
|
274
415
|
} satisfies Infer<typeof vFilePart>;
|
|
275
416
|
}
|
|
276
417
|
case "tool-call": {
|
|
277
|
-
|
|
418
|
+
// Handle legacy data where only args field exists
|
|
419
|
+
const input = part.input ?? (part as any)?.args ?? {};
|
|
278
420
|
return {
|
|
279
421
|
type: part.type,
|
|
280
|
-
|
|
422
|
+
input,
|
|
423
|
+
/** @deprecated Use `input` instead. */
|
|
424
|
+
args: input,
|
|
281
425
|
toolCallId: part.toolCallId,
|
|
282
426
|
toolName: part.toolName,
|
|
283
|
-
providerOptions: part.providerOptions,
|
|
284
427
|
providerExecuted: part.providerExecuted,
|
|
428
|
+
...metadata,
|
|
285
429
|
} satisfies Infer<typeof vToolCallPart>;
|
|
286
430
|
}
|
|
287
431
|
case "tool-result": {
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
type: part.type,
|
|
291
|
-
result: result ?? null,
|
|
292
|
-
toolCallId: part.toolCallId,
|
|
293
|
-
toolName: part.toolName,
|
|
294
|
-
providerOptions: part.providerOptions,
|
|
295
|
-
} satisfies Infer<typeof vToolResultPart>;
|
|
432
|
+
return normalizeToolResult(part, metadata);
|
|
296
433
|
}
|
|
297
434
|
case "reasoning": {
|
|
298
435
|
return {
|
|
299
436
|
type: part.type,
|
|
300
437
|
text: part.text,
|
|
301
|
-
|
|
438
|
+
...metadata,
|
|
302
439
|
} satisfies Infer<typeof vReasoningPart>;
|
|
303
440
|
}
|
|
441
|
+
// Not in current generation output, but could be in historical messages
|
|
304
442
|
case "redacted-reasoning": {
|
|
305
443
|
return {
|
|
306
444
|
type: part.type,
|
|
307
445
|
data: part.data,
|
|
308
|
-
|
|
446
|
+
...metadata,
|
|
309
447
|
} satisfies Infer<typeof vRedactedReasoningPart>;
|
|
310
448
|
}
|
|
449
|
+
case "source": {
|
|
450
|
+
return part satisfies Infer<typeof vSourcePart>;
|
|
451
|
+
}
|
|
452
|
+
case "tool-approval-request": {
|
|
453
|
+
return {
|
|
454
|
+
type: part.type,
|
|
455
|
+
approvalId: part.approvalId,
|
|
456
|
+
toolCallId: part.toolCallId,
|
|
457
|
+
...metadata,
|
|
458
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
459
|
+
}
|
|
460
|
+
case "tool-approval-response": {
|
|
461
|
+
return {
|
|
462
|
+
type: part.type,
|
|
463
|
+
approvalId: part.approvalId,
|
|
464
|
+
approved: part.approved,
|
|
465
|
+
reason: part.reason,
|
|
466
|
+
providerExecuted: part.providerExecuted,
|
|
467
|
+
...metadata,
|
|
468
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
469
|
+
}
|
|
311
470
|
default:
|
|
312
|
-
return
|
|
471
|
+
return null;
|
|
313
472
|
}
|
|
314
473
|
}),
|
|
315
474
|
);
|
|
316
475
|
return {
|
|
317
|
-
content: serialized as SerializedContent,
|
|
476
|
+
content: serialized.filter((p) => p !== null) as SerializedContent,
|
|
318
477
|
fileIds: fileIds.length > 0 ? fileIds : undefined,
|
|
319
478
|
};
|
|
320
479
|
}
|
|
321
480
|
|
|
322
|
-
export function
|
|
481
|
+
export function fromModelMessageContent(content: Content): Message["content"] {
|
|
323
482
|
if (typeof content === "string") {
|
|
324
483
|
return content;
|
|
325
484
|
}
|
|
326
|
-
return content
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
485
|
+
return content
|
|
486
|
+
.map((part) => {
|
|
487
|
+
const metadata: {
|
|
488
|
+
providerOptions?: ProviderOptions;
|
|
489
|
+
providerMetadata?: ProviderMetadata;
|
|
490
|
+
} = {};
|
|
491
|
+
if ("providerOptions" in part) {
|
|
492
|
+
metadata.providerOptions = part.providerOptions as ProviderOptions;
|
|
493
|
+
}
|
|
494
|
+
if ("providerMetadata" in part) {
|
|
495
|
+
metadata.providerMetadata = part.providerMetadata as ProviderMetadata;
|
|
496
|
+
}
|
|
497
|
+
switch (part.type) {
|
|
498
|
+
case "text":
|
|
499
|
+
return part satisfies Infer<typeof vTextPart>;
|
|
500
|
+
case "image":
|
|
501
|
+
return {
|
|
502
|
+
type: part.type,
|
|
503
|
+
mediaType: getMimeOrMediaType(part),
|
|
504
|
+
...metadata,
|
|
505
|
+
image: serializeDataOrUrl(part.image),
|
|
506
|
+
} satisfies Infer<typeof vImagePart>;
|
|
507
|
+
case "file":
|
|
508
|
+
return {
|
|
509
|
+
type: part.type,
|
|
510
|
+
data: serializeDataOrUrl(part.data),
|
|
511
|
+
filename: part.filename,
|
|
512
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
513
|
+
...metadata,
|
|
514
|
+
} satisfies Infer<typeof vFilePart>;
|
|
515
|
+
case "tool-call":
|
|
516
|
+
// Handle legacy data where only args field exists
|
|
517
|
+
return {
|
|
518
|
+
type: part.type,
|
|
519
|
+
input: part.input ?? (part as any)?.args ?? {},
|
|
520
|
+
/** @deprecated Use `input` instead. */
|
|
521
|
+
args: part.input ?? (part as any)?.args ?? {},
|
|
522
|
+
toolCallId: part.toolCallId,
|
|
523
|
+
toolName: part.toolName,
|
|
524
|
+
providerExecuted: part.providerExecuted,
|
|
525
|
+
...metadata,
|
|
526
|
+
} satisfies Infer<typeof vToolCallPart>;
|
|
527
|
+
case "tool-result":
|
|
528
|
+
return normalizeToolResult(part, metadata);
|
|
529
|
+
case "reasoning":
|
|
530
|
+
return {
|
|
531
|
+
type: part.type,
|
|
532
|
+
text: part.text,
|
|
533
|
+
...metadata,
|
|
534
|
+
} satisfies Infer<typeof vReasoningPart>;
|
|
535
|
+
case "tool-approval-request":
|
|
536
|
+
return {
|
|
537
|
+
type: part.type,
|
|
538
|
+
approvalId: part.approvalId,
|
|
539
|
+
toolCallId: part.toolCallId,
|
|
540
|
+
...metadata,
|
|
541
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
542
|
+
case "tool-approval-response":
|
|
543
|
+
return {
|
|
544
|
+
type: part.type,
|
|
545
|
+
approvalId: part.approvalId,
|
|
546
|
+
approved: part.approved,
|
|
547
|
+
reason: part.reason,
|
|
548
|
+
providerExecuted: part.providerExecuted,
|
|
549
|
+
...metadata,
|
|
550
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
551
|
+
// Not in current generation output, but could be in historical messages
|
|
552
|
+
default:
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
})
|
|
556
|
+
.filter((p) => p !== null) as Message["content"];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function toModelMessageContent(
|
|
560
|
+
content: SerializedContent | ModelMessage["content"],
|
|
561
|
+
): Content {
|
|
562
|
+
if (typeof content === "string") {
|
|
563
|
+
return content;
|
|
564
|
+
}
|
|
565
|
+
return content
|
|
566
|
+
.map((part) => {
|
|
567
|
+
const metadata: {
|
|
568
|
+
providerOptions?: ProviderOptions;
|
|
569
|
+
providerMetadata?: ProviderMetadata;
|
|
570
|
+
} = {};
|
|
571
|
+
if ("providerOptions" in part) {
|
|
572
|
+
metadata.providerOptions = part.providerOptions;
|
|
573
|
+
}
|
|
574
|
+
if ("providerMetadata" in part) {
|
|
575
|
+
metadata.providerMetadata = part.providerMetadata;
|
|
576
|
+
}
|
|
577
|
+
switch (part.type) {
|
|
578
|
+
case "text":
|
|
579
|
+
return {
|
|
580
|
+
type: part.type,
|
|
581
|
+
text: part.text,
|
|
582
|
+
...metadata,
|
|
583
|
+
} satisfies TextPart;
|
|
584
|
+
case "image":
|
|
585
|
+
return {
|
|
586
|
+
type: part.type,
|
|
587
|
+
image: toModelMessageDataOrUrl(part.image),
|
|
588
|
+
mediaType: getMimeOrMediaType(part),
|
|
589
|
+
...metadata,
|
|
590
|
+
} satisfies ImagePart;
|
|
591
|
+
case "file":
|
|
592
|
+
return {
|
|
593
|
+
type: part.type,
|
|
594
|
+
data: toModelMessageDataOrUrl(part.data),
|
|
595
|
+
filename: part.filename,
|
|
596
|
+
mediaType: getMimeOrMediaType(part)!,
|
|
597
|
+
...metadata,
|
|
598
|
+
} satisfies FilePart;
|
|
599
|
+
case "tool-call": {
|
|
600
|
+
// Handle legacy data where only args field exists
|
|
601
|
+
const input = part.input ?? (part as any)?.args ?? {};
|
|
602
|
+
return {
|
|
603
|
+
type: part.type,
|
|
604
|
+
input,
|
|
605
|
+
toolCallId: part.toolCallId,
|
|
606
|
+
toolName: part.toolName,
|
|
607
|
+
providerExecuted: part.providerExecuted,
|
|
608
|
+
...metadata,
|
|
609
|
+
} satisfies ToolCallPart;
|
|
610
|
+
}
|
|
611
|
+
case "tool-result": {
|
|
612
|
+
return normalizeToolResult(part, metadata);
|
|
613
|
+
}
|
|
614
|
+
case "reasoning":
|
|
615
|
+
return {
|
|
616
|
+
type: part.type,
|
|
617
|
+
text: part.text,
|
|
618
|
+
...metadata,
|
|
619
|
+
} satisfies ReasoningPart;
|
|
620
|
+
case "redacted-reasoning":
|
|
621
|
+
// TODO: should we just drop this?
|
|
622
|
+
return {
|
|
623
|
+
type: "reasoning",
|
|
624
|
+
text: "",
|
|
625
|
+
...metadata,
|
|
626
|
+
providerOptions: metadata.providerOptions
|
|
627
|
+
? {
|
|
628
|
+
...Object.fromEntries(
|
|
629
|
+
Object.entries(metadata.providerOptions ?? {}).map(
|
|
630
|
+
([key, value]) => [
|
|
631
|
+
key,
|
|
632
|
+
{ ...value, redactedData: part.data },
|
|
633
|
+
],
|
|
634
|
+
),
|
|
635
|
+
),
|
|
636
|
+
}
|
|
637
|
+
: undefined,
|
|
638
|
+
} satisfies ReasoningPart;
|
|
639
|
+
case "source":
|
|
640
|
+
return part satisfies SourcePart;
|
|
641
|
+
case "tool-approval-request":
|
|
642
|
+
return {
|
|
643
|
+
type: part.type,
|
|
644
|
+
approvalId: part.approvalId,
|
|
645
|
+
toolCallId: part.toolCallId,
|
|
646
|
+
...metadata,
|
|
647
|
+
} satisfies Infer<typeof vToolApprovalRequest>;
|
|
648
|
+
case "tool-approval-response":
|
|
649
|
+
return {
|
|
650
|
+
type: part.type,
|
|
651
|
+
approvalId: part.approvalId,
|
|
652
|
+
approved: part.approved,
|
|
653
|
+
reason: part.reason,
|
|
654
|
+
providerExecuted: part.providerExecuted,
|
|
655
|
+
...metadata,
|
|
656
|
+
} satisfies Infer<typeof vToolApprovalResponse>;
|
|
657
|
+
default:
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
.filter((p) => p !== null) as Content;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export function normalizeToolOutput(
|
|
665
|
+
result: string | JSONValue | undefined,
|
|
666
|
+
): ToolResultPart["output"] {
|
|
667
|
+
if (typeof result === "string") {
|
|
668
|
+
return {
|
|
669
|
+
type: "text",
|
|
670
|
+
value: result,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
if (validate(vToolResultOutput, result)) {
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
type: "json",
|
|
678
|
+
value: result ?? null,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function normalizeToolResult(
|
|
683
|
+
part: ToolResultPart | Infer<typeof vToolResultPart>,
|
|
684
|
+
metadata: {
|
|
685
|
+
providerOptions?: ProviderOptions;
|
|
686
|
+
providerMetadata?: ProviderMetadata;
|
|
687
|
+
},
|
|
688
|
+
): ToolResultPart & Infer<typeof vToolResultPart> {
|
|
689
|
+
return {
|
|
690
|
+
type: part.type,
|
|
691
|
+
output: part.output
|
|
692
|
+
? validate(vToolResultOutput, part.output)
|
|
693
|
+
? (part.output as any)
|
|
694
|
+
: normalizeToolOutput(JSON.stringify(part.output))
|
|
695
|
+
: normalizeToolOutput("result" in part ? part.result : undefined),
|
|
696
|
+
toolCallId: part.toolCallId,
|
|
697
|
+
toolName: part.toolName,
|
|
698
|
+
// Preserve isError flag for error reporting
|
|
699
|
+
...("isError" in part && part.isError ? { isError: true } : {}),
|
|
700
|
+
...metadata,
|
|
701
|
+
} satisfies ToolResultPart;
|
|
384
702
|
}
|
|
385
703
|
|
|
386
704
|
/**
|
|
@@ -473,9 +791,12 @@ export function serializeDataOrUrl(
|
|
|
473
791
|
) as ArrayBuffer;
|
|
474
792
|
}
|
|
475
793
|
|
|
476
|
-
export function
|
|
477
|
-
urlOrString: string | ArrayBuffer,
|
|
794
|
+
export function toModelMessageDataOrUrl(
|
|
795
|
+
urlOrString: string | ArrayBuffer | URL | DataContent,
|
|
478
796
|
): URL | DataContent {
|
|
797
|
+
if (urlOrString instanceof URL) {
|
|
798
|
+
return urlOrString;
|
|
799
|
+
}
|
|
479
800
|
if (typeof urlOrString === "string") {
|
|
480
801
|
if (
|
|
481
802
|
urlOrString.startsWith("http://") ||
|
|
@@ -492,7 +813,7 @@ export function toUIFilePart(part: ImagePart | FilePart): FileUIPart {
|
|
|
492
813
|
const dataOrUrl = part.type === "image" ? part.image : part.data;
|
|
493
814
|
const url =
|
|
494
815
|
dataOrUrl instanceof ArrayBuffer
|
|
495
|
-
?
|
|
816
|
+
? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
|
|
496
817
|
: dataOrUrl.toString();
|
|
497
818
|
|
|
498
819
|
return {
|
|
@@ -504,10 +825,6 @@ export function toUIFilePart(part: ImagePart | FilePart): FileUIPart {
|
|
|
504
825
|
};
|
|
505
826
|
}
|
|
506
827
|
|
|
507
|
-
function encodeBase64(data: ArrayBuffer): string {
|
|
508
|
-
return Buffer.from(data).toString("base64");
|
|
509
|
-
}
|
|
510
|
-
|
|
511
828
|
// Currently unused
|
|
512
829
|
// export function toModelMessages(args: {
|
|
513
830
|
// messages?: ModelMessage[] | AIMessageWithoutId[];
|