fix(sse): move Antigravity client system content to first user message to avoid upstream 429 (#9030)

Closes #9030
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-07 11:23:33 -03:00
committed by GitHub
parent ebf151e057
commit ff679ab86e
4 changed files with 153 additions and 5 deletions

View File

@@ -0,0 +1 @@
- fix(sse): move Antigravity client system content to first user message to avoid upstream 429 RESOURCE_EXHAUSTED on oversized systemInstruction (#9030)

View File

@@ -734,11 +734,24 @@ function wrapInCloudCodeEnvelope(model, cloudCodeRequest, credentials = null) {
envelope._toolNameMap = cloudCodeRequest._toolNameMap;
}
// #9030 — Client system content must NOT be combined with default in systemInstruction
//
// The upstream Antigravity / Cloud Code endpoint rejects oversized systemInstruction
// with 429 RESOURCE_EXHAUSTED. Keep only the lightweight ANTIGRAVITY_DEFAULT_SYSTEM
// in systemInstruction and relocate any client system content (which can be very
// large — Hermes ~125k tokens) to the first user message.
const defaultPart: GeminiPart = { text: ANTIGRAVITY_DEFAULT_SYSTEM };
if (envelope.request.systemInstruction?.parts) {
envelope.request.systemInstruction.parts.unshift(defaultPart);
} else {
envelope.request.systemInstruction = { role: "system", parts: [defaultPart] };
const clientParts = envelope.request.systemInstruction?.parts?.slice() ?? [];
envelope.request.systemInstruction = { role: "system", parts: [defaultPart] };
if (clientParts.length > 0) {
// Prepend client system parts to the first user message so they still guide
// the model's behavior early in the conversation.
if (envelope.request.contents && envelope.request.contents.length > 0) {
envelope.request.contents[0].parts.unshift(...clientParts);
} else {
envelope.request.contents = [{ role: "user", parts: [...clientParts] }];
}
}
// Strip Gemini built-in tool *names* out of functionDeclarations: Antigravity's

View File

@@ -0,0 +1,131 @@
import test from "node:test";
import assert from "node:assert/strict";
const { openaiToAntigravityRequest } =
await import("../../open-sse/translator/request/openai-to-gemini.ts");
const { ANTIGRAVITY_DEFAULT_SYSTEM } = await import("../../open-sse/config/constants.ts");
type PartsArray = Array<{ text?: string; functionCall?: unknown; functionResponse?: unknown }>;
/**
* #9030 — Hermes + Antigravity system messages cause 429s
*
* Root cause: openai-to-gemini.ts's wrapInCloudCodeEnvelope prepends
* ANTIGRAVITY_DEFAULT_SYSTEM to systemInstruction.parts that already contain
* the client's system message. This doubles the systemInstruction size well
* beyond what the upstream Antigravity/Google Cloud Code endpoint accepts,
* causing a 429 RESOURCE_EXHAUSTED.
*
* Fix: move client system content to the first user message and keep only the
* default system in systemInstruction.
*
* This repro test asserts that the fix is in place — after translation, the
* systemInstruction contains ONLY ANTIGRAVITY_DEFAULT_SYSTEM, and the client's
* system text is in contents[0].
*/
test("OpenAI -> Antigravity: client system message must NOT be in systemInstruction with default (#9030)", () => {
const result = openaiToAntigravityRequest(
"gemini-2.5-flash",
{
messages: [
{ role: "system", content: "You are a helpful assistant specialized in coding tasks." },
{ role: "user", content: "Write a function to sort an array." },
],
},
false
);
// systemInstruction must contain ONLY the ANTIGRAVITY_DEFAULT_SYSTEM
assert.ok(result.request.systemInstruction, "systemInstruction must exist");
assert.equal(
result.request.systemInstruction.parts.length,
1,
"systemInstruction must have exactly 1 part (only ANTIGRAVITY_DEFAULT_SYSTEM)"
);
assert.equal(
result.request.systemInstruction.parts[0].text,
ANTIGRAVITY_DEFAULT_SYSTEM,
"systemInstruction must contain only ANTIGRAVITY_DEFAULT_SYSTEM"
);
// The client system message must be moved to the first user content
const parts = result.request.contents[0]?.parts as PartsArray | undefined;
assert.ok(parts, "contents[0] must exist");
const firstUserText = parts.map((p) => p.text ?? "").join("");
assert.ok(
firstUserText.includes("You are a helpful assistant specialized in coding tasks."),
"client system text must be in first user message contents[0]"
);
assert.ok(
firstUserText.includes("Write a function to sort an array."),
"user message must also be in contents[0]"
);
});
test("OpenAI -> Antigravity: systemInstruction with only ANTIGRAVITY_DEFAULT_SYSTEM when no client system (#9030)", () => {
const result = openaiToAntigravityRequest(
"gemini-2.5-flash",
{
messages: [
{ role: "user", content: "Hello, how are you?" },
],
},
false
);
// systemInstruction must still have only the ANTIGRAVITY_DEFAULT_SYSTEM
assert.ok(result.request.systemInstruction, "systemInstruction must exist");
assert.equal(
result.request.systemInstruction.parts.length,
1,
"systemInstruction must have exactly 1 part"
);
assert.equal(
result.request.systemInstruction.parts[0].text,
ANTIGRAVITY_DEFAULT_SYSTEM,
"systemInstruction must contain ANTIGRAVITY_DEFAULT_SYSTEM"
);
});
test("OpenAI -> Antigravity: multiple system messages all moved to first user content (#9030)", () => {
const result = openaiToAntigravityRequest(
"gemini-2.5-flash",
{
messages: [
{ role: "system", content: "Rule one: be concise." },
{ role: "system", content: "Rule two: use TypeScript." },
{ role: "user", content: "Write code." },
],
},
false
);
// systemInstruction must contain ONLY ANTIGRAVITY_DEFAULT_SYSTEM
assert.equal(
result.request.systemInstruction.parts.length,
1,
"systemInstruction must have exactly 1 part"
);
assert.equal(
result.request.systemInstruction.parts[0].text,
ANTIGRAVITY_DEFAULT_SYSTEM,
"systemInstruction must contain only ANTIGRAVITY_DEFAULT_SYSTEM"
);
// The combined system messages + user message must be in contents[0]
const parts = result.request.contents[0]?.parts as PartsArray | undefined;
assert.ok(parts, "contents[0] must exist");
const firstUserText = parts.map((p) => p.text ?? "").join("");
assert.ok(
firstUserText.includes("Rule one: be concise."),
"first system text must be in contents[0]"
);
assert.ok(
firstUserText.includes("Rule two: use TypeScript."),
"second system text must be in contents[0]"
);
assert.ok(
firstUserText.includes("Write code."),
"user message must be in contents[0]"
);
});

View File

@@ -863,7 +863,10 @@ test("OpenAI -> Antigravity maps Claude-family models to Gemini-compatible schem
assert.match(result.requestId, /^agent\/\d+\/[0-9a-f]{8}$/);
assert.equal(result.enabledCreditTypes, undefined);
assert.equal(result.request.systemInstruction.parts[0].text, ANTIGRAVITY_DEFAULT_SYSTEM);
assert.equal(result.request.systemInstruction.parts[1].text, "Project rules");
assert.equal(result.request.systemInstruction.parts.length, 1, "systemInstruction must contain only ANTIGRAVITY_DEFAULT_SYSTEM (#9030)");
// #9030 — Client system content moved to first user message to avoid upstream 429s
assert.equal(result.request.contents[0].parts[0].text, "Project rules");
assert.equal(result.request.contents[0].parts[1].text, "Read a file");
assert.equal((result as any).request?.generationConfig.maxOutputTokens, undefined);
assert.equal((result as any).request?.messages, undefined);
assert.equal((result as any).request?.system, undefined);