Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate images via OpenAI, Google, OpenRouter, DashScope, Jimeng, Seedream, and Replicate APIs with batch support.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/providers/openrouter.test.ts
1import assert from "node:assert/strict";2import test from "node:test";34import type { CliArgs } from "../types.ts";5import {6buildContent,7buildRequestBody,8extractImageFromResponse,9getAspectRatio,10getImageSize,11validateArgs,12} from "./openrouter.ts";1314const GEMINI_MODEL = "google/gemini-3.1-flash-image-preview";15const GEMINI_25_MODEL = "google/gemini-2.5-flash-image";16const GPT_5_IMAGE_MODEL = "openai/gpt-5-image";17const OPENROUTER_AUTO_MODEL = "openrouter/auto";18const FLUX_MODEL = "black-forest-labs/flux.2-pro";1920function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {21return {22prompt: null,23promptFiles: [],24imagePath: null,25provider: null,26model: null,27aspectRatio: null,28size: null,29quality: null,30imageSize: null,31referenceImages: [],32n: 1,33batchFile: null,34jobs: null,35json: false,36help: false,37...overrides,38};39}4041test("OpenRouter request body uses image_config and string content for text-only prompts", () => {42const args = makeArgs({ aspectRatio: "16:9", quality: "2k" });43const body = buildRequestBody("hello", GEMINI_MODEL, args, []);4445assert.deepEqual(body.image_config, {46image_size: "2K",47aspect_ratio: "16:9",48});49assert.deepEqual(body.provider, {50require_parameters: true,51});52assert.deepEqual(body.modalities, ["image", "text"]);53assert.equal(body.stream, false);54assert.equal(body.messages[0].content, "hello");55});5657test("OpenRouter request body keeps text+image modalities for current text+image models", () => {58for (const model of [GEMINI_MODEL, GEMINI_25_MODEL, GPT_5_IMAGE_MODEL, OPENROUTER_AUTO_MODEL]) {59const body = buildRequestBody("hello", model, makeArgs({ quality: "2k" }), []);6061assert.deepEqual(body.image_config, {62image_size: "2K",63});64assert.deepEqual(body.provider, {65require_parameters: true,66});67assert.deepEqual(body.modalities, ["image", "text"]);68assert.equal(body.messages[0].content, "hello");69}70});7172test("OpenRouter request body uses image-only modalities for image-only models under CLI defaults", () => {73const body = buildRequestBody("hello", FLUX_MODEL, makeArgs({ quality: "2k" }), []);7475assert.deepEqual(body.image_config, {76image_size: "2K",77});78assert.deepEqual(body.provider, {79require_parameters: true,80});81assert.deepEqual(body.modalities, ["image"]);82assert.equal(body.stream, false);83assert.equal(body.messages[0].content, "hello");84});8586test("OpenRouter helper omits image_config when no size or quality is passed", () => {87const body = buildRequestBody("hello", FLUX_MODEL, makeArgs(), []);8889assert.equal(body.image_config, undefined);90assert.equal(body.provider, undefined);91assert.deepEqual(body.modalities, ["image"]);92assert.equal(body.stream, false);93assert.equal(body.messages[0].content, "hello");94});9596test("OpenRouter request body keeps multimodal array content when references are provided", () => {97const content = buildContent("hello", ["data:image/png;base64,abc"]);98assert.ok(Array.isArray(content));99assert.deepEqual(content[0], { type: "text", text: "hello" });100assert.deepEqual(content[1], {101type: "image_url",102image_url: { url: "data:image/png;base64,abc" },103});104});105106test("OpenRouter size and aspect helpers infer supported values", () => {107assert.equal(getImageSize(makeArgs()), null);108assert.equal(getImageSize(makeArgs({ quality: "normal" })), "1K");109assert.equal(getImageSize(makeArgs({ size: "2048x1024" })), "2K");110assert.equal(getAspectRatio(GEMINI_MODEL, makeArgs({ size: "1600x900" })), "16:9");111assert.equal(getAspectRatio(GEMINI_MODEL, makeArgs({ size: "1024x4096" })), "1:4");112assert.equal(getAspectRatio(GEMINI_25_MODEL, makeArgs({ size: "1600x900" })), "16:9");113assert.equal(getAspectRatio(FLUX_MODEL, makeArgs({ size: "1024x4096" })), null);114});115116test("OpenRouter validates explicit aspect ratios and inferred size ratios against model support", () => {117assert.doesNotThrow(() =>118validateArgs(GEMINI_MODEL, makeArgs({ aspectRatio: "1:4" })),119);120assert.doesNotThrow(() =>121validateArgs(GEMINI_MODEL, makeArgs({ size: "1024x4096" })),122);123assert.throws(124() => validateArgs(GEMINI_25_MODEL, makeArgs({ aspectRatio: "1:4" })),125/does not support aspect ratio 1:4/,126);127assert.throws(128() => validateArgs(FLUX_MODEL, makeArgs({ aspectRatio: "1:4" })),129/does not support aspect ratio 1:4/,130);131assert.throws(132() => validateArgs(GEMINI_MODEL, makeArgs({ size: "2048x1024" })),133/does not support size 2048x1024 \(aspect ratio 2:1\)/,134);135});136137test("OpenRouter response extraction supports inline image data and finish_reason errors", async () => {138const bytes = await extractImageFromResponse({139choices: [140{141message: {142images: [143{144image_url: {145url: `data:image/png;base64,${Buffer.from("hello").toString("base64")}`,146},147},148],149},150},151],152});153assert.equal(Buffer.from(bytes).toString("utf8"), "hello");154155await assert.rejects(156() =>157extractImageFromResponse({158choices: [159{160finish_reason: "error",161native_finish_reason: "MALFORMED_FUNCTION_CALL",162message: { content: null },163},164],165}),166/finish_reason=MALFORMED_FUNCTION_CALL/,167);168});169