From 4ab777ec6f6aca7812f3d9cd0248f92a5894472a Mon Sep 17 00:00:00 2001 From: mdwsk88 <924038395@qq.com> Date: Thu, 3 Sep 2026 00:40:19 +0800 Subject: [PATCH] feat(antigravity): support Gemini 3.8 Flash tiered wire models and effort ladder - Collapse Gemini 3.8 Flash wire models (gemini-3.8-flash-low/medium/high) into picker base gemini-3.8-flash - Add low, medium, and high reasoning-effort ladder mapping for gemini-3.8-flash with medium default - Retain backwards-compatible identity aliases for explicit tier wire IDs - Add regression coverage for 3.8 Flash effort routing and tier collapse --- src/adapters/google.ts | 3 ++- src/providers/antigravity-models.ts | 22 +++++++++++++++ tests/google-antigravity-wire.test.ts | 39 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/adapters/google.ts b/src/adapters/google.ts index 01189a5938..52322a6b19 100644 --- a/src/adapters/google.ts +++ b/src/adapters/google.ts @@ -107,6 +107,7 @@ export function clampGoogleMaxOutputTokens( * stable and make the mapping configurable for deployments that still serve the bare id. */ const GEMINI_DIRECT_WIRE_RENAMES: Record = { + "gemini-3.8-flash": "gemini-3.8-flash-tiered", "gemini-3.7-flash": "gemini-3.7-flash-tiered", "gemini-3.6-flash": "gemini-3.6-flash-tiered", }; @@ -748,7 +749,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte // AI Studio's `-tiered` spelling is wire-only; CCA aliases may migrate to another generation. const identityModelId = provider.googleMode === "cloud-code-assist" ? routedModelId : parsed.modelId; const stripRejectedClaudeSdkParagraph = provider.googleMode === "cloud-code-assist" - && parsed.modelId === "gemini-3.7-flash"; + && (parsed.modelId === "gemini-3.7-flash" || parsed.modelId === "gemini-3.8-flash"); const { systemInstruction, contents, replayedCallIds } = messagesToGeminiFormat( parsed, identityModelId, diff --git a/src/providers/antigravity-models.ts b/src/providers/antigravity-models.ts index 9b9045cb9b..cc17bd5513 100644 --- a/src/providers/antigravity-models.ts +++ b/src/providers/antigravity-models.ts @@ -50,6 +50,9 @@ const RETIRED_FLASH_TIERS: Record = { }; const ANTIGRAVITY_WIRE_MODELS = [ + "gemini-3.8-flash-low", + "gemini-3.8-flash-medium", + "gemini-3.8-flash-high", "gemini-3.7-flash-tiered", "gemini-3.1-pro-low", "gemini-pro-agent", @@ -60,6 +63,9 @@ const ANTIGRAVITY_WIRE_MODELS = [ ]; const ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID: Record = { + "gemini-3.8-flash-low": "gemini-3.8-flash", + "gemini-3.8-flash-medium": "gemini-3.8-flash", + "gemini-3.8-flash-high": "gemini-3.8-flash", "gemini-3.1-pro-low": "gemini-3.1-pro", "gemini-pro-agent": "gemini-3.1-pro", }; @@ -143,6 +149,7 @@ function collapsesIntoKnownPickerModel(candidateId: string): boolean { // Gemini models: effort → wire model suffix (official agy UI pattern). // Claude Opus: effort → thinkingConfig.thinkingLevel (CLIProxyAPI proven pattern). export const ANTIGRAVITY_MODEL_EFFORTS: Record = { + "gemini-3.8-flash": ["low", "medium", "high"], "gemini-3.7-flash": ["low", "medium", "high"], "gemini-3.1-pro": ["low", "high"], "claude-sonnet-4-6": ["low", "medium", "high", "max"], @@ -151,6 +158,11 @@ export const ANTIGRAVITY_MODEL_EFFORTS: Record = { // ── Effort → wire model map for Gemini base models ── const ANTIGRAVITY_EFFORT_WIRE_MAP: Record> = { + "gemini-3.8-flash": { + low: "gemini-3.8-flash-low", + medium: "gemini-3.8-flash-medium", + high: "gemini-3.8-flash-high", + }, "gemini-3.1-pro": { low: "gemini-3.1-pro-low", high: "gemini-pro-agent", @@ -178,6 +190,7 @@ function completeDiscoveredEffortWireModelIds( // ── Default effort per Gemini base model ── const ANTIGRAVITY_DEFAULT_EFFORT: Record = { + "gemini-3.8-flash": "medium", "gemini-3.1-pro": "high", }; @@ -221,6 +234,9 @@ const ANTIGRAVITY_VISIBLE_MODEL_ALIASES: Record = { // Wire suffix IDs are identity aliases — they resolve to themselves so saved configs // with explicit suffixes (e.g. gemini-3.6-flash-low) continue to work. const ANTIGRAVITY_COMPATIBILITY_MODEL_ALIASES: Record = { + "gemini-3.8-flash-low": "gemini-3.8-flash-low", + "gemini-3.8-flash-medium": "gemini-3.8-flash-medium", + "gemini-3.8-flash-high": "gemini-3.8-flash-high", "gemini-3.1-pro-low": "gemini-3.1-pro-low", "gemini-pro-agent": "gemini-pro-agent", // ── Retired Flash generations ── @@ -241,6 +257,7 @@ export const ANTIGRAVITY_MODEL_ALIASES: Record = { // Picker-visible: collapsed base models only. export const ANTIGRAVITY_MODELS = [ + "gemini-3.8-flash", GEMINI_FLASH_CURRENT, "gemini-3.1-pro", "gemini-3.1-flash-image", @@ -255,6 +272,9 @@ function isKnownAntigravityPickerModelId(value: string): boolean { // Context windows from the upstream `:fetchAvailableModels` maxTokens per model. const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record = { + "gemini-3.8-flash-low": 1_048_576, + "gemini-3.8-flash-medium": 1_048_576, + "gemini-3.8-flash-high": 1_048_576, "gemini-3.7-flash-tiered": 1_048_576, "gemini-3.1-pro-low": 1_048_576, "gemini-pro-agent": 1_048_576, @@ -266,6 +286,7 @@ const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record = { export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record = { // Collapsed base IDs — explicit entries for the picker. + "gemini-3.8-flash": 1_048_576, "gemini-3.7-flash": 1_048_576, "gemini-3.1-pro": 1_048_576, // Wire IDs and aliases via derivation. @@ -283,6 +304,7 @@ export const ANTIGRAVITY_MODEL_INPUT_MODALITIES: Record = { // carries only text and image parts (`OcxImageContent`, src/types.ts) and the Codex // catalog normalizes `input_modalities` against a closed enum. Advertising a modality // the wire cannot carry would be a promise we break at request time. + "gemini-3.8-flash": ["text", "image"], "gemini-3.7-flash": ["text", "image"], "gemini-3.1-pro": ["text", "image"], "gemini-3.1-flash-image": ["text", "image"], diff --git a/tests/google-antigravity-wire.test.ts b/tests/google-antigravity-wire.test.ts index d00ba03b8a..919542e944 100644 --- a/tests/google-antigravity-wire.test.ts +++ b/tests/google-antigravity-wire.test.ts @@ -79,6 +79,7 @@ describe("antigravity CCA envelope", () => { test("exposes Gemini 3.7 Flash while retired Flash ids resolve to it", async () => { // Collapsed picker: base models only. expect(ANTIGRAVITY_MODELS).toEqual([ + "gemini-3.8-flash", "gemini-3.7-flash", "gemini-3.1-pro", "gemini-3.1-flash-image", @@ -87,6 +88,9 @@ describe("antigravity CCA envelope", () => { "gpt-oss-120b-medium", ]); for (const hidden of [ + "gemini-3.8-flash-low", + "gemini-3.8-flash-medium", + "gemini-3.8-flash-high", "gemini-3.6-flash", "gemini-3.6-flash-low", "gemini-3.6-flash-medium", @@ -515,6 +519,41 @@ describe("antigravity CCA envelope", () => { // 3.7 Flash carries its tiers on thinkingLevel against ONE wire id, unlike the 3.6 // generation which used suffixed wire ids. + test("gemini-3.8-flash with effort=low routes to gemini-3.8-flash-low + thinkingConfig", async () => { + const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.8-flash", "low")); + const env = JSON.parse(req.body); + expect(env.model).toBe("gemini-3.8-flash-low"); + expect(env.request.generationConfig?.thinkingConfig?.thinkingLevel).toBe("low"); + }); + + test("gemini-3.8-flash with effort=medium routes to gemini-3.8-flash-medium + thinkingConfig", async () => { + const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.8-flash", "medium")); + const env = JSON.parse(req.body); + expect(env.model).toBe("gemini-3.8-flash-medium"); + expect(env.request.generationConfig?.thinkingConfig?.thinkingLevel).toBe("medium"); + }); + + test("gemini-3.8-flash with effort=high routes to gemini-3.8-flash-high + thinkingConfig", async () => { + const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.8-flash", "high")); + const env = JSON.parse(req.body); + expect(env.model).toBe("gemini-3.8-flash-high"); + expect(env.request.generationConfig?.thinkingConfig?.thinkingLevel).toBe("high"); + }); + + test("gemini-3.8-flash with no effort defaults to gemini-3.8-flash-medium", async () => { + const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.8-flash")); + const env = JSON.parse(req.body); + expect(env.model).toBe("gemini-3.8-flash-medium"); + expect(env.request.generationConfig?.thinkingConfig).toBeUndefined(); + }); + + test("gemini-3.8-flash with effort=max clamps to high", async () => { + const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.8-flash", "max")); + const env = JSON.parse(req.body); + expect(env.model).toBe("gemini-3.8-flash-high"); + expect(env.request.generationConfig?.thinkingConfig?.thinkingLevel).toBe("high"); + }); + test("gemini-3.7-flash with effort=high keeps the wire id + thinkingConfig", async () => { const req = await createGoogleAdapter(effortProvider).buildRequest(parsedWithEffort("gemini-3.7-flash", "high")); const env = JSON.parse(req.body);