feat(antigravity): native image generation support

Add image generation for Antigravity provider via gemini-3.1-flash-image
and gemini-3-pro-image, exposed through Text to Image UI and
/v1/images/generations.

- registry: serviceKinds ['llm','image'] + image model entries
- executor: image model detection + image_gen request envelope
- chatCore: force stream=false for image models (generateContent)
- nonStreamingHandler: parse inlineData -> markdown image
- imageGenerationCore: useExecutor fast-path for executor delegation
- imageProviders/antigravity: image adapter with image input support
- usage/google: image models in quota whitelist

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Nautilaceae
2026-06-21 17:54:32 +07:00
committed by decolua
co-authored by Cursor
parent b4d2754d32
commit 5306bd904e
8 changed files with 220 additions and 1 deletions
+41
View File
@@ -50,6 +50,47 @@ export async function handleImageGenerationCore({
);
}
// Executor-delegating adapters: skip manual URL/headers/body, use the proven executor flow
if (adapter.useExecutor && adapter.executeViaExecutor) {
try {
log?.debug?.("IMAGE", `${provider.toUpperCase()} | ${model} | prompt="${body.prompt.slice(0, 50)}..." (executor)`);
const responseBody = await adapter.executeViaExecutor(model, body, credentials, log);
if (onRequestSuccess) await onRequestSuccess();
const normalized = adapter.normalize(responseBody, body.prompt);
const finalBody = (normalized.created && Array.isArray(normalized.data)) ? normalized : responseBody;
if (binaryOutput) {
const first = finalBody.data?.[0];
let b64 = first?.b64_json;
if (!b64 && first?.url) {
try { b64 = await urlToBase64(first.url); } catch {}
}
if (b64) {
const buf = Buffer.from(b64, "base64");
const fmt = (body.output_format || "png").toLowerCase();
const mime = fmt === "jpeg" || fmt === "jpg" ? "image/jpeg" : fmt === "webp" ? "image/webp" : "image/png";
return {
success: true,
response: new Response(buf, {
headers: { "Content-Type": mime, "Content-Disposition": `inline; filename="image.${fmt === "jpeg" ? "jpg" : fmt}"`, "Access-Control-Allow-Origin": "*" },
}),
};
}
}
return {
success: true,
response: new Response(JSON.stringify(finalBody), {
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
}),
};
} catch (error) {
const errMsg = formatProviderError(error, provider, model, HTTP_STATUS.BAD_GATEWAY);
log?.debug?.("IMAGE", `Executor error: ${errMsg}`);
return createErrorResult(HTTP_STATUS.BAD_GATEWAY, errMsg);
}
}
let url;
let headers;
let requestBody;