
























@@ -4,8 +4,18 @@ import {
44registerSingleProviderPlugin,
55resolveProviderPluginChoice,
66} from "openclaw/plugin-sdk/plugin-test-runtime";
7-import { describe, expect, it } from "vitest";
7+import { afterEach, describe, expect, it, vi } from "vitest";
88import plugin from "./index.js";
9+import { clearNvidiaFeaturedModelCacheForTests } from "./provider-catalog.js";
10+11+const ssrfRuntimeMocks = vi.hoisted(() => ({
12+fetchWithSsrFGuard: vi.fn(),
13+ssrfPolicyFromHttpBaseUrlAllowedHostname: vi.fn((baseUrl: string) => ({
14+allowedHostnames: [new URL(baseUrl).hostname],
15+})),
16+}));
17+18+vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ssrfRuntimeMocks);
9191020type NvidiaManifest = {
1121providerAuthChoices?: Array<Record<string, unknown>>;
@@ -24,6 +34,62 @@ async function registerNvidiaProvider() {
2434return registerSingleProviderPlugin(plugin);
2535}
263637+afterEach(() => {
38+clearNvidiaFeaturedModelCacheForTests();
39+ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
40+ssrfRuntimeMocks.ssrfPolicyFromHttpBaseUrlAllowedHostname.mockClear();
41+});
42+43+function mockFeaturedCatalogResponse(payload: unknown, status = 200) {
44+ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
45+response: Response.json(payload, { status }),
46+release: vi.fn(),
47+});
48+}
49+50+function registerNvidiaPluginApi() {
51+const registeredProviders: string[] = [];
52+const registeredModelCatalogProviders: RegisteredModelCatalogProvider[] = [];
53+54+plugin.register(
55+createTestPluginApi({
56+registerProvider(provider: { id: string }) {
57+registeredProviders.push(provider.id);
58+},
59+registerModelCatalogProvider(provider) {
60+registeredModelCatalogProviders.push(provider);
61+},
62+}),
63+);
64+65+return { registeredProviders, registeredModelCatalogProviders };
66+}
67+68+function buildCatalogContext(apiKey?: string) {
69+return {
70+config: {},
71+env: process.env,
72+resolveProviderApiKey: () => ({ apiKey }),
73+resolveProviderAuth: () => ({
74+ apiKey,
75+mode: apiKey ? ("api_key" as const) : ("none" as const),
76+source: apiKey ? ("env" as const) : ("none" as const),
77+}),
78+};
79+}
80+81+function buildAugmentCatalogContext(apiKey?: string) {
82+const env = { ...process.env };
83+if (!apiKey) {
84+delete env.NVIDIA_API_KEY;
85+}
86+return {
87+ ...buildCatalogContext(apiKey),
88+ env,
89+entries: [],
90+};
91+}
92+2793describe("nvidia provider hooks", () => {
2894it("registers the nvidia provider with correct metadata", async () => {
2995const provider = await registerNvidiaProvider();
@@ -129,21 +195,57 @@ describe("nvidia provider hooks", () => {
129195expect(provider.wrapStreamFn).toBeUndefined();
130196});
131197132-it("surfaces the bundled NVIDIA models via augmentModelCatalog", async () => {
198+it("surfaces the bundled NVIDIA models without fetching when no NVIDIA API token is available", async () => {
133199const provider = await registerNvidiaProvider();
134200135-const entries = await provider.augmentModelCatalog?.({
136-env: process.env,
137-entries: [],
138-});
201+const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext());
139202140203expect(entries?.map((entry) => entry.id)).toEqual([
141204"nvidia/nemotron-3-super-120b-a12b",
142205"moonshotai/kimi-k2.5",
206+"minimaxai/minimax-m2.7",
207+"z-ai/glm-5.1",
143208"minimaxai/minimax-m2.5",
144209"z-ai/glm5",
145210]);
146211expect(entries?.every((entry) => entry.provider === "nvidia")).toBe(true);
212+expect(ssrfRuntimeMocks.fetchWithSsrFGuard).not.toHaveBeenCalled();
213+});
214+215+it("surfaces the bundled NVIDIA models when authenticated featured catalog fetch fails", async () => {
216+mockFeaturedCatalogResponse({ error: "unavailable" }, 503);
217+const provider = await registerNvidiaProvider();
218+219+const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext("nvapi-test"));
220+221+expect(entries?.map((entry) => entry.id)).toEqual([
222+"nvidia/nemotron-3-super-120b-a12b",
223+"moonshotai/kimi-k2.5",
224+"minimaxai/minimax-m2.7",
225+"z-ai/glm-5.1",
226+"minimaxai/minimax-m2.5",
227+"z-ai/glm5",
228+]);
229+expect(entries?.every((entry) => entry.provider === "nvidia")).toBe(true);
230+expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(1);
231+});
232+233+it("surfaces live featured NVIDIA models via augmentModelCatalog", async () => {
234+mockFeaturedCatalogResponse({
235+"featured-models": [
236+{
237+model: "minimaxai/minimax-m2.7",
238+"model-name": "Minimax M2.7",
239+context: 196608,
240+"max-output": 8192,
241+},
242+],
243+});
244+const provider = await registerNvidiaProvider();
245+246+const entries = await provider.augmentModelCatalog?.(buildAugmentCatalogContext("nvapi-test"));
247+248+expect(entries?.map((entry) => entry.id)).toEqual(["minimaxai/minimax-m2.7"]);
147249});
148250149251it("opts into literal provider-prefix preservation", async () => {
@@ -158,23 +260,56 @@ describe("nvidia provider hooks", () => {
158260});
159261160262it("registers nvidia provider through the plugin api", () => {
161-const registeredProviders: string[] = [];
162-const registeredModelCatalogProviders: RegisteredModelCatalogProvider[] = [];
163-164-plugin.register(
165-createTestPluginApi({
166-registerProvider(provider: { id: string }) {
167-registeredProviders.push(provider.id);
168-},
169-registerModelCatalogProvider(provider) {
170-registeredModelCatalogProviders.push(provider);
171-},
172-}),
173-);
263+const { registeredProviders, registeredModelCatalogProviders } = registerNvidiaPluginApi();
174264175265expect(registeredProviders).toStrictEqual(["nvidia"]);
176266expect(registeredModelCatalogProviders.map((provider) => provider.provider)).toStrictEqual([
177267"nvidia",
178268]);
179269});
270+271+it("registers static and live nvidia model catalog rows", async () => {
272+mockFeaturedCatalogResponse({
273+"featured-models": [
274+{
275+model: "minimaxai/minimax-m2.7",
276+"model-name": "Minimax M2.7",
277+context: 196608,
278+"max-output": 8192,
279+},
280+],
281+});
282+const { registeredModelCatalogProviders } = registerNvidiaPluginApi();
283+const catalogProvider = registeredModelCatalogProviders[0];
284+285+expect(catalogProvider?.provider).toBe("nvidia");
286+expect(catalogProvider?.kinds).toStrictEqual(["text"]);
287+288+const staticRows = await catalogProvider?.staticCatalog?.(buildCatalogContext());
289+expect(staticRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([
290+"static:nvidia/nvidia/nemotron-3-super-120b-a12b",
291+"static:nvidia/moonshotai/kimi-k2.5",
292+"static:nvidia/minimaxai/minimax-m2.7",
293+"static:nvidia/z-ai/glm-5.1",
294+"static:nvidia/minimaxai/minimax-m2.5",
295+"static:nvidia/z-ai/glm5",
296+]);
297+298+await expect(catalogProvider?.liveCatalog?.(buildCatalogContext())).resolves.toEqual([]);
299+300+const liveRows = await catalogProvider?.liveCatalog?.(buildCatalogContext("nvapi-test"));
301+expect(liveRows?.map((entry) => `${entry.source}:${entry.provider}/${entry.model}`)).toEqual([
302+"live:nvidia/minimaxai/minimax-m2.7",
303+]);
304+});
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+});
180315});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。