fix(nvidia): hide retired models while preserving live vendor discovery (#117620)

* fix(model-catalog): preserve manifest lifecycle metadata on refresh

* fix(nvidia): simplify Qwen model lifecycle

* test(nvidia): avoid map-spread lint

* docs(nvidia): clarify retired Qwen models

* fix(models): preserve NVIDIA lifecycle states

* fix(models): keep NVIDIA lifecycle plugin-owned

* fix(nvidia): retain fresh featured catalog models

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@macos.shared>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
clawsweeper[bot]
2026-08-02 04:40:17 -07:00
committed by GitHub
parent 0cc0475306
commit 8f46e31994
6 changed files with 80 additions and 90 deletions
+16 -12
View File
@@ -70,10 +70,11 @@ When an NVIDIA API key is configured, setup and model-selection paths fetch
NVIDIA's public featured-model catalog from
`https://assets.ngc.nvidia.com/products/api-catalog/featured-models.json` and
cache the result for 24 hours (first 32 entries, imported as free text-input
rows). New featured models from build.nvidia.com therefore appear in setup and
model-selection surfaces without waiting for an OpenClaw release. When the
live feed is available, the first returned model is the preselected option
during NVIDIA setup.
rows). New or republished featured models from build.nvidia.com therefore appear
in setup and model-selection surfaces after the cache refreshes, without waiting
for an OpenClaw release. A fresh NVIDIA catalog overrides bundled retirement
metadata. When the live feed is available, its first model is preselected during
NVIDIA setup.
The fetch uses a fixed HTTPS host policy for `assets.ngc.nvidia.com`. If no
NVIDIA API key is configured, or if the feed is unavailable or malformed,
@@ -97,8 +98,8 @@ hosted in NVIDIA's catalog when their context, latency, or behavior fits better.
## Bundled fallback catalog
The selectable bundled rows snapshot NVIDIA's featured-model catalog. Deprecated
compatibility rows remain resolvable by exact reference but stay out of model
pickers.
compatibility rows keep existing exact model references recognizable but stay
out of model pickers.
| Model ref | Name | Context | Max output |
| ------------------------------------------ | --------------------- | --------- | ---------- |
@@ -108,12 +109,14 @@ pickers.
| `nvidia/moonshotai/kimi-k2.6` | Kimi K2.6 | 262,144 | 65,536 |
| `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 | 32,768 |
The full compatibility catalog also retains these shipped refs for existing
configurations: `nvidia/moonshotai/kimi-k2.5`, `nvidia/z-ai/glm-5.1`,
`nvidia/z-ai/glm5`, and `nvidia/minimaxai/minimax-m2.7`. They remain available
by exact reference but never appear in onboarding or model pickers.
configurations and migration: `nvidia/qwen/qwen3.5-397b-a17b`,
`nvidia/moonshotai/kimi-k2.5`, `nvidia/z-ai/glm-5.1`, `nvidia/z-ai/glm5`, and
`nvidia/minimaxai/minimax-m2.7`. These references stay hidden from bundled and
offline model pickers unless NVIDIA republishes them in its featured catalog.
NVIDIA has retired the Qwen endpoint, so requests using its model reference no
longer work. Migrate existing Qwen configurations to an active model.
## Advanced configuration
@@ -128,8 +131,9 @@ by exact reference but never appear in onboarding or model pickers.
OpenClaw prefers NVIDIA's public featured-model catalog when NVIDIA auth is
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.
compatibility rows stay hidden from that fallback. Fresh featured rows can
restore models that NVIDIA has republished. Costs default to `0` in source
since NVIDIA currently offers free API access for the listed models.
</Accordion>
<Accordion title="OpenAI-compatible endpoint">
+18 -5
View File
@@ -213,7 +213,6 @@ describe("nvidia provider hooks", () => {
"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();
@@ -232,13 +231,12 @@ describe("nvidia provider hooks", () => {
"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);
});
it("surfaces live featured NVIDIA models via augmentModelCatalog", async () => {
it("surfaces republished NVIDIA featured models via augmentModelCatalog", async () => {
mockFeaturedCatalogResponse({
"featured-models": [
{
@@ -247,13 +245,22 @@ describe("nvidia provider hooks", () => {
context: 196608,
"max-output": 8192,
},
{
model: "qwen/qwen3.5-397b-a17b",
"model-name": "Qwen3.5 397B A17B",
context: 262144,
"max-output": 32768,
},
],
});
const provider = await registerNvidiaProvider();
const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext("nvapi-test"));
expect(entries?.map((entry) => entry.id)).toEqual(["minimaxai/minimax-m3"]);
expect(entries?.map((entry) => entry.id)).toEqual([
"minimaxai/minimax-m3",
"qwen/qwen3.5-397b-a17b",
]);
});
it("opts into literal provider-prefix preservation", async () => {
@@ -285,6 +292,12 @@ describe("nvidia provider hooks", () => {
context: 196608,
"max-output": 8192,
},
{
model: "qwen/qwen3.5-397b-a17b",
"model-name": "Qwen3.5 397B A17B",
context: 262144,
"max-output": 32768,
},
],
});
const { registeredModelCatalogProviders } = registerNvidiaPluginApi();
@@ -301,7 +314,6 @@ describe("nvidia provider hooks", () => {
"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([]);
@@ -309,6 +321,7 @@ describe("nvidia provider hooks", () => {
const liveRows = await catalogProvider?.liveCatalog?.(buildCatalogContext("nvapi-test"));
expect(liveRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([
"live:nvidia/minimaxai/minimax-m3",
"live:nvidia/qwen/qwen3.5-397b-a17b",
]);
});
+7 -7
View File
@@ -22,7 +22,6 @@ describe("nvidia onboard", () => {
"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.
@@ -50,30 +49,31 @@ describe("nvidia onboard", () => {
"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", () => {
it.each([
{ id: "minimaxai/minimax-m2.7", name: "MiniMax M2.7" },
{ id: "qwen/qwen3.5-397b-a17b", name: "Qwen3.5 397B A17B" },
])("preserves an existing deprecated exact-reference model: $id", ({ id, name }) => {
const provider = expectProviderOnboardMergedLegacyConfig({
applyProviderConfig: applyNvidiaProviderConfig,
providerId: "nvidia",
providerApi: "openai-completions",
baseUrl: "https://integrate.api.nvidia.com/v1",
legacyApi: "openai-completions",
legacyModelId: "minimaxai/minimax-m2.7",
legacyModelName: "MiniMax M2.7",
legacyModelId: id,
legacyModelName: name,
});
expect(provider?.models.map((model) => model.id)).toEqual([
"minimaxai/minimax-m2.7",
id,
"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",
]);
});
});
+18 -50
View File
@@ -5,18 +5,12 @@
"onStartup": false
},
"enabledByDefault": true,
"providers": [
"nvidia"
],
"providers": ["nvidia"],
"providerEndpoints": [
{
"endpointClass": "nvidia-native",
"hosts": [
"integrate.api.nvidia.com"
],
"baseUrls": [
"https://integrate.api.nvidia.com/v1"
]
"hosts": ["integrate.api.nvidia.com"],
"baseUrls": ["https://integrate.api.nvidia.com/v1"]
}
],
"modelIdNormalization": {
@@ -35,9 +29,7 @@
{
"id": "nvidia/nemotron-3-ultra-550b-a55b",
"name": "Nemotron 3 Ultra 550B",
"input": [
"text"
],
"input": ["text"],
"reasoning": true,
"contextWindow": 1048576,
"maxTokens": 8192,
@@ -54,9 +46,7 @@
{
"id": "nvidia/nemotron-3-super-120b-a12b",
"name": "Nemotron 3 Super 120B",
"input": [
"text"
],
"input": ["text"],
"reasoning": true,
"contextWindow": 1000000,
"maxTokens": 8192,
@@ -73,9 +63,7 @@
{
"id": "z-ai/glm-5.2",
"name": "GLM 5.2",
"input": [
"text"
],
"input": ["text"],
"reasoning": true,
"contextWindow": 202752,
"maxTokens": 8192,
@@ -93,10 +81,7 @@
{
"id": "moonshotai/kimi-k2.6",
"name": "Kimi K2.6",
"input": [
"text",
"image"
],
"input": ["text", "image"],
"reasoning": true,
"contextWindow": 262144,
"maxTokens": 65536,
@@ -113,10 +98,7 @@
{
"id": "minimaxai/minimax-m3",
"name": "Minimax M3",
"input": [
"text",
"image"
],
"input": ["text", "image"],
"reasoning": true,
"contextWindow": 196608,
"maxTokens": 8192,
@@ -133,9 +115,7 @@
{
"id": "deepseek-ai/deepseek-v4-pro",
"name": "DeepSeek V4 Pro",
"input": [
"text"
],
"input": ["text"],
"reasoning": true,
"contextWindow": 262144,
"maxTokens": 16384,
@@ -153,10 +133,7 @@
{
"id": "qwen/qwen3.5-397b-a17b",
"name": "Qwen3.5 397B A17B",
"input": [
"text",
"image"
],
"input": ["text", "image"],
"reasoning": true,
"contextWindow": 262144,
"maxTokens": 32768,
@@ -168,15 +145,14 @@
},
"compat": {
"requiresStringContent": true
}
},
"status": "deprecated",
"statusReason": "NVIDIA retired this model and no longer lists it in its model catalog."
},
{
"id": "moonshotai/kimi-k2.5",
"name": "Kimi K2.5",
"input": [
"text",
"image"
],
"input": ["text", "image"],
"reasoning": true,
"contextWindow": 262144,
"maxTokens": 32768,
@@ -196,9 +172,7 @@
{
"id": "z-ai/glm-5.1",
"name": "GLM 5.1",
"input": [
"text"
],
"input": ["text"],
"contextWindow": 202752,
"maxTokens": 8192,
"cost": {
@@ -218,9 +192,7 @@
{
"id": "z-ai/glm5",
"name": "GLM-5",
"input": [
"text"
],
"input": ["text"],
"contextWindow": 202752,
"maxTokens": 8192,
"cost": {
@@ -239,9 +211,7 @@
{
"id": "minimaxai/minimax-m2.7",
"name": "Minimax M2.7",
"input": [
"text"
],
"input": ["text"],
"reasoning": true,
"contextWindow": 204800,
"maxTokens": 16384,
@@ -269,9 +239,7 @@
"providers": [
{
"id": "nvidia",
"envVars": [
"NVIDIA_API_KEY"
]
"envVars": ["NVIDIA_API_KEY"]
}
]
},
+19 -14
View File
@@ -44,15 +44,15 @@ const EXPECTED_FEATURED_MODELS = [
contextWindow: 262_144,
maxTokens: 16_384,
},
] as const;
const EXPECTED_DEPRECATED_MODELS = [
{
id: "qwen/qwen3.5-397b-a17b",
name: "Qwen3.5 397B A17B",
contextWindow: 262_144,
maxTokens: 32_768,
},
] as const;
const EXPECTED_DEPRECATED_MODELS = [
{
id: "moonshotai/kimi-k2.5",
name: "Kimi K2.5",
@@ -152,11 +152,6 @@ describe("nvidia provider catalog", () => {
reasoning: true,
},
{ id: "deepseek-ai/deepseek-v4-pro", input: ["text"], reasoning: true },
{
id: "qwen/qwen3.5-397b-a17b",
input: ["text", "image"],
reasoning: true,
},
]);
expect(provider.models[0]).toMatchObject({
contextWindow: 1_048_576,
@@ -175,8 +170,11 @@ describe("nvidia provider catalog", () => {
expect(
manifest.modelCatalog.providers.nvidia.models
.filter((model) => "status" in model && model.status === "deprecated")
.map((model) => ({ id: model.id, replacedBy: model.replacedBy })),
.map((model) =>
"replacedBy" in model ? { id: model.id, replacedBy: model.replacedBy } : { id: model.id },
),
).toEqual([
{ id: "qwen/qwen3.5-397b-a17b" },
{ id: "moonshotai/kimi-k2.5", replacedBy: "moonshotai/kimi-k2.6" },
{ id: "z-ai/glm-5.1", replacedBy: "z-ai/glm-5.2" },
{ id: "z-ai/glm5", replacedBy: "z-ai/glm-5.2" },
@@ -283,7 +281,7 @@ describe("nvidia provider catalog", () => {
]);
});
it("keeps every deprecated exact-reference row out of live catalogs", async () => {
it("restores bundled legacy models when NVIDIA republishes them in its featured catalog", async () => {
mockFeaturedCatalogResponse({
"featured-models": [
{
@@ -303,12 +301,16 @@ describe("nvidia provider catalog", () => {
const live = await buildLiveNvidiaProvider();
const selectableLive = await buildSelectableLiveNvidiaProvider();
const republishedIds = [
"minimaxai/minimax-m3",
...EXPECTED_DEPRECATED_MODELS.map((model) => model.id),
];
expect(live.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]);
expect(selectableLive.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m3"]);
expect(live.models.map((model) => model.id)).toEqual(republishedIds);
expect(selectableLive.models.map((model) => model.id)).toEqual(republishedIds);
});
it("maps current featured feed metadata for MiniMax, DeepSeek, and Qwen", async () => {
it("maps a republished Qwen model from NVIDIA's current featured catalog", async () => {
mockFeaturedCatalogResponse({
"featured-models": [
{
@@ -485,7 +487,10 @@ describe("nvidia provider catalog", () => {
const provider = await buildLiveNvidiaProvider();
expect(provider.models.map((model) => model.id)).toEqual(["nvidia/nemotron-3-ultra-550b-a55b"]);
expect(provider.models.map((model) => model.id)).toEqual([
"nvidia/nemotron-3-ultra-550b-a55b",
"minimaxai/minimax-m2.7",
]);
expect(provider.models[0]).toMatchObject({
name: "Nemotron 3 Ultra 550B",
contextWindow: 1_048_576,
+2 -2
View File
@@ -97,7 +97,7 @@ export async function buildLiveNvidiaProvider(): Promise<ModelProviderConfig> {
}
return {
...provider,
models: applyNvidiaModelDefaults(filterSelectableNvidiaModels(featuredModels)),
models: applyNvidiaModelDefaults(featuredModels),
};
}
@@ -112,7 +112,7 @@ export async function buildSelectableLiveNvidiaProvider(): Promise<ModelProvider
}
return {
...provider,
models: applyNvidiaModelDefaults(filterSelectableNvidiaModels(featuredModels)),
models: applyNvidiaModelDefaults(featuredModels),
};
}