Skip to content

Commit cc8b125

Browse files
IWhatsskillclawsweeper[bot]
authored andcommitted
fix(models): preserve authored aliases for set
1 parent fcde745 commit cc8b125

2 files changed

Lines changed: 76 additions & 4 deletions

File tree

src/commands/models/set.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,50 @@ describe("modelsSetCommand", () => {
8585
});
8686
expect(runtime.log).toHaveBeenCalledWith("Default model: anthropic/claude-sonnet-4-6");
8787
});
88+
89+
it("keeps authored aliases ahead of runtime-only aliases", async () => {
90+
const sourceConfig = {
91+
agents: {
92+
defaults: {
93+
models: {
94+
"openai/gpt-5.5": { alias: "sonnet" },
95+
},
96+
},
97+
},
98+
} as unknown as OpenClawConfig;
99+
const runtimeConfig = {
100+
agents: {
101+
defaults: {
102+
models: {
103+
"openai/gpt-5.5": { alias: "sonnet" },
104+
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
105+
},
106+
},
107+
},
108+
} as unknown as OpenClawConfig;
109+
mocks.readConfigFileSnapshot.mockResolvedValue({
110+
valid: true,
111+
hash: "config-hash",
112+
sourceConfig,
113+
runtimeConfig,
114+
config: runtimeConfig,
115+
});
116+
const runtime = makeRuntime();
117+
118+
await modelsSetCommand("sonnet", runtime);
119+
120+
expect(mocks.replaceConfigFile).toHaveBeenCalledOnce();
121+
const [replaceParams] = mocks.replaceConfigFile.mock.calls[0] ?? [];
122+
expect(replaceParams?.nextConfig.agents?.defaults?.model).toEqual({
123+
primary: "openai/gpt-5.5",
124+
});
125+
expect(replaceParams?.nextConfig.agents?.defaults?.models).toEqual({
126+
"openai/gpt-5.5": { alias: "sonnet" },
127+
});
128+
expect(mocks.repairCodexRuntimePluginInstallForModelSelection).toHaveBeenCalledWith({
129+
cfg: replaceParams?.nextConfig,
130+
model: "openai/gpt-5.5",
131+
});
132+
expect(runtime.log).toHaveBeenCalledWith("Default model: openai/gpt-5.5");
133+
});
88134
});

src/commands/models/shared.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ export function resolveModelTarget(params: { raw: string; cfg: OpenClawConfig })
100100
return resolved.ref;
101101
}
102102

103+
function resolveAuthoredModelAliasTarget(params: {
104+
raw: string;
105+
cfg: OpenClawConfig;
106+
}): { provider: string; model: string } | undefined {
107+
const aliasIndex = buildModelAliasIndex({
108+
cfg: params.cfg,
109+
defaultProvider: DEFAULT_PROVIDER,
110+
});
111+
const resolved = resolveModelRefFromString({
112+
raw: params.raw,
113+
defaultProvider: DEFAULT_PROVIDER,
114+
aliasIndex,
115+
});
116+
return resolved?.alias ? resolved.ref : undefined;
117+
}
118+
103119
export function resolveModelKeysFromEntries(params: {
104120
cfg: OpenClawConfig;
105121
entries: readonly string[];
@@ -219,10 +235,20 @@ export function applyDefaultModelPrimaryUpdate(params: {
219235
modelRaw: string;
220236
field: "model" | "imageModel";
221237
}): OpenClawConfig {
222-
const resolved = resolveModelTarget({
223-
raw: params.modelRaw,
224-
cfg: params.resolveCfg ?? params.cfg,
225-
});
238+
const resolved =
239+
params.resolveCfg && params.resolveCfg !== params.cfg
240+
? (resolveAuthoredModelAliasTarget({
241+
raw: params.modelRaw,
242+
cfg: params.cfg,
243+
}) ??
244+
resolveModelTarget({
245+
raw: params.modelRaw,
246+
cfg: params.resolveCfg,
247+
}))
248+
: resolveModelTarget({
249+
raw: params.modelRaw,
250+
cfg: params.cfg,
251+
});
226252
const nextModels = {
227253
...params.cfg.agents?.defaults?.models,
228254
} as Record<string, AgentModelEntryConfig>;

0 commit comments

Comments
 (0)