From 47ec6237ffbc28b9fa711799357d9745f47ad6ab Mon Sep 17 00:00:00 2001 From: Agustin Rivera <31522568+eleqtrizit@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:09:38 -0700 Subject: [PATCH] fix(nvidia): sync recommended models with featured catalog (#99242) * fix(nvidia): refresh bundled featured models * fix(nvidia): match featured model feed exactly * fix(nvidia): remove transient feed detail * fix(nvidia): correct super context window * fix(nvidia): align auth choice expectation * fix(nvidia): hide deprecated models from selection Refresh the bundled featured catalog while retaining deprecated model metadata for exact-reference compatibility. Route NVIDIA onboarding and catalog picker surfaces through selectable-only provider builders. * fix(nvidia): simplify featured model table * fix(nvidia): restore shipped compatibility rows --- docs/providers/nvidia.md | 38 +-- extensions/nvidia/index.test.ts | 42 ++-- extensions/nvidia/index.ts | 8 +- extensions/nvidia/onboard.test.ts | 41 +++- extensions/nvidia/onboard.ts | 4 +- extensions/nvidia/openclaw.plugin.json | 141 +++++++++-- extensions/nvidia/provider-catalog.test.ts | 264 ++++++++++++++++----- extensions/nvidia/provider-catalog.ts | 25 +- 8 files changed, 418 insertions(+), 145 deletions(-) diff --git a/docs/providers/nvidia.md b/docs/providers/nvidia.md index 1056e8c4e58e..0f3a1f3103c9 100644 --- a/docs/providers/nvidia.md +++ b/docs/providers/nvidia.md @@ -84,8 +84,6 @@ OpenClaw falls back to the bundled catalog and bundled default below. Nemotron 3 Ultra is the default NVIDIA model in OpenClaw. NVIDIA's build page for [`nvidia/nemotron-3-ultra-550b-a55b`](https://build.nvidia.com/nvidia/nemotron-3-ultra-550b-a55b) lists it as an available free endpoint with a 1M-token context specification. -The bundled catalog records a 16,384-token max output to match NVIDIA's current -OpenAI-compatible sample request for the hosted endpoint. The bundled Ultra row sends `chat_template_kwargs: { enable_thinking: false, force_nonempty_content: true }` @@ -98,15 +96,25 @@ hosted in NVIDIA's catalog when their context, latency, or behavior fits better. ## Bundled fallback catalog -| Model ref | Name | Context | Max output | Notes | -| ------------------------------------------ | ---------------------------- | --------- | ---------- | ---------------------------------------- | -| `nvidia/nvidia/nemotron-3-ultra-550b-a55b` | NVIDIA Nemotron 3 Ultra 550B | 1,000,000 | 16,384 | Default | -| `nvidia/nvidia/nemotron-3-super-120b-a12b` | NVIDIA Nemotron 3 Super 120B | 1,048,576 | 8,192 | | -| `nvidia/moonshotai/kimi-k2.5` | Kimi K2.5 | 262,144 | 8,192 | | -| `nvidia/minimaxai/minimax-m2.7` | Minimax M2.7 | 196,608 | 8,192 | | -| `nvidia/z-ai/glm-5.1` | GLM 5.1 | 202,752 | 8,192 | | -| `nvidia/minimaxai/minimax-m2.5` | MiniMax M2.5 | 196,608 | 8,192 | Deprecated; use `minimaxai/minimax-m2.7` | -| `nvidia/z-ai/glm5` | GLM-5 | 202,752 | 8,192 | Deprecated; use `z-ai/glm-5.1` | +The selectable bundled rows snapshot NVIDIA's featured-model catalog. Deprecated +compatibility rows remain resolvable by exact reference but stay out of model +pickers. + +| Model ref | Name | Context | Max output | +| ------------------------------------------ | --------------------- | --------- | ---------- | +| `nvidia/nvidia/nemotron-3-ultra-550b-a55b` | Nemotron 3 Ultra 550B | 1,048,576 | 8,192 | +| `nvidia/nvidia/nemotron-3-super-120b-a12b` | Nemotron 3 Super 120B | 1,000,000 | 8,192 | +| `nvidia/z-ai/glm-5.2` | GLM 5.2 | 202,752 | 8,192 | +| `nvidia/moonshotai/kimi-k2.6` | Kimi K2.6 | 262,144 | 8,192 | +| `nvidia/minimaxai/minimax-m3` | Minimax M3 | 196,608 | 8,192 | +| `nvidia/deepseek-ai/deepseek-v4-pro` | DeepSeek V4 Pro | 262,144 | 16,384 | +| `nvidia/qwen/qwen3.5-397b-a17b` | Qwen3.5 397B A17B | 262,144 | 16,384 | + +The full compatibility catalog also retains these shipped refs for existing +configurations: `nvidia/moonshotai/kimi-k2.5`, `nvidia/z-ai/glm-5.1`, +`nvidia/minimaxai/minimax-m2.5`, `nvidia/z-ai/glm5`, and +`nvidia/minimaxai/minimax-m2.7`. They remain available by exact reference but +never appear in onboarding or model pickers. ## Advanced configuration @@ -119,10 +127,10 @@ hosted in NVIDIA's catalog when their context, latency, or behavior fits better. OpenClaw prefers NVIDIA's public featured-model catalog when NVIDIA auth is - configured and caches it for 24 hours. The bundled fallback catalog is static - and keeps deprecated shipped refs for upgrade compatibility. Costs default - to `0` in source since NVIDIA currently offers free API access for the - listed models. + configured and caches it for 24 hours. The bundled selectable fallback is a + static snapshot of NVIDIA's featured-model catalog; deprecated exact-reference + compatibility rows are hidden from model pickers. Costs default to `0` in + source since NVIDIA currently offers free API access for the listed models. diff --git a/extensions/nvidia/index.test.ts b/extensions/nvidia/index.test.ts index 09379a916e70..828a0290cbe7 100644 --- a/extensions/nvidia/index.test.ts +++ b/extensions/nvidia/index.test.ts @@ -209,11 +209,11 @@ describe("nvidia provider hooks", () => { expect(entries?.map((entry) => entry.id)).toEqual([ "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", + "z-ai/glm-5.2", + "moonshotai/kimi-k2.6", + "minimaxai/minimax-m3", + "deepseek-ai/deepseek-v4-pro", + "qwen/qwen3.5-397b-a17b", ]); expect(entries?.every((entry) => entry.provider === "nvidia")).toBe(true); expect(ssrfRuntimeMocks.fetchWithSsrFGuard).not.toHaveBeenCalled(); @@ -228,11 +228,11 @@ describe("nvidia provider hooks", () => { expect(entries?.map((entry) => entry.id)).toEqual([ "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", + "z-ai/glm-5.2", + "moonshotai/kimi-k2.6", + "minimaxai/minimax-m3", + "deepseek-ai/deepseek-v4-pro", + "qwen/qwen3.5-397b-a17b", ]); expect(entries?.every((entry) => entry.provider === "nvidia")).toBe(true); expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(1); @@ -242,8 +242,8 @@ describe("nvidia provider hooks", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "minimaxai/minimax-m2.7", - "model-name": "Minimax M2.7", + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", context: 196608, "max-output": 8192, }, @@ -253,7 +253,7 @@ describe("nvidia provider hooks", () => { const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext("nvapi-test")); - expect(entries?.map((entry) => entry.id)).toEqual(["minimaxai/minimax-m2.7"]); + expect(entries?.map((entry) => entry.id)).toEqual(["minimaxai/minimax-m3"]); }); it("opts into literal provider-prefix preservation", async () => { @@ -280,8 +280,8 @@ describe("nvidia provider hooks", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "minimaxai/minimax-m2.7", - "model-name": "Minimax M2.7", + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", context: 196608, "max-output": 8192, }, @@ -297,18 +297,18 @@ describe("nvidia provider hooks", () => { expect(staticRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([ "static:nvidia/nvidia/nemotron-3-ultra-550b-a55b", "static:nvidia/nvidia/nemotron-3-super-120b-a12b", - "static:nvidia/moonshotai/kimi-k2.5", - "static:nvidia/minimaxai/minimax-m2.7", - "static:nvidia/z-ai/glm-5.1", - "static:nvidia/minimaxai/minimax-m2.5", - "static:nvidia/z-ai/glm5", + "static:nvidia/z-ai/glm-5.2", + "static:nvidia/moonshotai/kimi-k2.6", + "static:nvidia/minimaxai/minimax-m3", + "static:nvidia/deepseek-ai/deepseek-v4-pro", + "static:nvidia/qwen/qwen3.5-397b-a17b", ]); await expect(catalogProvider?.liveCatalog?.(buildCatalogContext())).resolves.toEqual([]); const liveRows = await catalogProvider?.liveCatalog?.(buildCatalogContext("nvapi-test")); expect(liveRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([ - "live:nvidia/minimaxai/minimax-m2.7", + "live:nvidia/minimaxai/minimax-m3", ]); }); diff --git a/extensions/nvidia/index.ts b/extensions/nvidia/index.ts index 9bcac3aaa048..e49cb70241fb 100644 --- a/extensions/nvidia/index.ts +++ b/extensions/nvidia/index.ts @@ -3,7 +3,7 @@ import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-en import { applyNvidiaConfig, NVIDIA_DEFAULT_MODEL_REF } from "./onboard.js"; import { buildLiveNvidiaProvider, - buildNvidiaProvider, + buildSelectableNvidiaProvider, buildSelectableLiveNvidiaProvider, } from "./provider-catalog.js"; @@ -22,7 +22,9 @@ async function buildNvidiaCatalogModels(ctx: { env: NodeJS.ProcessEnv; resolveProviderApiKey?: (providerId?: string) => { apiKey: string | undefined }; }) { - const provider = hasNvidiaApiToken(ctx) ? await buildLiveNvidiaProvider() : buildNvidiaProvider(); + const provider = hasNvidiaApiToken(ctx) + ? await buildLiveNvidiaProvider() + : buildSelectableNvidiaProvider(); return provider.models.map((model) => ({ provider: PROVIDER_ID, id: model.id, @@ -57,7 +59,7 @@ export default defineSingleProviderPluginEntry({ ], catalog: { buildProvider: buildSelectableLiveNvidiaProvider, - buildStaticProvider: buildNvidiaProvider, + buildStaticProvider: buildSelectableNvidiaProvider, }, augmentModelCatalog: buildNvidiaCatalogModels, wizard: { diff --git a/extensions/nvidia/onboard.test.ts b/extensions/nvidia/onboard.test.ts index c320391dc8cd..172ebc065816 100644 --- a/extensions/nvidia/onboard.test.ts +++ b/extensions/nvidia/onboard.test.ts @@ -18,11 +18,11 @@ describe("nvidia onboard", () => { expect(provider.models.map((model) => model.id)).toEqual([ "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", + "z-ai/glm-5.2", + "moonshotai/kimi-k2.6", + "minimaxai/minimax-m3", + "deepseek-ai/deepseek-v4-pro", + "qwen/qwen3.5-397b-a17b", ]); // Config stores the canonical form; the picker label shows the literal // form via preserveLiteralProviderPrefix. @@ -46,11 +46,34 @@ describe("nvidia onboard", () => { "nvidia/custom-model", "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-3-super-120b-a12b", + "z-ai/glm-5.2", + "moonshotai/kimi-k2.6", + "minimaxai/minimax-m3", + "deepseek-ai/deepseek-v4-pro", + "qwen/qwen3.5-397b-a17b", + ]); + }); + + it("preserves an existing deprecated exact-reference model", () => { + const provider = expectProviderOnboardMergedLegacyConfig({ + applyProviderConfig: applyNvidiaProviderConfig, + providerId: "nvidia", + providerApi: "openai-completions", + baseUrl: "https://integrate.api.nvidia.com/v1", + legacyApi: "openai-completions", + legacyModelId: "moonshotai/kimi-k2.5", + legacyModelName: "Kimi K2.5", + }); + + expect(provider?.models.map((model) => model.id)).toEqual([ "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", + "nvidia/nemotron-3-ultra-550b-a55b", + "nvidia/nemotron-3-super-120b-a12b", + "z-ai/glm-5.2", + "moonshotai/kimi-k2.6", + "minimaxai/minimax-m3", + "deepseek-ai/deepseek-v4-pro", + "qwen/qwen3.5-397b-a17b", ]); }); }); diff --git a/extensions/nvidia/onboard.ts b/extensions/nvidia/onboard.ts index ca5276654733..7a9c977663dc 100644 --- a/extensions/nvidia/onboard.ts +++ b/extensions/nvidia/onboard.ts @@ -3,14 +3,14 @@ import { createDefaultModelsPresetAppliers, type OpenClawConfig, } from "openclaw/plugin-sdk/provider-onboard"; -import { buildNvidiaProvider, NVIDIA_DEFAULT_MODEL_ID } from "./provider-catalog.js"; +import { buildSelectableNvidiaProvider, NVIDIA_DEFAULT_MODEL_ID } from "./provider-catalog.js"; export const NVIDIA_DEFAULT_MODEL_REF = NVIDIA_DEFAULT_MODEL_ID; const nvidiaPresetAppliers = createDefaultModelsPresetAppliers({ primaryModelRef: NVIDIA_DEFAULT_MODEL_REF, resolveParams: (_cfg: OpenClawConfig) => { - const defaultProvider = buildNvidiaProvider(); + const defaultProvider = buildSelectableNvidiaProvider(); return { providerId: "nvidia", api: defaultProvider.api ?? "openai-completions", diff --git a/extensions/nvidia/openclaw.plugin.json b/extensions/nvidia/openclaw.plugin.json index 4b7c5fdffe4f..7fe523d1f200 100644 --- a/extensions/nvidia/openclaw.plugin.json +++ b/extensions/nvidia/openclaw.plugin.json @@ -28,10 +28,10 @@ "models": [ { "id": "nvidia/nemotron-3-ultra-550b-a55b", - "name": "NVIDIA Nemotron 3 Ultra 550B", + "name": "Nemotron 3 Ultra 550B", "input": ["text"], - "contextWindow": 1000000, - "maxTokens": 16384, + "contextWindow": 1048576, + "maxTokens": 8192, "cost": { "input": 0, "output": 0, @@ -44,9 +44,9 @@ }, { "id": "nvidia/nemotron-3-super-120b-a12b", - "name": "NVIDIA Nemotron 3 Super 120B", + "name": "Nemotron 3 Super 120B", "input": ["text"], - "contextWindow": 1048576, + "contextWindow": 1000000, "maxTokens": 8192, "cost": { "input": 0, @@ -58,6 +58,86 @@ "requiresStringContent": true } }, + { + "id": "z-ai/glm-5.2", + "name": "GLM 5.2", + "input": ["text"], + "contextWindow": 202752, + "maxTokens": 8192, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + } + }, + { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "input": ["text"], + "contextWindow": 262144, + "maxTokens": 8192, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + } + }, + { + "id": "minimaxai/minimax-m3", + "name": "Minimax M3", + "input": ["text"], + "contextWindow": 196608, + "maxTokens": 8192, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + } + }, + { + "id": "deepseek-ai/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "input": ["text"], + "contextWindow": 262144, + "maxTokens": 16384, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + } + }, + { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "input": ["text"], + "contextWindow": 262144, + "maxTokens": 16384, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + } + }, { "id": "moonshotai/kimi-k2.5", "name": "Kimi K2.5", @@ -72,23 +152,10 @@ }, "compat": { "requiresStringContent": true - } - }, - { - "id": "minimaxai/minimax-m2.7", - "name": "Minimax M2.7", - "input": ["text"], - "contextWindow": 196608, - "maxTokens": 8192, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 }, - "compat": { - "requiresStringContent": true - } + "status": "deprecated", + "statusReason": "Still available by exact reference; use moonshotai/kimi-k2.6 for new NVIDIA setups.", + "replacedBy": "moonshotai/kimi-k2.6" }, { "id": "z-ai/glm-5.1", @@ -104,7 +171,10 @@ }, "compat": { "requiresStringContent": true - } + }, + "status": "deprecated", + "statusReason": "Still available by exact reference; use z-ai/glm-5.2 for new NVIDIA setups.", + "replacedBy": "z-ai/glm-5.2" }, { "id": "minimaxai/minimax-m2.5", @@ -122,8 +192,8 @@ "requiresStringContent": true }, "status": "deprecated", - "statusReason": "Shipped compatibility row; use minimaxai/minimax-m2.7 for new NVIDIA setups.", - "replacedBy": "minimaxai/minimax-m2.7" + "statusReason": "Still available by exact reference; use minimaxai/minimax-m3 for new NVIDIA setups.", + "replacedBy": "minimaxai/minimax-m3" }, { "id": "z-ai/glm5", @@ -141,8 +211,27 @@ "requiresStringContent": true }, "status": "deprecated", - "statusReason": "Shipped compatibility row; use z-ai/glm-5.1 for new NVIDIA setups.", - "replacedBy": "z-ai/glm-5.1" + "statusReason": "Still available by exact reference; use z-ai/glm-5.2 for new NVIDIA setups.", + "replacedBy": "z-ai/glm-5.2" + }, + { + "id": "minimaxai/minimax-m2.7", + "name": "Minimax M2.7", + "input": ["text"], + "contextWindow": 196608, + "maxTokens": 8192, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "compat": { + "requiresStringContent": true + }, + "status": "deprecated", + "statusReason": "Still available by exact reference; use minimaxai/minimax-m3 for new NVIDIA setups.", + "replacedBy": "minimaxai/minimax-m3" } ] } diff --git a/extensions/nvidia/provider-catalog.test.ts b/extensions/nvidia/provider-catalog.test.ts index bce32fb1f2d3..5c1e511e343d 100644 --- a/extensions/nvidia/provider-catalog.test.ts +++ b/extensions/nvidia/provider-catalog.test.ts @@ -1,13 +1,88 @@ // Nvidia tests cover provider catalog plugin behavior. import { afterEach, describe, expect, it, vi } from "vitest"; +import manifest from "./openclaw.plugin.json" with { type: "json" }; import { buildLiveNvidiaProvider, buildNvidiaProvider, + buildSelectableNvidiaProvider, buildSelectableLiveNvidiaProvider, clearNvidiaFeaturedModelCacheForTests, NVIDIA_FEATURED_MODELS_URL, } from "./provider-catalog.js"; +const EXPECTED_FEATURED_MODELS = [ + { + id: "nvidia/nemotron-3-ultra-550b-a55b", + name: "Nemotron 3 Ultra 550B", + contextWindow: 1_048_576, + maxTokens: 8_192, + }, + { + id: "nvidia/nemotron-3-super-120b-a12b", + name: "Nemotron 3 Super 120B", + contextWindow: 1_000_000, + maxTokens: 8_192, + }, + { id: "z-ai/glm-5.2", name: "GLM 5.2", contextWindow: 202_752, maxTokens: 8_192 }, + { + id: "moonshotai/kimi-k2.6", + name: "Kimi K2.6", + contextWindow: 262_144, + maxTokens: 8_192, + }, + { + id: "minimaxai/minimax-m3", + name: "Minimax M3", + contextWindow: 196_608, + maxTokens: 8_192, + }, + { + id: "deepseek-ai/deepseek-v4-pro", + name: "DeepSeek V4 Pro", + contextWindow: 262_144, + maxTokens: 16_384, + }, + { + id: "qwen/qwen3.5-397b-a17b", + name: "Qwen3.5 397B A17B", + contextWindow: 262_144, + maxTokens: 16_384, + }, +] as const; + +const EXPECTED_DEPRECATED_MODELS = [ + { + id: "moonshotai/kimi-k2.5", + name: "Kimi K2.5", + contextWindow: 262_144, + maxTokens: 8_192, + }, + { + id: "z-ai/glm-5.1", + name: "GLM 5.1", + contextWindow: 202_752, + maxTokens: 8_192, + }, + { + id: "minimaxai/minimax-m2.5", + name: "MiniMax M2.5", + contextWindow: 196_608, + maxTokens: 8_192, + }, + { id: "z-ai/glm5", name: "GLM-5", contextWindow: 202_752, maxTokens: 8_192 }, + { + id: "minimaxai/minimax-m2.7", + name: "Minimax M2.7", + contextWindow: 196_608, + maxTokens: 8_192, + }, +] as const; + +const EXPECTED_BUNDLED_MODELS = [ + ...EXPECTED_FEATURED_MODELS, + ...EXPECTED_DEPRECATED_MODELS, +] as const; + const ssrfRuntimeMocks = vi.hoisted(() => ({ fetchWithSsrFGuard: vi.fn(), ssrfPolicyFromHttpBaseUrlAllowedHostname: vi.fn((baseUrl: string) => ({ @@ -41,21 +116,20 @@ describe("nvidia provider catalog", () => { expect(provider.baseUrl).toBe("https://integrate.api.nvidia.com/v1"); expect(provider.api).toBe("openai-completions"); expect(provider.apiKey).toBe("NVIDIA_API_KEY"); - expect(provider.models.map((model) => model.id)).toEqual([ - "nvidia/nemotron-3-ultra-550b-a55b", - "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", - ]); + expect( + provider.models.map(({ id, name, contextWindow, maxTokens }) => ({ + id, + name, + contextWindow, + maxTokens, + })), + ).toEqual(EXPECTED_BUNDLED_MODELS); expect(provider.models.filter((model) => model.compat?.requiresStringContent !== true)).toEqual( [], ); expect(provider.models[0]).toMatchObject({ - contextWindow: 1_000_000, - maxTokens: 16_384, + contextWindow: 1_048_576, + maxTokens: 8_192, params: { chat_template_kwargs: { enable_thinking: false, @@ -65,16 +139,35 @@ describe("nvidia provider catalog", () => { }); expect(provider.models[1]).toMatchObject({ id: "nvidia/nemotron-3-super-120b-a12b", - contextWindow: 1_048_576, + contextWindow: 1_000_000, }); + expect( + manifest.modelCatalog.providers.nvidia.models + .filter((model) => "status" in model && model.status === "deprecated") + .map((model) => ({ id: model.id, replacedBy: model.replacedBy })), + ).toEqual([ + { id: "moonshotai/kimi-k2.5", replacedBy: "moonshotai/kimi-k2.6" }, + { id: "z-ai/glm-5.1", replacedBy: "z-ai/glm-5.2" }, + { id: "minimaxai/minimax-m2.5", replacedBy: "minimaxai/minimax-m3" }, + { id: "z-ai/glm5", replacedBy: "z-ai/glm-5.2" }, + { id: "minimaxai/minimax-m2.7", replacedBy: "minimaxai/minimax-m3" }, + ]); + }); + + it("keeps deprecated exact-reference rows out of the selectable catalog", () => { + const provider = buildSelectableNvidiaProvider(); + + expect(provider.models.map((model) => model.id)).toEqual( + EXPECTED_FEATURED_MODELS.map((model) => model.id), + ); }); it("promotes ranked models from NVIDIA's featured catalog", async () => { const release = mockFeaturedCatalogResponse({ "featured-models": [ { - model: "z-ai/glm-5.1", - "model-name": "GLM 5.1", + model: "z-ai/glm-5.2", + "model-name": "GLM 5.2", context: 202752, "max-output": 8192, }, @@ -90,11 +183,11 @@ describe("nvidia provider catalog", () => { const provider = await buildLiveNvidiaProvider(); expect(provider.models.map((model) => model.id)).toEqual([ - "z-ai/glm-5.1", + "z-ai/glm-5.2", "nvidia/nemotron-3-super-120b-a12b", ]); expect(provider.models[0]).toMatchObject({ - name: "GLM 5.1", + name: "GLM 5.2", contextWindow: 202752, maxTokens: 8192, compat: { requiresStringContent: true }, @@ -117,22 +210,8 @@ describe("nvidia provider catalog", () => { const provider = await buildLiveNvidiaProvider(); - expect(provider.models.map((model) => model.id)).toEqual([ - "nvidia/nemotron-3-ultra-550b-a55b", - "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", - ]); - }); - - it("retains shipped NVIDIA model refs as bundled fallback compatibility rows", () => { - const provider = buildNvidiaProvider(); - expect(provider.models.map((model) => model.id)).toEqual( - expect.arrayContaining(["minimaxai/minimax-m2.5", "z-ai/glm5"]), + EXPECTED_FEATURED_MODELS.map((model) => model.id), ); }); @@ -140,8 +219,8 @@ describe("nvidia provider catalog", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "z-ai/glm-5.1", - "model-name": "GLM 5.1", + model: "z-ai/glm-5.2", + "model-name": "GLM 5.2", context: 202752, "max-output": 8192, }, @@ -157,11 +236,75 @@ describe("nvidia provider catalog", () => { const provider = await buildSelectableLiveNvidiaProvider(); expect(provider.models.map((model) => model.id)).toEqual([ - "z-ai/glm-5.1", + "z-ai/glm-5.2", "nvidia/nemotron-3-super-120b-a12b", ]); }); + it("keeps every deprecated exact-reference row out of live catalogs", async () => { + mockFeaturedCatalogResponse({ + "featured-models": [ + { + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", + context: 196608, + "max-output": 8192, + }, + ...EXPECTED_DEPRECATED_MODELS.map((model) => ({ + model: model.id, + "model-name": model.name, + context: model.contextWindow, + "max-output": model.maxTokens, + })), + ], + }); + + const live = await buildLiveNvidiaProvider(); + const selectableLive = await buildSelectableLiveNvidiaProvider(); + + expect(live.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]); + expect(selectableLive.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]); + }); + + it("maps current featured feed metadata for MiniMax, DeepSeek, and Qwen", async () => { + mockFeaturedCatalogResponse({ + "featured-models": [ + { + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", + context: 196608, + "max-output": 8192, + }, + { + model: "deepseek-ai/deepseek-v4-pro", + "model-name": "DeepSeek V4 Pro", + context: 262144, + "max-output": 16384, + }, + { + model: "qwen/qwen3.5-397b-a17b", + "model-name": "Qwen3.5 397B A17B", + context: 262144, + "max-output": 16384, + }, + ], + }); + + const provider = await buildLiveNvidiaProvider(); + + expect( + provider.models.map(({ id, contextWindow, maxTokens }) => ({ + id, + contextWindow, + maxTokens, + })), + ).toEqual([ + { id: "minimaxai/minimax-m3", contextWindow: 196_608, maxTokens: 8_192 }, + { id: "deepseek-ai/deepseek-v4-pro", contextWindow: 262_144, maxTokens: 16_384 }, + { id: "qwen/qwen3.5-397b-a17b", contextWindow: 262_144, maxTokens: 16_384 }, + ]); + }); + it("returns no selectable live rows when the featured catalog is unavailable", async () => { mockFeaturedCatalogResponse({ error: "unavailable" }, 503); @@ -180,8 +323,8 @@ describe("nvidia provider catalog", () => { "max-output": 1000, }, { - model: "minimaxai/minimax-m2.7", - "model-name": "Minimax M2.7", + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", context: 196608, "max-output": 8192, }, @@ -196,15 +339,15 @@ describe("nvidia provider catalog", () => { const provider = await buildLiveNvidiaProvider(); - expect(provider.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m2.7"]); + expect(provider.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]); }); it("caches the featured catalog for repeated provider builds", async () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "minimaxai/minimax-m2.7", - "model-name": "Minimax M2.7", + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", context: 196608, "max-output": 8192, }, @@ -222,8 +365,8 @@ describe("nvidia provider catalog", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "minimaxai/minimax-m2.7", - "model-name": "Minimax M2.7", + model: "minimaxai/minimax-m3", + "model-name": "Minimax M3", context: 196608, "max-output": 8192, }, @@ -232,8 +375,8 @@ describe("nvidia provider catalog", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "z-ai/glm-5.1", - "model-name": "GLM 5.1", + model: "z-ai/glm-5.2", + "model-name": "GLM 5.2", context: 202752, "max-output": 8192, }, @@ -243,8 +386,8 @@ describe("nvidia provider catalog", () => { const first = await buildLiveNvidiaProvider(); const second = await buildLiveNvidiaProvider(); - expect(first.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m2.7"]); - expect(second.models.map((model) => model.id)).toEqual(["z-ai/glm-5.1"]); + expect(first.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]); + expect(second.models.map((model) => model.id)).toEqual(["z-ai/glm-5.2"]); expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(2); }); @@ -262,8 +405,8 @@ describe("nvidia provider catalog", () => { mockFeaturedCatalogResponse({ "featured-models": [ { - model: "z-ai/glm-5.1", - "model-name": "GLM 5.1", + model: "z-ai/glm-5.2", + "model-name": "GLM 5.2", context: 202752, "max-output": 8192, }, @@ -273,16 +416,10 @@ describe("nvidia provider catalog", () => { const first = await buildLiveNvidiaProvider(); const second = await buildLiveNvidiaProvider(); - expect(first.models.map((model) => model.id)).toEqual([ - "nvidia/nemotron-3-ultra-550b-a55b", - "nvidia/nemotron-3-super-120b-a12b", - "moonshotai/kimi-k2.5", - "minimaxai/minimax-m2.7", - "z-ai/glm-5.1", - "minimaxai/minimax-m2.5", - "z-ai/glm5", - ]); - expect(second.models.map((model) => model.id)).toEqual(["z-ai/glm-5.1"]); + expect(first.models.map((model) => model.id)).toEqual( + EXPECTED_FEATURED_MODELS.map((model) => model.id), + ); + expect(second.models.map((model) => model.id)).toEqual(["z-ai/glm-5.2"]); expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(2); }); @@ -292,8 +429,8 @@ describe("nvidia provider catalog", () => { { model: "nemotron-3-ultra-550b-a55b", "model-name": "Nemotron 3 Ultra 550B", - context: 1000000, - "max-output": 16384, + context: 1048576, + "max-output": 8192, }, { model: "minimaxai/minimax-m2.7", @@ -306,14 +443,11 @@ describe("nvidia provider catalog", () => { const provider = await buildLiveNvidiaProvider(); - expect(provider.models.map((model) => model.id)).toEqual([ - "nvidia/nemotron-3-ultra-550b-a55b", - "minimaxai/minimax-m2.7", - ]); + expect(provider.models.map((model) => model.id)).toEqual(["nvidia/nemotron-3-ultra-550b-a55b"]); expect(provider.models[0]).toMatchObject({ name: "Nemotron 3 Ultra 550B", - contextWindow: 1_000_000, - maxTokens: 16_384, + contextWindow: 1_048_576, + maxTokens: 8_192, params: { chat_template_kwargs: { enable_thinking: false, diff --git a/extensions/nvidia/provider-catalog.ts b/extensions/nvidia/provider-catalog.ts index 163e4cec35de..85d4efec6751 100644 --- a/extensions/nvidia/provider-catalog.ts +++ b/extensions/nvidia/provider-catalog.ts @@ -39,6 +39,11 @@ const NVIDIA_ULTRA_DEFAULT_PARAMS = { force_nonempty_content: true, }, } as const; +const DEPRECATED_NVIDIA_MODEL_IDS = new Set( + manifest.modelCatalog.providers.nvidia.models + .filter((model) => "status" in model && model.status === "deprecated") + .map((model) => model.id), +); type NvidiaFeaturedModel = { model: string; @@ -79,20 +84,28 @@ export function buildNvidiaProvider(): ModelProviderConfig { }; } -export async function buildLiveNvidiaProvider(): Promise { +export function buildSelectableNvidiaProvider(): ModelProviderConfig { const provider = buildNvidiaProvider(); + return { + ...provider, + models: filterSelectableNvidiaModels(provider.models ?? []), + }; +} + +export async function buildLiveNvidiaProvider(): Promise { + const provider = buildSelectableNvidiaProvider(); const featuredModels = await loadNvidiaFeaturedModels(); if (!featuredModels || featuredModels.length === 0) { return provider; } return { ...provider, - models: applyNvidiaModelDefaults(featuredModels), + models: applyNvidiaModelDefaults(filterSelectableNvidiaModels(featuredModels)), }; } export async function buildSelectableLiveNvidiaProvider(): Promise { - const provider = buildNvidiaProvider(); + const provider = buildSelectableNvidiaProvider(); const featuredModels = await loadNvidiaFeaturedModels(); if (!featuredModels || featuredModels.length === 0) { return { @@ -102,7 +115,7 @@ export async function buildSelectableLiveNvidiaProvider(): Promise !DEPRECATED_NVIDIA_MODEL_IDS.has(model.id)); +} + function parseNvidiaFeaturedModel(row: unknown): ModelDefinitionConfig | null { if (!row || typeof row !== "object") { return null;