pi-mono/packages/durable/test/images-wasm-module.test.ts
Mario Zechner 42a3497d03 feat(durable): read returns images, optional Photon image processor
read returns an image file as one image block (programs get an ImageContent) plus an info diagnostic,
instead of an unsupported_image error. Limits are the conversation model's inputLimits.images.resize,
by default 2000x2000 pixels and 4.5 MB of base64.

- Without a processor: PNG, JPEG, GIF, and WebP within the limits pass through undecoded; the byte limit
  is checked before reading the file, dimensions from the header. Larger images and BMP are error results.
- With createCodingTools({ images }): the processor orients by EXIF, converts BMP to PNG, and shrinks to
  fit. ImageProcessor.prepare is async.
- @earendil-works/pi-durable/images: createPhotonImages(wasm) on @silvia-odwyer/photon 0.3.3 (web build);
  /images/node and /images/cloudflare load the wasm. The root and ./tools entries never reach it (enforced
  in check-entry-graphs).
- An image must be read whole: one that changes while read is read again, unlike a growing text file.
- A model without image input gets a diagnostic that it sees a placeholder.
2026-10-09 19:38:02 +00:00

17 lines
1.2 KiB
TypeScript

// Its own file, so Photon is not yet initialized: vitest gives each file fresh modules, and Photon loads only once.
import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { createPhotonImages } from "@earendil-works/pi-durable/images";
import { DEFAULT_IMAGE_LIMITS } from "@earendil-works/pi-durable/tools";
import { expect, it } from "vitest";
it("createPhotonImages takes a compiled WebAssembly module, as Cloudflare Workers pass it", async () => {
const path = createRequire(import.meta.url).resolve("@silvia-odwyer/photon/photon_rs_bg.wasm");
const { WebAssembly } = globalThis as unknown as { WebAssembly: { compile(bytes: Uint8Array): Promise<object> } };
const module = (await WebAssembly.compile(await readFile(path))) as Parameters<typeof createPhotonImages>[0];
const images = createPhotonImages(module);
// A 1x1 GIF; returned as it is only once it decodes, which fails without an initialized Photon.
const data = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
const prepared = await images.prepare(Buffer.from(data, "base64"), "image/gif", DEFAULT_IMAGE_LIMITS);
expect(prepared).toEqual({ data, mimeType: "image/gif" });
});