@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/dist/mapping.js
CHANGED
|
@@ -1,38 +1,115 @@
|
|
|
1
1
|
import {} from "ai";
|
|
2
|
+
import { vMessageWithMetadata, vToolResultOutput, vToolApprovalRequest, vToolApprovalResponse, } from "./validators.js";
|
|
2
3
|
import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
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) {
|
|
6
8
|
const { content, fileIds } = await serializeContent(ctx, component, message.content);
|
|
7
9
|
return {
|
|
8
10
|
message: {
|
|
9
|
-
|
|
11
|
+
role: message.role,
|
|
10
12
|
content,
|
|
13
|
+
...(message.providerOptions
|
|
14
|
+
? { providerOptions: message.providerOptions }
|
|
15
|
+
: {}),
|
|
11
16
|
},
|
|
12
17
|
fileIds,
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
|
-
|
|
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) {
|
|
16
43
|
return {
|
|
17
44
|
...message,
|
|
18
|
-
content:
|
|
45
|
+
content: toModelMessageContent(message.content),
|
|
19
46
|
};
|
|
20
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
|
+
}
|
|
21
86
|
export function serializeUsage(usage) {
|
|
22
87
|
return {
|
|
23
88
|
promptTokens: usage.inputTokens ?? 0,
|
|
24
89
|
completionTokens: usage.outputTokens ?? 0,
|
|
25
90
|
totalTokens: usage.totalTokens ?? 0,
|
|
26
91
|
reasoningTokens: usage.reasoningTokens,
|
|
92
|
+
cachedInputTokens: usage.cachedInputTokens,
|
|
27
93
|
};
|
|
28
94
|
}
|
|
29
|
-
export function
|
|
95
|
+
export function toModelMessageUsage(usage) {
|
|
30
96
|
return {
|
|
31
97
|
inputTokens: usage.promptTokens,
|
|
32
98
|
outputTokens: usage.completionTokens,
|
|
33
99
|
totalTokens: usage.totalTokens,
|
|
34
100
|
reasoningTokens: usage.reasoningTokens,
|
|
35
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
|
+
},
|
|
36
113
|
};
|
|
37
114
|
}
|
|
38
115
|
export function serializeWarnings(warnings) {
|
|
@@ -40,25 +117,48 @@ export function serializeWarnings(warnings) {
|
|
|
40
117
|
return undefined;
|
|
41
118
|
}
|
|
42
119
|
return warnings.map((warning) => {
|
|
43
|
-
if (warning.type
|
|
44
|
-
return
|
|
120
|
+
if (warning.type === "compatibility") {
|
|
121
|
+
return {
|
|
122
|
+
type: "unsupported-setting",
|
|
123
|
+
setting: warning.feature,
|
|
124
|
+
details: warning.details,
|
|
125
|
+
};
|
|
45
126
|
}
|
|
46
|
-
return
|
|
47
|
-
...warning,
|
|
48
|
-
setting: warning.setting.toString(),
|
|
49
|
-
};
|
|
127
|
+
return warning;
|
|
50
128
|
});
|
|
51
129
|
}
|
|
52
|
-
export function
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
});
|
|
55
144
|
}
|
|
56
|
-
export async function serializeNewMessagesInStep(ctx, component, step,
|
|
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) {
|
|
57
156
|
// If there are tool results, there's another message with the tool results
|
|
58
|
-
// ref: https://github.com/vercel/ai/blob/main/packages/ai/
|
|
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";
|
|
59
159
|
const assistantFields = {
|
|
60
|
-
model:
|
|
61
|
-
provider:
|
|
160
|
+
model: model ? getModelName(model) : undefined,
|
|
161
|
+
provider: model ? getProviderName(model) : undefined,
|
|
62
162
|
providerMetadata: step.providerMetadata,
|
|
63
163
|
reasoning: step.reasoningText,
|
|
64
164
|
reasoningDetails: step.reasoning,
|
|
@@ -66,28 +166,36 @@ export async function serializeNewMessagesInStep(ctx, component, step, metadata)
|
|
|
66
166
|
warnings: serializeWarnings(step.warnings),
|
|
67
167
|
finishReason: step.finishReason,
|
|
68
168
|
// Only store the sources on one message
|
|
69
|
-
sources:
|
|
169
|
+
sources: hasToolMessage ? undefined : step.sources,
|
|
70
170
|
};
|
|
71
|
-
const toolFields = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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, {
|
|
79
189
|
message,
|
|
80
|
-
// Let's not store the ID by default here. It's being generated internally
|
|
81
|
-
// and not referenced elsewhere that we know of.
|
|
82
|
-
// id: message.id,
|
|
83
190
|
...(message.role === "tool" ? toolFields : assistantFields),
|
|
84
191
|
text: step.text,
|
|
85
192
|
fileIds,
|
|
86
|
-
};
|
|
193
|
+
});
|
|
87
194
|
}));
|
|
88
|
-
|
|
195
|
+
// TODO: capture step.files separately?
|
|
196
|
+
return { messages };
|
|
89
197
|
}
|
|
90
|
-
export async function serializeObjectResult(ctx, component, result,
|
|
198
|
+
export async function serializeObjectResult(ctx, component, result, model) {
|
|
91
199
|
const text = JSON.stringify(result.object);
|
|
92
200
|
const { message, fileIds } = await serializeMessage(ctx, component, {
|
|
93
201
|
role: "assistant",
|
|
@@ -97,9 +205,8 @@ export async function serializeObjectResult(ctx, component, result, metadata) {
|
|
|
97
205
|
messages: [
|
|
98
206
|
{
|
|
99
207
|
message,
|
|
100
|
-
|
|
101
|
-
model:
|
|
102
|
-
provider: metadata.provider,
|
|
208
|
+
model: model ? getModelName(model) : undefined,
|
|
209
|
+
provider: model ? getProviderName(model) : undefined,
|
|
103
210
|
providerMetadata: result.providerMetadata,
|
|
104
211
|
finishReason: result.finishReason,
|
|
105
212
|
text,
|
|
@@ -110,27 +217,34 @@ export async function serializeObjectResult(ctx, component, result, metadata) {
|
|
|
110
217
|
],
|
|
111
218
|
};
|
|
112
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
|
+
}
|
|
113
229
|
export async function serializeContent(ctx, component, content) {
|
|
114
230
|
if (typeof content === "string") {
|
|
115
231
|
return { content };
|
|
116
232
|
}
|
|
117
233
|
const fileIds = [];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
234
|
+
const serialized = await Promise.all(content.map(async (part) => {
|
|
235
|
+
const metadata = {};
|
|
236
|
+
if ("providerOptions" in part) {
|
|
237
|
+
metadata.providerOptions = part.providerOptions;
|
|
121
238
|
}
|
|
122
|
-
if ("
|
|
123
|
-
|
|
239
|
+
if ("providerMetadata" in part) {
|
|
240
|
+
metadata.providerMetadata = part.providerMetadata;
|
|
124
241
|
}
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
const serialized = await Promise.all(content.map(async (part) => {
|
|
128
242
|
switch (part.type) {
|
|
129
243
|
case "text": {
|
|
130
244
|
return {
|
|
131
245
|
type: part.type,
|
|
132
246
|
text: part.text,
|
|
133
|
-
|
|
247
|
+
...metadata,
|
|
134
248
|
};
|
|
135
249
|
}
|
|
136
250
|
case "image": {
|
|
@@ -138,22 +252,22 @@ export async function serializeContent(ctx, component, content) {
|
|
|
138
252
|
if (image instanceof ArrayBuffer &&
|
|
139
253
|
image.byteLength > MAX_FILE_SIZE) {
|
|
140
254
|
const { file } = await storeFile(ctx, component, new Blob([image], {
|
|
141
|
-
type:
|
|
255
|
+
type: getMimeOrMediaType(part) || guessMimeType(image),
|
|
142
256
|
}));
|
|
143
257
|
image = file.url;
|
|
144
258
|
fileIds.push(file.fileId);
|
|
145
259
|
}
|
|
146
260
|
return {
|
|
147
261
|
type: part.type,
|
|
148
|
-
|
|
149
|
-
|
|
262
|
+
mediaType: getMimeOrMediaType(part),
|
|
263
|
+
...metadata,
|
|
150
264
|
image,
|
|
151
265
|
};
|
|
152
266
|
}
|
|
153
267
|
case "file": {
|
|
154
268
|
let data = serializeDataOrUrl(part.data);
|
|
155
269
|
if (data instanceof ArrayBuffer && data.byteLength > MAX_FILE_SIZE) {
|
|
156
|
-
const { file } = await storeFile(ctx, component, new Blob([data], { type:
|
|
270
|
+
const { file } = await storeFile(ctx, component, new Blob([data], { type: getMimeOrMediaType(part) }));
|
|
157
271
|
data = file.url;
|
|
158
272
|
fileIds.push(file.fileId);
|
|
159
273
|
}
|
|
@@ -161,116 +275,270 @@ export async function serializeContent(ctx, component, content) {
|
|
|
161
275
|
type: part.type,
|
|
162
276
|
data,
|
|
163
277
|
filename: part.filename,
|
|
164
|
-
|
|
165
|
-
|
|
278
|
+
mediaType: getMimeOrMediaType(part),
|
|
279
|
+
...metadata,
|
|
166
280
|
};
|
|
167
281
|
}
|
|
168
282
|
case "tool-call": {
|
|
169
|
-
|
|
283
|
+
// Handle legacy data where only args field exists
|
|
284
|
+
const input = part.input ?? part?.args ?? {};
|
|
170
285
|
return {
|
|
171
286
|
type: part.type,
|
|
172
|
-
|
|
287
|
+
input,
|
|
288
|
+
/** @deprecated Use `input` instead. */
|
|
289
|
+
args: input,
|
|
173
290
|
toolCallId: part.toolCallId,
|
|
174
291
|
toolName: part.toolName,
|
|
175
|
-
providerOptions: part.providerOptions,
|
|
176
292
|
providerExecuted: part.providerExecuted,
|
|
293
|
+
...metadata,
|
|
177
294
|
};
|
|
178
295
|
}
|
|
179
296
|
case "tool-result": {
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
type: part.type,
|
|
183
|
-
result: result ?? null,
|
|
184
|
-
toolCallId: part.toolCallId,
|
|
185
|
-
toolName: part.toolName,
|
|
186
|
-
providerOptions: part.providerOptions,
|
|
187
|
-
};
|
|
297
|
+
return normalizeToolResult(part, metadata);
|
|
188
298
|
}
|
|
189
299
|
case "reasoning": {
|
|
190
300
|
return {
|
|
191
301
|
type: part.type,
|
|
192
302
|
text: part.text,
|
|
193
|
-
|
|
303
|
+
...metadata,
|
|
194
304
|
};
|
|
195
305
|
}
|
|
306
|
+
// Not in current generation output, but could be in historical messages
|
|
196
307
|
case "redacted-reasoning": {
|
|
197
308
|
return {
|
|
198
309
|
type: part.type,
|
|
199
310
|
data: part.data,
|
|
200
|
-
|
|
311
|
+
...metadata,
|
|
201
312
|
};
|
|
202
313
|
}
|
|
203
|
-
|
|
314
|
+
case "source": {
|
|
204
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;
|
|
205
337
|
}
|
|
206
338
|
}));
|
|
207
339
|
return {
|
|
208
|
-
content: serialized,
|
|
340
|
+
content: serialized.filter((p) => p !== null),
|
|
209
341
|
fileIds: fileIds.length > 0 ? fileIds : undefined,
|
|
210
342
|
};
|
|
211
343
|
}
|
|
212
|
-
export function
|
|
344
|
+
export function fromModelMessageContent(content) {
|
|
213
345
|
if (typeof content === "string") {
|
|
214
346
|
return content;
|
|
215
347
|
}
|
|
216
|
-
return content
|
|
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
|
+
}
|
|
217
357
|
switch (part.type) {
|
|
218
358
|
case "text":
|
|
219
|
-
return
|
|
220
|
-
type: part.type,
|
|
221
|
-
text: part.text,
|
|
222
|
-
providerOptions: part.providerOptions,
|
|
223
|
-
};
|
|
359
|
+
return part;
|
|
224
360
|
case "image":
|
|
225
361
|
return {
|
|
226
362
|
type: part.type,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
363
|
+
mediaType: getMimeOrMediaType(part),
|
|
364
|
+
...metadata,
|
|
365
|
+
image: serializeDataOrUrl(part.image),
|
|
230
366
|
};
|
|
231
367
|
case "file":
|
|
232
368
|
return {
|
|
233
369
|
type: part.type,
|
|
234
|
-
data:
|
|
370
|
+
data: serializeDataOrUrl(part.data),
|
|
235
371
|
filename: part.filename,
|
|
236
|
-
mediaType: part
|
|
237
|
-
|
|
372
|
+
mediaType: getMimeOrMediaType(part),
|
|
373
|
+
...metadata,
|
|
238
374
|
};
|
|
239
375
|
case "tool-call":
|
|
376
|
+
// Handle legacy data where only args field exists
|
|
240
377
|
return {
|
|
241
378
|
type: part.type,
|
|
242
|
-
input: part.args ??
|
|
243
|
-
|
|
379
|
+
input: part.input ?? part?.args ?? {},
|
|
380
|
+
/** @deprecated Use `input` instead. */
|
|
381
|
+
args: part.input ?? part?.args ?? {},
|
|
244
382
|
toolCallId: part.toolCallId,
|
|
245
383
|
toolName: part.toolName,
|
|
246
|
-
|
|
384
|
+
providerExecuted: part.providerExecuted,
|
|
385
|
+
...metadata,
|
|
247
386
|
};
|
|
248
387
|
case "tool-result":
|
|
388
|
+
return normalizeToolResult(part, metadata);
|
|
389
|
+
case "reasoning":
|
|
249
390
|
return {
|
|
250
391
|
type: part.type,
|
|
251
|
-
|
|
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,
|
|
252
459
|
toolCallId: part.toolCallId,
|
|
253
460
|
toolName: part.toolName,
|
|
254
|
-
|
|
461
|
+
providerExecuted: part.providerExecuted,
|
|
462
|
+
...metadata,
|
|
255
463
|
};
|
|
464
|
+
}
|
|
465
|
+
case "tool-result": {
|
|
466
|
+
return normalizeToolResult(part, metadata);
|
|
467
|
+
}
|
|
256
468
|
case "reasoning":
|
|
257
469
|
return {
|
|
258
470
|
type: part.type,
|
|
259
471
|
text: part.text,
|
|
260
|
-
|
|
261
|
-
state: part.state,
|
|
472
|
+
...metadata,
|
|
262
473
|
};
|
|
263
474
|
case "redacted-reasoning":
|
|
264
475
|
// TODO: should we just drop this?
|
|
265
476
|
return {
|
|
266
477
|
type: "reasoning",
|
|
267
|
-
text:
|
|
268
|
-
|
|
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,
|
|
269
488
|
};
|
|
270
|
-
|
|
489
|
+
case "source":
|
|
271
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;
|
|
272
509
|
}
|
|
273
|
-
})
|
|
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
|
+
};
|
|
274
542
|
}
|
|
275
543
|
/**
|
|
276
544
|
* Return a best-guess MIME type based on the magic-number signature
|
|
@@ -362,7 +630,10 @@ export function serializeDataOrUrl(dataOrUrl) {
|
|
|
362
630
|
}
|
|
363
631
|
return dataOrUrl.buffer.slice(dataOrUrl.byteOffset, dataOrUrl.byteOffset + dataOrUrl.byteLength);
|
|
364
632
|
}
|
|
365
|
-
export function
|
|
633
|
+
export function toModelMessageDataOrUrl(urlOrString) {
|
|
634
|
+
if (urlOrString instanceof URL) {
|
|
635
|
+
return urlOrString;
|
|
636
|
+
}
|
|
366
637
|
if (typeof urlOrString === "string") {
|
|
367
638
|
if (urlOrString.startsWith("http://") ||
|
|
368
639
|
urlOrString.startsWith("https://")) {
|
|
@@ -375,7 +646,7 @@ export function deserializeUrl(urlOrString) {
|
|
|
375
646
|
export function toUIFilePart(part) {
|
|
376
647
|
const dataOrUrl = part.type === "image" ? part.image : part.data;
|
|
377
648
|
const url = dataOrUrl instanceof ArrayBuffer
|
|
378
|
-
?
|
|
649
|
+
? convertUint8ArrayToBase64(new Uint8Array(dataOrUrl))
|
|
379
650
|
: dataOrUrl.toString();
|
|
380
651
|
return {
|
|
381
652
|
type: "file",
|
|
@@ -385,9 +656,6 @@ export function toUIFilePart(part) {
|
|
|
385
656
|
providerMetadata: part.providerOptions,
|
|
386
657
|
};
|
|
387
658
|
}
|
|
388
|
-
function encodeBase64(data) {
|
|
389
|
-
return Buffer.from(data).toString("base64");
|
|
390
|
-
}
|
|
391
659
|
// Currently unused
|
|
392
660
|
// export function toModelMessages(args: {
|
|
393
661
|
// messages?: ModelMessage[] | AIMessageWithoutId[];
|