Image Generation
The image generation endpoints support both OpenAI
POST /v1/images/generations and Gemini Imagen
POST /v1beta/models/{model}:predict formats. Return single or multiple images
as URLs or base64-encoded data.
POST /v1/images/variations is not in that list —
OpenAI removed the endpoint, and aimock mocks it as removed. See
Variations: mocked as removed.
Endpoints
| Method | Path | Format |
|---|---|---|
| POST | /v1/images/generations | JSON (OpenAI) |
| POST | /v1beta/models/{model}:predict | JSON (Gemini Imagen) |
OpenAI image generation and Gemini Imagen requests accept a string or a nonempty array of
content parts for prompt. Each part must be an object with a string
type. If a part includes text, that value must be a string.
Mixed parts, nontext parts, and empty text remain accepted. Unsupported values return HTTP
400 before fixture matching and do not consume a fixture sequence position.
Unit Test: Single Image URL
Using the programmatic API with vitest, register a fixture and assert on the response.
import { LLMock } from "@copilotkit/aimock";
import { describe, it, expect, beforeAll, afterAll } from "vitest";
let mock: LLMock;
beforeAll(async () => {
mock = new LLMock();
await mock.start();
});
afterAll(async () => {
await mock.stop();
});
it("returns a single image URL", async () => {
mock.onImage("a sunset over mountains", {
image: { url: "https://example.com/sunset.png" },
});
const res = await fetch(`${mock.url}/v1/images/generations`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "dall-e-3",
prompt: "a sunset over mountains",
n: 1,
size: "1024x1024",
}),
});
const body = await res.json();
expect(body.data[0].url).toBe("https://example.com/sunset.png");
});
Unit Test: Multiple Images
it("returns multiple images", async () => {
mock.onImage("cats", {
images: [
{ url: "https://example.com/cat1.png" },
{ url: "https://example.com/cat2.png" },
],
});
const res = await fetch(`${mock.url}/v1/images/generations`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "dall-e-3",
prompt: "cats playing",
n: 2,
}),
});
const body = await res.json();
expect(body.data).toHaveLength(2);
expect(body.data[0].url).toBe("https://example.com/cat1.png");
expect(body.data[1].url).toBe("https://example.com/cat2.png");
});
Unit Test: Base64 Response
it("returns base64-encoded image", async () => {
mock.onImage("logo", {
image: { b64_json: "iVBORw0KGgoAAAANSUhEUg..." },
});
const res = await fetch(`${mock.url}/v1/images/generations`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "dall-e-3",
prompt: "a company logo",
response_format: "b64_json",
}),
});
const body = await res.json();
expect(body.data[0].b64_json).toBeDefined();
});
Unit Test: Gemini Imagen Format
it("handles Gemini Imagen predict endpoint", async () => {
mock.onImage("landscape", {
image: { url: "https://example.com/landscape.png" },
});
const res = await fetch(
`${mock.url}/v1beta/models/imagen-3.0-generate-002:predict`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
instances: [{ prompt: "a beautiful landscape" }],
parameters: { sampleCount: 1 },
}),
}
);
const body = await res.json();
expect(body.predictions).toBeDefined();
});
JSON Fixture
{
"fixtures": [
{
"match": { "userMessage": "sunset" },
"response": {
"image": { "url": "https://example.com/sunset.png" }
}
},
{
"match": { "userMessage": "cats" },
"response": {
"images": [
{ "url": "https://example.com/cat1.png" },
{ "url": "https://example.com/cat2.png" }
]
}
}
]
}
Response Format
Matches the OpenAI /v1/images/generations response format:
created— Unix timestamp-
data[].url— URL of the generated image (when using URL format) -
data[].b64_json— base64-encoded image data (when using b64_json format) -
data[].revised_prompt— the prompt as revised by the model (optional)
Image fixtures use match.userMessage which maps to the
prompt field in the request body. The prompt matcher checks
for substring matches.
Variations: mocked as removed
POST /v1/images/variations no longer exists upstream. It
only ever served dall-e-2, which OpenAI removed on 2026-05-12, and the
endpoint went with it. aimock answers the path with the removal the real API returns
today rather than a plausible image envelope, so the removal surfaces in your tests
instead of in production. This follows aimock’s
deprecated & removed APIs policy — a removed
surface is mocked as removed, never as a success.
A live request to https://api.openai.com/v1/images/variations returns a bare
404 with a
zero-byte body and no Content-Type — not a JSON error
envelope. The 404 arrives before authentication (a keyless call to
/v1/images/generations still returns 401), which is how you can
tell the path itself is gone rather than the model being rejected. aimock replays exactly
that.
curl -i -X POST $MOCK_URL/v1/images/variations -F [email protected]
HTTP/1.1 404 Not Found
<no Content-Type, empty body>
Tests that drove variations should move to POST /v1/images/edits, which
OpenAI still serves and aimock still mocks normally — along with
POST /v1/images/generations. Only variations is affected. The endpoint is
also excluded from aimock’s drift pipeline, because there is no live endpoint left
to compare against.
Record & Replay
When no fixture matches an incoming request, aimock can proxy it to the real API and
record the response as a fixture for future replays. Enable recording with the
--record flag or via RecordConfig in the programmatic API.
Recorded image fixtures capture the url or b64_json from the
provider response and save them to disk, so subsequent runs replay instantly without
hitting the real API.
npx -p @copilotkit/aimock llmock --record --provider-openai https://api.openai.com