Compress MSR analysis images before upload

This commit is contained in:
OpenClaw Agent
2026-07-10 11:42:33 +00:00
parent f372a2ed69
commit e2c5c38b14

View File

@@ -57,6 +57,57 @@ function resolveCandidateImageUrls(originNode) {
return urls;
}
async function blobToOptimizedDataUrl(blob, maxDim = 1024, quality = 0.82) {
const objectUrl = URL.createObjectURL(blob);
try {
const image = await new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error("Failed to decode image blob"));
img.src = objectUrl;
});
const width = image.naturalWidth || image.width || 0;
const height = image.naturalHeight || image.height || 0;
const scale = width > 0 && height > 0 ? Math.min(1, maxDim / Math.max(width, height)) : 1;
const targetWidth = Math.max(1, Math.round(width * scale)) || width || 1;
const targetHeight = Math.max(1, Math.round(height * scale)) || height || 1;
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("Could not acquire canvas context");
}
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
const optimizedBlob = await new Promise((resolve, reject) => {
canvas.toBlob((result) => {
if (result) resolve(result);
else reject(new Error("Canvas toBlob failed"));
}, "image/jpeg", quality);
});
const dataUrl = await new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(optimizedBlob);
});
return {
dataUrl,
originalWidth: width,
originalHeight: height,
outputWidth: targetWidth,
outputHeight: targetHeight,
outputBytes: optimizedBlob.size || 0,
};
} finally {
URL.revokeObjectURL(objectUrl);
}
}
async function imageInputToDataUrl(node, inputName) {
const originNode = getOriginNodeForInput(node, inputName);
const candidateUrls = resolveCandidateImageUrls(originNode);
@@ -72,6 +123,11 @@ async function imageInputToDataUrl(node, inputName) {
mimeType: null,
blobBytes: 0,
dataUrlLength: 0,
originalWidth: 0,
originalHeight: 0,
outputWidth: 0,
outputHeight: 0,
outputBytes: 0,
error: "No candidate image URLs found",
},
};
@@ -87,11 +143,8 @@ async function imageInputToDataUrl(node, inputName) {
if (!blob.type.startsWith("image/")) {
throw new Error(`Unexpected blob type: ${blob.type || "unknown"}`);
}
const dataUrl = await new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
const optimized = await blobToOptimizedDataUrl(blob);
const dataUrl = optimized.dataUrl;
return {
dataUrl,
debug: {
@@ -103,6 +156,11 @@ async function imageInputToDataUrl(node, inputName) {
mimeType: blob.type || null,
blobBytes: blob.size || 0,
dataUrlLength: typeof dataUrl === "string" ? dataUrl.length : 0,
originalWidth: optimized.originalWidth,
originalHeight: optimized.originalHeight,
outputWidth: optimized.outputWidth,
outputHeight: optimized.outputHeight,
outputBytes: optimized.outputBytes,
error: null,
},
};
@@ -122,6 +180,11 @@ async function imageInputToDataUrl(node, inputName) {
mimeType: null,
blobBytes: 0,
dataUrlLength: 0,
originalWidth: 0,
originalHeight: 0,
outputWidth: 0,
outputHeight: 0,
outputBytes: 0,
error: "All candidate image URLs failed",
},
};