mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-15 19:52:50 +03:00
Compare commits
1 Commits
fix/10244-
...
fix/10104-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4ee73dee0 |
@@ -0,0 +1 @@
|
||||
- fix(antigravity): strip trailing model turn for native Gemini requests too, not just Claude (#10104)
|
||||
@@ -397,9 +397,10 @@ function sanitizeAntigravityGeminiRequest(
|
||||
* `"assistant"`). Mirrors the trailing-strip pop-loop already used for Mistral
|
||||
* (#3396), Copilot (#5802), and the CC-bridge in `claudeCodeCompatible.ts`.
|
||||
*
|
||||
* Scoped strictly to the Claude path by the caller (`isClaude` branch only) — native
|
||||
* Gemini models via Antigravity must be unaffected, since Vertex-Claude is the only
|
||||
* documented rejection surface.
|
||||
* Wired in by the caller for both the Claude path (`isClaude`) and native Gemini
|
||||
* models (`isGemini`, #10104) — newer Gemini endpoints reject a trailing `model` turn
|
||||
* with the same "ending with a model turn" class of 400 that Claude hits via Vertex.
|
||||
* Other model families routed through Antigravity are left untouched.
|
||||
*
|
||||
* Guard: never strip `contents` down to empty — an empty `contents` array is itself
|
||||
* an invalid request, so at least one entry (even a lone trailing "model" turn) is
|
||||
@@ -597,6 +598,14 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
|
||||
const upstreamModel = await cleanModelName(model, modelIdOverride);
|
||||
const isClaude = upstreamModel.toLowerCase().includes("claude");
|
||||
// #10104: 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 surface Claude hits via Vertex (see stripTrailingAntigravityAssistantTurn's
|
||||
// doc comment above). Native Gemini models routed through Antigravity (`agy/gemini-*`,
|
||||
// e.g. the Gemini 3.x Flash/Pro tiers from PR #8013's catalog) need the same guarded
|
||||
// strip. Scoped to models whose id names Gemini so unrelated model families are
|
||||
// untouched; the strip itself never empties `contents` (see the guard above).
|
||||
const isGemini = upstreamModel.toLowerCase().includes("gemini");
|
||||
const baseBody = bodyRecord;
|
||||
const normalizedBody = shouldStripCloudCodeThinking(this.provider, upstreamModel)
|
||||
? stripCloudCodeThinkingConfig(baseBody)
|
||||
@@ -660,11 +669,16 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
: normalizedRequest?.toolConfig,
|
||||
};
|
||||
|
||||
// Note: sanitizeAntigravityGeminiRequest() applies a Claude-only field whitelist
|
||||
// (dropping fields native Gemini requests may legitimately carry), so the Gemini
|
||||
// branch only runs the trailing-turn strip — never the sanitize/whitelist step.
|
||||
const transformedRequest = isClaude
|
||||
? stripTrailingAntigravityAssistantTurn(
|
||||
sanitizeAntigravityGeminiRequest(rawTransformedRequest)
|
||||
)
|
||||
: rawTransformedRequest;
|
||||
: isGemini
|
||||
? stripTrailingAntigravityAssistantTurn(rawTransformedRequest)
|
||||
: rawTransformedRequest;
|
||||
|
||||
applyAntigravityGenerationDefaults(transformedRequest, upstreamModel);
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user