SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
2.6 KB · 57 lines typescript
Raw Blame History
1// Probe (f): vision with a tiny generated PNG (2x2, red/green/blue/white) as base64 data URL.2import { deflateSync } from "node:zlib";3import { CHAT_MODELS, raw, save, short } from "./lib.ts";45function crc32(buf: Buffer) {6  let c, crc = 0xffffffff;7  for (let n = 0; n < buf.length; n++) {8    c = (crc ^ buf[n]) & 0xff;9    for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;10    crc = (crc >>> 8) ^ c;11  }12  return (crc ^ 0xffffffff) >>> 0;13}14function chunk(type: string, data: Buffer) {15  const len = Buffer.alloc(4); len.writeUInt32BE(data.length);16  const td = Buffer.concat([Buffer.from(type, "ascii"), data]);17  const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(td));18  return Buffer.concat([len, td, crc]);19}20// xAI rejects images < 8x8 ("Image dimensions 2x2 are too small. Both width and height must be at least 8 pixels.")21// so we build an 8x8 PNG with four solid quadrants: red, green / blue, white.22function png32x32() {23  // second rejection: "Image has 64 total pixels (8x8), which is below the minimum of 512 pixels." -> use 32x3224  const W = 32, H = 32;25  const ihdr = Buffer.alloc(13);26  ihdr.writeUInt32BE(W, 0); ihdr.writeUInt32BE(H, 4); ihdr[8] = 8; ihdr[9] = 2; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;27  const rows: number[] = [];28  for (let y = 0; y < H; y++) {29    rows.push(0); // filter byte30    for (let x = 0; x < W; x++) {31      const top = y < H / 2, left = x < W / 2;32      rows.push(...(top ? (left ? [255, 0, 0] : [0, 255, 0]) : left ? [0, 0, 255] : [255, 255, 255]));33    }34  }35  return Buffer.concat([Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), chunk("IHDR", ihdr), chunk("IDAT", deflateSync(Buffer.from(rows))), chunk("IEND", Buffer.alloc(0))]);36}37const dataUrl = `data:image/png;base64,${png32x32().toString("base64")}`;38console.log("data url length", dataUrl.length);3940const results: Record<string, any> = {};41await Promise.all(42  CHAT_MODELS.map(async (model) => {43    const r = await raw("/chat/completions", {44      method: "POST",45      body: JSON.stringify({46        model,47        messages: [{ role: "user", content: [{ type: "image_url", image_url: { url: dataUrl, detail: "high" } }, { type: "text", text: "Describe this image in one sentence. What colors do you see?" }] }],48        max_completion_tokens: 200,49      }),50    });51    const b: any = r.body;52    results[model] = { status: r.status, content: b.choices?.[0]?.message?.content, usage: b.usage, error: r.status !== 200 ? b : undefined };53    console.log(model.padEnd(30), r.status, short(b.choices?.[0]?.message?.content ?? b, 160), "| usage", short(b.usage, 200));54  }),55);56save("05-vision.json", results);57