mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
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:
+16
-12
@@ -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">
|
||||
|
||||
@@ -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",
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -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",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user