Skip to content

Commit a2247a5

Browse files
committed
fix(catalog): preserve cached suppression semantics
1 parent 11a3996 commit a2247a5

3 files changed

Lines changed: 71 additions & 21 deletions

File tree

src/agents/model-suppression.test.ts

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
22

33
const mocks = vi.hoisted(() => ({
4-
resolveManifestBuiltInModelSuppression: vi.fn(),
54
buildManifestBuiltInModelSuppressionResolver: vi.fn(),
5+
resolveManifestBuiltInModelSuppression: vi.fn(),
66
}));
77

88
vi.mock("../plugins/manifest-model-suppression.js", () => ({
9-
resolveManifestBuiltInModelSuppression: mocks.resolveManifestBuiltInModelSuppression,
109
buildManifestBuiltInModelSuppressionResolver: mocks.buildManifestBuiltInModelSuppressionResolver,
10+
resolveManifestBuiltInModelSuppression: mocks.resolveManifestBuiltInModelSuppression,
1111
}));
1212

13-
import { buildShouldSuppressBuiltInModel, shouldSuppressBuiltInModel } from "./model-suppression.js";
13+
import {
14+
buildShouldSuppressBuiltInModel,
15+
shouldSuppressBuiltInModel,
16+
} from "./model-suppression.js";
1417

1518
describe("model suppression", () => {
1619
beforeEach(() => {
20+
mocks.buildManifestBuiltInModelSuppressionResolver.mockReset();
1721
mocks.resolveManifestBuiltInModelSuppression.mockReset();
1822
});
1923

@@ -51,18 +55,42 @@ describe("model suppression", () => {
5155
mocks.buildManifestBuiltInModelSuppressionResolver.mockReset();
5256
});
5357

54-
it("normalizes provider aliases before checking suppressions", () => {
55-
const resolverMock = vi.fn().mockReturnValue({ suppress: true });
56-
mocks.buildManifestBuiltInModelSuppressionResolver.mockReturnValueOnce(resolverMock);
58+
it("creates a reusable manifest resolver with normalized provider and model ids", () => {
59+
const resolver = vi
60+
.fn()
61+
.mockReturnValueOnce({ suppress: true, errorMessage: "manifest suppression" })
62+
.mockReturnValueOnce(undefined);
63+
const config = {};
64+
mocks.buildManifestBuiltInModelSuppressionResolver.mockReturnValueOnce(resolver);
65+
66+
const shouldSuppress = buildShouldSuppressBuiltInModel({ config });
67+
68+
expect(shouldSuppress({ provider: "bedrock", id: "Claude-3" })).toBe(true);
69+
expect(shouldSuppress({ provider: "aws-bedrock", id: "claude-4" })).toBe(false);
70+
expect(mocks.buildManifestBuiltInModelSuppressionResolver).toHaveBeenCalledOnce();
71+
expect(mocks.buildManifestBuiltInModelSuppressionResolver).toHaveBeenCalledWith({
72+
config,
73+
env: process.env,
74+
});
75+
expect(resolver).toHaveBeenNthCalledWith(1, {
76+
provider: "amazon-bedrock",
77+
id: "claude-3",
78+
});
79+
expect(resolver).toHaveBeenNthCalledWith(2, {
80+
provider: "amazon-bedrock",
81+
id: "claude-4",
82+
});
83+
});
84+
85+
it("does not call the manifest resolver for empty provider or model ids", () => {
86+
const resolver = vi.fn();
87+
mocks.buildManifestBuiltInModelSuppressionResolver.mockReturnValueOnce(resolver);
5788

58-
const predicate = buildShouldSuppressBuiltInModel({ config: {} });
59-
60-
expect(predicate({ provider: "bedrock", id: "anthropic.claude-3-5-sonnet" })).toBe(true);
89+
const shouldSuppress = buildShouldSuppressBuiltInModel({});
6190

62-
expect(resolverMock).toHaveBeenCalledOnce();
63-
expect(resolverMock).toHaveBeenCalledWith(
64-
expect.objectContaining({ provider: "amazon-bedrock", id: "anthropic.claude-3-5-sonnet" })
65-
);
91+
expect(shouldSuppress({ provider: "openai", id: "" })).toBe(false);
92+
expect(shouldSuppress({ provider: "", id: "gpt-5.5" })).toBe(false);
93+
expect(resolver).not.toHaveBeenCalled();
6694
});
6795
});
6896
});

src/agents/model-suppression.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,18 @@ export function buildSuppressedBuiltInModelError(params: {
7272

7373
export function buildShouldSuppressBuiltInModel(params: {
7474
config?: OpenClawConfig;
75-
}): (input: {
76-
provider?: string | null;
77-
id?: string | null;
78-
baseUrl?: string | null;
79-
}) => boolean {
75+
}): (input: { provider?: string | null; id?: string | null; baseUrl?: string | null }) => boolean {
8076
const resolver = buildManifestBuiltInModelSuppressionResolver({
8177
config: params.config,
8278
env: process.env,
8379
});
8480

8581
return (input) => {
86-
return (
87-
resolver({ ...input, provider: normalizeProviderId(input.provider ?? "") })?.suppress ?? false
88-
);
82+
const provider = normalizeProviderId(input.provider ?? "");
83+
const id = normalizeLowercaseStringOrEmpty(input.id);
84+
if (!provider || !id) {
85+
return false;
86+
}
87+
return resolver({ ...input, provider, id })?.suppress ?? false;
8988
};
9089
}

src/plugins/manifest-model-suppression.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,29 @@ describe("manifest model suppression", () => {
114114
expect(mocks.loadPluginManifestRegistryForPluginRegistry).toHaveBeenCalledTimes(2);
115115
});
116116

117+
it("reuses planned manifest suppressions inside a resolver instance", () => {
118+
const config = { plugins: { entries: { openai: { enabled: true } } } };
119+
120+
const resolver = buildManifestBuiltInModelSuppressionResolver({
121+
config,
122+
env: process.env,
123+
});
124+
125+
expect(
126+
resolver({
127+
provider: "azure-openai-responses",
128+
id: "gpt-5.3-codex-spark",
129+
})?.suppress,
130+
).toBe(true);
131+
expect(
132+
resolver({
133+
provider: "azure-openai-responses",
134+
id: "gpt-4.1",
135+
}),
136+
).toBeUndefined();
137+
expect(mocks.loadPluginManifestRegistryForPluginRegistry).toHaveBeenCalledTimes(1);
138+
});
139+
117140
it("matches conditional suppressions by base URL host", () => {
118141
mocks.loadPluginManifestRegistryForPluginRegistry.mockReturnValue({
119142
diagnostics: [],

0 commit comments

Comments
 (0)