EDIT_ENGINE=grok по умолчанию, XAI_API_KEY/XAI_BASE_URL/XAI_IMAGE_MODEL/XAI_TIMEOUT_MS; Codex остаётся для подсказок и как fallback (EDIT_ENGINE=codex).
61 lines
2.8 KiB
JavaScript
61 lines
2.8 KiB
JavaScript
"use strict";
|
|
|
|
const { test, describe } = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const { XaiImageClient } = require("../src/core/xai/client");
|
|
const { buildGrokEditPrompt } = require("../src/core/engines/grok-edit-engine");
|
|
const { loadConfig } = require("../src/config");
|
|
|
|
describe("xAI Grok image edit", () => {
|
|
test("sends JSON to /images/edits and decodes b64_json", async () => {
|
|
let captured;
|
|
const fetchImpl = async (url, opts) => {
|
|
captured = { url, opts, body: JSON.parse(opts.body) };
|
|
return new Response(JSON.stringify({ data: [{ b64_json: Buffer.from("JPEGDATA").toString("base64"), mime_type: "image/jpeg" }] }), { status: 200 });
|
|
};
|
|
const c = new XaiImageClient({ apiKey: "k", baseUrl: "https://api.x.ai/v1/", fetchImpl });
|
|
const r = await c.edit({ prompt: "p", images: [{ buffer: Buffer.from("a"), mimetype: "image/png" }] });
|
|
assert.equal(captured.url, "https://api.x.ai/v1/images/edits");
|
|
assert.equal(captured.opts.headers.Authorization, "Bearer k");
|
|
assert.equal(captured.body.model, "grok-imagine-image-2.0");
|
|
assert.equal(captured.body.response_format, "b64_json");
|
|
assert.equal(captured.body.image.type, "image_url");
|
|
assert.ok(captured.body.image.url.startsWith("data:image/png;base64,"));
|
|
assert.equal(r.buffer.toString(), "JPEGDATA");
|
|
});
|
|
|
|
test("uses images[] for multiple inputs and surfaces API errors", async () => {
|
|
let body;
|
|
const fetchImpl = async (_u, o) => {
|
|
body = JSON.parse(o.body);
|
|
return new Response(JSON.stringify({ error: "bad" }), { status: 400 });
|
|
};
|
|
const c = new XaiImageClient({ apiKey: "k", fetchImpl });
|
|
const img = { buffer: Buffer.from("a"), mimetype: "image/jpeg" };
|
|
await assert.rejects(c.edit({ prompt: "p", images: [img, img] }), /Grok API 400: bad/);
|
|
assert.equal(body.images.length, 2);
|
|
assert.equal(body.image, undefined);
|
|
});
|
|
|
|
test("fails clearly without API key", async () => {
|
|
const c = new XaiImageClient({ apiKey: "" });
|
|
await assert.rejects(c.edit({ prompt: "p", images: [{ buffer: Buffer.from("a") }] }), /XAI_API_KEY/);
|
|
});
|
|
|
|
test("prompt mentions reference region", () => {
|
|
const t = buildGrokEditPrompt({ prompt: "add hat", hasReference: true, region: { x: 1, y: 2, w: 3, h: 4 } });
|
|
assert.match(t, /add hat/);
|
|
assert.match(t, /x=1, y=2, width=3, height=4/);
|
|
});
|
|
|
|
test("config defaults and key is not in sanitized output", () => {
|
|
const cfg = loadConfig({ XAI_API_KEY: "secret" });
|
|
assert.equal(cfg.EDIT_ENGINE, "grok");
|
|
assert.equal(cfg.XAI_IMAGE_MODEL, "grok-imagine-image-2.0");
|
|
const s = cfg.getSanitized();
|
|
assert.equal(s.XAI_API_KEY, undefined);
|
|
assert.equal(s.XAI_API_KEY_SET, true);
|
|
assert.throws(() => loadConfig({ EDIT_ENGINE: "foo" }));
|
|
});
|
|
});
|