fix(antigravity): strip trailing model turn for native Gemini requests too

Newer Gemini endpoints reject a request ending on a model turn with HTTP
400 'Requests ending with a model turn are not supported' — the same
rejection class Claude hits via Vertex. transformRequest() previously
wired stripTrailingAntigravityAssistantTurn() only into the isClaude
branch, so native Gemini models routed through Antigravity kept a
trailing role:model entry and hit the 400.

Extend the guarded strip (never empties contents) to native Gemini
models too, gated by upstreamModel including "gemini". The Claude
path is untouched (byte-identical), preserving PR #6114's live
validation against Vertex Claude.

Flips tests/unit/antigravity-claude-prefill-strip.test.ts test (b),
which previously asserted the buggy pass-through, and adds (b2) for
the gemini-3-flash-agent tier.

Closes #10104
This commit is contained in:
adevwithpurpose
2026-08-14 21:54:06 -03:00
parent abd4df63dc
commit c4ee73dee0
3 changed files with 36 additions and 7 deletions

View File

@@ -44,14 +44,28 @@ test("(a) strips a single trailing assistant (model) turn for Claude models", as
assert.equal(contents.at(-1)?.role, "user");
});
test("(b) does NOT strip a trailing model turn for non-Claude (native Gemini) models", async () => {
test("(b) strips a trailing model turn for native Gemini models too (#10104)", async () => {
// Newer Gemini endpoints reject a request ending on a `model` turn with the same
// class of 400 Claude hits via Vertex ("Requests ending with a model turn are not
// supported"), so native Gemini models routed through Antigravity get the same
// guarded strip as the Claude path.
const request = await transform("antigravity/gemini-3.1-pro", [
{ role: "user", parts: [{ text: "Hello" }] },
{ role: "model", parts: [{ text: "Hi there" }] },
]);
const contents = request.contents as Array<{ role: string }>;
assert.equal(contents.length, 2);
assert.equal(contents.at(-1)?.role, "model", "native Gemini requests via Antigravity are untouched");
assert.equal(contents.length, 1);
assert.equal(contents.at(-1)?.role, "user", "transformed native Gemini request must end on user");
});
test("(b2) native Gemini agent tier (gemini-3-flash-agent, #10104) also gets the strip", async () => {
const request = await transform("antigravity/gemini-3-flash-agent", [
{ role: "user", parts: [{ text: "Hello" }] },
{ role: "model", parts: [{ text: "Hi there" }] }, // trailing model turn -> 400 source
]);
const contents = request.contents as Array<{ role: string }>;
assert.equal(contents.length, 1, "trailing model turn should be stripped for native Gemini");
assert.equal(contents.at(-1)?.role, "user", "transformed native Gemini request must end on user");
});
test("(c) a Claude conversation already ending on user is unchanged", async () => {