From 8bd0b840f6ae3cd84c16eb42801fe2d30d90859a Mon Sep 17 00:00:00 2001 From: Chewji <126886556+Chewji9875@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:04:07 +0700 Subject: [PATCH] fix(antigravity): unblock Gemini and Claude reasoning capabilities (#10376) * fix(antigravity): unblock Gemini and Claude reasoning capabilities * fix(antigravity): align two unit tests with unblocked Gemini/Claude reasoning The PR unblocks Antigravity Gemini/Claude reasoning (removed from REASONING_UNSUPPORTED_PATTERNS, mirroring model-capabilities-registry.test.ts). models-catalog-combo-metadata and services-branch-hardening still asserted the pre-PR deny contract; align them to the new verified behavior. No production code changed. Co-authored-by: diegosouzapw --------- Co-authored-by: adevwithpurpose Co-authored-by: Chewji9875 Co-authored-by: diegosouzapw --- src/lib/modelCapabilities.ts | 7 - ...avity-thinking-config-preservation.test.ts | 130 ++++++++++++++++++ .../unit/model-capabilities-registry.test.ts | 4 +- .../models-catalog-combo-metadata.test.ts | 16 +-- tests/unit/services-branch-hardening.test.ts | 4 +- 5 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 tests/unit/antigravity-thinking-config-preservation.test.ts diff --git a/src/lib/modelCapabilities.ts b/src/lib/modelCapabilities.ts index 3c2e037fe2..83afd6a34b 100644 --- a/src/lib/modelCapabilities.ts +++ b/src/lib/modelCapabilities.ts @@ -47,13 +47,6 @@ const TOOL_CALLING_UNSUPPORTED_PATTERNS: string[] = [ "stable-diffusion", ]; const REASONING_UNSUPPORTED_PATTERNS = [ - "antigravity/claude-sonnet-4-6", - "antigravity/claude-sonnet-4-5", - "antigravity/claude-sonnet-4", - // Non-Claude antigravity models don't support thinking params (#1361) - "antigravity/gemini-", - "antigravity/gpt-oss-", - "antigravity/gemini-3", "antigravity/tab_", // Specialty / non-chat surfaces (#8016) "whisper", diff --git a/tests/unit/antigravity-thinking-config-preservation.test.ts b/tests/unit/antigravity-thinking-config-preservation.test.ts new file mode 100644 index 0000000000..904d453287 --- /dev/null +++ b/tests/unit/antigravity-thinking-config-preservation.test.ts @@ -0,0 +1,130 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + getResolvedModelCapabilities, + supportsReasoning, +} from "../../src/lib/modelCapabilities.ts"; +import { applyThinkingBudget } from "../../open-sse/services/thinkingBudget.ts"; +import { translateRequest } from "../../open-sse/translator/index.ts"; +import { FORMATS } from "../../open-sse/translator/formats.ts"; + +test("antigravity reasoning capabilities: Gemini and Claude models support reasoning", () => { + const geminiModels = [ + "antigravity/gemini-3-flash-agent", + "antigravity/gemini-pro-agent", + "antigravity/gemini-3.1-pro-low", + "antigravity/claude-sonnet-4-6", + "antigravity/claude-opus-4-6-thinking", + ]; + + for (const modelId of geminiModels) { + const isReasoning = supportsReasoning(modelId); + assert.equal(isReasoning, true, `supportsReasoning should be true for ${modelId}`); + + const caps = getResolvedModelCapabilities(modelId); + assert.equal(caps.reasoning, true, `caps.reasoning should be true for ${modelId}`); + assert.equal( + caps.supportsThinking, + true, + `caps.supportsThinking should be true for ${modelId}` + ); + } + + // Passthrough / unlisted Gemini model should still heuristically resolve reasoning + assert.equal(supportsReasoning("antigravity/gemini-2.5-pro"), true); + const gemini25Caps = getResolvedModelCapabilities("antigravity/gemini-2.5-pro"); + assert.equal(gemini25Caps.reasoning, true); + + // Non-reasoning models like tab completion should return false + assert.equal(supportsReasoning("antigravity/tab_flash_lite"), false); + const tabCaps = getResolvedModelCapabilities("antigravity/tab_flash_lite"); + assert.equal(tabCaps.reasoning, false); +}); + +test("antigravity request pipeline: applyThinkingBudget preserves reasoning params", () => { + const req: Record = { + model: "antigravity/gemini-pro-agent", + messages: [{ role: "user", content: "Solve math problem" }], + reasoning_effort: "max", + }; + + const processed = applyThinkingBudget(req); + assert.equal( + (processed as Record).reasoning_effort, + "max", + "reasoning_effort must not be stripped" + ); +}); + +test("antigravity translator: translates reasoning_effort into Gemini thinkingConfig", () => { + const inputReq = { + model: "antigravity/gemini-pro-agent", + messages: [{ role: "user", content: "Solve math problem" }], + reasoning_effort: "max", + }; + + const translated = translateRequest( + FORMATS.OPENAI, + FORMATS.ANTIGRAVITY, + "antigravity/gemini-pro-agent", + inputReq, + true, + null, + "antigravity" + ); + + const generationConfig = ( + translated as { request?: { generationConfig?: Record } } + )?.request?.generationConfig; + + assert.ok(generationConfig, "generationConfig must exist in Cloud Code envelope"); + assert.ok( + generationConfig.thinkingConfig, + "thinkingConfig must exist in generationConfig for Gemini reasoning models" + ); + assert.equal( + (generationConfig.thinkingConfig as { includeThoughts?: boolean }).includeThoughts, + true + ); + assert.equal( + typeof (generationConfig.thinkingConfig as { thinkingBudget?: number }).thinkingBudget, + "number" + ); + assert.ok( + (generationConfig.thinkingConfig as { thinkingBudget: number }).thinkingBudget > 0, + "thinkingBudget should be positive" + ); +}); + +test("antigravity translator: Claude models bump maxOutputTokens and strip raw thinkingConfig", () => { + const inputReq = { + model: "antigravity/claude-sonnet-4-6", + messages: [{ role: "user", content: "Explain quantum mechanics" }], + reasoning_effort: "high", + }; + + const translated = translateRequest( + FORMATS.OPENAI, + FORMATS.ANTIGRAVITY, + "antigravity/claude-sonnet-4-6", + inputReq, + true, + null, + "antigravity" + ); + + const generationConfig = ( + translated as { request?: { generationConfig?: Record } } + )?.request?.generationConfig; + + assert.ok(generationConfig, "generationConfig must exist in Cloud Code envelope"); + assert.equal( + generationConfig.thinkingConfig, + undefined, + "raw thinkingConfig must be stripped for Claude models on Antigravity" + ); + assert.ok( + (generationConfig.maxOutputTokens as number) >= 16384, + "maxOutputTokens should be preserved/bumped for Claude reasoning" + ); +}); diff --git a/tests/unit/model-capabilities-registry.test.ts b/tests/unit/model-capabilities-registry.test.ts index 93f80274e5..c23e05a061 100644 --- a/tests/unit/model-capabilities-registry.test.ts +++ b/tests/unit/model-capabilities-registry.test.ts @@ -165,7 +165,7 @@ test("Antigravity Gemini 3.5 upstream IDs share the Flash capability profile", ( const capabilities = modelCapabilities.getResolvedModelCapabilities(`antigravity/${modelId}`); assert.equal(capabilities.contextWindow, 1048576, modelId); assert.equal(capabilities.maxOutputTokens, 65536, modelId); - assert.equal(capabilities.supportsThinking, false, modelId); + assert.equal(capabilities.supportsThinking, true, modelId); assert.equal(capabilities.supportsTools, true, modelId); assert.equal(capabilities.supportsVision, true, modelId); } @@ -184,7 +184,7 @@ test("Antigravity Gemini 3.7 and 3.6 tier IDs share the Flash capability profile const capabilities = modelCapabilities.getResolvedModelCapabilities(`antigravity/${modelId}`); assert.equal(capabilities.contextWindow, 1048576, modelId); assert.equal(capabilities.maxOutputTokens, 65536, modelId); - assert.equal(capabilities.supportsThinking, false, modelId); + assert.equal(capabilities.supportsThinking, true, modelId); assert.equal(capabilities.supportsTools, true, modelId); assert.equal(capabilities.supportsVision, true, modelId); } diff --git a/tests/unit/models-catalog-combo-metadata.test.ts b/tests/unit/models-catalog-combo-metadata.test.ts index 51d5c5360b..e262bf4971 100644 --- a/tests/unit/models-catalog-combo-metadata.test.ts +++ b/tests/unit/models-catalog-combo-metadata.test.ts @@ -87,18 +87,18 @@ test("single-target combo respects registry reasoning overrides before specs", a assert.equal(Object.hasOwn(capabilities, "effort_tiers"), false); }); -test("single-target combo respects resolved reasoning deny patterns", async () => { +test("single-target combo reflects unblocked Antigravity Gemini reasoning", async () => { await providersDb.createProviderConnection({ provider: "antigravity", authType: "oauth", - name: "antigravity-gemini-no-thinking-combo", + name: "antigravity-gemini-reasoning-combo", accessToken: "antigravity-test-token", isActive: true, testStatus: "active", providerSpecificData: {}, }); await combosDb.createCombo({ - name: "antigravity-gemini-no-thinking-combo", + name: "antigravity-gemini-reasoning-combo", strategy: "auto", models: ["antigravity/gemini-3.1-pro-high"], }); @@ -107,13 +107,13 @@ test("single-target combo respects resolved reasoning deny patterns", async () = new Request("http://localhost/api/v1/models") ); const body = (await response.json()) as { data: Array> }; - const combo = body.data.find((item) => item.id === "antigravity-gemini-no-thinking-combo"); + const combo = body.data.find((item) => item.id === "antigravity-gemini-reasoning-combo"); assert.equal(response.status, 200); assert.ok(combo); const capabilities = combo.capabilities as Record; - assert.equal(capabilities.reasoning, false); - assert.equal(capabilities.thinking, false); - assert.equal(capabilities.supportsThinking, false); - assert.equal(Object.hasOwn(capabilities, "effort_tiers"), false); + assert.equal(capabilities.reasoning, true); + assert.equal(capabilities.thinking, true); + assert.equal(capabilities.supportsThinking, true); + assert.equal(Object.hasOwn(capabilities, "effort_tiers"), true); }); diff --git a/tests/unit/services-branch-hardening.test.ts b/tests/unit/services-branch-hardening.test.ts index fa6458eab5..085cd4df53 100644 --- a/tests/unit/services-branch-hardening.test.ts +++ b/tests/unit/services-branch-hardening.test.ts @@ -113,8 +113,8 @@ test("model capability helpers cover denylist, empty input and default-safe path ); assert.equal(modelCapabilities.supportsReasoning(""), true); - assert.equal(modelCapabilities.supportsReasoning("antigravity/claude-sonnet-4-6"), false); - assert.equal(modelCapabilities.supportsReasoning("antigravity/claude-sonnet-4"), false); + assert.equal(modelCapabilities.supportsReasoning("antigravity/claude-sonnet-4-6"), true); + assert.equal(modelCapabilities.supportsReasoning("antigravity/claude-sonnet-4"), true); assert.equal(modelCapabilities.supportsReasoning("openai/nonexistent-default-safe-model"), true); });