diff --git a/changelog.d/fixes/13195-gemini-38-output-spec.md b/changelog.d/fixes/13195-gemini-38-output-spec.md new file mode 100644 index 0000000000..ee4772c47e --- /dev/null +++ b/changelog.d/fixes/13195-gemini-38-output-spec.md @@ -0,0 +1 @@ +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index c8b6faaf4b..1260e26f7f 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -181,9 +181,56 @@ export const MODEL_SPECS: Record = { supportsTools: true, supportsVision: true, }, - // ── Gemini 3.7 Flash (current Antigravity/AGY live tiers) ───────── - // The tier suffix configures the thinking budget passed to the upstream - // gemini-3.7-flash-tiered backend (high: 24.5k, medium: 8k, low: 1k). + // Output limit published at https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash. + // Thinking budgets follow the 3.7 Flash high/medium/low/tiered split. + "gemini-3.8-flash-high": { + maxOutputTokens: 65536, + contextWindow: 1048576, + defaultThinkingBudget: 24576, + thinkingBudgetCap: 24576, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + }, + "gemini-3.8-flash-medium": { + maxOutputTokens: 65536, + contextWindow: 1048576, + defaultThinkingBudget: 8192, + thinkingBudgetCap: 24576, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + }, + "gemini-3.8-flash-low": { + maxOutputTokens: 65536, + contextWindow: 1048576, + defaultThinkingBudget: 1024, + thinkingBudgetCap: 24576, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + }, + "gemini-3.8-flash": { + maxOutputTokens: 65536, + contextWindow: 1048576, + defaultThinkingBudget: 8192, + thinkingBudgetCap: 24576, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + aliases: ["gemini-3.8-flash-tiered"], + }, + "gemini-3.8-flash-tiered": { + maxOutputTokens: 65536, + contextWindow: 1048576, + defaultThinkingBudget: 8192, + thinkingBudgetCap: 24576, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + }, + + // Gemini 3.7 Flash tiers: high 24.5k, medium 8k, low 1k thinking tokens. "gemini-3.7-flash-high": { maxOutputTokens: 65536, contextWindow: 1048576, diff --git a/tests/unit/antigravity-per-model-output-cap.test.ts b/tests/unit/antigravity-per-model-output-cap.test.ts index c194f66910..ab2571d992 100644 --- a/tests/unit/antigravity-per-model-output-cap.test.ts +++ b/tests/unit/antigravity-per-model-output-cap.test.ts @@ -20,6 +20,7 @@ import { ANTIGRAVITY_MODEL_ALIASES, ANTIGRAVITY_PUBLIC_MODELS, } from "../../open-sse/config/antigravityModelAliases.ts"; +import { getResolvedModelCapabilities } from "../../src/lib/modelCapabilities.ts"; function generationConfigOf(request: unknown): Record { const gc = (request as Record)?.generationConfig; @@ -197,6 +198,57 @@ test("an aliased id is capped by the model it resolves to", async () => { } }); +test("Gemini 3.8 Flash retains its output allowance above the thinking budget", async () => { + const executor = new AntigravityExecutor(); + for (const model of [ + "gemini-3.8-flash-high", + "gemini-3.8-flash-medium", + "gemini-3.8-flash-low", + "gemini-3.8-flash-tiered", + ]) { + const result = await executor.transformRequest( + `antigravity/${model}`, + { + request: { + contents: [{ role: "user", parts: [{ text: "Hello" }] }], + generationConfig: { + maxOutputTokens: 65536, + thinkingConfig: { thinkingBudget: 24576, includeThoughts: true }, + }, + }, + }, + true, + { projectId: "project-1" } + ); + if (result instanceof Response) throw new Error("Unexpected Response from transformRequest"); + const config = generationConfigOf(result.request); + assert.equal(config.maxOutputTokens, 65536, model); + assert.equal((config.thinkingConfig as Record).thinkingBudget, 24576, model); + } +}); + +test("Gemini 3.8 Flash static spec keeps thinking and context, not only the output cap", () => { + const expectedBudget: Record = { + "gemini-3.8-flash-high": 24576, + "gemini-3.8-flash-medium": 8192, + "gemini-3.8-flash-low": 1024, + "gemini-3.8-flash-tiered": 8192, + }; + for (const model of Object.keys(expectedBudget)) { + const caps = getResolvedModelCapabilities({ + provider: "antigravity", + model, + }); + assert.equal(caps.maxOutputTokens, 65536, model); + assert.equal(caps.supportsThinking, true, model); + assert.equal(caps.supportsTools, true, model); + assert.equal(caps.supportsVision, true, model); + assert.equal(caps.contextWindow, 1048576, model); + assert.equal(caps.defaultThinkingBudget, expectedBudget[model], model); + assert.equal(caps.thinkingBudgetCap, 24576, model); + } +}); + test("the executor's cap differs per model on the same code path", async () => { const executor = new AntigravityExecutor(); @@ -227,6 +279,7 @@ test("a provider-prefixed model id resolves to the model's ceiling, not the fall ["agy/gemini-3.1-pro-high", 65535], ["antigravity/gemini-3.1-pro-high", 65535], ["agy/gemini-3.7-flash-high", 65536], + ["agy/gemini-3.8-flash-high", 65536], ["agy/gpt-oss-120b-medium", 32768], ];