





























@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
22import { discoverAuthStorage, discoverModels } from "../pi-model-discovery.js";
33import { createProviderRuntimeTestMock } from "./model.provider-runtime.test-support.js";
445+const resolveBundledStaticCatalogModelMock = vi.hoisted(() => vi.fn());
6+57vi.mock("../model-suppression.js", () => {
68// Mirrors the canonical manifest-driven suppression in
79// extensions/qwen/openclaw.plugin.json and src/plugins/manifest-model-suppression.ts.
@@ -140,6 +142,10 @@ vi.mock("../pi-model-discovery.js", () => ({
140142discoverModels: vi.fn(() => ({ find: vi.fn(() => null) })),
141143}));
142144145+vi.mock("./model.static-catalog.js", () => ({
146+resolveBundledStaticCatalogModel: resolveBundledStaticCatalogModelMock,
147+}));
148+143149import type { OpenRouterModelCapabilities } from "./openrouter-model-capabilities.js";
144150145151const mockGetOpenRouterModelCapabilities = vi.fn<
@@ -180,6 +186,7 @@ beforeEach(() => {
180186mockGetOpenRouterModelCapabilities.mockReturnValue(undefined);
181187mockLoadOpenRouterModelCapabilities.mockReset();
182188mockLoadOpenRouterModelCapabilities.mockResolvedValue();
189+resolveBundledStaticCatalogModelMock.mockReset();
183190});
184191185192function createRuntimeHooks() {
@@ -265,6 +272,70 @@ describe("resolveModel", () => {
265272expect(discoverModels).not.toHaveBeenCalled();
266273});
267274275+it("resolves opt-in bundled static catalog rows while skipping PI discovery", async () => {
276+resolveBundledStaticCatalogModelMock.mockReturnValueOnce({
277+provider: "mistral",
278+id: "mistral-medium-3-5",
279+name: "Mistral Medium 3.5",
280+api: "openai-completions",
281+baseUrl: "https://api.mistral.ai/v1",
282+reasoning: true,
283+input: ["text", "image"],
284+cost: { input: 1.5, output: 7.5, cacheRead: 0, cacheWrite: 0 },
285+contextWindow: 262144,
286+maxTokens: 8192,
287+});
288+289+const result = await resolveModelAsync(
290+"mistral",
291+"mistral-medium-3-5",
292+"/tmp/agent",
293+undefined,
294+{
295+allowBundledStaticCatalogFallback: true,
296+runtimeHooks: createRuntimeHooks(),
297+skipPiDiscovery: true,
298+},
299+);
300+301+expect(expectResolvedModel(result)).toMatchObject({
302+provider: "mistral",
303+id: "mistral-medium-3-5",
304+api: "openai-completions",
305+baseUrl: "https://api.mistral.ai/v1",
306+reasoning: true,
307+contextWindow: 262144,
308+maxTokens: 8192,
309+});
310+expect(resolveBundledStaticCatalogModelMock).toHaveBeenCalledWith({
311+provider: "mistral",
312+modelId: "mistral-medium-3-5",
313+cfg: undefined,
314+workspaceDir: undefined,
315+});
316+expect(discoverAuthStorage).not.toHaveBeenCalled();
317+expect(discoverModels).not.toHaveBeenCalled();
318+});
319+320+it("does not use bundled static catalog rows unless the caller opts in", async () => {
321+const result = await resolveModelAsync(
322+"mistral",
323+"mistral-medium-3-5",
324+"/tmp/agent",
325+undefined,
326+{
327+runtimeHooks: createRuntimeHooks(),
328+skipPiDiscovery: true,
329+},
330+);
331+332+expect(result.model).toBeUndefined();
333+expect(result.error).toBe("Unknown model: mistral/mistral-medium-3-5");
334+expect(resolveBundledStaticCatalogModelMock).not.toHaveBeenCalled();
335+expect(discoverAuthStorage).not.toHaveBeenCalled();
336+expect(discoverModels).not.toHaveBeenCalled();
337+});
338+268339it("defaults model input to text when discovery omits input", () => {
269340mockDiscoveredModel(discoverModels, {
270341provider: "custom",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。