veryfront 0.1.291 → 0.1.293
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/esm/deno.js +1 -1
- package/esm/src/chat/conversation.d.ts +4 -0
- package/esm/src/chat/conversation.d.ts.map +1 -1
- package/esm/src/chat/conversation.js +123 -0
- package/esm/src/chat/message-prep.d.ts +7 -1
- package/esm/src/chat/message-prep.d.ts.map +1 -1
- package/esm/src/chat/message-prep.js +192 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/chat/conversation.ts +151 -0
- package/src/src/chat/message-prep.ts +226 -1
- package/src/src/utils/version-constant.ts +1 -1
package/esm/deno.js
CHANGED
|
@@ -226,6 +226,10 @@ export declare function pushToolParts(parts: MessagePart[], toolName: string, to
|
|
|
226
226
|
output?: unknown;
|
|
227
227
|
errorText?: unknown;
|
|
228
228
|
}): void;
|
|
229
|
+
export declare function toConversationPartsFromUiMessage(message: ChatUiMessage): MessagePart[];
|
|
230
|
+
export declare function hasIncompleteToolParts(message: ChatUiMessage): boolean;
|
|
231
|
+
export declare function markIncompleteToolPartsAsStopped(message: ChatUiMessage): ChatUiMessage;
|
|
232
|
+
export declare function markIncompleteToolPartsAsErrored(message: ChatUiMessage, errorText: string): ChatUiMessage;
|
|
229
233
|
export declare function isToolCallPart(value: unknown): value is ToolCallLike;
|
|
230
234
|
export declare function isToolResultPart(value: unknown): value is ToolResultLike;
|
|
231
235
|
export declare function isTextPart(value: unknown): value is TextPartLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../src/src/chat/conversation.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../src/src/chat/conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -214,6 +214,129 @@ export function pushToolParts(parts, toolName, toolCallId, state, part) {
|
|
|
214
214
|
state: mapToolState(state),
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
|
+
function pushFileConversationPart(parts, part) {
|
|
218
|
+
const uploadId = part.uploadId ?? extractUploadId(part.url);
|
|
219
|
+
if (!uploadId)
|
|
220
|
+
return;
|
|
221
|
+
if (part.mediaType.startsWith("image/")) {
|
|
222
|
+
parts.push({
|
|
223
|
+
type: "image",
|
|
224
|
+
upload_id: uploadId,
|
|
225
|
+
media_type: part.mediaType,
|
|
226
|
+
...(part.url ? { url: part.url } : {}),
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
parts.push({
|
|
231
|
+
type: "file",
|
|
232
|
+
upload_id: uploadId,
|
|
233
|
+
media_type: part.mediaType,
|
|
234
|
+
...(part.url ? { url: part.url } : {}),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
export function toConversationPartsFromUiMessage(message) {
|
|
238
|
+
const parts = [];
|
|
239
|
+
for (const part of message.parts) {
|
|
240
|
+
if (part.type === "text") {
|
|
241
|
+
parts.push({ type: "text", text: part.text });
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (part.type === "reasoning") {
|
|
245
|
+
parts.push({ type: "reasoning", text: part.text });
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (part.type === "step-start") {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (part.type === "source-url") {
|
|
252
|
+
parts.push({
|
|
253
|
+
type: "citation",
|
|
254
|
+
source_id: part.sourceId,
|
|
255
|
+
title: part.title,
|
|
256
|
+
url: part.url,
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (part.type === "source-document") {
|
|
261
|
+
parts.push({
|
|
262
|
+
type: "citation",
|
|
263
|
+
source_id: part.sourceId,
|
|
264
|
+
title: part.title,
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (part.type === "file") {
|
|
269
|
+
pushFileConversationPart(parts, part);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (isDataUiPart(part)) {
|
|
273
|
+
const name = part.type.replace(/^data-/, "");
|
|
274
|
+
if (name.length > 0) {
|
|
275
|
+
parts.push({
|
|
276
|
+
type: "data",
|
|
277
|
+
name,
|
|
278
|
+
value: part.data,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (isToolUiPart(part)) {
|
|
284
|
+
const toolName = getUiToolName(part);
|
|
285
|
+
if (!toolName) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
pushToolParts(parts, toolName, part.toolCallId, part.state, part);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return parts.filter((part) => messagePartSchema.safeParse(part).success);
|
|
292
|
+
}
|
|
293
|
+
function isToolComplete(part) {
|
|
294
|
+
return part.state === "output-available" || part.state === "output-error" ||
|
|
295
|
+
part.state === "output-denied";
|
|
296
|
+
}
|
|
297
|
+
export function hasIncompleteToolParts(message) {
|
|
298
|
+
return message.parts.some((part) => isToolUiPart(part) && !isToolComplete(part));
|
|
299
|
+
}
|
|
300
|
+
export function markIncompleteToolPartsAsStopped(message) {
|
|
301
|
+
return markIncompleteToolPartsAsErrored(message, "Stopped by user");
|
|
302
|
+
}
|
|
303
|
+
export function markIncompleteToolPartsAsErrored(message, errorText) {
|
|
304
|
+
let mutated = false;
|
|
305
|
+
const parts = message.parts.map((part) => {
|
|
306
|
+
if (!isToolUiPart(part) || isToolComplete(part)) {
|
|
307
|
+
return part;
|
|
308
|
+
}
|
|
309
|
+
mutated = true;
|
|
310
|
+
return markToolPartAsErrored(part, errorText);
|
|
311
|
+
});
|
|
312
|
+
return mutated ? { ...message, parts } : message;
|
|
313
|
+
}
|
|
314
|
+
function markToolPartAsErrored(part, errorText) {
|
|
315
|
+
if (part.type === "dynamic-tool") {
|
|
316
|
+
return {
|
|
317
|
+
type: "dynamic-tool",
|
|
318
|
+
toolName: part.toolName,
|
|
319
|
+
toolCallId: part.toolCallId,
|
|
320
|
+
...(part.title ? { title: part.title } : {}),
|
|
321
|
+
...(part.providerExecuted !== undefined ? { providerExecuted: part.providerExecuted } : {}),
|
|
322
|
+
...(part.callProviderMetadata ? { callProviderMetadata: part.callProviderMetadata } : {}),
|
|
323
|
+
input: part.input,
|
|
324
|
+
state: "output-error",
|
|
325
|
+
errorText,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
type: part.type,
|
|
330
|
+
toolCallId: part.toolCallId,
|
|
331
|
+
...(part.toolName ? { toolName: part.toolName } : {}),
|
|
332
|
+
...(part.title ? { title: part.title } : {}),
|
|
333
|
+
...(part.providerExecuted !== undefined ? { providerExecuted: part.providerExecuted } : {}),
|
|
334
|
+
...(part.callProviderMetadata ? { callProviderMetadata: part.callProviderMetadata } : {}),
|
|
335
|
+
input: part.input,
|
|
336
|
+
state: "output-error",
|
|
337
|
+
errorText,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
217
340
|
export function isToolCallPart(value) {
|
|
218
341
|
return (isRecord(value) &&
|
|
219
342
|
value.type === "tool-call" &&
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ChatModelMessage, type ChatUiMessage } from "./types.js";
|
|
2
2
|
export declare function estimateTokens(value: unknown): number;
|
|
3
3
|
export declare function compressTurn(messages: ChatModelMessage[], startIdx: number, endIdx: number): ChatModelMessage[];
|
|
4
4
|
export declare function enforceTokenBudgetWithTurnCompression(messages: ChatModelMessage[], budget: number, overhead: number): ChatModelMessage[];
|
|
5
|
+
export declare function isModelSupportedFileMediaType(mediaType: string): boolean;
|
|
6
|
+
export declare function normalizeMessageFilePartMediaTypes(messages: ChatUiMessage[]): ChatUiMessage[];
|
|
7
|
+
export declare function rewriteUnsupportedFilePartsAsAnnotations(messages: ChatUiMessage[]): ChatUiMessage[];
|
|
8
|
+
export declare function stripPendingToolParts(messages: ChatUiMessage[]): ChatUiMessage[];
|
|
9
|
+
export declare function sanitizeModelMessages(messages: ChatModelMessage[]): ChatModelMessage[];
|
|
10
|
+
export declare function prepareModelMessagesFromUiMessages(messages: ChatUiMessage[]): ChatModelMessage[];
|
|
5
11
|
export declare function maskOldToolOutputs(messages: ChatModelMessage[]): ChatModelMessage[];
|
|
6
12
|
export declare function repairToolPairs(messages: ChatModelMessage[]): ChatModelMessage[];
|
|
7
13
|
export declare function estimateOverhead(instructions: unknown, toolCount: number): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-prep.d.ts","sourceRoot":"","sources":["../../../src/src/chat/message-prep.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"message-prep.d.ts","sourceRoot":"","sources":["../../../src/src/chat/message-prep.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAC;AAQjC,OAAO,EAEL,KAAK,gBAAgB,EAGrB,KAAK,aAAa,EAInB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD;AAOD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,gBAAgB,EAAE,CA+BpB;AAkCD,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,gBAAgB,EAAE,CAwDpB;AA+BD,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAGxE;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAwB7F;AAED,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,aAAa,EAAE,GACxB,aAAa,EAAE,CAmDjB;AAiBD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAiBhF;AA6CD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAwBtF;AAYD,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,gBAAgB,EAAE,CAchG;AA4FD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAqEnF;AAWD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CA2IhF;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CA4BrF;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,QAAQ,GAAE,MAAU,GACnB,gBAAgB,EAAE,CAepB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CA0DlF;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,MAAM,GAAE,MAA6B,EACrC,QAAQ,GAAE,MAAU,GACnB,gBAAgB,EAAE,CAMpB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getStringField, isReasoningPart, isToolCallPart, isToolResultPart, } from "./conversation.js";
|
|
1
|
+
import { convertUiMessagesToModelMessages, getStringField, isReasoningPart, isToolCallPart, isToolResultPart, } from "./conversation.js";
|
|
2
|
+
import { buildDataFileAnnotation, normalizeInlineAttachmentMediaType, } from "./types.js";
|
|
2
3
|
const CHARS_PER_TOKEN = 4;
|
|
3
4
|
export function estimateTokens(value) {
|
|
4
5
|
return Math.ceil(JSON.stringify(value ?? "").length / CHARS_PER_TOKEN);
|
|
@@ -132,6 +133,196 @@ function tryParseJson(value) {
|
|
|
132
133
|
function isRecord(value) {
|
|
133
134
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
135
|
}
|
|
136
|
+
export function isModelSupportedFileMediaType(mediaType) {
|
|
137
|
+
return mediaType.startsWith("image/") || mediaType === "application/pdf" ||
|
|
138
|
+
mediaType === "text/plain";
|
|
139
|
+
}
|
|
140
|
+
export function normalizeMessageFilePartMediaTypes(messages) {
|
|
141
|
+
return messages.map((message) => {
|
|
142
|
+
if (!message.parts.some((part) => part.type === "file")) {
|
|
143
|
+
return message;
|
|
144
|
+
}
|
|
145
|
+
const parts = message.parts.map((part) => {
|
|
146
|
+
if (part.type !== "file") {
|
|
147
|
+
return part;
|
|
148
|
+
}
|
|
149
|
+
const mediaType = normalizeInlineAttachmentMediaType(part.filename, part.mediaType);
|
|
150
|
+
if (mediaType === part.mediaType) {
|
|
151
|
+
return part;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
...part,
|
|
155
|
+
mediaType,
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
return { ...message, parts };
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function rewriteUnsupportedFilePartsAsAnnotations(messages) {
|
|
162
|
+
return messages.map((message) => {
|
|
163
|
+
if (message.parts.length === 0) {
|
|
164
|
+
return message;
|
|
165
|
+
}
|
|
166
|
+
const kept = [];
|
|
167
|
+
const dataFiles = [];
|
|
168
|
+
for (const part of message.parts) {
|
|
169
|
+
if (part.type !== "file") {
|
|
170
|
+
kept.push(part);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const normalizedMediaType = normalizeInlineAttachmentMediaType(part.filename, part.mediaType);
|
|
174
|
+
if (isModelSupportedFileMediaType(normalizedMediaType)) {
|
|
175
|
+
kept.push({
|
|
176
|
+
...part,
|
|
177
|
+
mediaType: normalizedMediaType,
|
|
178
|
+
});
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
dataFiles.push({
|
|
182
|
+
name: part.filename || "file",
|
|
183
|
+
mediaType: normalizedMediaType,
|
|
184
|
+
...(part.url ? { url: part.url } : {}),
|
|
185
|
+
...(part.uploadId ? { uploadId: part.uploadId } : {}),
|
|
186
|
+
...(part.uploadPath ? { path: part.uploadPath } : {}),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (dataFiles.length === 0) {
|
|
190
|
+
return message;
|
|
191
|
+
}
|
|
192
|
+
const annotation = buildDataFileAnnotation(dataFiles);
|
|
193
|
+
const lastTextIndex = kept.findLastIndex((part) => part.type === "text");
|
|
194
|
+
if (lastTextIndex >= 0) {
|
|
195
|
+
const textPart = kept[lastTextIndex];
|
|
196
|
+
if (textPart.type === "text") {
|
|
197
|
+
kept[lastTextIndex] = { type: "text", text: textPart.text + annotation };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
kept.push({ type: "text", text: annotation.trimStart() });
|
|
202
|
+
}
|
|
203
|
+
return { ...message, parts: kept };
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function isPendingToolPart(part) {
|
|
207
|
+
if (!isRecord(part) || typeof part.type !== "string") {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const state = typeof part.state === "string" ? part.state : null;
|
|
211
|
+
const isPendingState = state === "pending" || state === "input-available" ||
|
|
212
|
+
state === "input-streaming";
|
|
213
|
+
if (!isPendingState) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
return part.type === "dynamic-tool" || part.type === "tool_call" || part.type.startsWith("tool-");
|
|
217
|
+
}
|
|
218
|
+
export function stripPendingToolParts(messages) {
|
|
219
|
+
return messages.flatMap((message) => {
|
|
220
|
+
if (message.role !== "assistant" || message.parts.length === 0) {
|
|
221
|
+
return [message];
|
|
222
|
+
}
|
|
223
|
+
const parts = message.parts.filter((part) => !isPendingToolPart(part));
|
|
224
|
+
if (parts.length === message.parts.length) {
|
|
225
|
+
return [message];
|
|
226
|
+
}
|
|
227
|
+
if (parts.length === 0) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
return [{ ...message, parts }];
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function isKeepableModelPart(part, includeReasoning) {
|
|
234
|
+
if (!isRecord(part) || typeof part.type !== "string")
|
|
235
|
+
return false;
|
|
236
|
+
switch (part.type) {
|
|
237
|
+
case "text":
|
|
238
|
+
return typeof part.text === "string" && part.text.trim().length > 0;
|
|
239
|
+
case "reasoning":
|
|
240
|
+
return includeReasoning;
|
|
241
|
+
case "tool-call":
|
|
242
|
+
case "tool-result":
|
|
243
|
+
case "image":
|
|
244
|
+
return true;
|
|
245
|
+
case "file": {
|
|
246
|
+
const hasMediaType = typeof part.mediaType === "string" && part.mediaType.length > 0;
|
|
247
|
+
if (!hasMediaType) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
const url = typeof part.url === "string" ? part.url : "";
|
|
251
|
+
if (url.startsWith("data:image/") && part.filename === "preview-screenshot.png") {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
default:
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function hasValidContent(message) {
|
|
261
|
+
const content = message.content;
|
|
262
|
+
if (content === undefined || content === null)
|
|
263
|
+
return false;
|
|
264
|
+
if (typeof content === "string")
|
|
265
|
+
return content.trim().length > 0;
|
|
266
|
+
if (Array.isArray(content))
|
|
267
|
+
return content.some((part) => isKeepableModelPart(part, false));
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
function cleanContent(content) {
|
|
271
|
+
const hasSubstantiveContent = content.some((part) => isKeepableModelPart(part, false));
|
|
272
|
+
return content.filter((part) => isKeepableModelPart(part, hasSubstantiveContent));
|
|
273
|
+
}
|
|
274
|
+
export function sanitizeModelMessages(messages) {
|
|
275
|
+
const result = [];
|
|
276
|
+
for (const message of messages) {
|
|
277
|
+
if (Array.isArray(message.content)) {
|
|
278
|
+
if (message.role === "user") {
|
|
279
|
+
const cleaned = cleanContent(message.content);
|
|
280
|
+
if (cleaned.length > 0)
|
|
281
|
+
result.push({ ...message, content: cleaned });
|
|
282
|
+
}
|
|
283
|
+
else if (message.role === "assistant") {
|
|
284
|
+
const cleaned = cleanContent(message.content);
|
|
285
|
+
if (cleaned.length > 0)
|
|
286
|
+
result.push({ ...message, content: cleaned });
|
|
287
|
+
}
|
|
288
|
+
else if (message.role === "tool") {
|
|
289
|
+
const cleaned = cleanContent(message.content);
|
|
290
|
+
if (cleaned.length > 0)
|
|
291
|
+
result.push({ ...message, content: cleaned });
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (hasValidContent(message)) {
|
|
296
|
+
result.push(message);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
function filterValidMessages(messages) {
|
|
302
|
+
return messages.filter((message) => {
|
|
303
|
+
const content = message.content;
|
|
304
|
+
if (content === undefined || content === null)
|
|
305
|
+
return false;
|
|
306
|
+
if (typeof content === "string")
|
|
307
|
+
return content.trim().length > 0;
|
|
308
|
+
if (Array.isArray(content))
|
|
309
|
+
return content.length > 0;
|
|
310
|
+
return true;
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
export function prepareModelMessagesFromUiMessages(messages) {
|
|
314
|
+
const validMessages = messages.filter((message) => message && typeof message === "object" && "role" in message);
|
|
315
|
+
const normalizedMessages = normalizeMessageFilePartMediaTypes(validMessages);
|
|
316
|
+
const strippedPendingToolMessages = stripPendingToolParts(normalizedMessages);
|
|
317
|
+
const rewrittenMessages = rewriteUnsupportedFilePartsAsAnnotations(strippedPendingToolMessages);
|
|
318
|
+
const modelMessages = convertUiMessagesToModelMessages(rewrittenMessages);
|
|
319
|
+
const patchedMessages = ensureToolCallInputs(dedupeToolHistory(modelMessages));
|
|
320
|
+
const sanitized = sanitizeModelMessages(patchedMessages);
|
|
321
|
+
const masked = maskOldToolOutputs(sanitized);
|
|
322
|
+
const compacted = enforceTokenBudget(masked);
|
|
323
|
+
const filtered = filterValidMessages(compacted);
|
|
324
|
+
return repairToolPairs(filtered);
|
|
325
|
+
}
|
|
135
326
|
function buildToolCallMap(messages) {
|
|
136
327
|
const map = new Map();
|
|
137
328
|
for (const msg of messages) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.293";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -293,6 +293,157 @@ export function pushToolParts(
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
function pushFileConversationPart(
|
|
297
|
+
parts: MessagePart[],
|
|
298
|
+
part: Extract<ChatUiMessagePart, { type: "file" }>,
|
|
299
|
+
): void {
|
|
300
|
+
const uploadId = part.uploadId ?? extractUploadId(part.url);
|
|
301
|
+
if (!uploadId) return;
|
|
302
|
+
|
|
303
|
+
if (part.mediaType.startsWith("image/")) {
|
|
304
|
+
parts.push({
|
|
305
|
+
type: "image",
|
|
306
|
+
upload_id: uploadId,
|
|
307
|
+
media_type: part.mediaType,
|
|
308
|
+
...(part.url ? { url: part.url } : {}),
|
|
309
|
+
});
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
parts.push({
|
|
314
|
+
type: "file",
|
|
315
|
+
upload_id: uploadId,
|
|
316
|
+
media_type: part.mediaType,
|
|
317
|
+
...(part.url ? { url: part.url } : {}),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function toConversationPartsFromUiMessage(message: ChatUiMessage): MessagePart[] {
|
|
322
|
+
const parts: MessagePart[] = [];
|
|
323
|
+
|
|
324
|
+
for (const part of message.parts) {
|
|
325
|
+
if (part.type === "text") {
|
|
326
|
+
parts.push({ type: "text", text: part.text });
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (part.type === "reasoning") {
|
|
331
|
+
parts.push({ type: "reasoning", text: part.text });
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (part.type === "step-start") {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (part.type === "source-url") {
|
|
340
|
+
parts.push({
|
|
341
|
+
type: "citation",
|
|
342
|
+
source_id: part.sourceId,
|
|
343
|
+
title: part.title,
|
|
344
|
+
url: part.url,
|
|
345
|
+
});
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (part.type === "source-document") {
|
|
350
|
+
parts.push({
|
|
351
|
+
type: "citation",
|
|
352
|
+
source_id: part.sourceId,
|
|
353
|
+
title: part.title,
|
|
354
|
+
});
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (part.type === "file") {
|
|
359
|
+
pushFileConversationPart(parts, part);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (isDataUiPart(part)) {
|
|
364
|
+
const name = part.type.replace(/^data-/, "");
|
|
365
|
+
if (name.length > 0) {
|
|
366
|
+
parts.push({
|
|
367
|
+
type: "data",
|
|
368
|
+
name,
|
|
369
|
+
value: part.data,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (isToolUiPart(part)) {
|
|
376
|
+
const toolName = getUiToolName(part);
|
|
377
|
+
if (!toolName) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
pushToolParts(parts, toolName, part.toolCallId, part.state, part);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return parts.filter((part) => messagePartSchema.safeParse(part).success);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function isToolComplete(part: ToolUiPart): boolean {
|
|
389
|
+
return part.state === "output-available" || part.state === "output-error" ||
|
|
390
|
+
part.state === "output-denied";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function hasIncompleteToolParts(message: ChatUiMessage): boolean {
|
|
394
|
+
return message.parts.some((part) => isToolUiPart(part) && !isToolComplete(part));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function markIncompleteToolPartsAsStopped(message: ChatUiMessage): ChatUiMessage {
|
|
398
|
+
return markIncompleteToolPartsAsErrored(message, "Stopped by user");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function markIncompleteToolPartsAsErrored(
|
|
402
|
+
message: ChatUiMessage,
|
|
403
|
+
errorText: string,
|
|
404
|
+
): ChatUiMessage {
|
|
405
|
+
let mutated = false;
|
|
406
|
+
|
|
407
|
+
const parts = message.parts.map((part) => {
|
|
408
|
+
if (!isToolUiPart(part) || isToolComplete(part)) {
|
|
409
|
+
return part;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
mutated = true;
|
|
413
|
+
return markToolPartAsErrored(part, errorText);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
return mutated ? { ...message, parts } : message;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function markToolPartAsErrored(part: ToolUiPart, errorText: string): ChatUiMessagePart {
|
|
420
|
+
if (part.type === "dynamic-tool") {
|
|
421
|
+
return {
|
|
422
|
+
type: "dynamic-tool",
|
|
423
|
+
toolName: part.toolName,
|
|
424
|
+
toolCallId: part.toolCallId,
|
|
425
|
+
...(part.title ? { title: part.title } : {}),
|
|
426
|
+
...(part.providerExecuted !== undefined ? { providerExecuted: part.providerExecuted } : {}),
|
|
427
|
+
...(part.callProviderMetadata ? { callProviderMetadata: part.callProviderMetadata } : {}),
|
|
428
|
+
input: part.input,
|
|
429
|
+
state: "output-error",
|
|
430
|
+
errorText,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
type: part.type,
|
|
436
|
+
toolCallId: part.toolCallId,
|
|
437
|
+
...(part.toolName ? { toolName: part.toolName } : {}),
|
|
438
|
+
...(part.title ? { title: part.title } : {}),
|
|
439
|
+
...(part.providerExecuted !== undefined ? { providerExecuted: part.providerExecuted } : {}),
|
|
440
|
+
...(part.callProviderMetadata ? { callProviderMetadata: part.callProviderMetadata } : {}),
|
|
441
|
+
input: part.input,
|
|
442
|
+
state: "output-error",
|
|
443
|
+
errorText,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
296
447
|
export function isToolCallPart(value: unknown): value is ToolCallLike {
|
|
297
448
|
return (
|
|
298
449
|
isRecord(value) &&
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import "../../_dnt.polyfills.js";
|
|
2
2
|
import {
|
|
3
|
+
convertUiMessagesToModelMessages,
|
|
3
4
|
getStringField,
|
|
4
5
|
isReasoningPart,
|
|
5
6
|
isToolCallPart,
|
|
6
7
|
isToolResultPart,
|
|
7
8
|
} from "./conversation.js";
|
|
8
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
buildDataFileAnnotation,
|
|
11
|
+
type ChatModelMessage,
|
|
12
|
+
type ChatToolResultOutput,
|
|
13
|
+
type ChatToolResultPart,
|
|
14
|
+
type ChatUiMessage,
|
|
15
|
+
type ChatUiMessagePart,
|
|
16
|
+
normalizeInlineAttachmentMediaType,
|
|
17
|
+
type UploadedFileReference,
|
|
18
|
+
} from "./types.js";
|
|
9
19
|
|
|
10
20
|
const CHARS_PER_TOKEN = 4;
|
|
11
21
|
|
|
@@ -178,6 +188,221 @@ interface ToolCallInfo {
|
|
|
178
188
|
input: unknown;
|
|
179
189
|
}
|
|
180
190
|
|
|
191
|
+
export function isModelSupportedFileMediaType(mediaType: string): boolean {
|
|
192
|
+
return mediaType.startsWith("image/") || mediaType === "application/pdf" ||
|
|
193
|
+
mediaType === "text/plain";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function normalizeMessageFilePartMediaTypes(messages: ChatUiMessage[]): ChatUiMessage[] {
|
|
197
|
+
return messages.map((message) => {
|
|
198
|
+
if (!message.parts.some((part) => part.type === "file")) {
|
|
199
|
+
return message;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const parts = message.parts.map((part) => {
|
|
203
|
+
if (part.type !== "file") {
|
|
204
|
+
return part;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const mediaType = normalizeInlineAttachmentMediaType(part.filename, part.mediaType);
|
|
208
|
+
if (mediaType === part.mediaType) {
|
|
209
|
+
return part;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
...part,
|
|
214
|
+
mediaType,
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return { ...message, parts };
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function rewriteUnsupportedFilePartsAsAnnotations(
|
|
223
|
+
messages: ChatUiMessage[],
|
|
224
|
+
): ChatUiMessage[] {
|
|
225
|
+
return messages.map((message) => {
|
|
226
|
+
if (message.parts.length === 0) {
|
|
227
|
+
return message;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const kept: ChatUiMessagePart[] = [];
|
|
231
|
+
const dataFiles: UploadedFileReference[] = [];
|
|
232
|
+
|
|
233
|
+
for (const part of message.parts) {
|
|
234
|
+
if (part.type !== "file") {
|
|
235
|
+
kept.push(part);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const normalizedMediaType = normalizeInlineAttachmentMediaType(part.filename, part.mediaType);
|
|
240
|
+
if (isModelSupportedFileMediaType(normalizedMediaType)) {
|
|
241
|
+
kept.push({
|
|
242
|
+
...part,
|
|
243
|
+
mediaType: normalizedMediaType,
|
|
244
|
+
});
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
dataFiles.push({
|
|
249
|
+
name: part.filename || "file",
|
|
250
|
+
mediaType: normalizedMediaType,
|
|
251
|
+
...(part.url ? { url: part.url } : {}),
|
|
252
|
+
...(part.uploadId ? { uploadId: part.uploadId } : {}),
|
|
253
|
+
...(part.uploadPath ? { path: part.uploadPath } : {}),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (dataFiles.length === 0) {
|
|
258
|
+
return message;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const annotation = buildDataFileAnnotation(dataFiles);
|
|
262
|
+
const lastTextIndex = kept.findLastIndex((part) => part.type === "text");
|
|
263
|
+
|
|
264
|
+
if (lastTextIndex >= 0) {
|
|
265
|
+
const textPart = kept[lastTextIndex];
|
|
266
|
+
if (textPart.type === "text") {
|
|
267
|
+
kept[lastTextIndex] = { type: "text", text: textPart.text + annotation };
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
kept.push({ type: "text", text: annotation.trimStart() });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { ...message, parts: kept };
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isPendingToolPart(part: unknown): boolean {
|
|
278
|
+
if (!isRecord(part) || typeof part.type !== "string") {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const state = typeof part.state === "string" ? part.state : null;
|
|
283
|
+
const isPendingState = state === "pending" || state === "input-available" ||
|
|
284
|
+
state === "input-streaming";
|
|
285
|
+
if (!isPendingState) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return part.type === "dynamic-tool" || part.type === "tool_call" || part.type.startsWith("tool-");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function stripPendingToolParts(messages: ChatUiMessage[]): ChatUiMessage[] {
|
|
293
|
+
return messages.flatMap((message) => {
|
|
294
|
+
if (message.role !== "assistant" || message.parts.length === 0) {
|
|
295
|
+
return [message];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const parts = message.parts.filter((part) => !isPendingToolPart(part));
|
|
299
|
+
if (parts.length === message.parts.length) {
|
|
300
|
+
return [message];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (parts.length === 0) {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return [{ ...message, parts }];
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function isKeepableModelPart(part: unknown, includeReasoning: boolean): boolean {
|
|
312
|
+
if (!isRecord(part) || typeof part.type !== "string") return false;
|
|
313
|
+
|
|
314
|
+
switch (part.type) {
|
|
315
|
+
case "text":
|
|
316
|
+
return typeof part.text === "string" && part.text.trim().length > 0;
|
|
317
|
+
case "reasoning":
|
|
318
|
+
return includeReasoning;
|
|
319
|
+
case "tool-call":
|
|
320
|
+
case "tool-result":
|
|
321
|
+
case "image":
|
|
322
|
+
return true;
|
|
323
|
+
case "file": {
|
|
324
|
+
const hasMediaType = typeof part.mediaType === "string" && part.mediaType.length > 0;
|
|
325
|
+
if (!hasMediaType) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const url = typeof part.url === "string" ? part.url : "";
|
|
330
|
+
if (url.startsWith("data:image/") && part.filename === "preview-screenshot.png") {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
default:
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function hasValidContent(message: ChatModelMessage): boolean {
|
|
341
|
+
const content = message.content;
|
|
342
|
+
|
|
343
|
+
if (content === undefined || content === null) return false;
|
|
344
|
+
if (typeof content === "string") return content.trim().length > 0;
|
|
345
|
+
if (Array.isArray(content)) return content.some((part) => isKeepableModelPart(part, false));
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function cleanContent<T>(content: T[]): T[] {
|
|
350
|
+
const hasSubstantiveContent = content.some((part) => isKeepableModelPart(part, false));
|
|
351
|
+
return content.filter((part) => isKeepableModelPart(part, hasSubstantiveContent));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function sanitizeModelMessages(messages: ChatModelMessage[]): ChatModelMessage[] {
|
|
355
|
+
const result: ChatModelMessage[] = [];
|
|
356
|
+
|
|
357
|
+
for (const message of messages) {
|
|
358
|
+
if (Array.isArray(message.content)) {
|
|
359
|
+
if (message.role === "user") {
|
|
360
|
+
const cleaned = cleanContent(message.content);
|
|
361
|
+
if (cleaned.length > 0) result.push({ ...message, content: cleaned });
|
|
362
|
+
} else if (message.role === "assistant") {
|
|
363
|
+
const cleaned = cleanContent(message.content);
|
|
364
|
+
if (cleaned.length > 0) result.push({ ...message, content: cleaned });
|
|
365
|
+
} else if (message.role === "tool") {
|
|
366
|
+
const cleaned = cleanContent(message.content);
|
|
367
|
+
if (cleaned.length > 0) result.push({ ...message, content: cleaned });
|
|
368
|
+
}
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (hasValidContent(message)) {
|
|
373
|
+
result.push(message);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function filterValidMessages(messages: ChatModelMessage[]): ChatModelMessage[] {
|
|
381
|
+
return messages.filter((message) => {
|
|
382
|
+
const content = message.content;
|
|
383
|
+
if (content === undefined || content === null) return false;
|
|
384
|
+
if (typeof content === "string") return content.trim().length > 0;
|
|
385
|
+
if (Array.isArray(content)) return content.length > 0;
|
|
386
|
+
return true;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function prepareModelMessagesFromUiMessages(messages: ChatUiMessage[]): ChatModelMessage[] {
|
|
391
|
+
const validMessages = messages.filter((message) =>
|
|
392
|
+
message && typeof message === "object" && "role" in message
|
|
393
|
+
);
|
|
394
|
+
const normalizedMessages = normalizeMessageFilePartMediaTypes(validMessages);
|
|
395
|
+
const strippedPendingToolMessages = stripPendingToolParts(normalizedMessages);
|
|
396
|
+
const rewrittenMessages = rewriteUnsupportedFilePartsAsAnnotations(strippedPendingToolMessages);
|
|
397
|
+
const modelMessages = convertUiMessagesToModelMessages(rewrittenMessages);
|
|
398
|
+
const patchedMessages = ensureToolCallInputs(dedupeToolHistory(modelMessages));
|
|
399
|
+
const sanitized = sanitizeModelMessages(patchedMessages);
|
|
400
|
+
const masked = maskOldToolOutputs(sanitized);
|
|
401
|
+
const compacted = enforceTokenBudget(masked);
|
|
402
|
+
const filtered = filterValidMessages(compacted);
|
|
403
|
+
return repairToolPairs(filtered);
|
|
404
|
+
}
|
|
405
|
+
|
|
181
406
|
function buildToolCallMap(messages: ChatModelMessage[]): Map<string, ToolCallInfo> {
|
|
182
407
|
const map = new Map<string, ToolCallInfo>();
|
|
183
408
|
|