* Image limits test suite
*
* Tests provider-specific image limitations:
* - Maximum number of images in a context (with small 100x100 images)
* - Maximum image size (bytes)
* - Maximum image dimensions
* - Maximum payload (realistic large images stress test)
*
* ============================================================================
* DISCOVERED LIMITS (Dec 2025):
* ============================================================================
*
* BASIC LIMITS (small images):
* | Provider | Model | Max Images | Max Size | Max Dim |
* |-------------|--------------------|------------|----------|----------|
* | Anthropic | claude-haiku-4-5 | 100 | 5MB | 8000px |
* | OpenAI | gpt-4o-mini | 500 | ≥25MB | ≥20000px |
* | Gemini | gemini-2.5-flash | ~2000* | ≥40MB | 8000px |
* | Mistral | pixtral-12b | 8 | ~15MB | 8000px |
* | xAI | grok-2-vision | ≥100 | 25MB | 8000px |
* | Groq | llama-4-scout-17b | 5 | ~5MB | ~5760px**|
* | zAI | glm-4.5v | *** | ≥20MB | 8000px |
* | OpenRouter | z-ai/glm-4.5v | *** | ~10MB | ≥20000px |
*
* REALISTIC PAYLOAD LIMITS (large images):
* | Provider | Image Size | Max Count | Total Payload | Limit Hit |
* |-------------|------------|-----------|---------------|---------------------|
* | Anthropic | ~3MB | 6 | ~18MB | Request too large |
* | OpenAI | ~15MB | 2 | ~30MB | Generic error |
* | Gemini | ~20MB | 10 | ~200MB | String length |
* | Mistral | ~10MB | 4 | ~40MB | 413 Payload too large|
* | xAI | ~20MB | 1 | ~20MB | 413 Entity too large|
* | Groq | 5760px | 5 | N/A | 5 image limit |
* | zAI | ~15MB | 2 | ~30MB | 50MB request limit |
* | OpenRouter | ~5MB | 2 | ~10MB | Provider error |
*
* Notes:
* - Anthropic: 100 image hard limit, 5MB per image, but ~18MB total request
* limit in practice (32MB documented but hit limit at ~24MB).
* - OpenAI: 500 image limit but total payload limited to ~30-45MB.
* - Gemini: * Very permissive. 10 x 20MB = 200MB worked!
* - Mistral: 8 images max, ~40MB total payload.
* - xAI: 25MB per image but strict request size limit (~20MB total).
* - Groq: ** Most restrictive. 5 images max, 33177600 pixels max (≈5760x5760).
* - zAI: 50MB request limit (explicit in error message).
* - OpenRouter: *** Context-window limited (65536 tokens).
*
* ============================================================================
* PRACTICAL RECOMMENDATIONS FOR CODING AGENTS:
* ============================================================================
*
* Conservative cross-provider safe limits:
* - Max 2 images per request at ~5MB each (~10MB total)
* - Max 5760px dimension (for Groq pixel limit)
*
* If excluding Groq:
* - Max 4 images per request at ~5MB each (~20MB total)
* - Max 8000px dimension
*
* For Anthropic-only (most common case):
* - Max 6 images at ~3MB each OR 100 images at <200KB each
* - Max 5MB per image
* - Max 8000px dimension
* - Stay under ~18MB total request size
*
* ============================================================================
*/
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import { getBundledModel } from "@oh-my-pi/pi-ai/models";
import { complete } from "@oh-my-pi/pi-ai/stream";
import type { Api, Context, ImageContent, Model, OptionsForApi, UserMessage } from "@oh-my-pi/pi-ai/types";
import { $which } from "@oh-my-pi/pi-utils";
import { e2eApiKey } from "./oauth";
const TEMP_DIR = path.join(import.meta.dir, ".temp-images");
* Generate a valid PNG image of specified dimensions using ImageMagick
*/
async function generateImage(width: number, height: number, filename: string): Promise<string> {
const filepath = path.join(TEMP_DIR, filename);
execSync(`magick -size ${width}x${height} xc:red "${filepath}"`, { stdio: "ignore" });
const buffer = await fs.promises.readFile(filepath);
return buffer.toBase64();
}
* Generate a valid PNG image of approximately the specified size in bytes
*/
async function generateImageWithSize(targetBytes: number, filename: string): Promise<string> {
const filepath = path.join(TEMP_DIR, filename);
const side = Math.ceil(Math.sqrt(targetBytes / 3));
execSync(`magick -size ${side}x${side} xc: +noise Random -depth 8 PNG24:"${filepath}"`, { stdio: "ignore" });
const { size } = await Bun.file(filepath).stat();
if (size < targetBytes * 0.8) {
const newSide = Math.ceil(side * Math.sqrt(targetBytes / size));
execSync(`magick -size ${newSide}x${newSide} xc: +noise Random -depth 8 PNG24:"${filepath}"`, {
stdio: "ignore",
});
}
const buffer = await fs.promises.readFile(filepath);
return buffer.toBase64();
}
* Create a user message with multiple images
*/
function createMultiImageMessage(imageCount: number, imageBase64: string): UserMessage {
const content: (ImageContent | { type: "text"; text: string })[] = [
{ type: "text", text: `I am sending you ${imageCount} images. Just reply with "received ${imageCount}".` },
];
for (let i = 0; i < imageCount; i++) {
content.push({
type: "image",
data: imageBase64,
mimeType: "image/png",
});
}
return {
role: "user",
content,
timestamp: Date.now(),
};
}
* Test sending a specific number of images to a model
*/
async function testImageCount<TApi extends Api>(
model: Model<TApi>,
imageCount: number,
imageBase64: string,
options?: OptionsForApi<TApi>,
): Promise<{ success: boolean; error?: string }> {
const context: Context = {
messages: [createMultiImageMessage(imageCount, imageBase64)],
};
try {
const response = await complete(model, context, options);
if (response.stopReason === "error") {
return { success: false, error: response.errorMessage };
}
return { success: true };
} catch (e) {
return { success: false, error: e instanceof Error ? e.message : String(e) };
}
}
* Test sending a single image (used for both size and dimension tests)
*/
async function testSingleImage<TApi extends Api>(
model: Model<TApi>,
imageBase64: string,
options?: OptionsForApi<TApi>,
): Promise<{ success: boolean; error?: string }> {
const context: Context = {
messages: [
{
role: "user",
content: [
{ type: "text", text: "I am sending you an image. Just reply with 'received'." },
{ type: "image", data: imageBase64, mimeType: "image/png" },
],
timestamp: Date.now(),
},
],
};
try {
const response = await complete(model, context, options);
if (response.stopReason === "error") {
return { success: false, error: response.errorMessage };
}
return { success: true };
} catch (e) {
return { success: false, error: e instanceof Error ? e.message : String(e) };
}
}
* Find the maximum value that succeeds using linear search
*/
async function findLimit(
testFn: (value: number) => Promise<{ success: boolean; error?: string }>,
min: number,
max: number,
step: number,
): Promise<{ limit: number; lastError?: string }> {
let lastSuccess = min;
let lastError: string | undefined;
for (let value = min; value <= max; value += step) {
console.log(` Testing value: ${value}...`);
const result = await testFn(value);
if (result.success) {
lastSuccess = value;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
return { limit: lastSuccess, lastError };
}
describe("Image Limits E2E Tests", () => {
let smallImage: string;
beforeAll(async () => {
if (!$which("magick")) return;
fs.mkdirSync(TEMP_DIR, { recursive: true });
smallImage = await generateImage(100, 100, "small.png");
});
afterAll(() => {
fs.rmSync(TEMP_DIR, { recursive: true, force: true });
});
describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("Anthropic (claude-haiku-4-5-20251001)", () => {
const model = getBundledModel("anthropic", "claude-haiku-4-5-20251001");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(
count => testImageCount(model, count, smallImage),
20,
120,
20,
);
console.log(`\n Anthropic max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(80);
expect(limit).toBeLessThanOrEqual(100);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [1, 2, 3, 4, 5, 6];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Anthropic max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [1000, 2000, 4000, 6000, 8000, 10000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Anthropic max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(6000);
expect(lastSuccess).toBeLessThanOrEqual(8000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("OPENAI_API_KEY"))("OpenAI (gpt-4o-mini)", () => {
const model: Model<"openai-completions"> = {
...getBundledModel("openai", "gpt-4o-mini"),
api: "openai-completions",
};
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(
count => testImageCount(model, count, smallImage),
100,
600,
100,
);
console.log(`\n OpenAI max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(400);
expect(limit).toBeLessThanOrEqual(500);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [5, 10, 15, 20, 25];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n OpenAI max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(15);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n OpenAI max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("GEMINI_API_KEY"))("Gemini (gemini-2.5-flash)", () => {
const model = getBundledModel("google", "gemini-2.5-flash");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(
count => testImageCount(model, count, smallImage),
500,
3000,
500,
);
console.log(`\n Gemini max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(500);
},
{ timeout: 900000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [10, 20, 30, 40];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Gemini max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(20);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Gemini max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("MISTRAL_API_KEY"))("Mistral (pixtral-12b)", () => {
const model = getBundledModel("mistral", "pixtral-12b");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 5, 15, 1);
console.log(`\n Mistral max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [5, 10, 15, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Mistral max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Mistral max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("OPENROUTER_API_KEY"))("OpenRouter (z-ai/glm-4.5v)", () => {
const model = getBundledModel("openrouter", "z-ai/glm-4.5v");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 10, 60, 10);
console.log(`\n OpenRouter max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(10);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [5, 10, 15, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n OpenRouter max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n OpenRouter max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("XAI_API_KEY"))("xAI (grok-2-vision)", () => {
const model = getBundledModel("xai", "grok-2-vision");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(
count => testImageCount(model, count, smallImage),
10,
100,
10,
);
console.log(`\n xAI max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [5, 10, 15, 20, 25];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n xAI max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n xAI max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("GROQ_API_KEY"))("Groq (llama-4-scout-17b)", () => {
const model = getBundledModel("groq", "meta-llama/llama-4-scout-17b-16e-instruct");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 5, 50, 5);
console.log(`\n Groq max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [1, 5, 10, 15, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Groq max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n Groq max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe.skipIf(!e2eApiKey("ZAI_API_KEY"))("zAI (glm-4.5v)", () => {
const model = getBundledModel("zai", "glm-4.5v");
it("should accept a small number of images (5)", async () => {
const result = await testImageCount(model, 5, smallImage);
expect(result.success, result.error).toBe(true);
});
it(
"should find maximum image count limit",
async () => {
const { limit, lastError } = await findLimit(
count => testImageCount(model, count, smallImage),
10,
100,
10,
);
console.log(`\n zAI max images: ~${limit} (last error: ${lastError})`);
expect(limit).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image size limit",
async () => {
const MB = 1024 * 1024;
const sizes = [5, 10, 15, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const sizeMB of sizes) {
console.log(` Testing size: ${sizeMB}MB...`);
const imageBase64 = await generateImageWithSize(sizeMB * MB, `size-${sizeMB}mb.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = sizeMB;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n zAI max image size: ~${lastSuccess}MB (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(5);
},
{ timeout: 600000 },
);
it(
"should find maximum image dimension limit",
async () => {
const dimensions = [2000, 4000, 8000, 16000, 20000];
let lastSuccess = 0;
let lastError: string | undefined;
for (const dim of dimensions) {
console.log(` Testing dimension: ${dim}x${dim}...`);
const imageBase64 = await generateImage(dim, dim, `dim-${dim}.png`);
const result = await testSingleImage(model, imageBase64);
if (result.success) {
lastSuccess = dim;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 100)}`);
break;
}
}
console.log(`\n zAI max dimension: ~${lastSuccess}px (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(2000);
},
{ timeout: 600000 },
);
});
describe("Max Size Images (realistic payload stress test)", () => {
const imageCache: Map<number, string> = new Map();
async function getImageAtSize(targetMB: number): Promise<string> {
if (imageCache.has(targetMB)) {
return imageCache.get(targetMB)!;
}
console.log(` Generating ~${targetMB}MB noise image...`);
const imageBase64 = await generateImageWithSize(targetMB * 1024 * 1024, `stress-${targetMB}mb.png`);
const actualSize = Buffer.from(imageBase64, "base64").length;
console.log(` Actual size: ${(actualSize / 1024 / 1024).toFixed(2)}MB`);
imageCache.set(targetMB, imageBase64);
return imageBase64;
}
it.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))(
"Anthropic: max ~3MB images before rejection",
async () => {
const model = getBundledModel("anthropic", "claude-haiku-4-5-20251001");
const image3mb = await getImageAtSize(3);
const counts = [1, 2, 4, 6, 8, 10, 12];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~3MB images...`);
const result = await testImageCount(model, count, image3mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n Anthropic max ~3MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 900000 },
);
it.skipIf(!e2eApiKey("OPENAI_API_KEY"))(
"OpenAI: max ~15MB images before rejection",
async () => {
const model = getBundledModel("openai", "gpt-4o-mini");
const image15mb = await getImageAtSize(15);
const counts = [1, 2, 5, 10, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~15MB images...`);
const result = await testImageCount(model, count, image15mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n OpenAI max ~15MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 1800000 },
);
it.skipIf(!e2eApiKey("GEMINI_API_KEY"))(
"Gemini: max ~20MB images before rejection",
async () => {
const model = getBundledModel("google", "gemini-2.5-flash");
const image20mb = await getImageAtSize(20);
const counts = [1, 2, 5, 10, 20, 50];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~20MB images...`);
const result = await testImageCount(model, count, image20mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n Gemini max ~20MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 1800000 },
);
it.skipIf(!e2eApiKey("MISTRAL_API_KEY"))(
"Mistral: max ~10MB images before rejection",
async () => {
const model = getBundledModel("mistral", "pixtral-12b");
const image10mb = await getImageAtSize(10);
const counts = [1, 2, 4, 6, 8, 9];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~10MB images...`);
const result = await testImageCount(model, count, image10mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n Mistral max ~10MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 600000 },
);
it.skipIf(!e2eApiKey("XAI_API_KEY"))(
"xAI: max ~20MB images before rejection",
async () => {
const model = getBundledModel("xai", "grok-2-vision");
const image20mb = await getImageAtSize(20);
const counts = [1, 2, 5, 10, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~20MB images...`);
const result = await testImageCount(model, count, image20mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n xAI max ~20MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 1200000 },
);
it.skipIf(!e2eApiKey("GROQ_API_KEY"))(
"Groq: max 5760px images before rejection",
async () => {
const model = getBundledModel("groq", "meta-llama/llama-4-scout-17b-16e-instruct");
console.log(" Generating 5760x5760 test image for Groq...");
const image5760 = await generateImage(5760, 5760, "stress-5760.png");
const counts = [1, 2, 3, 4, 5, 6];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x 5760px images...`);
const result = await testImageCount(model, count, image5760);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n Groq max 5760px images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 600000 },
);
it.skipIf(!e2eApiKey("ZAI_API_KEY"))(
"zAI: max ~15MB images before rejection",
async () => {
const model = getBundledModel("zai", "glm-4.5v");
const image15mb = await getImageAtSize(15);
const counts = [1, 2, 5, 10, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~15MB images...`);
const result = await testImageCount(model, count, image15mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n zAI max ~15MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 1200000 },
);
it.skipIf(!e2eApiKey("OPENROUTER_API_KEY"))(
"OpenRouter: max ~5MB images before rejection",
async () => {
const model = getBundledModel("openrouter", "z-ai/glm-4.5v");
const image5mb = await getImageAtSize(5);
const counts = [1, 2, 5, 10, 20];
let lastSuccess = 0;
let lastError: string | undefined;
for (const count of counts) {
console.log(` Testing ${count} x ~5MB images...`);
const result = await testImageCount(model, count, image5mb);
if (result.success) {
lastSuccess = count;
console.log(` SUCCESS`);
} else {
lastError = result.error;
console.log(` FAILED: ${result.error?.substring(0, 150)}`);
break;
}
}
console.log(`\n OpenRouter max ~5MB images: ${lastSuccess} (last error: ${lastError})`);
expect(lastSuccess).toBeGreaterThanOrEqual(1);
},
{ timeout: 900000 },
);
});
});