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 <diegosouza.pw@gmail.com>

---------

Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
Co-authored-by: Chewji9875 <Chewji9875@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
Chewji
2026-08-17 17:04:07 +07:00
committed by GitHub
parent 3e8a8f71cc
commit 8bd0b840f6
5 changed files with 142 additions and 19 deletions

View File

@@ -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",

View File

@@ -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<string, unknown> = {
model: "antigravity/gemini-pro-agent",
messages: [{ role: "user", content: "Solve math problem" }],
reasoning_effort: "max",
};
const processed = applyThinkingBudget(req);
assert.equal(
(processed as Record<string, unknown>).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<string, unknown> } }
)?.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<string, unknown> } }
)?.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"
);
});

View File

@@ -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);
}

View File

@@ -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<Record<string, unknown>> };
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<string, unknown>;
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);
});

View File

@@ -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);
});