mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-19 05:32:19 +03:00
feat(providers): refresh curated model catalogs and retire Imagen 4 (#10537)
* feat(providers): refresh Gemini Flash catalogs and pricing * fix(providers): refresh gemini-web Flash catalog * chore(providers): eliminate Gemini 3.5/3.6 Flash models * feat(providers): refresh Perplexity Web model mappings * feat(providers): refresh PromptQL and Notion catalogs * feat(providers): refresh KIE TinyCMS and Conol catalogs * feat(providers): refresh OpenCode Zen catalog * chore(providers): finish Gemini Flash cleanup * chore(providers): retire Google Imagen 4
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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<Record<string, string>>((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);
|
||||
}
|
||||
|
||||
@@ -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<string>([
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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<string, ImageProviderConfig> = {
|
||||
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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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"],
|
||||
};
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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" },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -69,24 +69,26 @@ export const MODEL_MAP: Record<string, [string, string]> = {
|
||||
"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<string, string> = {
|
||||
"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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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");
|
||||
|
||||
|
||||
@@ -100,7 +100,10 @@ export const CLI_TOOLS: Record<string, CliCatalogEntry> = {
|
||||
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<string, CliCatalogEntry> = {
|
||||
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"),
|
||||
|
||||
@@ -153,7 +153,7 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
||||
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<string, ModelSpec> = {
|
||||
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 },
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<string, any> }).quotas["gemini-3-flash-agent"];
|
||||
const quota = (result as { quotas: Record<string, any> }).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);
|
||||
});
|
||||
|
||||
@@ -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 = <public model id>`
|
||||
* (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");
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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],
|
||||
];
|
||||
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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: {} }
|
||||
|
||||
@@ -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<string, string> = {};
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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, []);
|
||||
});
|
||||
@@ -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), []);
|
||||
|
||||
@@ -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"],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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<string, Record<string, unknown>>;
|
||||
|
||||
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<string, Record<string, unknown>>;
|
||||
|
||||
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"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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 },
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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<string, { models?: { id: string; supportsVision?: boolean }[] }>
|
||||
)) {
|
||||
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,
|
||||
[],
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user