fix: canonicalize openrouter native model keys

This commit is contained in:
Peter Steinberger
2026-03-12 16:50:31 +00:00
parent 115f24819e
commit 0b34671de3
7 changed files with 149 additions and 14 deletions

View File

@@ -73,6 +73,12 @@ describe("model-selection", () => {
});
});
describe("modelKey", () => {
it("keeps canonical OpenRouter native ids without duplicating the provider", () => {
expect(modelKey("openrouter", "openrouter/hunter-alpha")).toBe("openrouter/hunter-alpha");
});
});
describe("parseModelRef", () => {
it("should parse full model refs", () => {
expect(parseModelRef("anthropic/claude-3-5-sonnet", "openai")).toEqual({
@@ -754,6 +760,28 @@ describe("model-selection", () => {
expect(resolveAnthropicOpusThinking(cfg)).toBe("high");
});
it("accepts legacy duplicated OpenRouter keys for per-model thinking", () => {
const cfg = {
agents: {
defaults: {
models: {
"openrouter/openrouter/hunter-alpha": {
params: { thinking: "high" },
},
},
},
},
} as OpenClawConfig;
expect(
resolveThinkingDefault({
cfg,
provider: "openrouter",
model: "openrouter/hunter-alpha",
}),
).toBe("high");
});
it("accepts per-model params.thinking=adaptive", () => {
const cfg = {
agents: {

View File

@@ -43,7 +43,28 @@ function normalizeAliasKey(value: string): string {
}
export function modelKey(provider: string, model: string) {
return `${provider}/${model}`;
const providerId = provider.trim();
const modelId = model.trim();
if (!providerId) {
return modelId;
}
if (!modelId) {
return providerId;
}
return modelId.toLowerCase().startsWith(`${providerId.toLowerCase()}/`)
? modelId
: `${providerId}/${modelId}`;
}
export function legacyModelKey(provider: string, model: string): string | null {
const providerId = provider.trim();
const modelId = model.trim();
if (!providerId || !modelId) {
return null;
}
const rawKey = `${providerId}/${modelId}`;
const canonicalKey = modelKey(providerId, modelId);
return rawKey === canonicalKey ? null : rawKey;
}
export function normalizeProviderId(provider: string): string {
@@ -610,9 +631,12 @@ export function resolveThinkingDefault(params: {
}): ThinkLevel {
const normalizedProvider = normalizeProviderId(params.provider);
const modelLower = params.model.toLowerCase();
const configuredModels = params.cfg.agents?.defaults?.models;
const canonicalKey = modelKey(params.provider, params.model);
const legacyKey = legacyModelKey(params.provider, params.model);
const perModelThinking =
params.cfg.agents?.defaults?.models?.[modelKey(params.provider, params.model)]?.params
?.thinking;
configuredModels?.[canonicalKey]?.params?.thinking ??
(legacyKey ? configuredModels?.[legacyKey]?.params?.thinking : undefined);
if (
perModelThinking === "off" ||
perModelThinking === "minimal" ||