// Probe 05: vision with a 32x32 PNG data URL on every probe model (incl. the text-only one to see the error shape). import { deflateSync } from "node:zlib"; import { raw, save, short } from "./lib.ts"; import { MODELS, byId } from "./models.ts"; function crc32(buf: Buffer) { let c: number; const table: number[] = []; for (let n = 0; n < 256; n++) { c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; table[n] = c >>> 0; } let crc = 0xffffffff; for (const b of buf) crc = table[(crc ^ b) & 0xff] ^ (crc >>> 8); return (crc ^ 0xffffffff) >>> 0; } function chunk(type: string, data: Buffer) { const len = Buffer.alloc(4); len.writeUInt32BE(data.length); const td = Buffer.concat([Buffer.from(type, "ascii"), data]); const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(td)); return Buffer.concat([len, td, crc]); } /** Solid red square with a blue diagonal, sizeƗsize, RGB PNG. */ function png(size: number) { const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(size, 0); ihdr.writeUInt32BE(size, 4); ihdr[8] = 8; // bit depth ihdr[9] = 2; // RGB const rows: Buffer[] = []; for (let y = 0; y < size; y++) { const row = Buffer.alloc(1 + size * 3); for (let x = 0; x < size; x++) { const diag = Math.abs(x - y) < 2; row[1 + x * 3] = diag ? 0 : 220; row[2 + x * 3] = 0; row[3 + x * 3] = diag ? 220 : 0; } rows.push(row); } const idat = deflateSync(Buffer.concat(rows)); return Buffer.concat([Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), chunk("IHDR", ihdr), chunk("IDAT", idat), chunk("IEND", Buffer.alloc(0))]); } const dataUrl = `data:image/png;base64,${png(32).toString("base64")}`; const results: any = {}; for (const model of MODELS) { const body: any = { model, messages: [ { role: "user", content: [ { type: "text", text: "Describe this image in one sentence: main colour and any shape." }, { type: "image_url", image_url: { url: dataUrl, detail: "low" } }, ], }, ], max_tokens: 100, usage: { include: true }, ...(byId.get(model)?.reasoning ? { reasoning: { effort: "low" } } : {}), }; const r = await raw("/chat/completions", { method: "POST", body: JSON.stringify(body) }); const b: any = r.body; results[model] = { status: r.status, supportsImage: (byId.get(model)?.architecture?.input_modalities ?? []).includes("image"), provider: b?.provider, content: b?.choices?.[0]?.message?.content, usage: b?.usage, error: b?.error, }; console.log(`[${model}] ${r.status} img=${results[model].supportsImage} provider=${b?.provider} prompt_tokens=${b?.usage?.prompt_tokens} cost=${b?.usage?.cost} ${short(b?.choices?.[0]?.message?.content, 140)} ${b?.error ? "ERR=" + short(b.error, 300) : ""}`); } save("05-vision.json", results);