diff --git a/src/agents/tool-images.ts b/src/agents/tool-images.ts index 52d1ad8e72..e209a9e6f1 100644 --- a/src/agents/tool-images.ts +++ b/src/agents/tool-images.ts @@ -1,7 +1,12 @@ import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import type { ImageContent } from "@mariozechner/pi-ai"; import { createSubsystemLogger } from "../logging/subsystem.js"; -import { getImageMetadata, resizeToJpeg } from "../media/image-ops.js"; +import { + buildImageResizeSideGrid, + getImageMetadata, + IMAGE_REDUCE_QUALITY_STEPS, + resizeToJpeg, +} from "../media/image-ops.js"; import { DEFAULT_IMAGE_MAX_BYTES, DEFAULT_IMAGE_MAX_DIMENSION_PX, @@ -101,17 +106,13 @@ async function resizeImageBase64IfNeeded(params: { }; } - const qualities = [85, 75, 65, 55, 45, 35]; const maxDim = hasDimensions ? Math.max(width ?? 0, height ?? 0) : params.maxDimensionPx; const sideStart = maxDim > 0 ? Math.min(params.maxDimensionPx, maxDim) : params.maxDimensionPx; - const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800] - .filter((v) => v > 0 && v <= params.maxDimensionPx) - .filter((v, i, arr) => v > 0 && arr.indexOf(v) === i) - .toSorted((a, b) => b - a); + const sideGrid = buildImageResizeSideGrid(params.maxDimensionPx, sideStart); let smallest: { buffer: Buffer; size: number } | null = null; for (const side of sideGrid) { - for (const quality of qualities) { + for (const quality of IMAGE_REDUCE_QUALITY_STEPS) { const out = await resizeToJpeg({ buffer: buf, maxSide: side, diff --git a/src/browser/screenshot.ts b/src/browser/screenshot.ts index 303e6ba14c..35d39a354e 100644 --- a/src/browser/screenshot.ts +++ b/src/browser/screenshot.ts @@ -1,4 +1,9 @@ -import { getImageMetadata, resizeToJpeg } from "../media/image-ops.js"; +import { + buildImageResizeSideGrid, + getImageMetadata, + IMAGE_REDUCE_QUALITY_STEPS, + resizeToJpeg, +} from "../media/image-ops.js"; export const DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE = 2000; export const DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024; @@ -22,17 +27,13 @@ export async function normalizeBrowserScreenshot( return { buffer }; } - const qualities = [85, 75, 65, 55, 45, 35]; const sideStart = maxDim > 0 ? Math.min(maxSide, maxDim) : maxSide; - const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800] - .map((v) => Math.min(maxSide, v)) - .filter((v, i, arr) => v > 0 && arr.indexOf(v) === i) - .toSorted((a, b) => b - a); + const sideGrid = buildImageResizeSideGrid(maxSide, sideStart); let smallest: { buffer: Buffer; size: number } | null = null; for (const side of sideGrid) { - for (const quality of qualities) { + for (const quality of IMAGE_REDUCE_QUALITY_STEPS) { const out = await resizeToJpeg({ buffer, maxSide: side, diff --git a/src/media/image-ops.helpers.test.ts b/src/media/image-ops.helpers.test.ts new file mode 100644 index 0000000000..09e908362e --- /dev/null +++ b/src/media/image-ops.helpers.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from "vitest"; +import { buildImageResizeSideGrid, IMAGE_REDUCE_QUALITY_STEPS } from "./image-ops.js"; + +describe("buildImageResizeSideGrid", () => { + it("returns descending unique sides capped by maxSide", () => { + expect(buildImageResizeSideGrid(1200, 900)).toEqual([1200, 1000, 900, 800]); + }); + + it("keeps only positive side values", () => { + expect(buildImageResizeSideGrid(0, 0)).toEqual([]); + }); +}); + +describe("IMAGE_REDUCE_QUALITY_STEPS", () => { + it("keeps expected quality ladder", () => { + expect([...IMAGE_REDUCE_QUALITY_STEPS]).toEqual([85, 75, 65, 55, 45, 35]); + }); +}); diff --git a/src/media/image-ops.ts b/src/media/image-ops.ts index 3973d45283..cbf0ed20ff 100644 --- a/src/media/image-ops.ts +++ b/src/media/image-ops.ts @@ -10,6 +10,15 @@ export type ImageMetadata = { height: number; }; +export const IMAGE_REDUCE_QUALITY_STEPS = [85, 75, 65, 55, 45, 35] as const; + +export function buildImageResizeSideGrid(maxSide: number, sideStart: number): number[] { + return [sideStart, 1800, 1600, 1400, 1200, 1000, 800] + .map((value) => Math.min(maxSide, value)) + .filter((value, idx, arr) => value > 0 && arr.indexOf(value) === idx) + .toSorted((a, b) => b - a); +} + function isBun(): boolean { return typeof (process.versions as { bun?: unknown }).bun === "string"; }