diff --git a/open-sse/config/agyModels.ts b/open-sse/config/agyModels.ts index 51c4f60f22..14f9e0490c 100644 --- a/open-sse/config/agyModels.ts +++ b/open-sse/config/agyModels.ts @@ -106,6 +106,18 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ supportsVision: true, toolCalling: true, }, + // Gemini 3.7 Flash: single callable public model (upstream exposes only + // gemini-3.7-flash-tiered; suffixed tier ids 404). One entry so it does not + // collide under the #3696 public-id uniqueness invariant. + { + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", + contextLength: 1048576, + maxOutputTokens: 65536, + supportsReasoning: true, + supportsVision: true, + toolCalling: true, + }, { id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash Lite", diff --git a/open-sse/config/antigravityModelAliases.ts b/open-sse/config/antigravityModelAliases.ts index 80e066592d..eeacbdf8e3 100644 --- a/open-sse/config/antigravityModelAliases.ts +++ b/open-sse/config/antigravityModelAliases.ts @@ -124,6 +124,18 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ supportsVision: true, toolCalling: true, }, + // Gemini 3.7 Flash: Antigravity's live catalog exposes a single upstream id + // gemini-3.7-flash-tiered; the suffixed tier ids 404 upstream. Kept as one + // callable public model so it does not collide with the #3696 uniqueness invariant. + { + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", + contextLength: 1048576, + maxOutputTokens: 65536, + supportsReasoning: true, + supportsVision: true, + toolCalling: true, + }, { id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash Lite", @@ -163,6 +175,12 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ ]); export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({ + // Gemini 3.7 Flash: the live catalog (fetchAvailableModels on daily-cloudcode-pa) + // exposes a single upstream id `gemini-3.7-flash-tiered`; the agy CLI maps all + // display tiers (high/medium/low) to it. Verified 200 OK with thinking_level and + // thinkingBudget configs. The suffixed ids 404 upstream ("Requested entity was not found"). + // Exposed as ONE callable model (see #3696: public ids must be unique upstream ids). + "gemini-3.7-flash": "gemini-3.7-flash-tiered", // gemini-3.1-pro-low is not aliased: the upstream accepts it verbatim. // gemini-3.1-pro-high: the discovery slot returns HTTP 400 on v1internal; // the live upstream id is gemini-pro-agent (see ANTIGRAVITY_PUBLIC_MODELS). diff --git a/open-sse/config/freeModelCatalog.data.ts b/open-sse/config/freeModelCatalog.data.ts index 3d43bdd020..78047f5299 100644 --- a/open-sse/config/freeModelCatalog.data.ts +++ b/open-sse/config/freeModelCatalog.data.ts @@ -26,6 +26,7 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [ { provider: "agy", modelId: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6 (Thinking)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gemini-3.1-pro-low", displayName: "Gemini 3.1 Pro (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gemini-pro-agent", displayName: "Gemini 3.1 Pro (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, + { provider: "agy", modelId: "gemini-3.7-flash", displayName: "Gemini 3.7 Flash", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gemini-3.6-flash-high", displayName: "Gemini 3.6 Flash (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gemini-3.6-flash-medium", displayName: "Gemini 3.6 Flash (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gemini-3.6-flash-low", displayName: "Gemini 3.6 Flash (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, diff --git a/open-sse/config/providers/registry/agy/index.ts b/open-sse/config/providers/registry/agy/index.ts index 6aab7fe3d9..661f3f8ab7 100644 --- a/open-sse/config/providers/registry/agy/index.ts +++ b/open-sse/config/providers/registry/agy/index.ts @@ -25,4 +25,5 @@ export const agyProvider: RegistryEntry = { }, models: [...AGY_PUBLIC_MODELS], passthroughModels: true, + liveCatalogAuthoritative: false, }; diff --git a/open-sse/config/providers/registry/antigravity/index.ts b/open-sse/config/providers/registry/antigravity/index.ts index 74addaf815..c3080b0103 100644 --- a/open-sse/config/providers/registry/antigravity/index.ts +++ b/open-sse/config/providers/registry/antigravity/index.ts @@ -25,4 +25,5 @@ export const antigravityProvider: RegistryEntry = { }, models: [...ANTIGRAVITY_PUBLIC_MODELS], passthroughModels: true, + liveCatalogAuthoritative: false, }; diff --git a/open-sse/executors/antigravity.ts b/open-sse/executors/antigravity.ts index dda321c266..4cd1031cec 100644 --- a/open-sse/executors/antigravity.ts +++ b/open-sse/executors/antigravity.ts @@ -521,6 +521,17 @@ export class AntigravityExecutor extends BaseExecutor { super("antigravity", PROVIDERS.antigravity); } + override shouldRetry(status: number, urlIndex: number): boolean { + return ( + (status === HTTP_STATUS.RATE_LIMITED || + status === HTTP_STATUS.NOT_FOUND || + status === HTTP_STATUS.BAD_GATEWAY || + status === HTTP_STATUS.SERVICE_UNAVAILABLE || + status === HTTP_STATUS.GATEWAY_TIMEOUT) && + urlIndex + 1 < this.getFallbackCount() + ); + } + buildUrl(model: string, _stream: boolean, urlIndex = 0): string { void model; const baseUrls = this.getBaseUrls(); diff --git a/src/shared/constants/cliTools.ts b/src/shared/constants/cliTools.ts index acdba14050..86907635d2 100644 --- a/src/shared/constants/cliTools.ts +++ b/src/shared/constants/cliTools.ts @@ -239,6 +239,7 @@ export const CLI_TOOLS: Record = { acpSpawnable: false, baseUrlSupport: "none", modelAliases: [ + "gemini-3.7-flash", "gemini-3.6-flash-high", "gemini-3.6-flash-medium", "gemini-3.6-flash-low", @@ -252,6 +253,7 @@ export const CLI_TOOLS: Record = { "gpt-oss-120b-medium", ], defaultModels: [ + createCliModel("gemini-3.7-flash", "Gemini 3.7 Flash"), createCliModel("gemini-3.6-flash-high", "Gemini 3.6 Flash High"), createCliModel("gemini-3.6-flash-medium", "Gemini 3.6 Flash Medium"), createCliModel("gemini-3.6-flash-low", "Gemini 3.6 Flash Low"), diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index 8afe74d3f3..b2ecabbc6c 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -174,6 +174,18 @@ export const MODEL_SPECS: Record = { thinkingBudgetCap: 0, }, + // ── Gemini 3.7 Flash (Antigravity) — collapsed live id ────────── + // Upstream (fetchAvailableModels on daily-cloudcode-pa) also serves this model as a + // single `gemini-3.7-flash-tiered` id via the `gemini-3.7-flash` alias in + // antigravityModelAliases.ts. Registered independently of the suffixed tier ids below + // (#3696 uniqueness invariant: each public id resolves to a distinct upstream id). + "gemini-3.7-flash": { + ...GEMINI_35_FLASH_MODEL_SPEC, + defaultThinkingBudget: 8192, + thinkingBudgetCap: 65536, + supportsThinking: true, + }, + // ── Gemini 3.7 / 3.6 Flash (Antigravity live tiers) ───────────── // The model id itself selects the upstream 10k/4k/1k reasoning tier. Antigravity // still rejects client-supplied thinking parameters, so keep the explicit-parameter diff --git a/src/shared/constants/pricing/oauth-subscriptions.ts b/src/shared/constants/pricing/oauth-subscriptions.ts index 7c4a8406c4..b03db8be0c 100644 --- a/src/shared/constants/pricing/oauth-subscriptions.ts +++ b/src/shared/constants/pricing/oauth-subscriptions.ts @@ -319,6 +319,13 @@ export const DEFAULT_PRICING_OAUTH = { // downstream cost and quota calculations silently fall back to $0. // Pricing: $1.50 input / $7.50 output / $0.15 cached per MTok. Thinking tokens // billed at output rate. + "gemini-3.7-flash": { + input: 1.5, + output: 7.5, + cached: 0.15, + reasoning: 7.5, + cache_creation: 1.5, + }, "gemini-3.6-flash-low": { input: 1.5, output: 7.5, diff --git a/tests/unit/antigravity-model-aliases.test.ts b/tests/unit/antigravity-model-aliases.test.ts index f1e5cda7f2..ff6c5b550d 100644 --- a/tests/unit/antigravity-model-aliases.test.ts +++ b/tests/unit/antigravity-model-aliases.test.ts @@ -18,6 +18,7 @@ function getPublicModel(id: string) { } const EXPECTED_FLASH_TIERS = [ + ["gemini-3.7-flash", "Gemini 3.7 Flash"], ["gemini-3.7-flash-high", "Gemini 3.7 Flash (High)"], ["gemini-3.7-flash-medium", "Gemini 3.7 Flash (Medium)"], ["gemini-3.6-flash-low", "Gemini 3.6 Flash (Low)"], @@ -49,7 +50,11 @@ test("toClientAntigravityQuotaModelId preserves upstream Gemini Flash bucket IDs test("resolveAntigravityModelId maps the documented Antigravity aliases to upstream IDs", () => { assert.equal(resolveAntigravityModelId("gemini-3-pro-image-preview"), "gemini-3-pro-image"); for (const [modelId] of EXPECTED_FLASH_TIERS) { - assert.equal(resolveAntigravityModelId(modelId), modelId); + // Only the collapsed gemini-3.7-flash id is aliased to the live upstream + // gemini-3.7-flash-tiered id; the suffixed gemini-3.7-flash-high/medium tier ids + // (like the 3.6/3.5 tiers) have no alias entry and pass through verbatim. + const expected = modelId === "gemini-3.7-flash" ? "gemini-3.7-flash-tiered" : modelId; + assert.equal(resolveAntigravityModelId(modelId), expected); } assert.equal(resolveAntigravityModelId("gemini-claude-sonnet-4-5"), "claude-sonnet-4-6"); assert.equal(resolveAntigravityModelId("gemini-claude-sonnet-4-5-thinking"), "claude-sonnet-4-6"); @@ -193,7 +198,7 @@ test("AntigravityExecutor.transformRequest preserves Gemini Flash upstream IDs", ); if (result instanceof Response) throw new Error("Unexpected Response from transformRequest"); - assert.equal(result.model, modelId); + assert.equal(result.model, resolveAntigravityModelId(modelId)); assert.deepEqual(result.request.contents, [{ role: "user", parts: [{ text: "Hello" }] }]); } });