@sema-agent/core
Version:
Stateless, task-oriented AI agent core
98 lines (97 loc) • 5.02 kB
JavaScript
export const MCP_IMAGE_MAX_BASE64 = 5 * 1024 * 1024;
export const IMAGE_TARGET_RAW_SIZE = (MCP_IMAGE_MAX_BASE64 * 3) / 4;
export const IMAGE_MAX_WIDTH = 2000;
export const IMAGE_MAX_HEIGHT = 2000;
export function createImageDownsampler(sharp) {
const BOX = { fit: "inside", withoutEnlargement: true };
return async (input, mimeType) => {
if (input.length === 0)
return undefined;
try {
const meta = await sharp(input).metadata();
const fmt = meta.format === "jpg" ? "jpeg" : meta.format;
const passMime = fmt ? `image/${fmt}` : mimeType;
const originalSize = input.length;
if (!meta.width || !meta.height) {
if (originalSize > IMAGE_TARGET_RAW_SIZE) {
const buf = await sharp(input).jpeg({ quality: 80 }).toBuffer();
return { base64: buf.toString("base64"), mimeType: "image/jpeg" };
}
return { base64: input.toString("base64"), mimeType: passMime };
}
const originalWidth = meta.width;
const originalHeight = meta.height;
let width = originalWidth;
let height = originalHeight;
const dims = (dw, dh) => ({ originalWidth, originalHeight, displayWidth: dw, displayHeight: dh });
if (originalSize <= IMAGE_TARGET_RAW_SIZE && width <= IMAGE_MAX_WIDTH && height <= IMAGE_MAX_HEIGHT) {
return { base64: input.toString("base64"), mimeType: passMime, dimensions: dims(width, height) };
}
const needsDimensionResize = width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT;
const isPng = fmt === "png";
if (!needsDimensionResize) {
if (isPng) {
const png = await sharp(input).png({ compressionLevel: 9, palette: true }).toBuffer();
if (png.length <= IMAGE_TARGET_RAW_SIZE)
return { base64: png.toString("base64"), mimeType: "image/png", dimensions: dims(width, height) };
}
for (const quality of [80, 60, 40, 20]) {
const buf = await sharp(input).jpeg({ quality }).toBuffer();
if (buf.length <= IMAGE_TARGET_RAW_SIZE)
return { base64: buf.toString("base64"), mimeType: "image/jpeg", dimensions: dims(width, height) };
}
}
if (width > IMAGE_MAX_WIDTH) {
height = Math.round((height * IMAGE_MAX_WIDTH) / width);
width = IMAGE_MAX_WIDTH;
}
if (height > IMAGE_MAX_HEIGHT) {
width = Math.round((width * IMAGE_MAX_HEIGHT) / height);
height = IMAGE_MAX_HEIGHT;
}
const resized = await sharp(input).resize(width, height, BOX).toBuffer();
if (resized.length <= IMAGE_TARGET_RAW_SIZE) {
return { base64: resized.toString("base64"), mimeType: passMime, dimensions: dims(width, height) };
}
if (isPng) {
const png = await sharp(input).resize(width, height, BOX).png({ compressionLevel: 9, palette: true }).toBuffer();
if (png.length <= IMAGE_TARGET_RAW_SIZE)
return { base64: png.toString("base64"), mimeType: "image/png", dimensions: dims(width, height) };
}
for (const quality of [80, 60, 40, 20]) {
const buf = await sharp(input).resize(width, height, BOX).jpeg({ quality }).toBuffer();
if (buf.length <= IMAGE_TARGET_RAW_SIZE)
return { base64: buf.toString("base64"), mimeType: "image/jpeg", dimensions: dims(width, height) };
}
const smallerWidth = Math.min(width, 1000);
const smallerHeight = Math.round((height * smallerWidth) / Math.max(width, 1));
const last = await sharp(input).resize(smallerWidth, smallerHeight, BOX).jpeg({ quality: 20 }).toBuffer();
return { base64: last.toString("base64"), mimeType: "image/jpeg", dimensions: dims(smallerWidth, smallerHeight) };
}
catch {
return undefined;
}
};
}
const SHARP_LIMIT_INPUT_PIXELS = 100_000_000;
export async function sharpImageDownsampler() {
try {
const mod = (await import("sharp"));
const real = (mod.default ?? mod);
return createImageDownsampler((input) => real(input, { limitInputPixels: SHARP_LIMIT_INPUT_PIXELS }));
}
catch {
return undefined;
}
}
export async function sharpImageResizer() {
const down = await sharpImageDownsampler();
if (!down)
return undefined;
return async (base64, mimeType) => {
const out = await down(Buffer.from(base64, "base64"), mimeType).catch(() => undefined);
if (!out || out.base64.length > MCP_IMAGE_MAX_BASE64)
return undefined;
return { base64: out.base64, mimeType: out.mimeType };
};
}