Skip to content

Commit b7563a8

Browse files
committed
fix(nvidia): restore live catalog priority
1 parent 32bd0a0 commit b7563a8

10 files changed

Lines changed: 814 additions & 267 deletions

extensions/nvidia/index.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,7 @@ describe("nvidia provider hooks", () => {
245245

246246
const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext("nvapi-test"));
247247

248-
expect(entries?.map((entry) => entry.id)).toEqual([
249-
"minimaxai/minimax-m2.7",
250-
"nvidia/nemotron-3-super-120b-a12b",
251-
"moonshotai/kimi-k2.5",
252-
"z-ai/glm-5.1",
253-
"minimaxai/minimax-m2.5",
254-
"z-ai/glm5",
255-
]);
248+
expect(entries?.map((entry) => entry.id)).toEqual(["minimaxai/minimax-m2.7"]);
256249
});
257250

258251
it("opts into literal provider-prefix preservation", async () => {
@@ -307,11 +300,16 @@ describe("nvidia provider hooks", () => {
307300
const liveRows = await catalogProvider?.liveCatalog?.(buildCatalogContext("nvapi-test"));
308301
expect(liveRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([
309302
"live:nvidia/minimaxai/minimax-m2.7",
310-
"live:nvidia/nvidia/nemotron-3-super-120b-a12b",
311-
"live:nvidia/moonshotai/kimi-k2.5",
312-
"live:nvidia/z-ai/glm-5.1",
313-
"live:nvidia/minimaxai/minimax-m2.5",
314-
"live:nvidia/z-ai/glm5",
315303
]);
316304
});
305+
306+
it("keeps static rows out of the live catalog when the featured catalog is unavailable", async () => {
307+
mockFeaturedCatalogResponse({ error: "unavailable" }, 503);
308+
const { registeredModelCatalogProviders } = registerNvidiaPluginApi();
309+
const catalogProvider = registeredModelCatalogProviders[0];
310+
311+
await expect(
312+
catalogProvider?.liveCatalog?.(buildCatalogContext("nvapi-test")),
313+
).resolves.toEqual([]);
314+
});
317315
});

extensions/nvidia/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
22
import { applyNvidiaConfig, NVIDIA_DEFAULT_MODEL_REF } from "./onboard.js";
3-
import { buildLiveNvidiaProvider, buildNvidiaProvider } from "./provider-catalog.js";
3+
import {
4+
buildLiveNvidiaProvider,
5+
buildNvidiaProvider,
6+
buildSelectableLiveNvidiaProvider,
7+
} from "./provider-catalog.js";
48

59
const PROVIDER_ID = "nvidia";
610

@@ -51,7 +55,7 @@ export default defineSingleProviderPluginEntry({
5155
},
5256
],
5357
catalog: {
54-
buildProvider: buildLiveNvidiaProvider,
58+
buildProvider: buildSelectableLiveNvidiaProvider,
5559
buildStaticProvider: buildNvidiaProvider,
5660
},
5761
augmentModelCatalog: buildNvidiaCatalogModels,

extensions/nvidia/provider-catalog.test.ts

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
22
import {
33
buildLiveNvidiaProvider,
44
buildNvidiaProvider,
5+
buildSelectableLiveNvidiaProvider,
56
clearNvidiaFeaturedModelCacheForTests,
67
NVIDIA_FEATURED_MODELS_URL,
78
} from "./provider-catalog.js";
@@ -73,10 +74,6 @@ describe("nvidia provider catalog", () => {
7374
expect(provider.models.map((model) => model.id)).toEqual([
7475
"z-ai/glm-5.1",
7576
"nvidia/nemotron-3-super-120b-a12b",
76-
"moonshotai/kimi-k2.5",
77-
"minimaxai/minimax-m2.7",
78-
"minimaxai/minimax-m2.5",
79-
"z-ai/glm5",
8077
]);
8178
expect(provider.models[0]).toMatchObject({
8279
name: "GLM 5.1",
@@ -118,6 +115,40 @@ describe("nvidia provider catalog", () => {
118115
);
119116
});
120117

118+
it("uses only selectable live catalog rows when the featured catalog returns models", async () => {
119+
mockFeaturedCatalogResponse({
120+
"featured-models": [
121+
{
122+
model: "z-ai/glm-5.1",
123+
"model-name": "GLM 5.1",
124+
context: 202752,
125+
"max-output": 8192,
126+
},
127+
{
128+
model: "nemotron-3-super-120b-a12b",
129+
"model-name": "Nemotron 3 Super 120B",
130+
context: 262144,
131+
"max-output": 8192,
132+
},
133+
],
134+
});
135+
136+
const provider = await buildSelectableLiveNvidiaProvider();
137+
138+
expect(provider.models.map((model) => model.id)).toEqual([
139+
"z-ai/glm-5.1",
140+
"nvidia/nemotron-3-super-120b-a12b",
141+
]);
142+
});
143+
144+
it("returns no selectable live rows when the featured catalog is unavailable", async () => {
145+
mockFeaturedCatalogResponse({ error: "unavailable" }, 503);
146+
147+
const provider = await buildSelectableLiveNvidiaProvider();
148+
149+
expect(provider.models.map((model) => model.id)).toEqual([]);
150+
});
151+
121152
it("ignores malformed featured catalog rows and keeps valid entries", async () => {
122153
mockFeaturedCatalogResponse({
123154
"featured-models": [
@@ -144,14 +175,7 @@ describe("nvidia provider catalog", () => {
144175

145176
const provider = await buildLiveNvidiaProvider();
146177

147-
expect(provider.models.map((model) => model.id)).toEqual([
148-
"minimaxai/minimax-m2.7",
149-
"nvidia/nemotron-3-super-120b-a12b",
150-
"moonshotai/kimi-k2.5",
151-
"z-ai/glm-5.1",
152-
"minimaxai/minimax-m2.5",
153-
"z-ai/glm5",
154-
]);
178+
expect(provider.models.map((model) => model.id)).toEqual(["minimaxai/minimax-m2.7"]);
155179
});
156180

157181
it("caches the featured catalog for repeated provider builds", async () => {

extensions/nvidia/provider-catalog.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,22 @@ export async function buildLiveNvidiaProvider(): Promise<ModelProviderConfig> {
8080
}
8181
return {
8282
...provider,
83-
models: mergeFeaturedModels(featuredModels, provider.models),
83+
models: featuredModels,
84+
};
85+
}
86+
87+
export async function buildSelectableLiveNvidiaProvider(): Promise<ModelProviderConfig> {
88+
const provider = buildNvidiaProvider();
89+
const featuredModels = await loadNvidiaFeaturedModels();
90+
if (!featuredModels || featuredModels.length === 0) {
91+
return {
92+
...provider,
93+
models: [],
94+
};
95+
}
96+
return {
97+
...provider,
98+
models: featuredModels,
8499
};
85100
}
86101

@@ -89,23 +104,6 @@ export function clearNvidiaFeaturedModelCacheForTests() {
89104
featuredModelRequest = undefined;
90105
}
91106

92-
function mergeFeaturedModels(
93-
featuredModels: ModelDefinitionConfig[],
94-
fallbackModels: ModelDefinitionConfig[],
95-
): ModelDefinitionConfig[] {
96-
const seen = new Set<string>();
97-
const merged: ModelDefinitionConfig[] = [];
98-
for (const model of [...featuredModels, ...fallbackModels]) {
99-
const key = model.id.trim().toLowerCase();
100-
if (!key || seen.has(key)) {
101-
continue;
102-
}
103-
seen.add(key);
104-
merged.push(model);
105-
}
106-
return merged;
107-
}
108-
109107
async function loadNvidiaFeaturedModels(): Promise<ModelDefinitionConfig[] | null> {
110108
const now = Date.now();
111109
if (featuredModelCache && featuredModelCache.expiresAtMs > now) {

src/commands/configure.gateway-auth.prompt-auth-config.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ function promptModelAllowlistOptions(index = 0) {
218218
loadCatalog?: boolean;
219219
message?: string;
220220
preferredProvider?: string;
221+
providerScopedCatalog?: boolean;
221222
}
222223
| undefined;
223224
}
@@ -528,6 +529,7 @@ describe("promptAuthConfig", () => {
528529

529530
expect(promptModelAllowlistOptions()?.preferredProvider).toBe("github-copilot");
530531
expect(promptModelAllowlistOptions()?.loadCatalog).toBe(true);
532+
expect(promptModelAllowlistOptions()?.providerScopedCatalog).toBe(false);
531533
});
532534

533535
it("loads configured provider models after Ollama Cloud + Local and Cloud only setup", async () => {
@@ -559,6 +561,7 @@ describe("promptAuthConfig", () => {
559561
const allowlistOptions = promptModelAllowlistOptions();
560562
expect(allowlistOptions?.preferredProvider).toBe("ollama");
561563
expect(allowlistOptions?.loadCatalog).toBe(true);
564+
expect(allowlistOptions?.providerScopedCatalog).toBe(true);
562565
});
563566

564567
it("loads plugin catalog when the selected provider allowlist requires it", async () => {
@@ -600,6 +603,7 @@ describe("promptAuthConfig", () => {
600603
const allowlistOptions = promptModelAllowlistOptions();
601604
expect(allowlistOptions?.preferredProvider).toBe("github-copilot");
602605
expect(allowlistOptions?.loadCatalog).toBe(true);
606+
expect(allowlistOptions?.providerScopedCatalog).toBe(true);
603607
});
604608

605609
it("loads catalog when the selected provider has manifest catalog rows", async () => {
@@ -639,6 +643,7 @@ describe("promptAuthConfig", () => {
639643
const call = promptModelAllowlistOptions();
640644
expect(call?.preferredProvider).toBe("github-copilot");
641645
expect(call?.loadCatalog).toBe(true);
646+
expect(call?.providerScopedCatalog).toBe(true);
642647
});
643648

644649
it("lets skip-auth model browsing scope the allowlist to the selected model provider", async () => {

src/commands/configure.gateway-auth.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,16 @@ export async function promptAuthConfig(
262262
});
263263
const promptProvider =
264264
modelPrompt?.provider ?? preferredProvider ?? resolveSingleConfiguredProvider(next);
265+
const hasPromptProviderConfiguredModels = hasConfiguredProviderModels(next, promptProvider);
266+
const hasPromptProviderStaticManifestRows = hasStaticManifestCatalogRows(next, promptProvider);
267+
const shouldLoadModelCatalog =
268+
modelPrompt?.loadCatalog ??
269+
(hasPromptProviderConfiguredModels || hasPromptProviderStaticManifestRows);
270+
const useProviderScopedCatalog = Boolean(
271+
promptProvider &&
272+
shouldLoadModelCatalog &&
273+
(modelPrompt?.loadCatalog === true || hasPromptProviderConfiguredModels),
274+
);
265275
const allowlistSelection = await promptModelAllowlist({
266276
config: next,
267277
prompter,
@@ -271,10 +281,8 @@ export async function promptAuthConfig(
271281
initialSelections: modelPrompt?.initialSelections,
272282
message: modelPrompt?.message,
273283
preferredProvider: promptProvider,
274-
loadCatalog:
275-
modelPrompt?.loadCatalog ??
276-
(hasConfiguredProviderModels(next, promptProvider) ||
277-
hasStaticManifestCatalogRows(next, promptProvider)),
284+
providerScopedCatalog: useProviderScopedCatalog,
285+
loadCatalog: shouldLoadModelCatalog,
278286
});
279287
if (allowlistSelection.models) {
280288
next = applyModelFallbacksFromSelection(next, allowlistSelection.models, {

0 commit comments

Comments
 (0)