Skip to content

Commit fe00512

Browse files
committed
fix(configure): allow pruning stale provider models
1 parent 5a55135 commit fe00512

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

src/commands/model-picker.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33
import { testing as cliBackendsTesting } from "../agents/cli-backends.js";
44
import type { ModelCatalogEntry } from "../agents/model-catalog.js";
55
import type { OpenClawConfig } from "../config/config.js";
6+
import type { WizardMultiSelectParams, WizardPrompter } from "../wizard/prompts.js";
67
import {
78
applyModelAllowlist,
89
applyModelFallbacksFromSelection,
@@ -1424,6 +1425,64 @@ describe("promptModelAllowlist", () => {
14241425
expect(optionValues(options)).toEqual(["openai/gpt-5.5", "openai/gpt-5.4-mini"]);
14251426
});
14261427

1428+
it("includes stale configured preferred provider models in the scoped cleanup", async () => {
1429+
loadModelCatalog.mockResolvedValue([
1430+
{
1431+
provider: "openrouter",
1432+
id: "meta-llama/llama-3.3-70b:free",
1433+
name: "Llama 3.3 70B",
1434+
},
1435+
{
1436+
provider: "openai",
1437+
id: "gpt-5.5",
1438+
name: "GPT-5.5",
1439+
},
1440+
]);
1441+
1442+
const activeModel = "openrouter/meta-llama/llama-3.3-70b:free";
1443+
const staleModel = "openrouter/elephant-alpha";
1444+
const multiselect = vi.fn(async (params: WizardMultiSelectParams) => {
1445+
return params.options.map((option) => option.value).filter((value) => value === activeModel);
1446+
});
1447+
const prompter = makePrompter({
1448+
multiselect: multiselect as unknown as WizardPrompter["multiselect"],
1449+
});
1450+
const config = {
1451+
agents: {
1452+
defaults: {
1453+
models: {
1454+
[activeModel]: { alias: "llama" },
1455+
[staleModel]: { alias: "elephant" },
1456+
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
1457+
},
1458+
},
1459+
},
1460+
} as OpenClawConfig;
1461+
1462+
const result = await promptModelAllowlist({
1463+
config,
1464+
prompter,
1465+
preferredProvider: "openrouter",
1466+
});
1467+
1468+
const options = pickerOptions(multiselect);
1469+
expect(optionValues(options)).toEqual([activeModel, staleModel]);
1470+
expect(requireOption(options, staleModel).hint).toBe("configured (not in catalog)");
1471+
expect(multiselect.mock.calls[0]?.[0]?.initialValues).toEqual([activeModel, staleModel]);
1472+
expect(result).toEqual({
1473+
models: [activeModel],
1474+
scopeKeys: [activeModel, staleModel],
1475+
});
1476+
1477+
const next = applyModelAllowlist(config, result.models ?? [], {
1478+
scopeKeys: result.scopeKeys,
1479+
});
1480+
expect(next.agents?.defaults?.models).toEqual({
1481+
[activeModel]: { alias: "llama" },
1482+
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
1483+
});
1484+
});
1485+
14271486
it("shows configured preferred provider models when the catalog has no entries", async () => {
14281487
loadModelCatalog.mockResolvedValue([]);
14291488

src/flows/model-picker.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1219,11 +1219,24 @@ export async function promptModelAllowlist(params: {
12191219
preferredProvider && allowedCatalog.some((entry) => matchesPreferredProvider?.(entry.provider))
12201220
? allowedCatalog.filter((entry) => matchesPreferredProvider?.(entry.provider))
12211221
: allowedCatalog;
1222+
const scopedConfiguredKeys =
1223+
preferredProvider && !allowedKeySet
1224+
? existingKeys.filter((key) => {
1225+
if (!isVisibleModelRef(key)) {
1226+
return false;
1227+
}
1228+
const entry = splitModelKey(key);
1229+
return entry ? matchesPreferredProvider?.(entry.provider) === true : false;
1230+
})
1231+
: [];
12221232

12231233
const scopeKeys = allowedKeySet
12241234
? allowedKeys
12251235
: preferredProvider
1226-
? filteredCatalog.map((entry) => modelKey(entry.provider, entry.id))
1236+
? normalizeModelKeys([
1237+
...filteredCatalog.map((entry) => modelKey(entry.provider, entry.id)),
1238+
...scopedConfiguredKeys,
1239+
])
12271240
: undefined;
12281241
const scopeKeySet = scopeKeys ? new Set(scopeKeys) : null;
12291242
const selectableInitialSeeds =

0 commit comments

Comments
 (0)