diff --git a/README.md b/README.md index da6c91f3bf..a94b3da477 100644 --- a/README.md +++ b/README.md @@ -557,7 +557,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute - **🧠 Memory you control** — off by default, opt-in int8 vector quantization + typed decay, per-request `x-omniroute-no-memory`. → [Memory](docs/frameworks/MEMORY.md) - **🛡️ Security** — prompt-injection guard on every LLM route (red-team suite), opt-in credential-masking guardrail (redacts leaked API keys/secrets in both directions), free DuckDuckGo last-resort web search, and an optional OIDC login gate for the dashboard (password login always stays available). → [Guardrails](docs/security/GUARDRAILS.md) - **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md) -- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Google Imagen, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md) +- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md) - **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md) - **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **340-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md) - **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md) diff --git a/open-sse/config/agyModels.ts b/open-sse/config/agyModels.ts index 14f9e0490c..412e073044 100644 --- a/open-sse/config/agyModels.ts +++ b/open-sse/config/agyModels.ts @@ -2,8 +2,8 @@ // // These models are pinned from the live `:fetchAvailableModels` endpoint // (https://daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels) using a -// real `agy` consumer-OAuth token. The public catalog exposes the upstream Gemini 3.6 -// and 3.5 Flash ids verbatim; the shared Antigravity executor dispatches them unchanged. +// real `agy` consumer-OAuth token. The public catalog exposes the upstream Gemini 3.7 +// Flash ids verbatim; the shared Antigravity executor dispatches them unchanged. // // The `agy` provider reuses the `antigravity` executor/translator (identical backend), // but keeps its own catalog so the CLI and IDE model surfaces can evolve independently. @@ -12,11 +12,11 @@ // they are not chat-callable. export const AGY_PUBLIC_MODELS = Object.freeze([ - // Gemini 3.6 Flash tiers. The live endpoint selects High by default and advertises - // all three ids to both the IDE 2.1.1 and CLI 1.1.x clients. + // Gemini 3.7 Flash tiers. The live endpoint selects High by default and advertises + // all three ids to both the IDE 2.5.5 and CLI 1.1.x clients. { - id: "gemini-3.6-flash-high", - name: "Gemini 3.6 Flash (High)", + id: "gemini-3.7-flash-high", + name: "Gemini 3.7 Flash (High)", contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, @@ -24,8 +24,8 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ toolCalling: true, }, { - id: "gemini-3.6-flash-medium", - name: "Gemini 3.6 Flash (Medium)", + id: "gemini-3.7-flash-medium", + name: "Gemini 3.7 Flash (Medium)", contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, @@ -33,27 +33,8 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ toolCalling: true, }, { - id: "gemini-3.6-flash-low", - name: "Gemini 3.6 Flash (Low)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - // Claude (Antigravity backend). - { - id: "claude-opus-4-6-thinking", - name: "Claude Opus 4.6 (Thinking)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6 (Thinking)", + id: "gemini-3.7-flash-low", + name: "Gemini 3.7 Flash (Low)", contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, @@ -79,45 +60,6 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ supportsVision: true, toolCalling: true, }, - { - id: "gemini-3-flash-agent", - name: "Gemini 3.5 Flash (High)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.5-flash-low", - name: "Gemini 3.5 Flash (Medium)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.5-flash-extra-low", - name: "Gemini 3.5 Flash (Low)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - 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", @@ -125,27 +67,23 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ maxOutputTokens: 65535, toolCalling: true, }, - // Gemini 2.5 + // Claude (Antigravity backend). { - id: "gemini-2.5-flash-thinking", - name: "Gemini 2.5 Flash Thinking", + id: "claude-opus-4-6-thinking", + name: "Claude Opus 4.6 (Thinking)", contextLength: 1048576, - maxOutputTokens: 65535, + maxOutputTokens: 65536, supportsReasoning: true, + supportsVision: true, toolCalling: true, }, { - id: "gemini-2.5-flash", - name: "Gemini 2.5 Flash", + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (Thinking)", contextLength: 1048576, - maxOutputTokens: 65535, - toolCalling: true, - }, - { - id: "gemini-2.5-flash-lite", - name: "Gemini 2.5 Flash Lite", - contextLength: 1048576, - maxOutputTokens: 65535, + maxOutputTokens: 65536, + supportsReasoning: true, + supportsVision: true, toolCalling: true, }, // GPT-OSS @@ -161,6 +99,21 @@ export const AGY_PUBLIC_MODELS = Object.freeze([ const AGY_PUBLIC_MODEL_IDS = new Set(AGY_PUBLIC_MODELS.map((model) => model.id)); const AGY_NON_CHAT_MODEL_IDS = new Set(["tab_flash_lite_preview", "tab_jump_flash_lite_preview"]); +const AGY_RETIRED_MODEL_IDS = new Set([ + "gemini-3.6-flash-high", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-low", + "gemini-3-flash-agent", + "gemini-3.5-flash-extra-low", + "gemini-3.5-flash-low", + "gemini-3.5-flash-high", + "gemini-3.5-flash-medium", + "gemini-3.5-flash-preview", + "gemini-2.5-pro", + "gemini-2.5-flash-thinking", + "gemini-2.5-flash", + "gemini-2.5-flash-lite", +]); const AGY_CLIENT_VISIBLE_MODEL_NAMES = Object.freeze( AGY_PUBLIC_MODELS.reduce>((acc, model) => { @@ -178,5 +131,5 @@ export function isUserCallableAgyModelId(modelId: string): boolean { } export function isDiscoverableAgyModelId(modelId: string): boolean { - return !!modelId && !AGY_NON_CHAT_MODEL_IDS.has(modelId); + return !!modelId && !AGY_NON_CHAT_MODEL_IDS.has(modelId) && !AGY_RETIRED_MODEL_IDS.has(modelId); } diff --git a/open-sse/config/antigravityModelAliases.ts b/open-sse/config/antigravityModelAliases.ts index eeacbdf8e3..5eab3f4c8e 100644 --- a/open-sse/config/antigravityModelAliases.ts +++ b/open-sse/config/antigravityModelAliases.ts @@ -1,7 +1,7 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ - // Gemini 3.7 Flash tiers listed by the current official Antigravity model catalog - // alongside the existing Gemini 3.6 tiers. Keep the upstream model ids unchanged so - // discovery and execution address the same models selected by the native client. + // Gemini 3.7 Flash tiers listed by the current official Antigravity model catalog. + // Keep the upstream model ids unchanged so discovery and execution address the same + // models selected by the native client. { id: "gemini-3.7-flash-high", name: "Gemini 3.7 Flash (High)", @@ -20,51 +20,9 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ supportsVision: true, toolCalling: true, }, - // Gemini 3.6 Flash tiers retained alongside the newer Gemini 3.7 tiers. { - id: "gemini-3.6-flash-high", - name: "Gemini 3.6 Flash (High)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.6-flash-medium", - name: "Gemini 3.6 Flash (Medium)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.6-flash-low", - name: "Gemini 3.6 Flash (Low)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - // Claude (Antigravity backend). The `agy` provider already ships these from the live - // :fetchAvailableModels probe (see agyModels.ts) and discussion #3184 confirmed they - // are user-callable through the `antigravity` OAuth provider too — same backend. - // `antigravity/claude-opus-4-6-thinking` and `antigravity/claude-sonnet-4-6` both work. - // They are upstream IDs, so no alias remapping is required. - { - id: "claude-opus-4-6-thinking", - name: "Claude Opus 4.6 (Thinking)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6 (Thinking)", + id: "gemini-3.7-flash-low", + name: "Gemini 3.7 Flash (Low)", contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, @@ -92,50 +50,6 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ supportsVision: true, toolCalling: true, }, - // Gemini 3.5 Flash tiers exposed by Antigravity's model selector. Public ids match - // fetchAvailableModels and are forwarded upstream unchanged: - // High -> gemini-3-flash-agent (displayName: Gemini 3.5 Flash (High)) - // Medium -> gemini-3.5-flash-low (displayName: Gemini 3.5 Flash (Medium)) - // Low -> gemini-3.5-flash-extra-low (displayName: Gemini 3.5 Flash (Low)) - { - id: "gemini-3-flash-agent", - name: "Gemini 3.5 Flash (High)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.5-flash-low", - name: "Gemini 3.5 Flash (Medium)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - supportsVision: true, - toolCalling: true, - }, - { - id: "gemini-3.5-flash-extra-low", - name: "Gemini 3.5 Flash (Low)", - contextLength: 1048576, - maxOutputTokens: 65536, - supportsReasoning: true, - 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", @@ -143,25 +57,27 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([ maxOutputTokens: 65535, toolCalling: true, }, + // Claude (Antigravity backend). The `agy` provider already ships these from the live + // :fetchAvailableModels probe (see agyModels.ts) and discussion #3184 confirmed they + // are user-callable through the `antigravity` OAuth provider too — same backend. + // `antigravity/claude-opus-4-6-thinking` and `antigravity/claude-sonnet-4-6` both work. + // They are upstream IDs, so no alias remapping is required. { - id: "gemini-2.5-flash-thinking", - name: "Gemini 2.5 Flash Thinking", + id: "claude-opus-4-6-thinking", + name: "Claude Opus 4.6 (Thinking)", contextLength: 1048576, - maxOutputTokens: 65535, + maxOutputTokens: 65536, + supportsReasoning: true, + supportsVision: true, toolCalling: true, }, { - id: "gemini-2.5-flash", - name: "Gemini 2.5 Flash", + id: "claude-sonnet-4-6", + name: "Claude Sonnet 4.6 (Thinking)", contextLength: 1048576, - maxOutputTokens: 65535, - toolCalling: true, - }, - { - id: "gemini-2.5-flash-lite", - name: "Gemini 2.5 Flash Lite", - contextLength: 1048576, - maxOutputTokens: 65535, + maxOutputTokens: 65536, + supportsReasoning: true, + supportsVision: true, toolCalling: true, }, { @@ -175,12 +91,6 @@ 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). @@ -249,10 +159,19 @@ const ANTIGRAVITY_NON_CHAT_MODEL_IDS = new Set([ const ANTIGRAVITY_RETIRED_MODEL_IDS = new Set([ "gemini-3-pro-preview", "gemini-3.1-pro", + "gemini-3.6-flash-high", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-low", + "gemini-3-flash-agent", + "gemini-3.5-flash-extra-low", + "gemini-3.5-flash-low", "gemini-3.5-flash-high", "gemini-3.5-flash-medium", "gemini-3.5-flash-preview", "gemini-2.5-pro", + "gemini-2.5-flash-thinking", + "gemini-2.5-flash", + "gemini-2.5-flash-lite", "gemini-2.5-computer-use-preview-10-2025", ]); @@ -281,7 +200,12 @@ const ANTIGRAVITY_DROPPED_QUOTA_BUCKETS = new Set([ */ export function toClientAntigravityQuotaModelId(modelId: string): string | null { if (!modelId) return null; - if (ANTIGRAVITY_DROPPED_QUOTA_BUCKETS.has(modelId)) return null; + if ( + ANTIGRAVITY_DROPPED_QUOTA_BUCKETS.has(modelId) || + ANTIGRAVITY_RETIRED_MODEL_IDS.has(modelId) + ) { + return null; + } return toClientAntigravityModelId(modelId); } diff --git a/open-sse/config/freeModelCatalog.data.ts b/open-sse/config/freeModelCatalog.data.ts index 0ce4a04002..decef67c50 100644 --- a/open-sse/config/freeModelCatalog.data.ts +++ b/open-sse/config/freeModelCatalog.data.ts @@ -16,27 +16,20 @@ import type { FreeModelBudget } from "./freeModelCatalog.ts"; * rewrites file timestamps on every deploy, which would report a months-old * catalog as "updated today". Bump this whenever the entries below change. */ -export const FREE_CATALOG_CURATED_AT = "2026-07-22"; +export const FREE_CATALOG_CURATED_AT = "2026-08-16"; export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [ { provider: "agentrouter", modelId: "claude-opus-4-8", displayName: "Claude Opus 4.8", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" }, { provider: "agentrouter", modelId: "claude-opus-5", displayName: "Claude Opus 5", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" }, { provider: "agentrouter", modelId: "gpt-5.6-sol", displayName: "GPT-5.6 Sol", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" }, + { provider: "agy", modelId: "gemini-3.7-flash-high", displayName: "Gemini 3.7 Flash (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, + { provider: "agy", modelId: "gemini-3.7-flash-medium", displayName: "Gemini 3.7 Flash (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, + { provider: "agy", modelId: "gemini-3.7-flash-low", displayName: "Gemini 3.7 Flash (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.1-pro-low", displayName: "Gemini 3.1 Pro (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, + { provider: "agy", modelId: "gemini-3.1-flash-lite", displayName: "Gemini 3.1 Flash Lite", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "claude-opus-4-6-thinking", displayName: "Claude Opus 4.6 (Thinking)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { 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" }, - { provider: "agy", modelId: "gemini-3-flash-agent", displayName: "Gemini 3.5 Flash (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-3.5-flash-low", displayName: "Gemini 3.5 Flash (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-3.5-flash-extra-low", displayName: "Gemini 3.5 Flash (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-3.1-flash-lite", displayName: "Gemini 3.1 Flash Lite", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-2.5-flash", displayName: "Gemini 2.5 Flash", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-2.5-flash-thinking", displayName: "Gemini 2.5 Flash Thinking", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, - { provider: "agy", modelId: "gemini-2.5-flash-lite", displayName: "Gemini 2.5 Flash Lite", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "agy", modelId: "gpt-oss-120b-medium", displayName: "GPT-OSS 120B (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" }, { provider: "ai21", modelId: "jamba-large-1.7", displayName: "jamba-large-1.7", monthlyTokens: 0, creditTokens: 10000000, freeType: "one-time-initial", poolKey: "ai21", tos: "avoid" }, { provider: "ai21", modelId: "jamba-mini-2", displayName: "jamba-mini-2", monthlyTokens: 0, creditTokens: 10000000, freeType: "one-time-initial", poolKey: "ai21", tos: "avoid" }, @@ -188,7 +181,6 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [ { provider: "gemini", modelId: "gemini-2.5-flash-lite", displayName: "Gemini 2.5 Flash-Lite", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "gemini-free", tos: "caution" }, { provider: "gemini", modelId: "gemini-3-flash-preview", displayName: "Gemini 3 Flash Preview", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "gemini-free", tos: "caution" }, { provider: "gemini", modelId: "gemini-3.1-flash-lite", displayName: "Gemini 3.1 Flash-Lite", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "gemini-free", tos: "caution" }, - { provider: "gemini", modelId: "gemini-3.5-flash", displayName: "Gemini 3.5 Flash", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "gemini-free", tos: "caution" }, { provider: "glm-cn", modelId: "glm-4-flash", displayName: "GLM-4-Flash", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "zhipu-flash-free", tos: "ok" }, { provider: "glm-cn", modelId: "glm-4.5-flash", displayName: "GLM-4.5-Flash", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "zhipu-flash-free", tos: "ok" }, { provider: "glm-cn", modelId: "glm-4.7-flash", displayName: "GLM-4.7-Flash", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "zhipu-flash-free", tos: "ok" }, @@ -369,7 +361,7 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [ { provider: "qoder", modelId: "deepseek-v4-pro", displayName: "DeepSeek-V4-Pro", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" }, { provider: "qoder", modelId: "deepseek-v4-flash", displayName: "DeepSeek-V4-Flash", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" }, { provider: "qoder", modelId: "minimax-m3", displayName: "MiniMax-M3", monthlyTokens: 0, creditTokens: 1000000, freeType: "one-time-initial", poolKey: "qoder", tos: "caution" }, - { provider: "qwen-web", modelId: "qwen3.8-max", displayName: "Qwen3.8 Max", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" }, + { provider: "qwen-web", modelId: "qwen3.8-max-preview", displayName: "Qwen3.8 Max Preview", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" }, { provider: "qwen-web", modelId: "qwen3.7-max", displayName: "Qwen3.7 Max", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" }, { provider: "qwen-web", modelId: "qwen3.7-plus", displayName: "Qwen3.7 Plus", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" }, { provider: "qwen-web", modelId: "qwen3.6-plus", displayName: "Qwen3.6 Plus", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "qwen-web", tos: "avoid" }, diff --git a/open-sse/config/geminiRateLimits.json b/open-sse/config/geminiRateLimits.json index c33f580345..9a159073a4 100644 --- a/open-sse/config/geminiRateLimits.json +++ b/open-sse/config/geminiRateLimits.json @@ -5,9 +5,6 @@ "gemini-2-flash-lite": { "rpm": 0, "rpd": 0, "tpm": 0 }, "gemini-2.5-flash-tts": { "rpm": 3, "rpd": 10, "tpm": 10000 }, "gemini-2.5-pro-tts": { "rpm": 0, "rpd": 0, "tpm": 0 }, - "imagen-4-generate": { "rpm": -1, "rpd": 25, "tpm": -1 }, - "imagen-4-ultra-generate": { "rpm": -1, "rpd": 25, "tpm": -1 }, - "imagen-4-fast-generate": { "rpm": -1, "rpd": 25, "tpm": -1 }, "gemma-4-26b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 }, "gemma-4-31b-it": { "rpm": 16000, "rpd": 14400, "tpm": 16000 }, "gemini-embedding-exp-03-07": { "rpm": 100, "rpd": 1000, "tpm": 30000 }, diff --git a/open-sse/config/imageRegistry.ts b/open-sse/config/imageRegistry.ts index 3e24139e85..d50865599d 100644 --- a/open-sse/config/imageRegistry.ts +++ b/open-sse/config/imageRegistry.ts @@ -10,7 +10,6 @@ import { SEGMIND_IMAGE_PROVIDER } from "./providers/registry/segmind/imageModels import { KIE_IMAGE_MODELS } from "./providers/registry/kie/imageModels.ts"; import { FREEPIK_IMAGE_PROVIDER } from "./providers/registry/freepik/index.ts"; import { STABILITY_AI_IMAGE_MODELS } from "./providers/registry/stability-ai/imageModels.ts"; -import { GEMINI_IMAGEN_PROVIDER } from "./providers/registry/gemini/imageModels.ts"; import { CHEAPERINFERENCE_IMAGE_PROVIDER } from "./providers/registry/cheaperinference/imageModels.ts"; import { ADOBE_FIREFLY_IMAGE_ROUTING_ALIASES, @@ -378,10 +377,6 @@ export const IMAGE_PROVIDERS: Record = { supportedSizes: ["1024x1024"], }, - // Google AI Studio Imagen family — dedicated :predict endpoint, not generateContent. - // See providers/registry/gemini/imageModels.ts for the full rationale. - gemini: GEMINI_IMAGEN_PROVIDER, - //Curruntly no models serving nebius: { id: "nebius", diff --git a/open-sse/config/providers/registry/cheaperinference/index.ts b/open-sse/config/providers/registry/cheaperinference/index.ts index 7bab005d36..750b52f01b 100644 --- a/open-sse/config/providers/registry/cheaperinference/index.ts +++ b/open-sse/config/providers/registry/cheaperinference/index.ts @@ -107,7 +107,12 @@ export const CHEAPERINFERENCE_MODELS: RegistryModel[] = [ supportsReasoning: true, toolCalling: true, }, - { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", supportsReasoning: true, toolCalling: true }, + { + id: "deepseek-v4-flash", + name: "DeepSeek V4 Flash", + supportsReasoning: true, + toolCalling: true, + }, { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", supportsReasoning: true, toolCalling: true }, { id: "gemini-2.5-flash", @@ -117,8 +122,8 @@ export const CHEAPERINFERENCE_MODELS: RegistryModel[] = [ toolCalling: true, }, { - id: "gemini-3-5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", supportsVision: true, supportsReasoning: true, toolCalling: true, diff --git a/open-sse/config/providers/registry/cursor/index.ts b/open-sse/config/providers/registry/cursor/index.ts index ee9054cd0d..0e167d39bb 100644 --- a/open-sse/config/providers/registry/cursor/index.ts +++ b/open-sse/config/providers/registry/cursor/index.ts @@ -140,22 +140,19 @@ export const cursorProvider: RegistryEntry = { { id: "claude-4.6-sonnet-medium", name: "Claude 4.6 Sonnet Medium" }, { id: "claude-4.6-sonnet-medium-thinking", name: "Claude 4.6 Sonnet Medium Thinking" }, // - { id: "claude-4.5-sonnet", name: "Claude 4.5 Sonnet" }, - { id: "claude-4.5-sonnet-thinking", name: "Claude 4.5 Sonnet Thinking" }, - // { id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" }, // + { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash" }, { id: "gemini-3-flash", name: "Gemini 3 Flash" }, // - { id: "grok-4.3", name: "Grok 4.3" }, + { id: "grok-4.6-medium", name: "Grok 4.6 Medium" }, + { id: "grok-4.6-fast-medium", name: "Grok 4.6 Fast Medium" }, + { id: "grok-4.6-high", name: "Grok 4.6 High" }, + { id: "grok-4.6-fast-high", name: "Grok 4.6 Fast High" }, + { id: "grok-4.6-xhigh", name: "Grok 4.6 XHigh" }, + { id: "grok-4.6-fast-xhigh", name: "Grok 4.6 Fast XHigh" }, // - { id: "grok-4.5-medium", name: "Grok 4.5 Medium" }, - { id: "grok-4.5-fast-medium", name: "Grok 4.5 Fast Medium" }, - { id: "grok-4.5-high", name: "Grok 4.5 High" }, - { id: "grok-4.5-fast-high", name: "Grok 4.5 Fast High" }, - { id: "grok-4.5-xhigh", name: "Grok 4.5 XHigh" }, - { id: "grok-4.5-fast-xhigh", name: "Grok 4.5 Fast XHigh" }, - // - { id: "kimi-k2.5", name: "Kimi K2.5" }, + { id: "kimi-k3", name: "Kimi K3" }, + { id: "kimi-k2.7-code", name: "Kimi K2.7 Code" }, ], }; diff --git a/open-sse/config/providers/registry/devin/catalog.ts b/open-sse/config/providers/registry/devin/catalog.ts index 8195d311cf..d1e5c75894 100644 --- a/open-sse/config/providers/registry/devin/catalog.ts +++ b/open-sse/config/providers/registry/devin/catalog.ts @@ -84,14 +84,10 @@ export const DEVIN_MODEL_CATALOG: RegistryModel[] = [ // Gemini { id: "gemini-3-1-pro-high", name: "Gemini 3.1 Pro High", contextLength: 1048576 }, { id: "gemini-3-1-pro-low", name: "Gemini 3.1 Pro Low", contextLength: 1048576 }, - { id: "gemini-3-6-flash-high", name: "Gemini 3.6 Flash High" }, - { id: "gemini-3-6-flash-medium", name: "Gemini 3.6 Flash Medium" }, - { id: "gemini-3-6-flash-low", name: "Gemini 3.6 Flash Low" }, - { id: "gemini-3-6-flash-minimal", name: "Gemini 3.6 Flash Minimal" }, - { id: "gemini-3-5-flash-high", name: "Gemini 3.5 Flash High", contextLength: 1048576 }, - { id: "gemini-3-5-flash-medium", name: "Gemini 3.5 Flash Medium", contextLength: 1048576 }, - { id: "gemini-3-5-flash-low", name: "Gemini 3.5 Flash Low", contextLength: 1048576 }, - { id: "gemini-3-5-flash-minimal", name: "Gemini 3.5 Flash Minimal", contextLength: 1048576 }, + { id: "gemini-3-7-flash-high", name: "Gemini 3.7 Flash High" }, + { id: "gemini-3-7-flash-medium", name: "Gemini 3.7 Flash Medium" }, + { id: "gemini-3-7-flash-low", name: "Gemini 3.7 Flash Low" }, + { id: "gemini-3-7-flash-minimal", name: "Gemini 3.7 Flash Minimal" }, // Grok { id: "grok-4-5-high", name: "Grok 4.5 High", contextLength: 500000 }, { id: "grok-4-5-medium", name: "Grok 4.5 Medium", contextLength: 500000 }, diff --git a/open-sse/config/providers/registry/gemini/imageModels.ts b/open-sse/config/providers/registry/gemini/imageModels.ts deleted file mode 100644 index bc7002da01..0000000000 --- a/open-sse/config/providers/registry/gemini/imageModels.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Google AI Studio (Gemini API) Imagen family image-generation provider entry. - * - * Uses the dedicated `:predict` endpoint (handled by format "google-imagen"), NOT - * generateContent — so only imagen-* models belong here; gemini flash-image / - * nano-banana route through /v1/chat/completions instead. The models are also - * surfaced live via ListModels; this seed makes them addressable on - * /v1/images/generations. Note: Imagen requires a billing-enabled Google project — - * free-tier keys get 403 / quota 0. The handler builds `{baseUrl}/{model}:predict`. - * - * Extracted out of imageRegistry.ts (which sits right at the 800-line file-size - * cap) so the catalog lives in its own semantic family module, following the same - * pattern as `providers/registry/stability-ai/imageModels.ts` and - * `providers/registry/segmind/imageModels.ts`. Co-located with the existing - * `gemini/index.ts` chat-provider entry — same provider id, different - * modality/consumer (chat registry vs image registry), mirroring the - * `kie/index.ts` + `kie/imageModels.ts` split. - */ -export const GEMINI_IMAGEN_PROVIDER = { - id: "gemini", - alias: "gemini", - baseUrl: "https://generativelanguage.googleapis.com/v1beta/models", - authType: "apikey", - authHeader: "x-goog-api-key", - format: "google-imagen", - models: [ - { id: "imagen-4.0-generate-001", name: "Imagen 4" }, - { id: "imagen-4.0-ultra-generate-001", name: "Imagen 4 Ultra" }, - { id: "imagen-4.0-fast-generate-001", name: "Imagen 4 Fast" }, - ], - supportedSizes: ["1024x1024", "1792x1024", "1024x1792"], -}; diff --git a/open-sse/config/providers/registry/gemini/index.ts b/open-sse/config/providers/registry/gemini/index.ts index 9f6dbbb70a..8119ed9c41 100644 --- a/open-sse/config/providers/registry/gemini/index.ts +++ b/open-sse/config/providers/registry/gemini/index.ts @@ -22,14 +22,14 @@ export const geminiProvider: RegistryEntry = { }, models: [ { - id: "gemini-3.1-pro-preview", - name: "Gemini 3.1 Pro Preview", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", toolCalling: true, supportsVision: true, }, { - id: "gemini-3-flash-preview", - name: "Gemini 3 Flash Preview", + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro Preview", toolCalling: true, supportsVision: true, }, @@ -40,8 +40,8 @@ export const geminiProvider: RegistryEntry = { supportsVision: true, }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", toolCalling: true, supportsVision: true, }, diff --git a/open-sse/config/providers/registry/gemini/web/index.ts b/open-sse/config/providers/registry/gemini/web/index.ts index 276cfaf589..0e9373784d 100644 --- a/open-sse/config/providers/registry/gemini/web/index.ts +++ b/open-sse/config/providers/registry/gemini/web/index.ts @@ -24,8 +24,8 @@ export const gemini_webProvider: RegistryEntry = { supportsReasoning: false, }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", toolCalling: false, supportsReasoning: false, }, diff --git a/open-sse/config/providers/registry/ghe-copilot/index.ts b/open-sse/config/providers/registry/ghe-copilot/index.ts index 483aefa369..f494830414 100644 --- a/open-sse/config/providers/registry/ghe-copilot/index.ts +++ b/open-sse/config/providers/registry/ghe-copilot/index.ts @@ -97,14 +97,29 @@ export const gheCopilotProvider: RegistryEntry = { maxOutputTokens: 64000, }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", contextLength: 1000000, maxOutputTokens: 64000, }, - { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", targetFormat: "openai-responses", maxOutputTokens: 128000 }, - { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", targetFormat: "openai-responses", maxOutputTokens: 128000 }, - { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", targetFormat: "openai-responses", maxOutputTokens: 128000 }, + { + id: "gpt-5.6-sol", + name: "GPT-5.6 Sol", + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-terra", + name: "GPT-5.6 Terra", + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-luna", + name: "GPT-5.6 Luna", + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, { id: "gpt-5.5", name: "GPT-5.5", ...GPT_5_5_CODEX_CAPABILITIES, maxOutputTokens: 128000 }, { id: "gpt-5.4", diff --git a/open-sse/config/providers/registry/github/index.ts b/open-sse/config/providers/registry/github/index.ts index 3adda64763..d99fd1520c 100644 --- a/open-sse/config/providers/registry/github/index.ts +++ b/open-sse/config/providers/registry/github/index.ts @@ -117,8 +117,8 @@ export const githubProvider: RegistryEntry = { maxOutputTokens: 64000, }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", contextLength: 1000000, maxOutputTokens: 64000, }, diff --git a/open-sse/config/providers/registry/kie/imageModels.ts b/open-sse/config/providers/registry/kie/imageModels.ts index 5fbcd7b18d..249afd9dac 100644 --- a/open-sse/config/providers/registry/kie/imageModels.ts +++ b/open-sse/config/providers/registry/kie/imageModels.ts @@ -23,9 +23,6 @@ export const KIE_IMAGE_MODELS: KieImageModelEntry[] = [ { id: "seedream/5.0-lite-image-to-image", name: "Seedream 5.0 Lite I2I", isMarket: true }, { id: "z-image/4.0-text-to-image", name: "Z-Image v4.0", isMarket: true }, { id: "z-image/4.5-text-to-image", name: "Z-Image v4.5", isMarket: true }, - { id: "google-imagen/imagen4-fast", name: "Imagen 4 Fast", isMarket: true }, - { id: "google-imagen/imagen4-ultra", name: "Imagen 4 Ultra", isMarket: true }, - { id: "google-imagen/imagen4", name: "Imagen 4", isMarket: true }, { id: "google-imagen/nano-banana-2", name: "Nano Banana 2", isMarket: true }, { id: "google-imagen/nano-banana", name: "Nano Banana", isMarket: true }, { id: "google-imagen/nano-banana-pro", name: "Nano Banana Pro", isMarket: true }, diff --git a/open-sse/config/providers/registry/kie/index.ts b/open-sse/config/providers/registry/kie/index.ts index a87758561d..f404cb19df 100644 --- a/open-sse/config/providers/registry/kie/index.ts +++ b/open-sse/config/providers/registry/kie/index.ts @@ -12,16 +12,15 @@ export const kieProvider: RegistryEntry = { models: [ // Sweep 2026-06-19: + current flagships the kie proxy surfaces. gemini-3-pro was // skipped (registry already carries the newer gemini-3-1-pro). - { id: "claude-opus-4-8", name: "Claude 4.8 Opus" }, - { id: "claude-opus-4-7", name: "Claude 4.7 Opus" }, - { id: "claude-sonnet-4-6", name: "Claude 4.6 Sonnet" }, + { id: "claude-fable-5", name: "Claude 5 Fable" }, + { id: "claude-opus-5", name: "Claude 5 Opus" }, + { id: "claude-sonnet-5", name: "Claude 5 Sonnet" }, { id: "claude-haiku-4-5", name: "Claude 4.5 Haiku" }, - { id: "gpt-5-5", name: "GPT 5.5" }, - { id: "gpt-5-4", name: "GPT 5.4" }, - { id: "gpt-5-2", name: "GPT 5.2" }, + { id: "gpt-5-6-sol", name: "GPT 5.6 Sol" }, + { id: "gpt-5-6-terra", name: "GPT 5.6 Terra" }, + { id: "gpt-5-6-luna", name: "GPT 5.6 Luna" }, { id: "gemini-3-1-pro", name: "Gemini 3.1 Pro" }, - { id: "gemini-2-5-pro", name: "Gemini 2.5 Pro" }, - { id: "gemini-3-flash", name: "Gemini 3 Flash" }, - { id: "gemini-3-5-flash", name: "Gemini 3.5 Flash" }, + { id: "gemini-3-7-flash", name: "Gemini 3.7 Flash" }, + { id: "grok-4-6", name: "Grok 4.6" }, ], }; diff --git a/open-sse/config/providers/registry/kie/models.ts b/open-sse/config/providers/registry/kie/models.ts index 78cf81658b..5ec815aaf4 100644 --- a/open-sse/config/providers/registry/kie/models.ts +++ b/open-sse/config/providers/registry/kie/models.ts @@ -11,9 +11,6 @@ export const KIE_IMAGE_MODELS = [ { id: "seedream/5.0-lite-image-to-image", name: "Seedream 5.0 Lite I2I", isMarket: true }, { id: "z-image/4.0-text-to-image", name: "Z-Image v4.0", isMarket: true }, { id: "z-image/4.5-text-to-image", name: "Z-Image v4.5", isMarket: true }, - { id: "google-imagen/imagen4-fast", name: "Imagen 4 Fast", isMarket: true }, - { id: "google-imagen/imagen4-ultra", name: "Imagen 4 Ultra", isMarket: true }, - { id: "google-imagen/imagen4", name: "Imagen 4", isMarket: true }, { id: "google-imagen/nano-banana-2", name: "Nano Banana 2", isMarket: true }, { id: "google-imagen/nano-banana", name: "Nano Banana", isMarket: true }, { id: "google-imagen/nano-banana-pro", name: "Nano Banana Pro", isMarket: true }, diff --git a/open-sse/config/providers/registry/kilocode/index.ts b/open-sse/config/providers/registry/kilocode/index.ts index 9d42fa5855..567d9f1ce4 100644 --- a/open-sse/config/providers/registry/kilocode/index.ts +++ b/open-sse/config/providers/registry/kilocode/index.ts @@ -25,19 +25,20 @@ export const kilocodeProvider: RegistryEntry = { }, models: [ { id: "openrouter/free", name: "Free Models Router" }, - { id: "qwen/qwen3.6-plus", name: "Qwen3.6 Plus" }, - { id: "qwen/qwen3.5-397b-a17b", name: "Qwen3.5 397B A17B" }, - { id: "openai/gpt-5.5", name: "GPT-5.5" }, - { id: "openai/gpt-5.4-mini", name: "GPT-5.4 Mini" }, - { id: "anthropic/claude-opus-4.7", name: "Claude Opus 4.7" }, - { id: "anthropic/claude-sonnet-4.6", name: "Claude Sonnet 4.6" }, + { id: "openai/gpt-5.6-sol", name: "GPT-5.6 Sol" }, + { id: "openai/gpt-5.6-terra", name: "GPT-5.6 Terra" }, + { id: "openai/gpt-5.6-luna", name: "GPT-5.6 Luna" }, + { id: "anthropic/claude-opus-5", name: "Claude Opus 5" }, + { id: "anthropic/claude-sonnet-5", name: "Claude Sonnet 5" }, { id: "anthropic/claude-haiku-4.5", name: "Claude Haiku 4.5" }, { id: "google/gemini-3.1-pro-preview", name: "Gemini 3.1 Pro" }, - { id: "google/gemini-3-flash-preview", name: "Gemini 3 Flash" }, - { id: "google/gemini-3.1-flash-lite", name: "Gemini 3.1 Flash Lite" }, - { id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro", supportsReasoning: true }, - { id: "deepseek/deepseek-v4-flash", name: "DeepSeek V4 Flash", supportsReasoning: true }, - { id: "moonshotai/kimi-k2.6", name: "Kimi K2.6" }, + { id: "google/gemini-3.7-flash", name: "Gemini 3.7 Flash" }, + { id: "google/gemini-3.5-flash-lite", name: "Gemini 3.5 Flash Lite" }, + { id: "qwen/qwen3.8-max", name: "Qwen3.8 Max" }, + { id: "qwen/qwen3.7-plus", name: "Qwen3.7 Plus" }, + { id: "deepseek/deepseek-v4-pro-0813", name: "DeepSeek V4 Pro", supportsReasoning: true }, + { id: "deepseek/deepseek-v4-flash-0731", name: "DeepSeek V4 Flash", supportsReasoning: true }, + { id: "moonshotai/kimi-k3", name: "Kimi K3" }, ], passthroughModels: true, }; diff --git a/open-sse/config/providers/registry/lmarena/directModels.ts b/open-sse/config/providers/registry/lmarena/directModels.ts index 3e75bc7597..639cd33a2b 100644 --- a/open-sse/config/providers/registry/lmarena/directModels.ts +++ b/open-sse/config/providers/registry/lmarena/directModels.ts @@ -90,10 +90,10 @@ export const LMARENA_DIRECT_MODEL_ENTRIES: readonly LmarenaDirectModelEntry[] = category: "Text", }, { - catalogId: "gemini-3.5-flash-high", - arenaId: "019f406f-fc33-7b9d-9571-7b8443bc7ca0", - publicName: "gemini-3.5-flash-high", - displayName: "gemini-3.5-flash-high", + catalogId: "gemini-3.6-flash", + arenaId: "019f90b1-c0ac-71ce-b295-487f261bf0f4", + publicName: "gemini-3.6-flash", + displayName: "gemini-3.6-flash", organization: "google", vision: true, category: "Text", diff --git a/open-sse/config/providers/registry/opencode/zen/index.ts b/open-sse/config/providers/registry/opencode/zen/index.ts index a241b043ce..73da9c5dc5 100644 --- a/open-sse/config/providers/registry/opencode/zen/index.ts +++ b/open-sse/config/providers/registry/opencode/zen/index.ts @@ -25,55 +25,48 @@ export const opencode_zenProvider: RegistryEntry = { supportsReasoning: true, interleavedField: "reasoning_content", }, - { id: "gpt-5-nano", name: "GPT 5 Nano", contextLength: 400000 }, - { id: "gpt-5", name: "GPT 5" }, - { id: "gpt-5-codex", name: "GPT 5 Codex" }, - { id: "gpt-5.1", name: "GPT 5.1" }, - { id: "gpt-5.1-codex", name: "GPT 5.1 Codex" }, - { id: "gpt-5.1-codex-max", name: "GPT 5.1 Codex Max" }, - { id: "gpt-5.1-codex-mini", name: "GPT 5.1 Codex Mini" }, - { id: "gpt-5.2", name: "GPT 5.2" }, - { id: "gpt-5.2-codex", name: "GPT 5.2 Codex" }, - { id: "gpt-5.3-codex", name: "GPT 5.3 Codex" }, - { id: "gpt-5.3-codex-spark", name: "GPT 5.3 Codex Spark" }, + { id: "gpt-5.6-sol", name: "GPT 5.6 Sol" }, + { id: "gpt-5.6-terra", name: "GPT 5.6 Terra" }, + { id: "gpt-5.6-luna", name: "GPT 5.6 Luna" }, { id: "gpt-5.4", name: "GPT 5.4" }, { id: "gpt-5.4-mini", name: "GPT 5.4 Mini" }, { id: "gpt-5.4-nano", name: "GPT 5.4 Nano" }, - { id: "gpt-5.4-pro", name: "GPT 5.4 Pro" }, - { id: "gpt-5.5", name: "GPT 5.5" }, - { id: "gpt-5.5-pro", name: "GPT 5.5 Pro" }, + { id: "gpt-5.3-codex-spark", name: "GPT 5.3 Codex Spark" }, + { id: "gpt-5.1", name: "GPT 5.1" }, // ── Claude ───────────────────────────────────────────────── + { id: "claude-fable-5", name: "Claude Fable 5" }, + { id: "claude-opus-5", name: "Claude Opus 5" }, + { id: "claude-sonnet-5", name: "Claude Sonnet 5" }, { id: "claude-haiku-4-5", name: "Claude Haiku 4.5" }, - { id: "claude-sonnet-4", name: "Claude Sonnet 4" }, - { id: "claude-sonnet-4-5", name: "Claude Sonnet 4.5" }, - { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, - { id: "claude-opus-4-1", name: "Claude Opus 4.1" }, - { id: "claude-opus-4-5", name: "Claude Opus 4.5" }, - { id: "claude-opus-4-6", name: "Claude Opus 4.6" }, - { id: "claude-opus-4-7", name: "Claude Opus 4.7" }, // ── Gemini ───────────────────────────────────────────────── - { id: "gemini-3-flash", name: "Gemini 3 Flash" }, + { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash" }, + { id: "gemini-3.5-flash-lite", name: "Gemini 3.5 Flash Lite" }, { id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" }, - { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" }, + { id: "gemini-3-flash", name: "Gemini 3 Flash" }, // ── Grok ─────────────────────────────────────────────────── { id: "grok-build-0.1", name: "Grok Build 0.1" }, + { id: "grok-4.6", name: "Grok 4.6" }, + + // ── Muse ─────────────────────────────────────────────────── + { id: "muse-spark-1.2", name: "Muse Spark 1.2" }, + + // ── DeepSeek ──────────────────────────────────────────────── + { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" }, + { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" }, // ── GLM / Z.AI ───────────────────────────────────────────── - { id: "glm-5", name: "GLM-5" }, - { id: "glm-5.1", name: "GLM-5.1" }, + { id: "glm-5.2", name: "GLM-5.2" }, // ── MiniMax ──────────────────────────────────────────────── // #3110: MiniMax M3 — frontier coding model with 1M context { id: "minimax-m3", name: "MiniMax M3", contextLength: 1048576, supportsVision: true }, - { id: "minimax-m2.5", name: "MiniMax M2.5" }, - { id: "minimax-m2.7", name: "MiniMax M2.7" }, // ── Kimi / Moonshot ──────────────────────────────────────── - { id: "kimi-k2.5", name: "Kimi K2.5" }, - { id: "kimi-k2.6", name: "Kimi K2.6" }, + { id: "kimi-k3", name: "Kimi K3" }, + { id: "kimi-k2.7-code", name: "Kimi K2.7 Code" }, // ── Qwen ─────────────────────────────────────────────────── // Issue #2292: Qwen models return Claude-format SSE bodies even @@ -86,12 +79,14 @@ export const opencode_zenProvider: RegistryEntry = { // ── Free Tier ────────────────────────────────────────────── // #6998 (2026-07-14): upstream free tier rotated — minimax-m2.5-free, - // nemotron-3-super-free and qwen3.6-plus-free were delisted (401). Replaced - // by the 4 entries below with upstream-verified limits. + // nemotron-3-super-free and qwen3.6-plus-free were delisted (401). + // 2026-08-17 sync: north-mini-code-free delisted; nemotron-3.5-lightning-free + // and laguna-s-2.1-free added. { id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true }, { id: "mimo-v2.5-free", name: "MiMo V2.5 Free", contextLength: 200000 }, { id: "hy3-free", name: "HY3 Free", contextLength: 200000 }, { id: "nemotron-3-ultra-free", name: "Nemotron 3 Ultra Free", contextLength: 1000000 }, - { id: "north-mini-code-free", name: "North Mini Code Free", contextLength: 200000 }, + { id: "nemotron-3.5-lightning-free", name: "Nemotron 3.5 Lightning Free" }, + { id: "laguna-s-2.1-free", name: "Laguna S 2.1 Free" }, ], }; diff --git a/open-sse/config/providers/registry/orcarouter/index.ts b/open-sse/config/providers/registry/orcarouter/index.ts index c68a7104b5..d8d7d5bb59 100644 --- a/open-sse/config/providers/registry/orcarouter/index.ts +++ b/open-sse/config/providers/registry/orcarouter/index.ts @@ -35,8 +35,8 @@ export const orcarouterProvider: RegistryEntry = { maxOutputTokens: 128000, }, { - id: "google/gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "google/gemini-3.6-flash", + name: "Gemini 3.6 Flash", toolCalling: true, supportsReasoning: true, supportsVision: true, diff --git a/open-sse/config/providers/registry/perplexity/web/index.ts b/open-sse/config/providers/registry/perplexity/web/index.ts index 71a67bb22d..8fb03ac05b 100644 --- a/open-sse/config/providers/registry/perplexity/web/index.ts +++ b/open-sse/config/providers/registry/perplexity/web/index.ts @@ -13,12 +13,12 @@ export const perplexity_webProvider: RegistryEntry = { { id: "pplx-sonar", name: "Sonar 2 (via Perplexity)", toolCalling: false }, { id: "pplx-gpt-5.6-terra", name: "GPT-5.6 Terra (via Perplexity)", toolCalling: false }, { id: "pplx-gpt-5.6-sol", name: "GPT-5.6 Sol (via Perplexity)", toolCalling: false }, - { id: "pplx-gemini", name: "Gemini 3.1 Pro (via Perplexity)", toolCalling: false }, + { id: "pplx-gemini", name: "Gemini 3.7 Flash (via Perplexity)", toolCalling: false }, { id: "pplx-sonnet", name: "Claude Sonnet 5.0 (via Perplexity)", toolCalling: false }, { id: "pplx-opus", name: "Claude Opus 5.0 (via Perplexity)", toolCalling: false }, { id: "pplx-glm", name: "GLM-5.2 (via Perplexity)", toolCalling: false }, - { id: "pplx-kimi", name: "Kimi K2.6 (via Perplexity)", toolCalling: false }, - { id: "pplx-grok-4.5", name: "Grok 4.5 (via Perplexity)", toolCalling: false }, + { id: "pplx-kimi", name: "Kimi K3 (via Perplexity)", toolCalling: false }, + { id: "pplx-grok-4.6", name: "Grok 4.6 (via Perplexity)", toolCalling: false }, { id: "pplx-nemotron", name: "Nemotron 3 Ultra (via Perplexity)", toolCalling: false }, ], }; diff --git a/open-sse/config/providers/registry/raycast/index.ts b/open-sse/config/providers/registry/raycast/index.ts index cd55a343ff..aca286a6d1 100644 --- a/open-sse/config/providers/registry/raycast/index.ts +++ b/open-sse/config/providers/registry/raycast/index.ts @@ -19,15 +19,43 @@ export const raycastProvider: RegistryEntry = { authHeader: "bearer", defaultContextLength: 128000, models: [ - { id: "openai-gpt-5-mini", name: "GPT-5 Mini" }, - { id: "openai-gpt-4o-mini", name: "GPT-4o Mini" }, - { id: "anthropic-claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, - { id: "google-gemini-2.5-flash", name: "Gemini 2.5 Flash" }, - { id: "raycast-ray1", name: "Ray1" }, - { id: "raycast-ray1-mini", name: "Ray1 Mini" }, - { id: "perplexity-sonar", name: "Sonar" }, + //GPT + { id: "openai-gpt-5.6-sol", name: "GPT-5.6 Sol" }, + { id: "openai-gpt-5.6-terra", name: "GPT-5.6 Terra" }, + { id: "openai-gpt-5.6-luna", name: "GPT-5.6 Luna" }, + //Claude + { id: "anthropic-claude-opus-5", name: "Claude Opus 5" }, + { id: "anthropic-claude-sonnet-5", name: "Claude Sonnet 5" }, + { id: "anthropic-claude-4-5-haiku-reasoning", name: "Claude 4.5 Haiku Reasoning" }, + { id: "anthropic-claude-4-5-haiku", name: "Claude 4.5 Haiku" }, + //Gemini + { id: "google-gemini-3.1-pro", name: "Gemini 3.1 Pro" }, + { id: "google-gemini-3.7-flash", name: "Gemini 3.7 Flash" }, + { id: "google-gemini-3.5-flash-lite", name: "Gemini 3.5 Flash Lite" }, + //Perplexity + { id: "perplexity-sonar-reasoning-pro", name: "Sonar Reasoning Pro" }, { id: "perplexity-sonar-pro", name: "Sonar Pro" }, + { id: "perplexity-sonar", name: "Sonar" }, + //Mistral + { id: "mistral-mistral-large-latest", name: "Mistral Large" }, + { id: "mistral-mistral-medium-latest", name: "Mistral Medium" }, + { id: "mistral-mistral-small-latest", name: "Mistral Small" }, + { id: "mistral-codestral-latest", name: "Codestral" }, { id: "mistral-open-mistral-nemo", name: "Mistral Nemo" }, - { id: "xai-grok-3-mini", name: "Grok 3 Mini" }, + //Grok + { id: "xai-grok-4.6", name: "Grok 4.6" }, + //Opensource + { id: "gateway-alibaba/qwen3.8-max", name: "Qwen 3.8 Max" }, + { id: "gateway-moonshotai/kimi-k3", name: "Kimi K3" }, + { id: "baseten-deepseek-ai/DeepSeek-V4-Pro", name: "DeepSeek V4 Pro" }, + { id: "gateway-deepseek/deepseek-v4-flash", name: "DeepSeek V4 Flash" }, + { id: "baseten-zai-org/GLM-5.2", name: "GLM 5.2" }, + { id: "gateway-thinkingmachines/inkling-1.0", name: "Inkling 1.0" }, + { id: "gateway-google/gemma-4-31b-it", name: "Gemma 4 31B" }, + { id: "groq-openai/gpt-oss-120b", name: "GPT-OSS 120B" }, + { id: "groq-openai/gpt-oss-20b", name: "GPT-OSS 20B" }, + { id: "groq-qwen/qwen3-32b", name: "Qwen 3 32B" }, + { id: "groq-llama-3.3-70b-versatile", name: "LLaMA 3.3 70B" }, + { id: "groq-llama-3.1-8b-instant", name: "LLaMA 3.1 8B" }, ], }; diff --git a/open-sse/config/providers/registry/tinycms/index.ts b/open-sse/config/providers/registry/tinycms/index.ts index b124b3da07..9fdbc71725 100644 --- a/open-sse/config/providers/registry/tinycms/index.ts +++ b/open-sse/config/providers/registry/tinycms/index.ts @@ -18,22 +18,28 @@ export const tinycmsProvider: RegistryEntry = { authType: "apikey", authHeader: "uuid", models: [ - { id: "gpt-5-free", name: "GPT 5 Free" }, - { id: "gpt-5.3-free", name: "GPT 5.3 Free (Multimodal/Vision)" }, - { id: "gpt-5.3-thinking-free", name: "GPT 5.3 Thinking Free", supportsReasoning: true }, + { id: "claude-fable-5", name: "Claude Fable 5" }, + { id: "claude-opus-5", name: "Claude Opus 5" }, + { id: "claude-sonnet-5", name: "Claude Sonnet 5" }, + { id: "gpt-5.6-sol", name: "GPT 5.6 Sol" }, + { id: "gpt-5.6-luna", name: "GPT 5.6 Luna" }, + { id: "gpt-5.5", name: "GPT 5.5" }, { id: "gpt-5.4-mini", name: "GPT 5.4 Mini" }, { id: "gpt-5.4-nano", name: "GPT 5.4 Nano" }, - { id: "gpt-5-nano", name: "GPT 5 Nano" }, - { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" }, - { id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" }, - { id: "gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite Preview" }, - { id: "grok-4.20-fast", name: "Grok 4.20 Fast" }, - { id: "grok-4.20", name: "Grok 4.20" }, - { id: "grok-imagine", name: "Grok Imagine (Image Gen)" }, + { id: "gpt-5.3-thinking-free", name: "GPT 5.3 Thinking Free", supportsReasoning: true }, + { id: "gpt-5.3-free", name: "GPT 5.3 Free (Multimodal/Vision)" }, + { id: "gpt-oss-120b", name: "GPT-OSS 120B" }, + { id: "gemini-3.6-flash", name: "Gemini 3.6 Flash" }, + { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro" }, + { id: "gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite" }, + { id: "grok-4.5", name: "Grok 4.5" }, + { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" }, { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" }, - { id: "claude-sonnet-5", name: "Claude Sonnet 5" }, - { id: "gpt-image-2", name: "GPT Image 2 (Image Gen)" }, + { id: "kimi-k3", name: "Kimi K3" }, + { id: "glm-5.2", name: "GLM 5.2" }, { id: "qwen3.6-plus", name: "Qwen 3.6 Plus" }, + { id: "mimo-v2.5-pro", name: "Mimo V2.5 Pro" }, + { id: "mimo-v2.5", name: "Mimo V2.5" }, ], }; diff --git a/open-sse/executors/antigravityOutputCap.ts b/open-sse/executors/antigravityOutputCap.ts index c3d173cd92..d5e82908cc 100644 --- a/open-sse/executors/antigravityOutputCap.ts +++ b/open-sse/executors/antigravityOutputCap.ts @@ -1,4 +1,5 @@ import { getExplicitModelOutputCap } from "@/lib/modelCapabilities"; +import { isDiscoverableAntigravityModelId } from "../config/antigravityModelAliases"; /** * Fallback ceiling on `generationConfig.maxOutputTokens` for Antigravity @@ -13,7 +14,7 @@ import { getExplicitModelOutputCap } from "@/lib/modelCapabilities"; * Both of those models are catalogue-known today, so neither one reaches this * constant anymore: they get their own declared limit via * `resolveAntigravityOutputCap` (65536 and 65535, respectively). The higher - * limit holds against the live upstream. A gemini-3.6-flash-high request came + * limit holds against the live upstream. A gemini-3.7-flash-high request came * back with completion_tokens 16754 and finish_reason "stop", which exceeds * 16384 on its own and so cannot be an artifact of thinking-token accounting. * @@ -38,6 +39,10 @@ export const MAX_ANTIGRAVITY_OUTPUT_TOKENS = 16384; export function resolveAntigravityOutputCap(modelId: string | null | undefined): number { const id = typeof modelId === "string" ? modelId.trim() : ""; if (!id) return MAX_ANTIGRAVITY_OUTPUT_TOKENS; + // MODEL_SPECS is provider-neutral: other providers may continue serving old + // Gemini 3.5/3.6 ids after Antigravity retires them. Do not let those shared + // specs make a retired Antigravity id look active on this provider path. + if (!isDiscoverableAntigravityModelId(id)) return MAX_ANTIGRAVITY_OUTPUT_TOKENS; try { const declared = getExplicitModelOutputCap({ provider: "antigravity", model: id }); return typeof declared === "number" && Number.isFinite(declared) && declared > 0 diff --git a/open-sse/executors/perplexity-web/protocol.ts b/open-sse/executors/perplexity-web/protocol.ts index 11e81f05d3..f98680c4ff 100644 --- a/open-sse/executors/perplexity-web/protocol.ts +++ b/open-sse/executors/perplexity-web/protocol.ts @@ -69,24 +69,26 @@ export const MODEL_MAP: Record = { "pplx-sonar": ["copilot", "turbo"], "pplx-gpt-5.6-terra": ["copilot", "gpt56_terra"], "pplx-gpt-5.6-sol": ["copilot", "gpt56_sol"], - "pplx-gemini": ["copilot", "gemini31pro_high"], + "pplx-gemini": ["copilot", "gemini37flash"], "pplx-sonnet": ["copilot", "claude50sonnet"], // Perplexity's catalog moved Opus to 5.0; claude48opus is still accepted but // answers from the older model. "pplx-opus": ["copilot", "claude50opus"], "pplx-glm": ["copilot", "glm_5_2"], - "pplx-kimi": ["copilot", "kimik26instant"], - "pplx-grok-4.5": ["copilot", "grok45low"], + // The current Kimi K3 catalog entry only exposes its reasoning model. + "pplx-kimi": ["copilot", "kimik3thinking"], + "pplx-grok-4.6": ["copilot", "grok46low"], "pplx-nemotron": ["copilot", "nv_nemotron_3_ultra"], }; export const THINKING_MAP: Record = { "pplx-gpt-5.6-terra": "gpt56_terra_thinking", "pplx-gpt-5.6-sol": "gpt56_sol_thinking", + "pplx-gemini": "gemini37flashthinking", "pplx-sonnet": "claude50sonnetthinking", "pplx-opus": "claude50opusthinking", - "pplx-kimi": "kimik26thinking", - "pplx-grok-4.5": "grok45medium", + "pplx-kimi": "kimik3thinking", + "pplx-grok-4.6": "grok46medium", }; export const CITATION_RE = /\[\d+\]/g; diff --git a/open-sse/handlers/imageGeneration.ts b/open-sse/handlers/imageGeneration.ts index 5d76987286..7fb6357db1 100644 --- a/open-sse/handlers/imageGeneration.ts +++ b/open-sse/handlers/imageGeneration.ts @@ -41,7 +41,6 @@ import { handleHyperbolicImageGeneration } from "./imageGeneration/providers/hyp import { handleHuggingFaceImageGeneration } from "./imageGeneration/providers/huggingface.ts"; import { handleComfyUIImageGeneration } from "./imageGeneration/providers/comfyUI.ts"; import { handleImagen3ImageGeneration } from "./imageGeneration/providers/imagen3.ts"; -import { handleGoogleImagenGeneration } from "./imageGeneration/providers/googleImagen.ts"; import { handleIdeogramImageGeneration } from "./imageGeneration/providers/ideogram.ts"; import { handleHaiperImageGeneration } from "./imageGeneration/providers/haiper.ts"; import { handleLeonardoImageGeneration } from "./imageGeneration/providers/leonardo.ts"; @@ -383,17 +382,6 @@ export async function handleImageGeneration({ }); } - if (providerConfig.format === "google-imagen") { - return handleGoogleImagenGeneration({ - model, - provider, - providerConfig, - body, - credentials, - log, - }); - } - if (providerConfig.format === "hyperbolic") { return handleHyperbolicImageGeneration({ model, diff --git a/open-sse/handlers/imageGeneration/providers/googleImagen.ts b/open-sse/handlers/imageGeneration/providers/googleImagen.ts deleted file mode 100644 index b4b2ea67d8..0000000000 --- a/open-sse/handlers/imageGeneration/providers/googleImagen.ts +++ /dev/null @@ -1,147 +0,0 @@ -// Google AI Studio (Gemini API) Imagen image generation. -// -// Unlike the antigravity "gemini-image" format (which wraps generateContent in a -// Cloud Code envelope), the Imagen family on generativelanguage.googleapis.com uses -// the dedicated ":predict" endpoint with an instances/parameters body and returns -// base64 image bytes under `predictions[].bytesBase64Encoded`. -// -// Docs: https://ai.google.dev/gemini-api/docs/imagen (Imagen requires a billing- -// enabled Google project; free-tier keys get 403 / quota 0.) - -import { saveCallLog } from "@/lib/usageDb"; -import { mapImageSize } from "../../../translator/image/sizeMapper.ts"; -import { sanitizeErrorMessage } from "../../../utils/error.ts"; - -// Only the Imagen family routes through :predict. Other gemini image models -// (gemini-*-flash-image / nano-banana) use generateContent and belong on the chat -// route, so they must not be dispatched here. -export function isImagenModel(model) { - return /^imagen-/i.test(String(model || "")); -} - -/** - * Build the Imagen :predict request body from an OpenAI-style image request. - * Pure — no I/O — so it can be unit-tested without live credentials. - */ -export function buildImagenPredictBody(body) { - const prompt = typeof body?.prompt === "string" ? body.prompt : String(body?.prompt ?? ""); - const n = Number(body?.n); - const sampleCount = Number.isFinite(n) && n > 0 ? Math.min(Math.floor(n), 4) : 1; - return { - instances: [{ prompt }], - parameters: { - sampleCount, - aspectRatio: mapImageSize(body?.aspect_ratio || body?.size), - }, - }; -} - -/** - * Normalize an Imagen :predict response into the OpenAI image-generation shape - * ({ created, data: [{ b64_json, revised_prompt }] }). Pure — unit-testable. - */ -export function parseImagenPredictResponse(data, prompt) { - const predictions = Array.isArray(data?.predictions) ? data.predictions : []; - const images = []; - for (const p of predictions) { - const b64 = p?.bytesBase64Encoded ?? p?.b64_json ?? p?.image ?? null; - if (typeof b64 === "string" && b64.length > 0) { - images.push({ b64_json: b64, revised_prompt: prompt }); - } - } - return { created: Math.floor(Date.now() / 1000), data: images }; -} - -export async function handleGoogleImagenGeneration({ - model, - provider, - providerConfig, - body, - credentials, - log, -}) { - const startTime = Date.now(); - const token = credentials?.apiKey || credentials?.accessToken || ""; - const prompt = typeof body.prompt === "string" ? body.prompt : String(body.prompt ?? ""); - - if (!isImagenModel(model)) { - return { - success: false, - status: 400, - error: `Model ${model} is not an Imagen model. Gemini flash-image models route through /v1/chat/completions, not /v1/images/generations.`, - }; - } - - const upstreamBody = buildImagenPredictBody(body); - // baseUrl is https://generativelanguage.googleapis.com/v1beta/models - const url = `${providerConfig.baseUrl.replace(/\/$/, "")}/${model}:predict`; - - if (log) { - log.info( - "IMAGE", - `${provider}/${model} (google-imagen) | prompt: "${prompt.slice(0, 60)}..." | aspectRatio: ${upstreamBody.parameters.aspectRatio}` - ); - } - - try { - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - // Key travels in the header, never the URL, so it stays out of logs. - "x-goog-api-key": token, - }, - body: JSON.stringify(upstreamBody), - }); - - if (!response.ok) { - const errorText = await response.text(); - const safeError = sanitizeErrorMessage(errorText); - if (log) log.error("IMAGE", `${provider} error ${response.status}: ${safeError.slice(0, 200)}`); - - saveCallLog({ - method: "POST", - path: "/v1/images/generations", - status: response.status, - model: `${provider}/${model}`, - provider, - duration: Date.now() - startTime, - error: safeError.slice(0, 500), - }).catch(() => {}); - - return { success: false, status: response.status, error: safeError }; - } - - const data = await response.json(); - const normalized = parseImagenPredictResponse(data, prompt); - - saveCallLog({ - method: "POST", - path: "/v1/images/generations", - status: 200, - model: `${provider}/${model}`, - provider, - duration: Date.now() - startTime, - responseBody: { images_count: normalized.data.length }, - }).catch(() => {}); - - return { success: true, data: normalized }; - } catch (err) { - const errMsg = err instanceof Error ? err.message : String(err); - if (log) log.error("IMAGE", `${provider} fetch error: ${errMsg}`); - saveCallLog({ - method: "POST", - path: "/v1/images/generations", - status: 502, - model: `${provider}/${model}`, - provider, - duration: Date.now() - startTime, - error: errMsg, - }).catch(() => {}); - return { - success: false, - status: 502, - error: `Image provider error: ${sanitizeErrorMessage(errMsg)}`, - }; - } -} diff --git a/open-sse/services/__tests__/antigravity-quota-family.test.ts b/open-sse/services/__tests__/antigravity-quota-family.test.ts index b1983a0909..4be5e4f3ec 100644 --- a/open-sse/services/__tests__/antigravity-quota-family.test.ts +++ b/open-sse/services/__tests__/antigravity-quota-family.test.ts @@ -22,24 +22,24 @@ describe("Antigravity account quota-family cooldown", () => { }); it("maps Gemini variants to Gemini family and Claude/Cloud variants to Claude family", () => { - expect(getAntigravityQuotaFamily("gemini-3.5-flash-medium")).toBe("gemini"); - expect(getAntigravityQuotaFamily("google/gemini-3.5-flash-low")).toBe("gemini"); - expect(getAntigravityQuotaFamily("agy/gemini-3.5-flash-medium")).toBe("gemini"); + expect(getAntigravityQuotaFamily("gemini-3.7-flash-medium")).toBe("gemini"); + expect(getAntigravityQuotaFamily("google/gemini-3.7-flash-low")).toBe("gemini"); + expect(getAntigravityQuotaFamily("agy/gemini-3.7-flash-medium")).toBe("gemini"); expect(getAntigravityQuotaFamily("claude-sonnet-4")).toBe("claude"); expect(getAntigravityQuotaFamily("cloud/claude-opus-4")).toBe("claude"); expect(getAntigravityQuotaFamily("some-new-model")).toBe("other"); }); it("uses family-scoped lock key for Antigravity but preserves exact-model scope elsewhere", () => { - expect(getQuotaScopedModelForProvider("antigravity", "gemini-3.5-flash-medium")).toBe( + expect(getQuotaScopedModelForProvider("antigravity", "gemini-3.7-flash-medium")).toBe( "family:gemini" ); - expect(getQuotaScopedModelForProvider("agy", "gemini-3.5-flash-medium")).toBe("family:gemini"); - expect(getQuotaScopedModelForProvider(provider, "gemini-3.5-flash-low")).toBe("family:gemini"); + expect(getQuotaScopedModelForProvider("agy", "gemini-3.7-flash-medium")).toBe("family:gemini"); + expect(getQuotaScopedModelForProvider(provider, "gemini-3.7-flash-low")).toBe("family:gemini"); expect(getQuotaScopedModelForProvider(provider, "claude-sonnet-4")).toBe("family:claude"); expect(getQuotaScopedModelForProvider(provider, "unknown-model")).toBe("unknown-model"); - expect(getQuotaScopedModelForProvider("openai", "gemini-3.5-flash-medium")).toBe( - "gemini-3.5-flash-medium" + expect(getQuotaScopedModelForProvider("openai", "gemini-3.7-flash-medium")).toBe( + "gemini-3.7-flash-medium" ); }); @@ -47,7 +47,7 @@ describe("Antigravity account quota-family cooldown", () => { recordModelLockoutFailure( provider, "account-a", - "gemini-3.5-flash-medium", + "gemini-3.7-flash-medium", "rate_limited", 429, 60_000, @@ -55,10 +55,10 @@ describe("Antigravity account quota-family cooldown", () => { { maxCooldownMs: 300_000 } ); - expect(isModelLocked(provider, "account-a", "gemini-3.5-flash-medium")).toBe(true); - expect(isModelLocked(provider, "account-a", "gemini-3.5-flash-low")).toBe(true); + expect(isModelLocked(provider, "account-a", "gemini-3.7-flash-medium")).toBe(true); + expect(isModelLocked(provider, "account-a", "gemini-3.7-flash-low")).toBe(true); expect(isModelLocked(provider, "account-a", "claude-sonnet-4")).toBe(false); - expect(isModelLocked(provider, "account-b", "gemini-3.5-flash-low")).toBe(false); + expect(isModelLocked(provider, "account-b", "gemini-3.7-flash-low")).toBe(false); }); it("keeps Claude/Cloud family distinct from Gemini", () => { @@ -74,7 +74,7 @@ describe("Antigravity account quota-family cooldown", () => { ); expect(isModelLocked(provider, "account-a", "cloud/claude-opus-4")).toBe(true); - expect(isModelLocked(provider, "account-a", "gemini-3.5-flash-low")).toBe(false); + expect(isModelLocked(provider, "account-a", "gemini-3.7-flash-low")).toBe(false); }); it("can isolate a confirmed Antigravity quota exhaustion to one exact model", () => { @@ -88,7 +88,7 @@ describe("Antigravity account quota-family cooldown", () => { expect(isModelLocked(provider, "account-a", "claude-opus-4-6-thinking")).toBe(true); expect(isModelLocked(provider, "account-a", "claude-sonnet-4-6-thinking")).toBe(false); - expect(isModelLocked(provider, "account-a", "gemini-3.5-flash-medium")).toBe(false); + expect(isModelLocked(provider, "account-a", "gemini-3.7-flash-medium")).toBe(false); expect(clearModelLock(provider, "account-a", "claude-opus-4-6-thinking")).toBe(true); expect(isModelLocked(provider, "account-a", "claude-opus-4-6-thinking")).toBe(false); @@ -113,7 +113,7 @@ describe("Antigravity account quota-family cooldown", () => { const upstream = recordModelLockoutFailure( provider, "account-a", - "gemini-3.5-flash-medium", + "gemini-3.7-flash-medium", "rate_limited", 429, 1_000, @@ -122,13 +122,13 @@ describe("Antigravity account quota-family cooldown", () => { ); expect(upstream.cooldownMs).toBe(123_000); expect( - getModelLockoutInfo(provider, "account-a", "gemini-3.5-flash-low")?.remainingMs + getModelLockoutInfo(provider, "account-a", "gemini-3.7-flash-low")?.remainingMs ).toBeGreaterThan(100_000); const inferred = recordModelLockoutFailure( provider, "account-b", - "gemini-3.5-flash-medium", + "gemini-3.7-flash-medium", "rate_limited", 429, 1_000, diff --git a/open-sse/services/conolModels.ts b/open-sse/services/conolModels.ts index 47d87e5c15..ed96fe66f9 100644 --- a/open-sse/services/conolModels.ts +++ b/open-sse/services/conolModels.ts @@ -49,7 +49,6 @@ const FALLBACK_MODEL_SEEDS: FallbackModelSeed[] = [ { id: "claude-opus-5", vision: true, efforts: EFFORTS_XHIGH }, { id: "claude-opus-4-8", vision: true, efforts: EFFORTS_XHIGH }, { id: "claude-fable-5", vision: true, efforts: EFFORTS_XHIGH }, - { id: "claude-opus-4-7", vision: true, efforts: EFFORTS_XHIGH }, { id: "claude-sonnet-5", vision: true, efforts: EFFORTS_NO_XHIGH }, { id: "claude-sonnet-4-6", vision: true, efforts: EFFORTS_NO_XHIGH }, { id: "claude-haiku-4-5", vision: true, efforts: EFFORTS_STANDARD }, @@ -61,7 +60,6 @@ const FALLBACK_MODEL_SEEDS: FallbackModelSeed[] = [ { id: "deepseek/deepseek-v4-pro", vision: false, efforts: EFFORTS_HIGH_ONLY }, { id: "openrouter/fusion", vision: false, efforts: [] }, { id: "z-ai/glm-5.2", vision: false, efforts: EFFORTS_STANDARD }, - { id: "z-ai/glm-5.1", vision: false, efforts: EFFORTS_STANDARD }, { id: "tencent/hy3", vision: false, efforts: EFFORTS_STANDARD }, { id: "moonshotai/kimi-k3", vision: true, efforts: EFFORTS_STANDARD }, { id: "moonshotai/kimi-k2.7-code", vision: true, efforts: EFFORTS_STANDARD }, @@ -69,7 +67,7 @@ const FALLBACK_MODEL_SEEDS: FallbackModelSeed[] = [ { id: "qwen/qwen3.7-max", vision: false, efforts: EFFORTS_STANDARD }, { id: "minimax/minimax-m3", vision: true, efforts: EFFORTS_STANDARD }, { id: "stepfun/step-3.7-flash", vision: true, efforts: EFFORTS_STANDARD }, - { id: "google/gemini-3.5-flash", vision: true, efforts: EFFORTS_STANDARD }, + { id: "google/gemini-3.7-flash", vision: true, efforts: EFFORTS_STANDARD }, { id: "google/gemini-3.1-pro-preview", vision: true, efforts: EFFORTS_STANDARD }, { id: "google/gemini-3.1-flash-lite", vision: true, efforts: EFFORTS_STANDARD }, { id: "x-ai/grok-4.3", vision: true, efforts: EFFORTS_STANDARD }, diff --git a/open-sse/services/githubCopilotModels.ts b/open-sse/services/githubCopilotModels.ts index b1066716f2..ff54d1a293 100644 --- a/open-sse/services/githubCopilotModels.ts +++ b/open-sse/services/githubCopilotModels.ts @@ -32,7 +32,7 @@ export const GITHUB_COPILOT_MODEL_ALLOWLIST = [ "claude-sonnet-4.5", "claude-haiku-4.5", "gemini-3.1-pro-preview", - "gemini-3.5-flash", + "gemini-3.7-flash", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", diff --git a/open-sse/services/notionWebFallbackModels.ts b/open-sse/services/notionWebFallbackModels.ts index 89f3097d5d..0fd7442b63 100644 --- a/open-sse/services/notionWebFallbackModels.ts +++ b/open-sse/services/notionWebFallbackModels.ts @@ -30,84 +30,96 @@ export type NotionDiscoveredModel = { */ export const NOTION_WEB_FALLBACK_MODELS: NotionDiscoveredModel[] = [ { id: "notion-ai", name: "Notion AI (default)", owned_by: "notion" }, - { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", owned_by: "openai", notionCodename: "orange-mousse" }, + { + id: "gpt-5.6-sol", + name: "GPT-5.6 Sol", + owned_by: "openai", + supportsReasoning: true, + notionCodename: "orange-mousse", + }, { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", owned_by: "openai", + supportsReasoning: true, notionCodename: "orchid-muffin", }, { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", owned_by: "openai", + supportsReasoning: true, notionCodename: "olive-jellyroll", }, - { id: "gpt-5.2", name: "GPT-5.2", owned_by: "openai", notionCodename: "oatmeal-cookie" }, - { id: "gpt-5.4", name: "GPT-5.4", owned_by: "openai", notionCodename: "oval-kumquat-medium" }, - { id: "gpt-5.5", name: "GPT-5.5", owned_by: "openai", notionCodename: "opal-quince-medium" }, { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", owned_by: "openai", + supportsReasoning: true, notionCodename: "oregon-grape-medium", }, { id: "gpt-5.4-nano", name: "GPT-5.4 Nano", owned_by: "openai", + supportsReasoning: true, notionCodename: "otaheite-apple-medium", }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", owned_by: "gemini", - notionCodename: "vertex-gemini-3.5-flash", - }, - { - id: "gemini-3-flash", - name: "Gemini 3 Flash", - owned_by: "gemini", - notionCodename: "gingerbread", + supportsReasoning: true, + notionCodename: "grapefruit-zeppole", }, { id: "gemini-3.1-pro", name: "Gemini 3.1 Pro", owned_by: "gemini", + supportsReasoning: true, notionCodename: "galette-medium-thinking", }, { - id: "sonnet-4.6", - name: "Sonnet 4.6", + id: "fable-5", + name: "Claude Fable 5", owned_by: "anthropic", - notionCodename: "almond-croissant-low", - }, - { id: "sonnet-5", name: "Sonnet 5", owned_by: "anthropic", notionCodename: "angel-cake-high" }, - { - id: "opus-4.6", - name: "Opus 4.6", - owned_by: "anthropic", - notionCodename: "avocado-froyo-medium", + supportsReasoning: true, + disabled: true, + notionCodename: "acai-budino-high", }, { - id: "opus-4.7", - name: "Opus 4.7", + id: "opus-5", + name: "Claude Opus 5", owned_by: "anthropic", - notionCodename: "apricot-sorbet-high", + supportsReasoning: true, + notionCodename: "agave-flan", + }, + { + id: "sonnet-5", + name: "Claude Sonnet 5", + owned_by: "anthropic", + supportsReasoning: true, + notionCodename: "angel-cake-high", }, - { id: "opus-4.8", name: "Opus 4.8", owned_by: "anthropic", notionCodename: "ambrosia-tart-high" }, { id: "haiku-4.5", - name: "Haiku 4.5", + name: "Claude Haiku 4.5", owned_by: "anthropic", notionCodename: "anthropic-haiku-4.5", }, - { id: "fable-5", name: "Fable 5", owned_by: "anthropic", notionCodename: "acai-budino-high" }, { - id: "kimi-k2.6", - name: "Kimi K2.6", + id: "grok-4.6", + name: "Grok 4.6", + owned_by: "xai", + supportsReasoning: true, + notionCodename: "soursop-shortcake", + }, + { + id: "kimi-k3", + name: "Kimi K3", owned_by: "mystery", - notionCodename: "fireworks-kimi-k2.6", + supportsReasoning: true, + notionCodename: "fireworks-kimi-k3", }, { id: "kimi-k2.7-code", @@ -119,16 +131,14 @@ export const NOTION_WEB_FALLBACK_MODELS: NotionDiscoveredModel[] = [ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", owned_by: "mystery", + supportsReasoning: true, notionCodename: "baseten-deepseek-v4-pro", }, - { id: "glm-5.2", name: "GLM 5.2", owned_by: "mystery", notionCodename: "baseten-glm-5.2" }, - { id: "grok-4.3", name: "Grok 4.3", owned_by: "xai", notionCodename: "xigua-mochi-medium" }, - { id: "grok-4.5", name: "Grok 4.5", owned_by: "xai", notionCodename: "strawberry-whoopiepie" }, { - id: "grok-build-0.1", - name: "Grok Build 0.1", - owned_by: "xai", - notionCodename: "xinomavro-cake", + id: "glm-5.2", + name: "GLM 5.2", + owned_by: "mystery", + supportsReasoning: true, + notionCodename: "baseten-glm-5.2", }, ]; - diff --git a/open-sse/services/promptqlModels.ts b/open-sse/services/promptqlModels.ts index c7213d6915..55935c7719 100644 --- a/open-sse/services/promptqlModels.ts +++ b/open-sse/services/promptqlModels.ts @@ -2,7 +2,7 @@ * PromptQL (prompt.ql.app) model catalog helpers. * * Live catalog: GraphQL `FetchLlmConfigs` against the playground Hasura endpoint. - * Fallback: static seed captured 2026-07-20 (display_label / model_reference / model_id). + * Fallback: static seed captured 2026-08-17 (display_label / model_reference / model_id). */ export interface PromptQlModel { @@ -21,95 +21,102 @@ export interface PromptQlModel { /** Offline seed when discovery fails (from live FetchLlmConfigs capture). */ export const PROMPTQL_FALLBACK_MODELS: PromptQlModel[] = [ { - id: "vertex-claude-fable-5", + id: "bedrock-claude-fable-5", name: "Claude Fable 5", - configId: "967e6517-1d6b-4e22-82fb-3463bab239c4", - modelId: "anthropic/claude-fable-5", + configId: "c47a1e57-2fca-4cfe-913a-5fb821079f50", + modelId: "us.anthropic.claude-fable-5", + supportsVision: true, }, { - id: "bedrock-claude-opus-4-8", - name: "Claude Opus 4.8", - configId: "e97e7f50-9e4a-4685-bc14-1854f1f79782", - modelId: "us.anthropic.claude-opus-4-8", + id: "bedrock-claude-opus-5", + name: "Claude Opus 5", + configId: "8aed42aa-f7c8-48f9-8238-5046bbc0f4f7", + modelId: "us.anthropic.claude-opus-5", + supportsVision: true, }, { id: "bedrock-claude-sonnet-4-5", name: "Claude Sonnet 4.5", - configId: "48105d83-9a45-4ec6-8b58-f3cf44094f92", + configId: "0abcbc61-dbef-4958-96b9-e0cde7e3ad8f", modelId: "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + supportsVision: true, }, { - id: "deepseek-v4-pro", - name: "DeepSeek V4 Pro", - configId: "5a23af33-b31b-4215-892c-20ef633a8848", - modelId: "accounts/fireworks/models/deepseek-v4-pro", + id: "deepseek-v4-pro-0813", + name: "DeepSeek V4 Pro 0813", + configId: "255de820-3615-4921-a5fe-85b4af9e37a4", + modelId: "accounts/fireworks/models/deepseek-v4-pro-0813", + supportsVision: true, + }, + { + id: "deepseek-v4-flash-0731", + name: "DeepSeek V4 Flash 0731", + configId: "22d8bd9a-3c48-4e27-a4f8-dc67ad2242b7", + modelId: "accounts/fireworks/models/deepseek-v4-flash-0731", + supportsVision: true, }, { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview", - configId: "d2bda5cd-881b-4044-aeb9-02a83cc0ca27", + configId: "17703a97-41a4-469d-b5d4-7356f1c28948", modelId: "google/gemini-3.1-pro-preview", + supportsVision: true, }, { - id: "gemini-3.5-flash", - name: "Gemini 3.5 Flash", - configId: "c3a25aa0-ca48-4577-b52d-71282aacb687", - modelId: "google/gemini-3.5-flash", - }, - { - id: "glm-5.2", - name: "GLM 5.2", - configId: "64a1fa3d-bf2e-4bb9-8c2b-fa76c218d636", - modelId: "accounts/fireworks/models/glm-5p2", - }, - { - id: "gpt-5.5", - name: "GPT 5.5", - configId: "1762fbce-d5bf-4bf4-ba3d-8b1201f8e204", - modelId: "gpt-5.5", - }, - { - id: "gpt-5.6-luna", - name: "GPT-5.6 Luna", - configId: "a9c45ba7-87fa-49a1-8165-76b0864c3a55", - modelId: "gpt-5.6-luna", + id: "gemini-3.7-flash", + name: "Gemini 3.7 Flash", + configId: "60754535-a5e8-4ae7-acf2-43d046771700", + modelId: "google/gemini-3.7-flash", + supportsVision: true, }, { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", - configId: "34c80712-def3-4db3-9e7a-f57b0324b43d", + configId: "4914e63d-ea29-45dc-9a85-c367b1ad0be5", modelId: "gpt-5.6-sol", + supportsVision: true, }, { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", - configId: "04f1a08c-42b2-4371-b6d8-75c50b9bb990", + configId: "4e627eb9-a199-4a90-8050-b734b6ee5fda", modelId: "gpt-5.6-terra", + supportsVision: true, }, { - id: "xai-grok-4-5", - name: "Grok 4.5", - configId: "068b2ef2-e432-422b-98e5-5863a1852c47", - modelId: "grok-4.5", + id: "gpt-5.6-luna", + name: "GPT-5.6 Luna", + configId: "5eac2efb-7951-4da6-9a32-bfb31b1a7788", + modelId: "gpt-5.6-luna", + supportsVision: true, }, { - id: "kimi-k2.6", - name: "Kimi K2.6", - configId: "placeholder-kimi-k2.6", - modelId: "accounts/fireworks/models/kimi-k2p6", + id: "xai-grok-4-6", + name: "Grok 4.6", + configId: "673e97ba-7b15-4213-8984-9b2477ee3409", + modelId: "grok-4.6", + supportsVision: true, }, { - id: "kimi-k2.7-code", - name: "Kimi K2.7 Code", - configId: "placeholder-kimi-k2.7-code", - modelId: "accounts/fireworks/models/kimi-k2p7-code", + id: "kimi-k3", + name: "Kimi K3", + configId: "2a751e62-e281-4ab0-9be0-b05a6f8603db", + modelId: "accounts/fireworks/models/kimi-k3", + supportsVision: true, + }, + { + id: "glm-5.2", + name: "GLM 5.2", + configId: "d2694d4d-4285-4d3c-ada5-aea5956375d4", + modelId: "accounts/fireworks/models/glm-5p2", + supportsVision: false, }, { id: "minimax-m3", name: "Minimax M3", - configId: "placeholder-minimax-m3", + configId: "c4028069-0eb9-4a31-825c-a1cff9e5a085", modelId: "accounts/fireworks/models/minimax-m3", - supportsVision: true, + supportsVision: false, }, ]; diff --git a/src/app/(dashboard)/dashboard/combos/page.tsx b/src/app/(dashboard)/dashboard/combos/page.tsx index 8d36151319..5cf1c76784 100644 --- a/src/app/(dashboard)/dashboard/combos/page.tsx +++ b/src/app/(dashboard)/dashboard/combos/page.tsx @@ -2686,7 +2686,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo }; const FREE_STACK_PRESET_MODELS = [ - { model: "agy/gemini-3.5-flash-low", weight: 0 }, + { model: "agy/gemini-3.7-flash-low", weight: 0 }, { model: "kr/claude-sonnet-4.5", weight: 0 }, { model: "if/kimi-k2-thinking", weight: 0 }, { model: "if/qwen3-coder-plus", weight: 0 }, diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index 764a6c1d70..818156aaec 100755 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -1768,8 +1768,8 @@ export async function GET( // Vertex AI lists models from the Generative Language `v1beta/models` endpoint, which both // Express-mode API keys (via ?key=) and Service Account JSON (via a minted OAuth Bearer - // token) can reach. This surfaces the full live catalog — including image models - // (imagen-*, gemini-*-image) absent from the static registry list. + // token) can reach. This surfaces the live catalog, including gemini-*-image models + // absent from the static registry list. const credential = (apiKey || "").trim(); let queryKey: string | null = null; let bearerToken: string | null = null; diff --git a/src/lib/providerModels/geminiModelsParser.ts b/src/lib/providerModels/geminiModelsParser.ts index 2836846886..9fef2b3e52 100644 --- a/src/lib/providerModels/geminiModelsParser.ts +++ b/src/lib/providerModels/geminiModelsParser.ts @@ -3,15 +3,11 @@ * * Each model's `supportedGenerationMethods` is mapped to OmniRoute endpoints: * - generateContent / generateAnswer → "chat" - * - predict → "images" (Imagen image generation) * - predictLongRunning → "video" (Veo video generation) * - embedContent → "embeddings" * - bidiGenerateContent → "audio" (Live real-time audio) * - * Model-id heuristics refine the long-running bucket because Google exposes both - * Imagen and Veo via long-running methods on the same endpoint: - * - id contains "veo" → ensure "video" - * - id contains "imagen" → force "images" (never "video") + * Model-id heuristics ensure Veo models remain in the video bucket. * * Note: `gemini-*-image` models (e.g. gemini-3-pro-image) generate images via the * regular `generateContent` path, so they stay "chat" (image output is a chat @@ -25,7 +21,6 @@ const METHOD_TO_ENDPOINT: Record = { generateContent: "chat", embedContent: "embeddings", - predict: "images", predictLongRunning: "video", bidiGenerateContent: "audio", generateAnswer: "chat", @@ -65,15 +60,10 @@ export function parseGeminiModelsList(data: any): GeminiDiscoveryModel[] { const id = ((m.name as string) || (m.id as string) || "").replace(/^models\//, ""); const lowerId = id.toLowerCase(); - // Google exposes Imagen (image) and Veo (video) via long-running methods; the - // method alone can't always distinguish them, so refine by model id. + // Keep Veo models in the video bucket even when the method list is incomplete. if (lowerId.includes("veo")) { endpoints.add("video"); } - if (lowerId.includes("imagen")) { - endpoints.delete("video"); - endpoints.add("images"); - } if (endpoints.size === 0) endpoints.add("chat"); diff --git a/src/shared/constants/cliTools.ts b/src/shared/constants/cliTools.ts index 86907635d2..4fbecf35ba 100644 --- a/src/shared/constants/cliTools.ts +++ b/src/shared/constants/cliTools.ts @@ -100,7 +100,10 @@ export const CLI_TOOLS: Record = { baseUrlSupport: "none", defaultCommand: "zcode", notes: [ - { type: "info", text: "Uses the local ZCode app-server and its existing builtin:zai-coding-plan login." }, + { + type: "info", + text: "Uses the local ZCode app-server and its existing builtin:zai-coding-plan login.", + }, { type: "warning", text: "The response is buffered until the ZCode turn completes." }, ], }, @@ -239,29 +242,21 @@ 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", + "gemini-3.7-flash-high", + "gemini-3.7-flash-medium", + "gemini-3.7-flash-low", "claude-opus-4-6-thinking", "claude-sonnet-4-6", "gemini-pro-agent", "gemini-3.1-pro-low", - "gemini-3-flash-agent", - "gemini-3.5-flash-low", - "gemini-3.5-flash-extra-low", "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"), + createCliModel("gemini-3.7-flash-high", "Gemini 3.7 Flash High"), + createCliModel("gemini-3.7-flash-medium", "Gemini 3.7 Flash Medium"), + createCliModel("gemini-3.7-flash-low", "Gemini 3.7 Flash Low"), createCliModel("gemini-pro-agent", "Gemini 3.1 Pro High"), createCliModel("gemini-3.1-pro-low", "Gemini 3.1 Pro Low"), - createCliModel("gemini-3-flash-agent", "Gemini 3.5 Flash High"), - createCliModel("gemini-3.5-flash-low", "Gemini 3.5 Flash Medium"), - createCliModel("gemini-3.5-flash-extra-low", "Gemini 3.5 Flash Low"), createCliModel("claude-sonnet-4-6", "Claude Sonnet 4.6"), createCliModel("claude-opus-4-6-thinking", "Claude Opus 4.6 Thinking"), createCliModel("gpt-oss-120b-medium", "GPT OSS 120B Medium"), diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index b2ecabbc6c..f7342c54f4 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -153,7 +153,7 @@ export const MODEL_SPECS: Record = { aliases: ["openai/gpt-4o"], }, - // ── Gemini 2.5 and 3.5 Flash series ────────────────────────────── + // ── Gemini 2.5 and provider-neutral 3.5 Flash series ───────────── "gemini-2.5-flash": { maxOutputTokens: 65536, contextWindow: 1048576, @@ -174,24 +174,17 @@ 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) ───────────── + // ── Gemini 3.7 Flash (current Antigravity/AGY 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 - // capability aligned with the existing Gemini 3.5 tier ids. + // capability aligned with the existing Gemini Flash tier ids. "gemini-3.7-flash-high": { ...GEMINI_35_FLASH_MODEL_SPEC }, "gemini-3.7-flash-medium": { ...GEMINI_35_FLASH_MODEL_SPEC }, + "gemini-3.7-flash-low": { ...GEMINI_35_FLASH_MODEL_SPEC }, + + // Provider-neutral compatibility for providers that still serve Gemini 3.6. + // Antigravity/AGY availability is governed by their own provider catalogs and + // retirement filters; these shared specs must not be treated as an allowlist. "gemini-3.6-flash-high": { ...GEMINI_35_FLASH_MODEL_SPEC }, "gemini-3.6-flash-medium": { ...GEMINI_35_FLASH_MODEL_SPEC }, "gemini-3.6-flash-low": { ...GEMINI_35_FLASH_MODEL_SPEC }, diff --git a/src/shared/constants/pricing/frontier-labs.ts b/src/shared/constants/pricing/frontier-labs.ts index a7cbf79935..2923945b9c 100644 --- a/src/shared/constants/pricing/frontier-labs.ts +++ b/src/shared/constants/pricing/frontier-labs.ts @@ -3,6 +3,7 @@ * Pure data; merged by default-pricing.ts via spread (god-file decomposition; semantic split). */ import { + GEMINI_3_7_FLASH_PROMO_PRICING, GPT_5_5_PRICING, GPT_5_6_LUNA_PRICING, GPT_5_6_SOL_PRICING, @@ -226,6 +227,7 @@ export const DEFAULT_PRICING_FRONTIER = { "claude-opus-4": CLAUDE_OPUS_4_PRICING, }, gemini: { + "gemini-3.7-flash": GEMINI_3_7_FLASH_PROMO_PRICING, // Gemini 3.1 Pro — novo flagship Google (2026-03-17) // Context: 1.050.000 tokens | Max Output: 65.536 "gemini-3.1-pro": { diff --git a/src/shared/constants/pricing/inference-hosts.ts b/src/shared/constants/pricing/inference-hosts.ts index 3ceec84ec5..e09715b942 100644 --- a/src/shared/constants/pricing/inference-hosts.ts +++ b/src/shared/constants/pricing/inference-hosts.ts @@ -140,12 +140,13 @@ export const DEFAULT_PRICING_INFERENCE = { reasoning: 1.75, cache_creation: 0.0583, }, - "gemini-3-5-flash": { - input: 1.05, - output: 6.3, - cached: 0.1085, - reasoning: 6.3, - cache_creation: 0.0602, + // Live public catalog rate checked 2026-08-16. + "gemini-3.7-flash": { + input: 0.525, + output: 2.625, + cached: 0.13125, + reasoning: 2.625, + cache_creation: 0.017708, }, "gemini-3-flash-preview": { input: 0.35, @@ -218,7 +219,13 @@ export const DEFAULT_PRICING_INFERENCE = { reasoning: 21.0, cache_creation: 3.5, }, - "gpt-5.6-terra": { input: 1.0, output: 6.0, cached: 0.125, reasoning: 6.0, cache_creation: 1.0 }, + "gpt-5.6-terra": { + input: 1.0, + output: 6.0, + cached: 0.125, + reasoning: 6.0, + cache_creation: 1.0, + }, "grok-4.5": { input: 1.4, output: 4.2, cached: 0.35, reasoning: 4.2, cache_creation: 1.4 }, "kimi-k3": { input: 2.1, output: 10.5, cached: 0.2625, reasoning: 10.5, cache_creation: 2.1 }, "minimax-m2.7": { @@ -229,6 +236,15 @@ export const DEFAULT_PRICING_INFERENCE = { cache_creation: 0.168, }, }, + orcarouter: { + "google/gemini-3.6-flash": { + input: 1.5, + output: 7.5, + cached: 0.15, + reasoning: 7.5, + cache_creation: 1.5, + }, + }, synthetic: { "hf:openai/gpt-oss-120b": { input: 0.1, diff --git a/src/shared/constants/pricing/oauth-subscriptions.ts b/src/shared/constants/pricing/oauth-subscriptions.ts index b03db8be0c..87f5aa320f 100644 --- a/src/shared/constants/pricing/oauth-subscriptions.ts +++ b/src/shared/constants/pricing/oauth-subscriptions.ts @@ -4,6 +4,7 @@ */ import { CLAUDE_OPUS_5_PRICING, + GEMINI_3_7_FLASH_PROMO_PRICING, GPT_5_3_CODEX_PRICING, GPT_5_5_PRICING, GPT_5_6_LUNA_PRICING, @@ -11,6 +12,12 @@ import { GPT_5_6_TERRA_PRICING, } from "./shared-tiers"; +const ANTIGRAVITY_GEMINI_3_7_PRICING = { + "gemini-3.7-flash-low": GEMINI_3_7_FLASH_PROMO_PRICING, + "gemini-3.7-flash-medium": GEMINI_3_7_FLASH_PROMO_PRICING, + "gemini-3.7-flash-high": GEMINI_3_7_FLASH_PROMO_PRICING, +}; + export const DEFAULT_PRICING_OAUTH = { cc: { "claude-fable-5": { @@ -277,35 +284,6 @@ export const DEFAULT_PRICING_OAUTH = { reasoning: 18.0, cache_creation: 2.0, }, - // Antigravity 2.0.4+ exposes Gemini 3.5 Flash as three public client ids - // (see ANTIGRAVITY_PUBLIC_MODELS in open-sse/config/antigravityModelAliases.ts): - // gemini-3.5-flash-extra-low → "Gemini 3.5 Flash (Low)" - // gemini-3-flash-agent → "Gemini 3.5 Flash (High)" - // gemini-3.5-flash-low → "Gemini 3.5 Flash (Medium)" - // Without these rows, getPricingForModel("ag", id) returns null and downstream - // cost and quota calculations silently fall back to $0. - "gemini-3.5-flash-extra-low": { - input: 0.5, - output: 3.0, - cached: 0.03, - reasoning: 4.5, - cache_creation: 0.5, - }, - "gemini-3-flash-agent": { - input: 0.5, - output: 3.0, - cached: 0.03, - reasoning: 4.5, - cache_creation: 0.5, - }, - "gemini-3.5-flash-low": { - input: 0.5, - output: 3.0, - cached: 0.03, - reasoning: 4.5, - cache_creation: 0.5, - }, - // `gemini-pro-agent` is the callable Antigravity id for Gemini 3.1 Pro (High). "gemini-pro-agent": { input: 4.0, output: 18.0, @@ -313,40 +291,7 @@ export const DEFAULT_PRICING_OAUTH = { reasoning: 27.0, cache_creation: 4.0, }, - // Gemini 3.6 Flash (released 2026-07-21) - three tier variants like 3.5 Flash - // (see ANTIGRAVITY_PUBLIC_MODELS / MODEL_SPECS which already carry the catalog - // entries). Without these rows, getPricingForModel("ag", id) returns null and - // 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, - cached: 0.15, - reasoning: 7.5, - cache_creation: 1.5, - }, - "gemini-3.6-flash-medium": { - input: 1.5, - output: 7.5, - cached: 0.15, - reasoning: 7.5, - cache_creation: 1.5, - }, - "gemini-3.6-flash-high": { - input: 1.5, - output: 7.5, - cached: 0.15, - reasoning: 7.5, - cache_creation: 1.5, - }, + ...ANTIGRAVITY_GEMINI_3_7_PRICING, "claude-sonnet-4-6": { input: 3.0, output: 15.0, @@ -369,6 +314,8 @@ export const DEFAULT_PRICING_OAUTH = { cache_creation: 0.5, }, }, + antigravity: ANTIGRAVITY_GEMINI_3_7_PRICING, + agy: ANTIGRAVITY_GEMINI_3_7_PRICING, gh: { "claude-opus-5": CLAUDE_OPUS_5_PRICING, "gpt-5": { @@ -441,6 +388,7 @@ export const DEFAULT_PRICING_OAUTH = { reasoning: 4.5, cache_creation: 0.5, }, + "gemini-3.7-flash": GEMINI_3_7_FLASH_PROMO_PRICING, "gemini-2.5-pro": { input: 2.0, output: 12.0, diff --git a/src/shared/constants/pricing/shared-tiers.ts b/src/shared/constants/pricing/shared-tiers.ts index 26976b9581..325ac84fda 100644 --- a/src/shared/constants/pricing/shared-tiers.ts +++ b/src/shared/constants/pricing/shared-tiers.ts @@ -41,6 +41,17 @@ export const GPT_5_6_LUNA_PRICING = { cache_creation: 1.25, }; +// Gemini 3.7 Flash introductory pricing through 2026-12-31. Google and +// GitHub Copilot both publish the same current token rates; standard pricing +// becomes $1.50/$7.50/$0.15 (input/output/cached input) on 2027-01-01. +export const GEMINI_3_7_FLASH_PROMO_PRICING = { + input: 0.75, + output: 3.75, + cached: 0.075, + reasoning: 3.75, + cache_creation: 0.75, +}; + export const CLAUDE_FABLE_5_PRICING = { input: 15.0, output: 75.0, diff --git a/tests/unit/account-fallback-service.test.ts b/tests/unit/account-fallback-service.test.ts index 5bacfbe8fd..3711e6217c 100644 --- a/tests/unit/account-fallback-service.test.ts +++ b/tests/unit/account-fallback-service.test.ts @@ -127,7 +127,7 @@ test("checkFallbackError locks Antigravity quota-reached 429 for the full reset 429, message, 0, - "gemini-3-flash-agent", + "gemini-3.7-flash-high", "antigravity", null, makeProfile({ useUpstreamRetryHints: true }) @@ -143,7 +143,7 @@ test("checkFallbackError locks Antigravity quota-reached 429 for the full reset test("recordModelLockoutFailure honors a multi-day exactCooldownMs (under 30-day cap)", () => { const provider = "antigravity"; const connectionId = "conn-quota-window"; - const model = "gemini-3-flash-agent"; + const model = "gemini-3.7-flash-high"; const exactCooldownMs = (164 * 3600 + 27 * 60 + 24) * 1000; clearModelLock(provider, connectionId, model); diff --git a/tests/unit/agy-provider.test.ts b/tests/unit/agy-provider.test.ts index cb80a6ff8c..dc91937044 100644 --- a/tests/unit/agy-provider.test.ts +++ b/tests/unit/agy-provider.test.ts @@ -51,12 +51,15 @@ test("agy ships its own live callable model catalog", () => { const ids = REGISTRY.agy.models.map((m) => m.id); assert.ok(ids.includes("claude-opus-4-6-thinking"), "must expose Claude Opus 4.6 Thinking"); assert.ok(ids.includes("claude-sonnet-4-6"), "must expose Claude Sonnet 4.6"); - assert.ok(ids.includes("gemini-3.6-flash-low"), "must expose Gemini 3.6 Flash Low"); - assert.ok(ids.includes("gemini-3.6-flash-medium"), "must expose Gemini 3.6 Flash Medium"); - assert.ok(ids.includes("gemini-3.6-flash-high"), "must expose Gemini 3.6 Flash High"); - assert.ok(ids.includes("gemini-3.5-flash-extra-low"), "must expose upstream Flash Low tier"); - assert.ok(ids.includes("gemini-3.5-flash-low"), "must expose upstream Flash Medium tier"); - assert.ok(ids.includes("gemini-3-flash-agent"), "must expose upstream Flash High tier"); + assert.ok(ids.includes("gemini-3.7-flash-low"), "must expose Gemini 3.7 Flash Low"); + assert.ok(ids.includes("gemini-3.7-flash-medium"), "must expose Gemini 3.7 Flash Medium"); + assert.ok(ids.includes("gemini-3.7-flash-high"), "must expose Gemini 3.7 Flash High"); + assert.ok(!ids.includes("gemini-3.6-flash-low")); + assert.ok(!ids.includes("gemini-3.6-flash-medium")); + assert.ok(!ids.includes("gemini-3.6-flash-high")); + assert.ok(!ids.includes("gemini-3.5-flash-extra-low")); + assert.ok(!ids.includes("gemini-3.5-flash-low")); + assert.ok(!ids.includes("gemini-3-flash-agent")); assert.ok(!ids.includes("gemini-3.5-flash-medium")); assert.ok(!ids.includes("gemini-3.5-flash-high")); assert.ok(!ids.includes("gemini-3.5-flash-preview")); @@ -64,8 +67,8 @@ test("agy ships its own live callable model catalog", () => { assert.ok(!ids.includes("gemini-3.1-pro-high"), "must not expose rejected Pro High id"); assert.ok(ids.includes("gemini-pro-agent"), "must expose callable Pro High id"); assert.ok(!ids.includes("gemini-2.5-pro"), "must not expose unavailable Gemini 2.5 Pro"); - assert.ok(ids.includes("gemini-2.5-flash"), "must retain working Gemini 2.5 Flash"); - assert.ok(ids.includes("gemini-2.5-flash-lite"), "must retain working Gemini 2.5 Flash Lite"); + assert.ok(!ids.includes("gemini-2.5-flash")); + assert.ok(!ids.includes("gemini-2.5-flash-lite")); // Tab-completion models are not chat-callable and must be excluded. assert.ok(!ids.includes("tab_flash_lite_preview")); assert.ok(!ids.includes("tab_jump_flash_lite_preview")); @@ -75,15 +78,18 @@ test("agy ships its own live callable model catalog", () => { test("agy model helpers resolve catalog ids and display names", () => { assert.equal(isUserCallableAgyModelId("claude-opus-4-6-thinking"), true); assert.equal(isUserCallableAgyModelId("gemini-2.5-pro"), false); - assert.equal(isUserCallableAgyModelId("gemini-2.5-flash"), true); + assert.equal(isUserCallableAgyModelId("gemini-2.5-flash"), false); assert.equal(isUserCallableAgyModelId("gemini-3.1-pro-high"), false); assert.equal(isUserCallableAgyModelId("gemini-pro-agent"), true); - assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-low"), true); - assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-medium"), true); - assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-high"), true); - assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-extra-low"), true); - assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-low"), true); - assert.equal(isUserCallableAgyModelId("gemini-3-flash-agent"), true); + assert.equal(isUserCallableAgyModelId("gemini-3.7-flash-low"), true); + assert.equal(isUserCallableAgyModelId("gemini-3.7-flash-medium"), true); + assert.equal(isUserCallableAgyModelId("gemini-3.7-flash-high"), true); + assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-low"), false); + assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-medium"), false); + assert.equal(isUserCallableAgyModelId("gemini-3.6-flash-high"), false); + assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-extra-low"), false); + assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-low"), false); + assert.equal(isUserCallableAgyModelId("gemini-3-flash-agent"), false); assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-medium"), false); assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-high"), false); assert.equal(isUserCallableAgyModelId("gemini-3.5-flash-preview"), false); @@ -94,23 +100,20 @@ test("agy model helpers resolve catalog ids and display names", () => { "Claude Opus 4.6 (Thinking)" ); assert.equal(getClientVisibleAgyModelName("gemini-pro-agent"), "Gemini 3.1 Pro (High)"); + assert.equal(getClientVisibleAgyModelName("gemini-3.7-flash-low"), "Gemini 3.7 Flash (Low)"); assert.equal( - getClientVisibleAgyModelName("gemini-3.5-flash-extra-low"), - "Gemini 3.5 Flash (Low)" + getClientVisibleAgyModelName("gemini-3.7-flash-medium"), + "Gemini 3.7 Flash (Medium)" ); - assert.equal(getClientVisibleAgyModelName("gemini-3.6-flash-low"), "Gemini 3.6 Flash (Low)"); - assert.equal( - getClientVisibleAgyModelName("gemini-3.6-flash-medium"), - "Gemini 3.6 Flash (Medium)" - ); - assert.equal(getClientVisibleAgyModelName("gemini-3.6-flash-high"), "Gemini 3.6 Flash (High)"); - assert.equal(getClientVisibleAgyModelName("gemini-3.5-flash-low"), "Gemini 3.5 Flash (Medium)"); - assert.equal(getClientVisibleAgyModelName("gemini-3-flash-agent"), "Gemini 3.5 Flash (High)"); + assert.equal(getClientVisibleAgyModelName("gemini-3.7-flash-high"), "Gemini 3.7 Flash (High)"); assert.equal(getClientVisibleAgyModelName("unknown-model", "Fallback"), "Fallback"); }); test("agy live discovery accepts new chat models while excluding tab-completion models", () => { assert.equal(isDiscoverableAgyModelId("gemini-new-live-tier"), true); + assert.equal(isDiscoverableAgyModelId("gemini-3.6-flash-high"), false); + assert.equal(isDiscoverableAgyModelId("gemini-3-flash-agent"), false); + assert.equal(isDiscoverableAgyModelId("gemini-2.5-flash"), false); assert.equal(isDiscoverableAgyModelId("tab_flash_lite_preview"), false); assert.equal(isDiscoverableAgyModelId("tab_jump_flash_lite_preview"), false); assert.equal(isDiscoverableAgyModelId(""), false); diff --git a/tests/unit/agy-usage-quota.test.ts b/tests/unit/agy-usage-quota.test.ts index 3ac4508107..a9ed841de9 100644 --- a/tests/unit/agy-usage-quota.test.ts +++ b/tests/unit/agy-usage-quota.test.ts @@ -18,7 +18,7 @@ test("getUsageForProvider routes agy through the Antigravity usage implementatio new Response( JSON.stringify({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 0.75, resetTime: "2026-06-06T00:00:00Z", @@ -48,7 +48,7 @@ test("getUsageForProvider routes agy through the Antigravity usage implementatio ); assert.ok("quotas" in result, "agy should return quota data when upstream responds"); - const quota = (result as { quotas: Record }).quotas["gemini-3-flash-agent"]; + const quota = (result as { quotas: Record }).quotas["gemini-3.7-flash-high"]; assert.ok(quota, "should expose the upstream agy per-model quota"); assert.equal(quota.remainingPercentage, 75); assert.equal( @@ -65,7 +65,7 @@ test("parseQuotaData treats agy quota payloads like Antigravity", () => { const parsed = providerLimitUtils.parseQuotaData("agy", { quotas: { credits: { remaining: 42 }, - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { used: 250, total: 1000, remainingPercentage: 75, @@ -83,7 +83,7 @@ test("parseQuotaData treats agy quota payloads like Antigravity", () => { assert.ok(credits, "credits quota should be rendered"); assert.equal(credits.isCredits, true); - const modelQuota = parsed.find((quota: any) => quota.name === "gemini-3-flash-agent"); + const modelQuota = parsed.find((quota: any) => quota.name === "gemini-3.7-flash-high"); assert.ok(modelQuota, "model quota should be rendered"); assert.equal(modelQuota.remainingPercentage, 75); }); diff --git a/tests/unit/antigravity-local-usage-fallback-3821.test.ts b/tests/unit/antigravity-local-usage-fallback-3821.test.ts index d8b58b0078..38c5f1464d 100644 --- a/tests/unit/antigravity-local-usage-fallback-3821.test.ts +++ b/tests/unit/antigravity-local-usage-fallback-3821.test.ts @@ -7,7 +7,7 @@ * the fallback queries and asserts the flip — the regression guard for the id contract. * * Contract note: the fallback queries `usage_history WHERE model = ` - * (e.g. gemini-3-flash-agent), so the executor MUST log usage under that same model id + * (e.g. gemini-3.7-flash-high), so the executor MUST log usage under that same model id * for the fallback to fire. This test pins exactly that join. */ import test from "node:test"; @@ -47,7 +47,7 @@ test("Antigravity fetchAvailableModels(used=0) → localUsageHistory when usage_ db.prepare( `INSERT INTO usage_history (provider, model, connection_id, tokens_input, tokens_output, tokens_reasoning, success, timestamp) VALUES (?, ?, ?, ?, ?, ?, 1, ?)` - ).run("antigravity", "gemini-3-flash-agent", "conn-local-1", 1000, 1500, 500, seededTimestamp); + ).run("antigravity", "gemini-3.7-flash-high", "conn-local-1", 1000, 1500, 500, seededTimestamp); // Total seeded tokens = 3000 → ceil(3000/1000) = 3 units used. globalThis.fetch = (async (input: any) => { @@ -61,7 +61,7 @@ test("Antigravity fetchAvailableModels(used=0) → localUsageHistory when usage_ ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime }, }, }, @@ -79,8 +79,8 @@ test("Antigravity fetchAvailableModels(used=0) → localUsageHistory when usage_ const result = await getUsageForProvider(connection, { forceRefresh: true }); assert.ok(result && "quotas" in result, "should return quotas"); - const quota = (result as any).quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have the gemini-3-flash-agent quota"); + const quota = (result as any).quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have the gemini-3.7-flash-high quota"); assert.equal(quota.quotaSource, "localUsageHistory", "stale full bucket replaced by local usage"); assert.equal(quota.used, 3, "3000 seeded tokens → 3 units used"); }); @@ -99,7 +99,7 @@ test("Antigravity stays fetchAvailableModels when usage_history has no matching ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { quotaInfo: { remainingFraction: 1.0, resetTime } }, + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime } }, }, }), } as Response; @@ -114,7 +114,7 @@ test("Antigravity stays fetchAvailableModels when usage_history has no matching }; const result = await getUsageForProvider(connection, { forceRefresh: true }); - const quota = (result as any).quotas["gemini-3-flash-agent"]; + const quota = (result as any).quotas["gemini-3.7-flash-high"]; assert.ok(quota, "should have the quota"); assert.equal(quota.quotaSource, "fetchAvailableModels", "no local rows → keep the catalog view"); assert.equal(quota.used, 0, "full bucket stays at 0 used"); diff --git a/tests/unit/antigravity-model-aliases.test.ts b/tests/unit/antigravity-model-aliases.test.ts index ff6c5b550d..c821766058 100644 --- a/tests/unit/antigravity-model-aliases.test.ts +++ b/tests/unit/antigravity-model-aliases.test.ts @@ -21,15 +21,16 @@ 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)"], - ["gemini-3.6-flash-medium", "Gemini 3.6 Flash (Medium)"], - ["gemini-3.6-flash-high", "Gemini 3.6 Flash (High)"], - ["gemini-3.5-flash-extra-low", "Gemini 3.5 Flash (Low)"], - ["gemini-3.5-flash-low", "Gemini 3.5 Flash (Medium)"], - ["gemini-3-flash-agent", "Gemini 3.5 Flash (High)"], + ["gemini-3.7-flash-low", "Gemini 3.7 Flash (Low)"], ] as const; const RETIRED_FLASH_IDS = [ + "gemini-3.6-flash-low", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-high", + "gemini-3.5-flash-extra-low", + "gemini-3.5-flash-low", + "gemini-3-flash-agent", "gemini-3.5-flash-medium", "gemini-3.5-flash-high", "gemini-3.5-flash-preview", @@ -44,6 +45,9 @@ test("toClientAntigravityQuotaModelId preserves upstream Gemini Flash bucket IDs // Retired preview buckets are dropped (hidden from clients). assert.equal(toClientAntigravityQuotaModelId("gemini-3.5-flash-preview"), null); assert.equal(toClientAntigravityQuotaModelId("gemini-3-flash-preview"), null); + for (const retiredId of RETIRED_FLASH_IDS) { + assert.equal(toClientAntigravityQuotaModelId(retiredId), null); + } assert.equal(toClientAntigravityQuotaModelId(""), null); }); @@ -86,9 +90,9 @@ test("isUserCallableAntigravityModelId only allows public chat-capable model IDs } assert.equal(isUserCallableAntigravityModelId("gemini-3.1-flash-lite"), true); assert.equal(isUserCallableAntigravityModelId("gemini-2.5-pro"), false); - assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash"), true); - assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash-lite"), true); - assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash-thinking"), true); + assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash"), false); + assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash-lite"), false); + assert.equal(isUserCallableAntigravityModelId("gemini-2.5-flash-thinking"), false); assert.equal(isUserCallableAntigravityModelId("gemini-pro-agent"), true); // #3184: Claude IS user-callable through the Antigravity OAuth provider (same backend as // `agy`, verified empirically). An earlier assumption that it was removed in Antigravity @@ -109,6 +113,10 @@ test("isDiscoverableAntigravityModelId accepts new live chat models without a st assert.equal(isDiscoverableAntigravityModelId("claude-sonnet-5"), true); assert.equal(isDiscoverableAntigravityModelId("gemini-new-live-tier"), true); + for (const retiredId of RETIRED_FLASH_IDS) { + assert.equal(isDiscoverableAntigravityModelId(retiredId), false); + } + assert.equal(isDiscoverableAntigravityModelId("tab_flash_lite_preview"), false); assert.equal(isDiscoverableAntigravityModelId("gemini-3.1-flash-image"), false); assert.equal(isDiscoverableAntigravityModelId("gemini-3.1-flash-tts-preview"), false); @@ -150,15 +158,6 @@ test("ANTIGRAVITY_PUBLIC_MODELS exposes current live names and capabilities", () for (const retiredId of RETIRED_FLASH_IDS) { assert.equal(getPublicModel(retiredId), undefined); } - assert.equal(getClientVisibleAntigravityModelName("gemini-2.5-flash"), "Gemini 2.5 Flash"); - assert.equal( - getClientVisibleAntigravityModelName("gemini-2.5-flash-lite"), - "Gemini 2.5 Flash Lite" - ); - assert.equal( - getClientVisibleAntigravityModelName("gemini-2.5-flash-thinking"), - "Gemini 2.5 Flash Thinking" - ); assert.deepEqual(getPublicModel("gpt-oss-120b-medium"), { id: "gpt-oss-120b-medium", name: "GPT-OSS 120B (Medium)", diff --git a/tests/unit/antigravity-native-toolcall-collect.test.ts b/tests/unit/antigravity-native-toolcall-collect.test.ts index 429b0b0210..a9c5bc8cc8 100644 --- a/tests/unit/antigravity-native-toolcall-collect.test.ts +++ b/tests/unit/antigravity-native-toolcall-collect.test.ts @@ -7,7 +7,7 @@ * part (usually carrying a `thoughtSignature`), so any tools request collected * to an empty stream and was rewritten into a synthetic 502 "Provider returned * empty content" — breaking Chatwit Captain Copilot / reply suggestions on - * agy/gemini-3.5-flash-low while plain text completions kept working. + * agy/gemini-3.7-flash-low while plain text completions kept working. */ import test from "node:test"; import assert from "node:assert/strict"; diff --git a/tests/unit/antigravity-per-model-output-cap.test.ts b/tests/unit/antigravity-per-model-output-cap.test.ts index 4a8cc1b5ef..c194f66910 100644 --- a/tests/unit/antigravity-per-model-output-cap.test.ts +++ b/tests/unit/antigravity-per-model-output-cap.test.ts @@ -82,6 +82,21 @@ test("an unknown model id falls back to the conservative ceiling", () => { assert.equal(clampFor("no-such-model-xyz", 65535), MAX_ANTIGRAVITY_OUTPUT_TOKENS); }); +test("retired Antigravity Flash ids cannot inherit provider-neutral output caps", () => { + for (const modelId of [ + "gemini-3.6-flash-low", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-high", + "gemini-3.5-flash-extra-low", + "gemini-3.5-flash-low", + "gemini-3.5-flash-medium", + "gemini-3.5-flash-high", + "gemini-3-flash-agent", + ]) { + assert.equal(clampFor(modelId, 65536), MAX_ANTIGRAVITY_OUTPUT_TOKENS, modelId); + } +}); + test("a missing or empty model id falls back too", () => { assert.equal(clampFor(undefined, 65535), MAX_ANTIGRAVITY_OUTPUT_TOKENS); assert.equal(clampFor(null, 65535), MAX_ANTIGRAVITY_OUTPUT_TOKENS); @@ -211,7 +226,7 @@ test("a provider-prefixed model id resolves to the model's ceiling, not the fall const cases: Array<[string, number]> = [ ["agy/gemini-3.1-pro-high", 65535], ["antigravity/gemini-3.1-pro-high", 65535], - ["agy/gemini-3.6-flash-high", 65536], + ["agy/gemini-3.7-flash-high", 65536], ["agy/gpt-oss-120b-medium", 32768], ]; diff --git a/tests/unit/antigravity-quota-host-8965.test.ts b/tests/unit/antigravity-quota-host-8965.test.ts index a0e61832bc..a383292d85 100644 --- a/tests/unit/antigravity-quota-host-8965.test.ts +++ b/tests/unit/antigravity-quota-host-8965.test.ts @@ -89,7 +89,7 @@ test("#8965: quota reads use the runtime host (daily-cloudcode-pa), not cloudcod json: async () => ({ buckets: [ { - modelId: "gemini-3-flash-agent", + modelId: "gemini-3.7-flash-high", remainingFraction: 0.4, resetTime: RESET_IN_2_HOURS, }, @@ -103,10 +103,10 @@ test("#8965: quota reads use the runtime host (daily-cloudcode-pa), not cloudcod ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime: RESET_IN_2_HOURS }, }, - "gemini-3.5-flash-low": { + "gemini-3.7-flash-medium": { quotaInfo: { remainingFraction: 0.8, resetTime: RESET_IN_2_HOURS }, }, }, @@ -148,9 +148,9 @@ test("#8965: quota reads use the runtime host (daily-cloudcode-pa), not cloudcod // The per-model quota should come from retrieveUserQuota (the live source), // NOT fetchAvailableModels (the stale catalog fallback). - assert.ok(quotas["gemini-3-flash-agent"], "gemini-3-flash-agent quota present"); + assert.ok(quotas["gemini-3.7-flash-high"], "gemini-3.7-flash-high quota present"); assert.equal( - quotas["gemini-3-flash-agent"].quotaSource, + quotas["gemini-3.7-flash-high"].quotaSource, "retrieveUserQuota", "quota source is retrieveUserQuota (live), not fetchAvailableModels" ); @@ -197,7 +197,7 @@ test("#8965 behavioral impact: live quota source + weekly bucket unreachable whe json: async () => ({ buckets: [ { - modelId: "gemini-3-flash-agent", + modelId: "gemini-3.7-flash-high", remainingFraction: 0.4, resetTime: RESET_IN_2_HOURS, }, @@ -211,7 +211,7 @@ test("#8965 behavioral impact: live quota source + weekly bucket unreachable whe ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime: RESET_IN_2_HOURS }, }, }, @@ -250,9 +250,9 @@ test("#8965 behavioral impact: live quota source + weekly bucket unreachable whe const quotas = (result as UsageResult).quotas; // The per-model quota MUST come from retrieveUserQuota — the live signal. - assert.ok(quotas["gemini-3-flash-agent"], "gemini-3-flash-agent quota present"); + assert.ok(quotas["gemini-3.7-flash-high"], "gemini-3.7-flash-high quota present"); assert.equal( - quotas["gemini-3-flash-agent"].quotaSource, + quotas["gemini-3.7-flash-high"].quotaSource, "retrieveUserQuota", "quota source is retrieveUserQuota (live), not fetchAvailableModels" ); @@ -260,4 +260,4 @@ test("#8965 behavioral impact: live quota source + weekly bucket unreachable whe // The weekly group quota MUST also be present because retrieveUserQuotaSummary // was served by the runtime host. assert.ok(quotas.gemini_weekly, "weekly group quota present"); -}); \ No newline at end of file +}); diff --git a/tests/unit/antigravity-quota-skipping.test.ts b/tests/unit/antigravity-quota-skipping.test.ts index 260ae0ca03..f23a43883a 100644 --- a/tests/unit/antigravity-quota-skipping.test.ts +++ b/tests/unit/antigravity-quota-skipping.test.ts @@ -21,7 +21,7 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a // Simulate Claude Opus being exhausted, while Gemini is NOT. quotaCache.setQuotaCache(connectionId, "antigravity", { "claude-opus-4-6-thinking": { remainingPercentage: 0, resetAt: null }, - "gemini-3.5-flash-high": { remainingPercentage: 100, resetAt: null }, + "gemini-3.7-flash-high": { remainingPercentage: 100, resetAt: null }, }); // Verify that Claude models are considered exhausted. @@ -49,7 +49,7 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a quotaCache.isQuotaExhaustedForRequest( connectionId, "antigravity", - "antigravity/gemini-3.5-flash-high" + "antigravity/gemini-3.7-flash-high" ), false, "Gemini Flash should NOT be exhausted" @@ -58,7 +58,7 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a quotaCache.isQuotaExhaustedForRequest( connectionId, "antigravity", - "antigravity/gemini-2.5-pro" + "antigravity/gemini-pro-agent" ), false, "Gemini Pro should share Gemini family quota and NOT be exhausted" @@ -68,7 +68,7 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a const connectionIdAgy = "conn-agy-test"; quotaCache.setQuotaCache(connectionIdAgy, "agy", { "claude-opus-4-6-thinking": { remainingPercentage: 0, resetAt: null }, - "gemini-3.5-flash-high": { remainingPercentage: 100, resetAt: null }, + "gemini-3.7-flash-high": { remainingPercentage: 100, resetAt: null }, }); assert.equal( @@ -77,7 +77,7 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a "Claude Opus under 'agy' should be exhausted" ); assert.equal( - quotaCache.isQuotaExhaustedForRequest(connectionIdAgy, "agy", "agy/gemini-3.5-flash-high"), + quotaCache.isQuotaExhaustedForRequest(connectionIdAgy, "agy", "agy/gemini-3.7-flash-high"), false, "Gemini Flash under 'agy' should NOT be exhausted" ); @@ -112,8 +112,8 @@ test("isQuotaExhaustedForRequest isolates Claude and Gemini quota families for a test("isQuotaExhaustedForRequest scopes gemini exhaustion to the requested model, not sibling models", () => { const connectionId = "conn-gemini-sibling-test"; quotaCache.setQuotaCache(connectionId, "antigravity", { - "gemini-3.6-flash-medium": { remainingPercentage: 0, resetAt: null }, - "gemini-2.5-pro": { remainingPercentage: 100, resetAt: null }, + "gemini-3.7-flash-medium": { remainingPercentage: 0, resetAt: null }, + "gemini-pro-agent": { remainingPercentage: 100, resetAt: null }, gemini_weekly: { remainingPercentage: 0, resetAt: null }, }); @@ -121,33 +121,33 @@ test("isQuotaExhaustedForRequest scopes gemini exhaustion to the requested model quotaCache.isQuotaExhaustedForRequest( connectionId, "antigravity", - "antigravity/gemini-3.6-flash-medium" + "antigravity/gemini-3.7-flash-medium" ), true, - "gemini-3.6 at 0% should be exhausted even when gemini-2.5-pro still has quota" + "gemini-3.7 at 0% should be exhausted even when gemini-pro-agent still has quota" ); assert.equal( quotaCache.isQuotaExhaustedForRequest( connectionId, "antigravity", - "antigravity/gemini-2.5-pro" + "antigravity/gemini-pro-agent" ), false, - "gemini-2.5-pro should remain available when only gemini-3.6 is depleted" + "gemini-pro-agent should remain available when only gemini-3.7 Flash is depleted" ); }); test("isQuotaExhaustedForRequest treats near-zero remaining as exhausted at default threshold", () => { const connectionId = "conn-near-zero-test"; quotaCache.setQuotaCache(connectionId, "antigravity", { - "gemini-3.6-flash-medium": { remainingPercentage: 0.00000167, resetAt: null }, + "gemini-3.7-flash-medium": { remainingPercentage: 0.00000167, resetAt: null }, }); assert.equal( quotaCache.isQuotaExhaustedForRequest( connectionId, "antigravity", - "antigravity/gemini-3.6-flash-medium" + "antigravity/gemini-3.7-flash-medium" ), true, "effectively-zero remaining should count as exhausted" diff --git a/tests/unit/antigravity-retired-public-models.test.ts b/tests/unit/antigravity-retired-public-models.test.ts index 207e6aca43..5f778d5965 100644 --- a/tests/unit/antigravity-retired-public-models.test.ts +++ b/tests/unit/antigravity-retired-public-models.test.ts @@ -17,44 +17,40 @@ import { CLI_TOOLS } from "../../src/shared/constants/cliTools.ts"; const RETIRED_PUBLIC_MODELS = [ "gemini-3-pro-preview", + "gemini-3.6-flash-high", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-low", + "gemini-3-flash-agent", + "gemini-3.5-flash-low", + "gemini-3.5-flash-extra-low", "gemini-2.5-pro", + "gemini-2.5-flash-thinking", + "gemini-2.5-flash", + "gemini-2.5-flash-lite", "gemini-2.5-computer-use-preview-10-2025", ] as const; const EXPECTED_LEADING_MODEL_ORDER = [ - "gemini-3.6-flash-high", - "gemini-3.6-flash-medium", - "gemini-3.6-flash-low", - "claude-opus-4-6-thinking", - "claude-sonnet-4-6", - "gemini-pro-agent", - "gemini-3.1-pro-low", - "gemini-3-flash-agent", - "gemini-3.5-flash-low", - "gemini-3.5-flash-extra-low", -] as const; - -const EXPECTED_ANTIGRAVITY_LEADING_MODEL_ORDER = [ "gemini-3.7-flash-high", "gemini-3.7-flash-medium", - ...EXPECTED_LEADING_MODEL_ORDER, + "gemini-3.7-flash-low", + "gemini-pro-agent", + "gemini-3.1-pro-low", + "gemini-3.1-flash-lite", + "claude-opus-4-6-thinking", + "claude-sonnet-4-6", + "gpt-oss-120b-medium", ] as const; const ACTIVE_FLASH_MODEL_IDS = [ - "gemini-3-flash-agent", - "gemini-3.5-flash-low", - "gemini-3.5-flash-extra-low", -] as const; - -const CURRENT_36_FLASH_MODEL_IDS = [ - "gemini-3.6-flash-high", - "gemini-3.6-flash-medium", - "gemini-3.6-flash-low", + "gemini-3.7-flash-high", + "gemini-3.7-flash-medium", + "gemini-3.7-flash-low", ] as const; test("Antigravity and AGY place their live Gemini Flash tiers first", () => { for (const [provider, models, expectedOrder] of [ - ["antigravity", ANTIGRAVITY_PUBLIC_MODELS, EXPECTED_ANTIGRAVITY_LEADING_MODEL_ORDER], + ["antigravity", ANTIGRAVITY_PUBLIC_MODELS, EXPECTED_LEADING_MODEL_ORDER], ["agy", AGY_PUBLIC_MODELS, EXPECTED_LEADING_MODEL_ORDER], ] as const) { assert.deepEqual( @@ -114,7 +110,7 @@ test("Antigravity and AGY expose gemini-pro-agent and gemini-3.1-pro-high as cal ); }); -test("Antigravity support catalogs expose every live Gemini 3.6 Flash tier", () => { +test("Antigravity support catalogs expose every live Gemini 3.7 Flash tier", () => { const antigravityModelIds = new Set(ANTIGRAVITY_PUBLIC_MODELS.map((model) => model.id)); const agyModelIds = new Set(AGY_PUBLIC_MODELS.map((model) => model.id)); const cliAliases = new Set(CLI_TOOLS.antigravity.modelAliases); @@ -123,7 +119,7 @@ test("Antigravity support catalogs expose every live Gemini 3.6 Flash tier", () FREE_MODEL_BUDGETS.filter((model) => model.provider === "agy").map((model) => model.modelId) ); - for (const modelId of CURRENT_36_FLASH_MODEL_IDS) { + for (const modelId of ACTIVE_FLASH_MODEL_IDS) { assert.equal(antigravityModelIds.has(modelId), true, `${modelId} missing from Antigravity`); assert.equal(agyModelIds.has(modelId), true, `${modelId} missing from AGY`); assert.equal(cliAliases.has(modelId), true, `${modelId} missing from CLI aliases`); @@ -142,7 +138,7 @@ test("Antigravity support catalogs no longer advertise or price the rejected Hig assert.ok(pricing["gemini-pro-agent"]); }); -test("Antigravity and AGY support metadata excludes the retired Gemini 3 Flash id", () => { +test("Antigravity and AGY support metadata excludes retired Flash ids", () => { const cliAliases = CLI_TOOLS.antigravity.modelAliases; const cliModelIds = CLI_TOOLS.antigravity.defaultModels.map((model) => model.id); const agyFreeModelIds = FREE_MODEL_BUDGETS.filter((model) => model.provider === "agy").map( diff --git a/tests/unit/antigravity-thinking-config-preservation.test.ts b/tests/unit/antigravity-thinking-config-preservation.test.ts index 904d453287..547b61e687 100644 --- a/tests/unit/antigravity-thinking-config-preservation.test.ts +++ b/tests/unit/antigravity-thinking-config-preservation.test.ts @@ -10,7 +10,7 @@ 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-3.7-flash-high", "antigravity/gemini-pro-agent", "antigravity/gemini-3.1-pro-low", "antigravity/claude-sonnet-4-6", diff --git a/tests/unit/antigravity-usage-service.test.ts b/tests/unit/antigravity-usage-service.test.ts index 66bbb64a14..6e885c7710 100644 --- a/tests/unit/antigravity-usage-service.test.ts +++ b/tests/unit/antigravity-usage-service.test.ts @@ -35,7 +35,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: undefined, resetTime: "2026-05-26T00:00:00Z", @@ -51,8 +51,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 0, "remaining should be 0%"); assert.equal(quota.unlimited, false, "should not be unlimited"); assert.equal(quota.used > 0, true, "used should be > 0 when quota is exhausted"); @@ -69,7 +69,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 0, resetTime: "2026-05-26T00:00:00Z", @@ -88,8 +88,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 0, "remaining should be 0%"); assert.equal(quota.unlimited, false, "should not be unlimited"); } @@ -105,7 +105,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime: "2026-05-26T00:00:00Z", @@ -124,8 +124,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 100, "remaining should be 100%"); assert.equal(quota.unlimited, false, "should not be unlimited (has resetTime)"); } @@ -176,7 +176,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 0.5, resetTime: "2026-05-26T00:00:00Z", @@ -195,8 +195,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 50, "remaining should be 50%"); assert.equal(quota.unlimited, false, "should not be unlimited"); } @@ -212,7 +212,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.5, resetTime: "2026-05-26T00:00:00Z", @@ -231,8 +231,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 100, "remaining should be clamped to 100%"); assert.equal(quota.unlimited, false, "should not be unlimited (has resetTime)"); } @@ -248,7 +248,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: -0.5, resetTime: "2026-05-26T00:00:00Z", @@ -267,8 +267,8 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => { assert.ok("quotas" in result, "should have quotas"); if ("quotas" in result) { - const quota = result.quotas["gemini-3-flash-agent"]; - assert.ok(quota, "should have quota for gemini-3-flash-agent"); + const quota = result.quotas["gemini-3.7-flash-high"]; + assert.ok(quota, "should have quota for gemini-3.7-flash-high"); assert.equal(quota.remainingPercentage, 0, "remaining should be clamped to 0%"); assert.equal(quota.unlimited, false, "should not be unlimited"); } diff --git a/tests/unit/antigravity-weekly-quota-4017.test.ts b/tests/unit/antigravity-weekly-quota-4017.test.ts index 3f0f15fbee..ee29b1c52d 100644 --- a/tests/unit/antigravity-weekly-quota-4017.test.ts +++ b/tests/unit/antigravity-weekly-quota-4017.test.ts @@ -176,7 +176,7 @@ test("getUsageForProvider(antigravity) merges weekly quotas with the selected CL json: async () => ({ buckets: [ { - modelId: "gemini-3-flash-agent", + modelId: "gemini-3.7-flash-high", remainingFraction: 0.4, resetTime: RESET_IN_2_HOURS, }, @@ -190,7 +190,7 @@ test("getUsageForProvider(antigravity) merges weekly quotas with the selected CL ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime: RESET_IN_2_HOURS }, }, }, @@ -211,8 +211,8 @@ test("getUsageForProvider(antigravity) merges weekly quotas with the selected CL const quotas = (result as UsageResult).quotas; // Existing per-model 5h quota is untouched. - assert.ok(quotas["gemini-3-flash-agent"], "per-model 5h quota still present"); - assert.equal(quotas["gemini-3-flash-agent"].quotaSource, "retrieveUserQuota"); + assert.ok(quotas["gemini-3.7-flash-high"], "per-model 5h quota still present"); + assert.equal(quotas["gemini-3.7-flash-high"].quotaSource, "retrieveUserQuota"); // New weekly group quota is merged in alongside it. assert.ok(quotas.gemini_weekly, "weekly group quota merged in"); @@ -235,7 +235,7 @@ test("getUsageForProvider(antigravity) is unaffected when retrieveUserQuotaSumma ok: true, json: async () => ({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1.0, resetTime: RESET_IN_2_HOURS }, }, }, @@ -253,6 +253,6 @@ test("getUsageForProvider(antigravity) is unaffected when retrieveUserQuotaSumma const result = await getUsageForProvider(connection, { forceRefresh: true }); const quotas = (result as UsageResult).quotas; - assert.ok(quotas["gemini-3-flash-agent"], "per-model quota still present without weekly data"); + assert.ok(quotas["gemini-3.7-flash-high"], "per-model quota still present without weekly data"); assert.equal(quotas.gemini_weekly, undefined, "no weekly key when the RPC is unavailable"); }); diff --git a/tests/unit/auto-best-free-tier-filter.test.ts b/tests/unit/auto-best-free-tier-filter.test.ts index e7a1ed417d..fd3d5d3243 100644 --- a/tests/unit/auto-best-free-tier-filter.test.ts +++ b/tests/unit/auto-best-free-tier-filter.test.ts @@ -11,7 +11,7 @@ * `chat.ts` routes through `resolveAutoRoutingState` → `createVirtualAutoCombo` * (autoRouting.ts), NOT through `createBuiltinAutoCombo`. So the chat path * skipped the tier filter entirely and `auto/best-free` behaved as plain - * `auto/cheap`, allowing paid models (e.g. antigravity/gemini-3.6-flash-high) + * `auto/cheap`, allowing paid models (e.g. antigravity/gemini-3.7-flash-high) * to be selected from the full pool. * * classifyAutoModel() is module-private, so this exercises it through the public diff --git a/tests/unit/auto-combo-credentialed-model-pool.test.ts b/tests/unit/auto-combo-credentialed-model-pool.test.ts index 2fe14ac4fc..fb39b0d7c5 100644 --- a/tests/unit/auto-combo-credentialed-model-pool.test.ts +++ b/tests/unit/auto-combo-credentialed-model-pool.test.ts @@ -87,9 +87,9 @@ test("credentialed providers expose one logical candidate per visible registry m ); for (const model of [ "antigravity/claude-sonnet-4-6", - "antigravity/gemini-3.6-flash-low", - "antigravity/gemini-3.6-flash-medium", - "antigravity/gemini-3.6-flash-high", + "antigravity/gemini-3.7-flash-low", + "antigravity/gemini-3.7-flash-medium", + "antigravity/gemini-3.7-flash-high", ]) { assert.ok(modelStrings.includes(model), `${model} should be eligible for auto routing`); } @@ -121,15 +121,15 @@ test("candidate transparency expands a logical model into per-account rows", asy }); test("connection model exclusions narrow only that model's account allowlist", async () => { - const { first, second } = await seedConnections(["gemini-3.6-*"]); + const { first, second } = await seedConnections(["gemini-3.7-*"]); const combo = await virtualFactory.createVirtualAutoCombo(undefined); const candidates = antigravityCandidates(combo); const geminiCandidates = candidates.filter((candidate) => - candidate.model.startsWith("antigravity/gemini-3.6-") + candidate.model.startsWith("antigravity/gemini-3.7-") ); - assert.ok(geminiCandidates.length >= 3, "Gemini 3.6 candidates should remain available"); + assert.ok(geminiCandidates.length >= 3, "Gemini 3.7 candidates should remain available"); for (const candidate of geminiCandidates) { assert.deepEqual(candidate.allowedConnectionIds, [second.id]); } @@ -150,9 +150,9 @@ test("hiding the first registry model does not drop the credentialed provider", assert.equal(modelStrings.includes("antigravity/claude-sonnet-4-6"), false); for (const model of [ - "antigravity/gemini-3.6-flash-low", - "antigravity/gemini-3.6-flash-medium", - "antigravity/gemini-3.6-flash-high", + "antigravity/gemini-3.7-flash-low", + "antigravity/gemini-3.7-flash-medium", + "antigravity/gemini-3.7-flash-high", ]) { assert.ok(modelStrings.includes(model), `${model} should remain after Sonnet is hidden`); } diff --git a/tests/unit/chat-messages-validation-6402.test.ts b/tests/unit/chat-messages-validation-6402.test.ts index e7993d40d3..e4931b0c54 100644 --- a/tests/unit/chat-messages-validation-6402.test.ts +++ b/tests/unit/chat-messages-validation-6402.test.ts @@ -181,16 +181,10 @@ test("Antigravity Gemini-family regression covers every current callable tier", for (const model of [ "gemini-pro-agent", "gemini-3.1-pro-low", - "gemini-3.6-flash-high", - "gemini-3.6-flash-medium", - "gemini-3.6-flash-low", - "gemini-3-flash-agent", - "gemini-3.5-flash-low", - "gemini-3.5-flash-extra-low", + "gemini-3.7-flash-high", + "gemini-3.7-flash-medium", + "gemini-3.7-flash-low", "gemini-3.1-flash-lite", - "gemini-2.5-flash-thinking", - "gemini-2.5-flash", - "gemini-2.5-flash-lite", ]) { assert.ok(coveredModels.has(model), `expected regression coverage for ${model}`); } diff --git a/tests/unit/combo-prescreen.test.ts b/tests/unit/combo-prescreen.test.ts index 3db96b85e5..63d681b9c5 100644 --- a/tests/unit/combo-prescreen.test.ts +++ b/tests/unit/combo-prescreen.test.ts @@ -210,7 +210,7 @@ test("priority combo: quota 429 on passthrough provider does not skip another mo const combo = await combosDb.createCombo({ name: "passthrough-quota-scope", strategy: "priority", - models: ["antigravity/claude-opus-4-6-thinking", "antigravity/gemini-3-flash-agent"], + models: ["antigravity/claude-opus-4-6-thinking", "antigravity/gemini-3.7-flash-high"], }); const response = await handleComboChat({ @@ -235,7 +235,7 @@ test("priority combo: quota 429 on passthrough provider does not skip another mo }); assert.equal(response.status, 200); - assert.equal(calls.at(-1), "antigravity/gemini-3-flash-agent"); + assert.equal(calls.at(-1), "antigravity/gemini-3.7-flash-high"); assert.ok( calls.includes("antigravity/claude-opus-4-6-thinking"), "first passthrough model should be attempted before fallback" diff --git a/tests/unit/devin-cli-catalog.test.ts b/tests/unit/devin-cli-catalog.test.ts index 6fc24066f7..7cd92e15b1 100644 --- a/tests/unit/devin-cli-catalog.test.ts +++ b/tests/unit/devin-cli-catalog.test.ts @@ -28,10 +28,10 @@ test("Devin CLI catalog includes the refreshed native model ids", () => { "claude-opus-5-high", "claude-opus-5-xhigh", "claude-opus-5-max", - "gemini-3-6-flash-minimal", - "gemini-3-6-flash-low", - "gemini-3-6-flash-medium", - "gemini-3-6-flash-high", + "gemini-3-7-flash-minimal", + "gemini-3-7-flash-low", + "gemini-3-7-flash-medium", + "gemini-3-7-flash-high", "kimi-k3-low", "kimi-k3-high", "kimi-k3-max", diff --git a/tests/unit/executor-agy.test.ts b/tests/unit/executor-agy.test.ts index 2bab4fbfe9..008b6694fa 100644 --- a/tests/unit/executor-agy.test.ts +++ b/tests/unit/executor-agy.test.ts @@ -29,7 +29,7 @@ test("getExecutor('antigravity') returns AntigravityExecutor", () => { test("getExecutor('agy') builds valid streaming URL", () => { const executor = getExecutor("agy"); - const url = executor.buildUrl("gemini-3.5-flash-high", true); + const url = executor.buildUrl("gemini-3.7-flash-high", true); assert.ok( url.includes("streamGenerateContent?alt=sse"), `expected streaming endpoint URL, got: ${url}` @@ -38,7 +38,7 @@ test("getExecutor('agy') builds valid streaming URL", () => { test("getExecutor('agy') builds valid non-streaming URL", () => { const executor = getExecutor("agy"); - const url = executor.buildUrl("gemini-3.5-flash-high", false); + const url = executor.buildUrl("gemini-3.7-flash-high", false); // Antigravity executor always uses streaming endpoint (buildUrl ignores stream flag) assert.ok( url.includes("streamGenerateContent?alt=sse"), diff --git a/tests/unit/executor-antigravity.test.ts b/tests/unit/executor-antigravity.test.ts index 1bce1a6f59..facd6ba68b 100644 --- a/tests/unit/executor-antigravity.test.ts +++ b/tests/unit/executor-antigravity.test.ts @@ -516,7 +516,7 @@ test("AntigravityExecutor.collectStreamToResponse converts textual tool call SSE const result = await executor.collectStreamToResponse( response, - "gemini-3.5-flash-low", + "gemini-3.7-flash-low", "https://example.com", { Authorization: "Bearer ag-token" }, { request: {} } diff --git a/tests/unit/executor-notion-web.test.ts b/tests/unit/executor-notion-web.test.ts index 8fc3b32196..ca2da88ab4 100644 --- a/tests/unit/executor-notion-web.test.ts +++ b/tests/unit/executor-notion-web.test.ts @@ -48,11 +48,27 @@ describe("NotionWebExecutor — registry consistency", () => { it("registers a model catalog reachable via getModelsByProviderId", () => { const models = getModelsByProviderId("notion-web"); - assert.ok(models.length >= 1); - assert.ok(models.some((m) => m.id === "notion-ai")); - // Seed catalog uses real web-picker labels (fable-5 / gpt-5.6-sol), not food codenames. - assert.ok( - models.some((m) => m.id === "fable-5" || m.id === "gpt-5.6-sol" || m.id === "opus-4.8") + assert.deepEqual( + models.map((m) => m.id), + [ + "notion-ai", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gemini-3.7-flash", + "gemini-3.1-pro", + "fable-5", + "opus-5", + "sonnet-5", + "haiku-4.5", + "grok-4.6", + "kimi-k3", + "kimi-k2.7-code", + "deepseek-v4-pro", + "glm-5.2", + ] ); assert.equal( models.some( @@ -103,6 +119,7 @@ const COOKIE_WITH_SPACE = "token_v2=xyz; space_id=space-1; notion_user_id=user-1 describe("NotionWebExecutor — upstream translation (mocked TLS fetch)", () => { it("posts createThread + config/context/user and returns a chat.completion", async () => { + mod.__resetNotionThreadSessionsForTests(); const executor = new mod.NotionWebExecutor(); let capturedUrl = ""; let capturedHeaders: Record = {}; diff --git a/tests/unit/executor-promptql.test.ts b/tests/unit/executor-promptql.test.ts index 499813322a..07ebfb94c6 100644 --- a/tests/unit/executor-promptql.test.ts +++ b/tests/unit/executor-promptql.test.ts @@ -213,7 +213,7 @@ describe("PromptQl — helpers", () => { assert.equal(models.clientFacingPromptQlModelId("pql/gpt-5.6-sol"), "gpt-5.6-sol"); const r = models.resolvePromptQlModel("Claude Fable 5"); assert.ok(r); - assert.equal(r!.id, "vertex-claude-fable-5"); + assert.equal(r!.id, "bedrock-claude-fable-5"); }); it("converts credit micros to USD", () => { diff --git a/tests/unit/gemini-imagen-predict.test.ts b/tests/unit/gemini-imagen-predict.test.ts deleted file mode 100644 index 4ef1776c75..0000000000 --- a/tests/unit/gemini-imagen-predict.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Google AI Studio (Gemini API) Imagen support on /v1/images/generations. - * - * Imagen uses the dedicated ":predict" endpoint (instances/parameters body, - * base64 predictions), NOT generateContent. Before this, `gemini/imagen-4.0-*` - * was advertised in /v1/models but unroutable — the image route rejected it with - * "Invalid image model" because `gemini` was not in the image registry. - * - * These cover the pure request-builder / response-parser and the registry wiring. - * The live Google call is not exercised (Imagen needs a billing-enabled key). - */ -import test from "node:test"; -import assert from "node:assert/strict"; - -import { IMAGE_PROVIDERS, parseImageModel } from "../../open-sse/config/imageRegistry.ts"; -import { - buildImagenPredictBody, - parseImagenPredictResponse, - isImagenModel, -} from "../../open-sse/handlers/imageGeneration/providers/googleImagen.ts"; - -test("gemini image provider is registered for the Imagen family via google-imagen format", () => { - const gemini = IMAGE_PROVIDERS.gemini; - assert.ok(gemini, "gemini image provider must exist"); - assert.equal(gemini.format, "google-imagen"); - assert.equal(gemini.authHeader, "x-goog-api-key"); - assert.equal(gemini.baseUrl, "https://generativelanguage.googleapis.com/v1beta/models"); - assert.deepEqual( - gemini.models.map((m) => m.id), - ["imagen-4.0-generate-001", "imagen-4.0-ultra-generate-001", "imagen-4.0-fast-generate-001"] - ); -}); - -test("parseImageModel resolves gemini/imagen-4.0-* to the gemini provider", () => { - assert.deepEqual(parseImageModel("gemini/imagen-4.0-generate-001"), { - provider: "gemini", - model: "imagen-4.0-generate-001", - }); -}); - -test("isImagenModel gates only the Imagen family (flash-image belongs on the chat route)", () => { - assert.equal(isImagenModel("imagen-4.0-generate-001"), true); - assert.equal(isImagenModel("imagen-4.0-ultra-generate-001"), true); - assert.equal(isImagenModel("gemini-2.5-flash-image"), false); - assert.equal(isImagenModel("nano-banana-pro"), false); - assert.equal(isImagenModel(""), false); - assert.equal(isImagenModel(undefined), false); -}); - -test("buildImagenPredictBody produces the :predict instances/parameters shape", () => { - const body = buildImagenPredictBody({ prompt: "a red apple", n: 2, size: "1792x1024" }); - assert.deepEqual(body, { - instances: [{ prompt: "a red apple" }], - parameters: { sampleCount: 2, aspectRatio: "16:9" }, - }); -}); - -test("buildImagenPredictBody clamps sampleCount to [1,4] and defaults aspectRatio to 1:1", () => { - assert.equal(buildImagenPredictBody({ prompt: "x" }).parameters.sampleCount, 1); - assert.equal(buildImagenPredictBody({ prompt: "x", n: 0 }).parameters.sampleCount, 1); - assert.equal(buildImagenPredictBody({ prompt: "x", n: 99 }).parameters.sampleCount, 4); - assert.equal(buildImagenPredictBody({ prompt: "x" }).parameters.aspectRatio, "1:1"); - // Native aspect ratio passes through. - assert.equal(buildImagenPredictBody({ prompt: "x", aspect_ratio: "9:16" }).parameters.aspectRatio, "9:16"); -}); - -test("parseImagenPredictResponse normalizes predictions[].bytesBase64Encoded to OpenAI shape", () => { - const out = parseImagenPredictResponse( - { - predictions: [ - { bytesBase64Encoded: "AAAA", mimeType: "image/png" }, - { bytesBase64Encoded: "BBBB", mimeType: "image/png" }, - ], - }, - "a red apple" - ); - assert.equal(out.data.length, 2); - assert.deepEqual(out.data[0], { b64_json: "AAAA", revised_prompt: "a red apple" }); - assert.equal(typeof out.created, "number"); -}); - -test("parseImagenPredictResponse tolerates empty/absent predictions", () => { - assert.deepEqual(parseImagenPredictResponse({}, "x").data, []); - assert.deepEqual(parseImagenPredictResponse({ predictions: [] }, "x").data, []); - assert.deepEqual(parseImagenPredictResponse({ predictions: [{}] }, "x").data, []); -}); diff --git a/tests/unit/gemini-models-parser.test.ts b/tests/unit/gemini-models-parser.test.ts index 83007103c5..6d3dfbd752 100644 --- a/tests/unit/gemini-models-parser.test.ts +++ b/tests/unit/gemini-models-parser.test.ts @@ -3,9 +3,7 @@ import assert from "node:assert/strict"; import { parseGeminiModelsList } from "../../src/lib/providerModels/geminiModelsParser"; -// A representative slice of the live generativelanguage v1beta/models response — including the -// image models (gemini-*-image via generateContent, imagen-* via predict) that the Vertex catalog -// must surface dynamically. +// A representative slice of the live generativelanguage v1beta/models response. const SAMPLE = { models: [ { @@ -21,11 +19,6 @@ const SAMPLE = { displayName: "Gemini 3 Pro Image Preview", supportedGenerationMethods: ["generateContent", "countTokens"], }, - { - name: "models/imagen-4.0-generate-001", - displayName: "Imagen 4.0", - supportedGenerationMethods: ["predict"], - }, { name: "models/text-embedding-004", displayName: "Text Embedding 004", @@ -41,13 +34,6 @@ const SAMPLE = { displayName: "Veo 3.0", supportedGenerationMethods: ["predictLongRunning"], }, - { - // Defensive: an Imagen model exposed via a long-running method must stay - // "images", never "video". - name: "models/imagen-future-preview", - displayName: "Imagen Future", - supportedGenerationMethods: ["predictLongRunning"], - }, ], }; @@ -70,13 +56,6 @@ test("parseGeminiModelsList maps generateContent image models to the chat endpoi assert.deepEqual(proImage!.supportedEndpoints, ["chat"]); }); -test("parseGeminiModelsList maps Imagen predict models to the images endpoint", () => { - const models = parseGeminiModelsList(SAMPLE); - const imagen = models.find((m) => m.id === "imagen-4.0-generate-001"); - assert.ok(imagen, "imagen-4.0-generate-001 should be present"); - assert.deepEqual(imagen!.supportedEndpoints, ["images"]); -}); - test("parseGeminiModelsList maps embedContent and bidiGenerateContent", () => { const models = parseGeminiModelsList(SAMPLE); assert.deepEqual(models.find((m) => m.id === "text-embedding-004")!.supportedEndpoints, [ @@ -94,13 +73,6 @@ test("parseGeminiModelsList maps Veo predictLongRunning models to the video endp assert.deepEqual(veo!.supportedEndpoints, ["video"]); }); -test("parseGeminiModelsList keeps Imagen as images even via a long-running method", () => { - const models = parseGeminiModelsList(SAMPLE); - const imagen = models.find((m) => m.id === "imagen-future-preview"); - assert.ok(imagen, "imagen-future-preview should be present"); - assert.deepEqual(imagen!.supportedEndpoints, ["images"]); -}); - test("parseGeminiModelsList defaults to chat and tolerates empty/missing input", () => { assert.deepEqual(parseGeminiModelsList({}), []); assert.deepEqual(parseGeminiModelsList(null), []); diff --git a/tests/unit/gemini-web.test.ts b/tests/unit/gemini-web.test.ts index 8ae1fe9675..b8f31f4faa 100644 --- a/tests/unit/gemini-web.test.ts +++ b/tests/unit/gemini-web.test.ts @@ -171,7 +171,7 @@ test("Provider: gemini-web has correct models", async () => { models.map((m: any) => [m.id, m.name]), [ ["gemini-3.1-pro", "Gemini 3.1 Pro"], - ["gemini-3.5-flash", "Gemini 3.5 Flash"], + ["gemini-3.7-flash", "Gemini 3.7 Flash"], ["gemini-3.1-flash-lite", "Gemini 3.1 Flash-Lite"], ] ); diff --git a/tests/unit/ghe-copilot-targetformat-parity.test.ts b/tests/unit/ghe-copilot-targetformat-parity.test.ts index 505a26edf4..e941c59f33 100644 --- a/tests/unit/ghe-copilot-targetformat-parity.test.ts +++ b/tests/unit/ghe-copilot-targetformat-parity.test.ts @@ -8,7 +8,8 @@ import test from "node:test"; import assert from "node:assert/strict"; -const { gheCopilotProvider } = await import("../../open-sse/config/providers/registry/ghe-copilot/index.ts"); +const { gheCopilotProvider } = + await import("../../open-sse/config/providers/registry/ghe-copilot/index.ts"); const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts"); type ModelEntry = { id: string; targetFormat?: string; [k: string]: unknown }; @@ -30,7 +31,7 @@ const MUST_NOT_BE_RESPONSES = [ "claude-sonnet-4.5", "claude-haiku-4.5", "gemini-3.1-pro-preview", - "gemini-3.5-flash", + "gemini-3.7-flash", ]; for (const id of MUST_NOT_BE_RESPONSES) { diff --git a/tests/unit/github-copilot-model-discovery.test.ts b/tests/unit/github-copilot-model-discovery.test.ts index 5e0cb3c9fe..5c86f90873 100644 --- a/tests/unit/github-copilot-model-discovery.test.ts +++ b/tests/unit/github-copilot-model-discovery.test.ts @@ -140,7 +140,7 @@ test("curated Copilot allowlist contains the final approved model ids only", () "claude-sonnet-4.5", "claude-haiku-4.5", "gemini-3.1-pro-preview", - "gemini-3.5-flash", + "gemini-3.7-flash", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", diff --git a/tests/unit/model-capabilities-registry.test.ts b/tests/unit/model-capabilities-registry.test.ts index c23e05a061..80d4efd7aa 100644 --- a/tests/unit/model-capabilities-registry.test.ts +++ b/tests/unit/model-capabilities-registry.test.ts @@ -154,7 +154,7 @@ test("unknown models keep maxOutputTokens null instead of using a generic defaul ); }); -test("Antigravity Gemini 3.5 upstream IDs share the Flash capability profile", () => { +test("provider-neutral Gemini 3.5 tier IDs retain their non-thinking capabilities", () => { for (const modelId of [ "gemini-3.5-flash-extra-low", "gemini-3.5-flash-low", @@ -162,22 +162,21 @@ test("Antigravity Gemini 3.5 upstream IDs share the Flash capability profile", ( ]) { const spec = MODEL_SPECS[modelId]; assert.ok(spec, `missing exact MODEL_SPECS entry for ${modelId}`); - const capabilities = modelCapabilities.getResolvedModelCapabilities(`antigravity/${modelId}`); + const capabilities = modelCapabilities.getResolvedModelCapabilities(modelId); assert.equal(capabilities.contextWindow, 1048576, modelId); assert.equal(capabilities.maxOutputTokens, 65536, modelId); - assert.equal(capabilities.supportsThinking, true, modelId); + // These ids encode the upstream reasoning tier and do not accept a client-supplied effort. + assert.equal(capabilities.supportsThinking, false, modelId); assert.equal(capabilities.supportsTools, true, modelId); assert.equal(capabilities.supportsVision, true, modelId); } }); -test("Antigravity Gemini 3.7 and 3.6 tier IDs share the Flash capability profile", () => { +test("Antigravity Gemini 3.7 tier IDs share the Flash capability profile", () => { for (const modelId of [ "gemini-3.7-flash-high", "gemini-3.7-flash-medium", - "gemini-3.6-flash-high", - "gemini-3.6-flash-medium", - "gemini-3.6-flash-low", + "gemini-3.7-flash-low", ]) { const spec = MODEL_SPECS[modelId]; assert.ok(spec, `missing exact MODEL_SPECS entry for ${modelId}`); diff --git a/tests/unit/models-catalog-route.test.ts b/tests/unit/models-catalog-route.test.ts index cebb7cc89d..d3c073d8f9 100644 --- a/tests/unit/models-catalog-route.test.ts +++ b/tests/unit/models-catalog-route.test.ts @@ -696,12 +696,15 @@ test("v1 models catalog exposes current Antigravity aliases without retired mode assert.equal(ids.has("antigravity/gemini-3.1-pro"), false); assert.equal(ids.has("antigravity/gemini-2.5-computer-use-preview-10-2025"), false); assert.equal(ids.has("antigravity/rev19-uic3-1p"), false); - assert.ok(ids.has("antigravity/gemini-3.6-flash-high")); - assert.ok(ids.has("antigravity/gemini-3.6-flash-medium")); - assert.ok(ids.has("antigravity/gemini-3.6-flash-low")); - assert.ok(ids.has("antigravity/gemini-3.5-flash-extra-low")); - assert.ok(ids.has("antigravity/gemini-3.5-flash-low")); - assert.ok(ids.has("antigravity/gemini-3-flash-agent")); + assert.ok(ids.has("antigravity/gemini-3.7-flash-high")); + assert.ok(ids.has("antigravity/gemini-3.7-flash-medium")); + assert.ok(ids.has("antigravity/gemini-3.7-flash-low")); + assert.equal(ids.has("antigravity/gemini-3.6-flash-high"), false); + assert.equal(ids.has("antigravity/gemini-3.6-flash-medium"), false); + assert.equal(ids.has("antigravity/gemini-3.6-flash-low"), false); + assert.equal(ids.has("antigravity/gemini-3.5-flash-extra-low"), false); + assert.equal(ids.has("antigravity/gemini-3.5-flash-low"), false); + assert.equal(ids.has("antigravity/gemini-3-flash-agent"), false); assert.equal(ids.has("antigravity/gemini-3.5-flash-medium"), false); assert.equal(ids.has("antigravity/gemini-3.5-flash-high"), false); assert.equal(ids.has("antigravity/gemini-3.5-flash-preview"), false); diff --git a/tests/unit/opencode-go-effort-aliases-8353.test.ts b/tests/unit/opencode-go-effort-aliases-8353.test.ts index 2d6bc632ae..1c5d4b79dc 100644 --- a/tests/unit/opencode-go-effort-aliases-8353.test.ts +++ b/tests/unit/opencode-go-effort-aliases-8353.test.ts @@ -98,7 +98,12 @@ test("#8353 catalog: aliases are NOT synthesized on opencode-zen", () => { for (const { alias } of ISSUE_ALIASES) { assert.equal(zenIds.has(alias), false, `opencode-zen must not expose ${alias}`); } + // kimi-k3 became a live zen model in the 2026-08-17 registry sync (present in + // https://opencode.ai/zen/v1/models) — only the remaining Go-tier-only bases + // must stay absent from zen. The effort alias kimi-k3-max is still Go-only + // and remains covered by the ISSUE_ALIASES loop above. for (const base of NEW_BASES) { + if (base === "kimi-k3") continue; assert.equal(zenIds.has(base), false, `opencode-zen must not expose base ${base}`); } }); diff --git a/tests/unit/perplexity-web-model-mappings.test.ts b/tests/unit/perplexity-web-model-mappings.test.ts index 7b6999af50..d58b02597c 100644 --- a/tests/unit/perplexity-web-model-mappings.test.ts +++ b/tests/unit/perplexity-web-model-mappings.test.ts @@ -27,9 +27,14 @@ test("Perplexity Web registers the refreshed model catalog", () => { "pplx-sonar", "pplx-kimi", "pplx-glm", - "pplx-grok-4.5", + "pplx-grok-4.6", ]; assert.deepEqual([...modelIds].sort(), expectedModelIds.sort()); + + const modelNames = new Map(models.map((model) => [model.id, model.name])); + assert.equal(modelNames.get("pplx-gemini"), "Gemini 3.7 Flash (via Perplexity)"); + assert.equal(modelNames.get("pplx-kimi"), "Kimi K3 (via Perplexity)"); + assert.equal(modelNames.get("pplx-grok-4.6"), "Grok 4.6 (via Perplexity)"); }); test("every advertised Perplexity Web model has an explicit internal mapping", () => { @@ -37,10 +42,16 @@ test("every advertised Perplexity Web model has an explicit internal mapping", ( assert.deepEqual(missing, []); assert.deepEqual(MODEL_MAP["pplx-gpt-5.6-terra"], ["copilot", "gpt56_terra"]); assert.deepEqual(MODEL_MAP["pplx-gpt-5.6-sol"], ["copilot", "gpt56_sol"]); - assert.deepEqual(MODEL_MAP["pplx-grok-4.5"], ["copilot", "grok45low"]); + assert.deepEqual(MODEL_MAP["pplx-gemini"], ["copilot", "gemini37flash"]); + assert.deepEqual(MODEL_MAP["pplx-kimi"], ["copilot", "kimik3thinking"]); + assert.deepEqual(MODEL_MAP["pplx-grok-4.6"], ["copilot", "grok46low"]); assert.deepEqual(MODEL_MAP["pplx-opus"], ["copilot", "claude50opus"]); + assert.equal(THINKING_MAP["pplx-gemini"], "gemini37flashthinking"); assert.equal(THINKING_MAP["pplx-opus"], "claude50opusthinking"); assert.equal(THINKING_MAP["pplx-gpt-5.6-terra"], "gpt56_terra_thinking"); assert.equal(THINKING_MAP["pplx-gpt-5.6-sol"], "gpt56_sol_thinking"); - assert.equal(THINKING_MAP["pplx-grok-4.5"], "grok45medium"); + assert.equal(THINKING_MAP["pplx-kimi"], "kimik3thinking"); + assert.equal(THINKING_MAP["pplx-grok-4.6"], "grok46medium"); + assert.equal(MODEL_MAP["pplx-grok-4.5"], undefined); + assert.equal(THINKING_MAP["pplx-grok-4.5"], undefined); }); diff --git a/tests/unit/pricing-ag-flash-tiers.test.ts b/tests/unit/pricing-ag-flash-tiers.test.ts index 90c2f574e4..8e72760913 100644 --- a/tests/unit/pricing-ag-flash-tiers.test.ts +++ b/tests/unit/pricing-ag-flash-tiers.test.ts @@ -3,55 +3,52 @@ import assert from "node:assert/strict"; import { getDefaultPricing } from "../../src/shared/constants/pricing.ts"; -// Antigravity exposes Gemini 3.5 Flash via three public client IDs in -// ANTIGRAVITY_PUBLIC_MODELS (`open-sse/config/antigravityModelAliases.ts`): -// - gemini-3.5-flash-extra-low → "Gemini 3.5 Flash (Low)" — upstream Low tier -// - gemini-3-flash-agent → "Gemini 3.5 Flash (High)" — upstream High tier -// - gemini-3.5-flash-low → "Gemini 3.5 Flash (Medium)" — upstream Medium tier -// - gemini-pro-agent → "Gemini 3.1 Pro (High)" — upstream Pro High alias -// All three were missing pricing rows in `ag` (DEFAULT_PRICING.ag), so -// getPricingForModel("ag", id) returned null and downstream cost / quota -// calculations silently fell back to $0. Each row matches its upstream quota tier. +const EXPECTED_GEMINI_3_7_PROMO_PRICING = { + input: 0.75, + output: 3.75, + cached: 0.075, + reasoning: 3.75, + cache_creation: 0.75, +}; -for (const [modelId, tier] of [ - ["gemini-3.5-flash-extra-low", "Low"], - ["gemini-3.5-flash-low", "Medium"], - ["gemini-3-flash-agent", "High"], -] as const) { - test(`ag/${modelId} matches the Gemini 3.5 Flash (${tier}) tier`, () => { - const p = getDefaultPricing().ag[modelId]; - assert.ok(p); - assert.equal(p.input, 0.5); - assert.equal(p.output, 3.0); - assert.equal(p.cached, 0.03); - assert.equal(p.reasoning, 4.5); - assert.equal(p.cache_creation, 0.5); - }); -} +const ACTIVE_FLASH_IDS = [ + "gemini-3.7-flash-low", + "gemini-3.7-flash-medium", + "gemini-3.7-flash-high", +] as const; -test("ag/gemini-pro-agent matches the Gemini 3.1 Pro (High) tier", () => { - const p = getDefaultPricing().ag["gemini-pro-agent"]; - assert.equal(p.input, 4.0); - assert.equal(p.output, 18.0); - assert.equal(p.cached, 0.5); - assert.equal(p.reasoning, 27.0); - assert.equal(p.cache_creation, 4.0); +const RETIRED_ANTIGRAVITY_FLASH_IDS = [ + "gemini-3.6-flash-low", + "gemini-3.6-flash-medium", + "gemini-3.6-flash-high", + "gemini-3.5-flash-extra-low", + "gemini-3.5-flash-low", + "gemini-3.5-flash-medium", + "gemini-3.5-flash-high", + "gemini-3-flash-agent", +] as const; + +test("Antigravity provider namespaces price every live Gemini 3.7 Flash tier", () => { + const pricing = getDefaultPricing() as Record>; + + for (const provider of ["ag", "antigravity", "agy"] as const) { + for (const modelId of ACTIVE_FLASH_IDS) { + assert.deepEqual(pricing[provider][modelId], EXPECTED_GEMINI_3_7_PROMO_PRICING); + } + } }); -// Gemini 3.6 Flash (released 2026-07-21) ships three public client IDs in -// ANTIGRAVITY_PUBLIC_MODELS (`open-sse/config/antigravityModelAliases.ts`) and -// MODEL_SPECS (`src/shared/constants/modelSpecs.ts`), but was missing pricing -// rows in `ag` (DEFAULT_PRICING.ag) — cost / quota calculations silently fell -// back to $0. Pricing: $1.50 input / $7.50 output / $0.15 cached per MTok -// (Google's 2026-07-21 announcement). Thinking tokens billed at output rate. -for (const tier of ["low", "medium", "high"]) { - test(`ag/gemini-3.6-flash-${tier} has a non-null pricing row`, () => { - const p = getDefaultPricing().ag[`gemini-3.6-flash-${tier}`]; - assert.ok(p, `expected a pricing row for ag/gemini-3.6-flash-${tier}`); - assert.equal(p.input, 1.5); - assert.equal(p.output, 7.5); - assert.equal(p.cached, 0.15); - assert.equal(p.reasoning, 7.5); - assert.equal(p.cache_creation, 1.5); - }); -} +test("Antigravity pricing excludes retired Flash ids without affecting other providers", () => { + const pricing = getDefaultPricing() as Record>; + + for (const provider of ["ag", "antigravity", "agy"] as const) { + for (const modelId of RETIRED_ANTIGRAVITY_FLASH_IDS) { + assert.equal(pricing[provider][modelId], undefined, `${provider}/${modelId}`); + } + } + + assert.ok( + pricing.orcarouter["google/gemini-3.6-flash"], + "OrcaRouter must retain the Gemini 3.6 model it still serves" + ); +}); diff --git a/tests/unit/provider-columns.test.ts b/tests/unit/provider-columns.test.ts index 702571d38a..1dc0ca52c3 100644 --- a/tests/unit/provider-columns.test.ts +++ b/tests/unit/provider-columns.test.ts @@ -87,9 +87,9 @@ test("getProviderColumns: Antigravity falls back to dynamic schema (first 3 quot "claude-opus-4-6-thinking": { used: 0, total: 100, remainingPercentage: 100 }, "claude-sonnet-4-6": { used: 0, total: 100, remainingPercentage: 100 }, "gemini-3.1-pro-low": { used: 0, total: 100, remainingPercentage: 100 }, - "gemini-3.5-flash-low": { used: 0, total: 100, remainingPercentage: 100 }, - "gemini-3.5-flash-medium": { used: 0, total: 100, remainingPercentage: 100 }, - "gemini-3.5-flash-high": { used: 0, total: 100, remainingPercentage: 100 }, + "gemini-3.7-flash-low": { used: 0, total: 100, remainingPercentage: 100 }, + "gemini-3.7-flash-medium": { used: 0, total: 100, remainingPercentage: 100 }, + "gemini-3.7-flash-high": { used: 0, total: 100, remainingPercentage: 100 }, }, }); diff --git a/tests/unit/provider-models-config.test.ts b/tests/unit/provider-models-config.test.ts index 2345283959..74838a93f8 100644 --- a/tests/unit/provider-models-config.test.ts +++ b/tests/unit/provider-models-config.test.ts @@ -99,7 +99,7 @@ test("GitHub Copilot registry reflects the current supported model lineup", () = // the OpenAI shape. Port of decolua/9router#2608. assert.equal(getModelTargetFormat("gh", "claude-opus-4.8-fast"), "claude"); assert.equal(getModelTargetFormat("gh", "claude-sonnet-4.6"), "claude"); - assert.equal(getModelTargetFormat("gh", "gemini-3.5-flash"), null); + assert.equal(getModelTargetFormat("gh", "gemini-3.7-flash"), null); assert.equal(getModelTargetFormat("gh", "kimi-k2.7-code"), null); assert.equal(ids.includes("gpt-4"), false); assert.equal(ids.includes("gpt-4o"), false); diff --git a/tests/unit/provider-models-route.test.ts b/tests/unit/provider-models-route.test.ts index cad8888060..237e3bc3b1 100644 --- a/tests/unit/provider-models-route.test.ts +++ b/tests/unit/provider-models-route.test.ts @@ -982,13 +982,6 @@ test("provider models route retries Antigravity discovery endpoints before retur { id: "gemini-3.7-flash-high", name: "Gemini 3.7 Flash (High)" }, { id: "gemini-3.7-flash-medium", name: "Gemini 3.7 Flash (Medium)" }, { id: "gemini-3.8-flash-high", name: "Gemini 3.8 Flash High" }, - { id: "gemini-3.6-flash-high", name: "Gemini 3.6 Flash (High)" }, - { id: "gemini-3.6-flash-medium", name: "Gemini 3.6 Flash (Medium)" }, - { id: "gemini-3.6-flash-low", name: "Gemini 3.6 Flash (Low)" }, - { id: "gemini-3.5-flash-extra-low", name: "Gemini 3.5 Flash (Low)" }, - { id: "gemini-3.5-flash-low", name: "Gemini 3.5 Flash (Medium)" }, - { id: "gemini-3-flash-agent", name: "Gemini 3.5 Flash (High)" }, - { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" }, ]); }); diff --git a/tests/unit/provider-registry-github-copilot-targetformat.test.ts b/tests/unit/provider-registry-github-copilot-targetformat.test.ts index 3dde5fa2ff..21f92e3a46 100644 --- a/tests/unit/provider-registry-github-copilot-targetformat.test.ts +++ b/tests/unit/provider-registry-github-copilot-targetformat.test.ts @@ -40,7 +40,7 @@ const MUST_NOT_BE_RESPONSES = [ "claude-sonnet-4.5", "claude-haiku-4.5", "gemini-3.1-pro-preview", - "gemini-3.5-flash", + "gemini-3.7-flash", ]; for (const id of MUST_NOT_BE_RESPONSES) { diff --git a/tests/unit/provider-registry-openai-gemini-expanded-models.test.ts b/tests/unit/provider-registry-openai-gemini-expanded-models.test.ts index 32981fe1b5..8a2434e3e0 100644 --- a/tests/unit/provider-registry-openai-gemini-expanded-models.test.ts +++ b/tests/unit/provider-registry-openai-gemini-expanded-models.test.ts @@ -13,24 +13,12 @@ import test from "node:test"; import assert from "node:assert/strict"; -const { openaiProvider } = await import( - "../../open-sse/config/providers/registry/openai/index.ts" -); -const { geminiProvider } = await import( - "../../open-sse/config/providers/registry/gemini/index.ts" -); +const { openaiProvider } = await import("../../open-sse/config/providers/registry/openai/index.ts"); +const { geminiProvider } = await import("../../open-sse/config/providers/registry/gemini/index.ts"); -const OPENAI_ADDED_IDS = [ - "gpt-4.1-mini", - "gpt-4.1-nano", - "o3-mini", - "o4-mini", -] as const; +const OPENAI_ADDED_IDS = ["gpt-4.1-mini", "gpt-4.1-nano", "o3-mini", "o4-mini"] as const; -const GEMINI_ADDED_IDS = [ - "gemini-3.1-flash-lite", - "gemini-2.5-flash-lite", -] as const; +const GEMINI_ADDED_IDS = ["gemini-3.1-flash-lite", "gemini-2.5-flash-lite"] as const; test("openai registry exposes gpt-4.1 mini/nano and o3-mini/o4-mini reasoning variants", () => { const ids = new Set(openaiProvider.models.map((m) => m.id)); @@ -69,7 +57,7 @@ test("port did not regress previously curated openai/gemini ids", () => { "gemini-2.5-pro", "gemini-2.5-flash", "gemini-3.1-pro-preview", - "gemini-3.5-flash", + "gemini-3.7-flash", ] as const) { assert.ok(geminiIds.has(id), `existing gemini model ${id} must remain`); } diff --git a/tests/unit/provider-tinycms-web.test.ts b/tests/unit/provider-tinycms-web.test.ts index 48c34614ef..7fea970dee 100644 --- a/tests/unit/provider-tinycms-web.test.ts +++ b/tests/unit/provider-tinycms-web.test.ts @@ -74,15 +74,33 @@ test("tinycms-web is present in the provider REGISTRY with correct shape", () => test("tinycms-web registry has all expected models", () => { const r = REGISTRY["tinycms-web"]; assert.ok(r.models && r.models.length > 0, "must have at least one model"); - const ids = r.models.map((m) => m.id); - - assert.ok(ids.includes("gpt-5-free"), "gpt-5-free must be registered"); - assert.ok(ids.includes("gpt-5.3-free"), "gpt-5.3-free must be registered"); - assert.ok(ids.includes("gpt-5.3-thinking-free"), "gpt-5.3-thinking-free must be registered"); - assert.ok(ids.includes("deepseek-v4-flash"), "deepseek-v4-flash must be registered"); - assert.ok(ids.includes("claude-sonnet-5"), "claude-sonnet-5 must be registered"); - assert.ok(ids.includes("gemini-3.5-flash"), "gemini-3.5-flash must be registered"); - assert.equal(r.models.length, 16, "must have exactly 16 models"); + assert.deepEqual( + r.models.map((m) => m.id), + [ + "claude-fable-5", + "claude-opus-5", + "claude-sonnet-5", + "gpt-5.6-sol", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.3-thinking-free", + "gpt-5.3-free", + "gpt-oss-120b", + "gemini-3.6-flash", + "gemini-3.1-pro-preview", + "gemini-3.1-flash-lite-preview", + "grok-4.5", + "deepseek-v4-pro", + "deepseek-v4-flash", + "kimi-k3", + "glm-5.2", + "qwen3.6-plus", + "mimo-v2.5-pro", + "mimo-v2.5", + ] + ); }); test("tinycms-web model names are human-readable strings", () => { diff --git a/tests/unit/repro-8841-context-overflow-opencode.test.ts b/tests/unit/repro-8841-context-overflow-opencode.test.ts index ff049ab035..6847f47c6f 100644 --- a/tests/unit/repro-8841-context-overflow-opencode.test.ts +++ b/tests/unit/repro-8841-context-overflow-opencode.test.ts @@ -60,7 +60,7 @@ function upstreamContextOverflowResponse() { error: { code: "context_length_exceeded", message: - "Input exceeds the context window for opencode/north-mini-code-free: estimated 210724 input tokens, limit 200000. Reduce the prompt or route to a model with a larger context window.", + "Input exceeds the context window for opencode/mimo-v2.5-free: estimated 210724 input tokens, limit 200000. Reduce the prompt or route to a model with a larger context window.", }, }), { @@ -71,8 +71,8 @@ function upstreamContextOverflowResponse() { } test("#8841 advertised vs compat-filter limit agree", () => { - const advertised = getTokenLimit("opencode-zen", "north-mini-code-free"); - const caps = getResolvedModelCapabilities("opencode/north-mini-code-free"); + const advertised = getTokenLimit("opencode-zen", "mimo-v2.5-free"); + const caps = getResolvedModelCapabilities("opencode/mimo-v2.5-free"); assert.ok(advertised > 0); assert.ok( caps.contextWindow != null && caps.contextWindow > 0, @@ -83,7 +83,7 @@ test("#8841 advertised vs compat-filter limit agree", () => { test("#8841 oversized request rejected up front (no dispatch)", async () => { const body = largeBody(); const pool = [ - target("opencode/north-mini-code-free"), + target("opencode/mimo-v2.5-free"), target("opencode/hy3-free"), ]; @@ -96,7 +96,7 @@ test("#8841 oversized request rejected up front (no dispatch)", async () => { name: "pro-coding-repro-8841", strategy: "priority", models: [ - "opencode/north-mini-code-free", + "opencode/mimo-v2.5-free", "opencode/hy3-free", ], }, diff --git a/tests/unit/review-reviews-v3814-fixes.test.ts b/tests/unit/review-reviews-v3814-fixes.test.ts index 9e701e9eae..7e64496349 100644 --- a/tests/unit/review-reviews-v3814-fixes.test.ts +++ b/tests/unit/review-reviews-v3814-fixes.test.ts @@ -133,18 +133,22 @@ test("LEDGER-2: valid custom headers still pass", () => { assert.equal(res.success, true, res.success ? "" : JSON.stringify(res.error?.issues)); }); -// ── LEDGER-4: every minimax-m3 registry entry is flagged multimodal ── -test("LEDGER-4: all minimax-m3 registry entries set supportsVision (matches lite.ts)", () => { - const entries: { id: string; supportsVision?: boolean }[] = []; - for (const provider of Object.values( +// ── LEDGER-4: MiniMax M3 vision support is provider-specific ── +test("LEDGER-4: minimax-m3 vision metadata matches each provider", () => { + const entries: { provider: string; id: string; supportsVision?: boolean }[] = []; + for (const [provider, entry] of Object.entries( REGISTRY as Record )) { - for (const m of provider.models || []) { - if (/minimax-m3/i.test(m.id)) entries.push(m); + for (const model of entry.models || []) { + if (/minimax-m3/i.test(model.id)) entries.push({ provider, ...model }); } } assert.ok(entries.length >= 6, `expected several minimax-m3 entries, got ${entries.length}`); - const unflagged = entries.filter((m) => m.supportsVision !== true).map((m) => m.id); + const promptql = entries.find((entry) => entry.provider === "promptql"); + assert.notEqual(promptql?.supportsVision, true, "PromptQL MiniMax M3 is text-only"); + const unflagged = entries + .filter((entry) => entry.provider !== "promptql" && entry.supportsVision !== true) + .map((entry) => `${entry.provider}/${entry.id}`); assert.deepEqual( unflagged, [], diff --git a/tests/unit/services/geminiRateLimitTracker.test.ts b/tests/unit/services/geminiRateLimitTracker.test.ts index 4963488976..4f34a04e5a 100644 --- a/tests/unit/services/geminiRateLimitTracker.test.ts +++ b/tests/unit/services/geminiRateLimitTracker.test.ts @@ -47,11 +47,6 @@ test("getModelRpd strips gemma- prefix correctly for gemma models", () => { assert.equal(getModelRpd("gemini/gemma-4-31b-it"), 14400); }); -test("getModelRpd handles image-generation models (no RPM value, -1)", () => { - // RPD is 25 for imagen models; RPM is -1 in the JSON - assert.equal(getModelRpd("imagen-4-generate"), 25); -}); - test("getModelRpd handles models with unlimited RPD (-1)", () => { // gemini-3.5-live-translate has rpd: -1 assert.equal(getModelRpd("gemini-3.5-live-translate"), 0); @@ -207,8 +202,8 @@ test("getModelRpm returns 0 for empty string", () => { assert.equal(getModelRpm(""), 0); }); -test("getModelRpm returns 0 for models with RPM=-1 (imagen)", () => { - assert.equal(getModelRpm("imagen-4-generate"), 0); +test("getModelRpm returns 0 for models with RPM=-1", () => { + assert.equal(getModelRpm("gemini-3.5-live-translate"), 0); }); test("getModelRpm returns 0 for null input", () => { @@ -288,9 +283,9 @@ test("isRpmExhausted returns false for unknown model", () => { assert.equal(isRpmExhausted("gemini/unknown-model"), false); }); -test("isRpmExhausted returns false for model with RPM=-1 (imagen)", () => { - incrementRequestCount("imagen-4-generate"); - assert.equal(isRpmExhausted("imagen-4-generate"), false); +test("isRpmExhausted returns false for model with RPM=-1", () => { + incrementRequestCount("gemini-3.5-live-translate"); + assert.equal(isRpmExhausted("gemini-3.5-live-translate"), false); }); test("isRpmExhausted works with gemini/ prefix", () => { diff --git a/tests/unit/t28-model-catalog-updates.test.ts b/tests/unit/t28-model-catalog-updates.test.ts index 78da2a8ef9..6c44ebcf45 100644 --- a/tests/unit/t28-model-catalog-updates.test.ts +++ b/tests/unit/t28-model-catalog-updates.test.ts @@ -14,10 +14,10 @@ test("T28: gemini AI Studio catalog includes current preview models", () => { assert.ok(geminiIds.includes("gemini-3.1-pro-preview")); assert.ok(geminiIds.includes("gemini-3-flash-preview")); assert.ok(geminiIds.includes("gemini-3.1-flash-lite")); - assert.ok(geminiIds.includes("gemini-3.5-flash")); + assert.ok(geminiIds.includes("gemini-3.7-flash")); assert.ok(geminiIds.includes("gemini-2.5-flash")); assert.ok(geminiIds.includes("gemini-2.5-pro")); - assert.equal(geminiIds[0], "gemini-3.1-pro-preview", "preserve the existing Gemini default"); + assert.equal(geminiIds[0], "gemini-3.7-flash", "use the current Gemini Flash default"); }); test("T28: antigravity static catalog exposes only callable Gemini tier IDs", () => { @@ -26,12 +26,13 @@ test("T28: antigravity static catalog exposes only callable Gemini tier IDs", () assert.ok(!staticIds.includes("gemini-3-pro-preview")); assert.ok(staticIds.includes("gemini-3.7-flash-high")); assert.ok(staticIds.includes("gemini-3.7-flash-medium")); - assert.ok(staticIds.includes("gemini-3.6-flash-high")); - assert.ok(staticIds.includes("gemini-3.6-flash-medium")); - assert.ok(staticIds.includes("gemini-3.6-flash-low")); - assert.ok(staticIds.includes("gemini-3.5-flash-extra-low")); - assert.ok(staticIds.includes("gemini-3.5-flash-low")); - assert.ok(staticIds.includes("gemini-3-flash-agent")); + assert.ok(staticIds.includes("gemini-3.7-flash-low")); + assert.ok(!staticIds.includes("gemini-3.6-flash-high")); + assert.ok(!staticIds.includes("gemini-3.6-flash-medium")); + assert.ok(!staticIds.includes("gemini-3.6-flash-low")); + assert.ok(!staticIds.includes("gemini-3.5-flash-extra-low")); + assert.ok(!staticIds.includes("gemini-3.5-flash-low")); + assert.ok(!staticIds.includes("gemini-3-flash-agent")); assert.ok(!staticIds.includes("gemini-3.5-flash-medium")); assert.ok(!staticIds.includes("gemini-3.5-flash-high")); assert.ok(staticIds.includes("gemini-3.1-pro-low")); @@ -45,21 +46,21 @@ test("T28: antigravity static catalog exposes only callable Gemini tier IDs", () assert.ok(!staticIds.includes("gemini-claude-opus-4-5-thinking")); }); -test("T28: agy free-model metadata labels upstream Gemini 3.6 tier IDs", () => { +test("T28: agy free-model metadata labels upstream Gemini 3.7 tier IDs", () => { const flashNames = Object.fromEntries( FREE_MODEL_BUDGETS.filter( (entry) => entry.provider === "agy" && - ["gemini-3.6-flash-low", "gemini-3.6-flash-medium", "gemini-3.6-flash-high"].includes( + ["gemini-3.7-flash-low", "gemini-3.7-flash-medium", "gemini-3.7-flash-high"].includes( entry.modelId ) ).map((entry) => [entry.modelId, entry.displayName]) ); assert.deepEqual(flashNames, { - "gemini-3.6-flash-low": "Gemini 3.6 Flash (Low)", - "gemini-3.6-flash-medium": "Gemini 3.6 Flash (Medium)", - "gemini-3.6-flash-high": "Gemini 3.6 Flash (High)", + "gemini-3.7-flash-low": "Gemini 3.7 Flash (Low)", + "gemini-3.7-flash-medium": "Gemini 3.7 Flash (Medium)", + "gemini-3.7-flash-high": "Gemini 3.7 Flash (High)", }); }); @@ -90,7 +91,7 @@ test("T28: lmarena registry seeds Direct-chat Text/search; image models in IMAGE // Chat registry: Text + Search only (not Image thrash) assert.ok(lmarenaIds.length >= 40 && lmarenaIds.length < 60); assert.ok(lmarenaIds.includes("gemini-3.1-pro-preview")); - assert.ok(lmarenaIds.includes("gemini-3.5-flash-high")); + assert.ok(lmarenaIds.includes("gemini-3.6-flash")); assert.ok(lmarenaIds.includes("claude-sonnet-5")); assert.ok(!lmarenaIds.includes("flux-2-pro"), "image models must not live in chat registry"); diff --git a/tests/unit/t31-t33-t34-t38-model-specs.test.ts b/tests/unit/t31-t33-t34-t38-model-specs.test.ts index 72e278ec64..077c38a344 100644 --- a/tests/unit/t31-t33-t34-t38-model-specs.test.ts +++ b/tests/unit/t31-t33-t34-t38-model-specs.test.ts @@ -21,7 +21,7 @@ test("T31: antigravity static catalog exposes client-visible Gemini preview IDs" // catalog) retired the `gemini-3-pro-preview` alias, so assert the current // client-visible top flash tier instead. const staticIds = (getStaticModelsForProvider("antigravity") || []).map((m) => m.id); - assert.ok(staticIds.includes("gemini-3.6-flash-high")); + assert.ok(staticIds.includes("gemini-3.7-flash-high")); assert.ok(!staticIds.includes("gemini-3-pro-preview")); // #3303 (agy parity, discussion #3184): the Gemini + Claude budget tiers ARE // client-visible on the Antigravity OAuth backend (Claude was never removed). diff --git a/tests/unit/upstream-retry-hints-toggle.test.ts b/tests/unit/upstream-retry-hints-toggle.test.ts index 3ecd417d46..f8c91001e7 100644 --- a/tests/unit/upstream-retry-hints-toggle.test.ts +++ b/tests/unit/upstream-retry-hints-toggle.test.ts @@ -23,7 +23,7 @@ test("checkFallbackError ignores body reset text when upstream retry hints are d 429, ANTIGRAVITY_RESET_TEXT, 0, - "gemini-3-flash-agent", + "gemini-3.7-flash-high", "antigravity", null, antigravityProfile(false) @@ -40,7 +40,7 @@ test("checkFallbackError honors body reset text when upstream retry hints are en 429, ANTIGRAVITY_RESET_TEXT, 0, - "gemini-3-flash-agent", + "gemini-3.7-flash-high", "antigravity", null, antigravityProfile(true) diff --git a/tests/unit/usage-service-hardening.test.ts b/tests/unit/usage-service-hardening.test.ts index 1110065263..b403cd2f2b 100644 --- a/tests/unit/usage-service-hardening.test.ts +++ b/tests/unit/usage-service-hardening.test.ts @@ -259,7 +259,7 @@ test("usage service prefers Antigravity retrieveUserQuota over catalog quotaInfo return new Response( JSON.stringify({ models: { - "gemini-3-flash-agent": { + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1, resetTime: new Date(Date.now() + 60_000).toISOString(), @@ -276,7 +276,7 @@ test("usage service prefers Antigravity retrieveUserQuota over catalog quotaInfo JSON.stringify({ buckets: [ { - modelId: "gemini-3-flash-agent", + modelId: "gemini-3.7-flash-high", remainingFraction: 0.25, resetTime: new Date(Date.now() + 60_000).toISOString(), }, @@ -294,9 +294,9 @@ test("usage service prefers Antigravity retrieveUserQuota over catalog quotaInfo accessToken: `ag-token-live-quota-${Date.now()}`, }); - assert.equal(usage.quotas["gemini-3-flash-agent"].remainingPercentage, 25); - assert.equal(usage.quotas["gemini-3-flash-agent"].used, 750); - assert.equal(usage.quotas["gemini-3-flash-agent"].quotaSource, "retrieveUserQuota"); + assert.equal(usage.quotas["gemini-3.7-flash-high"].remainingPercentage, 25); + assert.equal(usage.quotas["gemini-3.7-flash-high"].used, 750); + assert.equal(usage.quotas["gemini-3.7-flash-high"].quotaSource, "retrieveUserQuota"); }); test("usage service preserves Antigravity upstream quota bucket ids", async () => { @@ -317,10 +317,10 @@ test("usage service preserves Antigravity upstream quota bucket ids", async () = return new Response( JSON.stringify({ models: { - "gemini-3.5-flash-low": { quotaInfo: { remainingFraction: 1 } }, - "gemini-3.5-flash-high": { quotaInfo: { remainingFraction: 1 } }, + "gemini-3.7-flash-low": { quotaInfo: { remainingFraction: 1 } }, + "gemini-3.7-flash-medium": { quotaInfo: { remainingFraction: 1 } }, + "gemini-3.7-flash-high": { quotaInfo: { remainingFraction: 1 } }, "gemini-3-flash-agent": { quotaInfo: { remainingFraction: 1 } }, - "gemini-3.5-flash-extra-low": { quotaInfo: { remainingFraction: 1 } }, }, }), { status: 200 } @@ -331,8 +331,8 @@ test("usage service preserves Antigravity upstream quota bucket ids", async () = return new Response( JSON.stringify({ buckets: [ - { modelId: "gemini-3-flash-agent", remainingFraction: 0.5 }, - { modelId: "gemini-3.5-flash-extra-low", remainingFraction: 0.25 }, + { modelId: "gemini-3.7-flash-high", remainingFraction: 0.5 }, + { modelId: "gemini-3.7-flash-low", remainingFraction: 0.25 }, ], }), { status: 200 } @@ -347,11 +347,10 @@ test("usage service preserves Antigravity upstream quota bucket ids", async () = accessToken: `ag-token-legacy-buckets-${Date.now()}`, }); - assert.equal(usage.quotas["gemini-3-flash-agent"].remainingPercentage, 50); - assert.equal(usage.quotas["gemini-3.5-flash-extra-low"].remainingPercentage, 25); - assert.equal(usage.quotas["gemini-3.5-flash-low"].remainingPercentage, 100); - assert.equal(usage.quotas["gemini-3.5-flash-medium"], undefined); - assert.equal(usage.quotas["gemini-3.5-flash-high"], undefined); + assert.equal(usage.quotas["gemini-3.7-flash-high"].remainingPercentage, 50); + assert.equal(usage.quotas["gemini-3.7-flash-low"].remainingPercentage, 25); + assert.equal(usage.quotas["gemini-3.7-flash-medium"].remainingPercentage, 100); + assert.equal(usage.quotas["gemini-3-flash-agent"], undefined); }); test("usage service retries Antigravity fetchAvailableModels across the shared fallback order", async () => { diff --git a/tests/unit/veoaifree-web-executor.test.ts b/tests/unit/veoaifree-web-executor.test.ts index e91e54f860..7cfaf344b1 100644 --- a/tests/unit/veoaifree-web-executor.test.ts +++ b/tests/unit/veoaifree-web-executor.test.ts @@ -52,7 +52,6 @@ test("detectIntent returns 'video' for veo models", () => { test("detectIntent returns 'image' for image models", () => { assert.equal(detectIntent("image-gen"), "image"); assert.equal(detectIntent("banana"), "image"); - assert.equal(detectIntent("imagen-4"), "image"); assert.equal(detectIntent("nano-banana"), "image"); });