mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 20:05:02 +00:00
fix: canonicalize openrouter native model keys
This commit is contained in:
@@ -273,6 +273,29 @@ describe("models list/status", () => {
|
||||
expect(runtime.log.mock.calls[0]?.[0]).toBe("zai/glm-4.7");
|
||||
});
|
||||
|
||||
it("models list plain keeps canonical OpenRouter native ids", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
agents: { defaults: { model: "openrouter/hunter-alpha" } },
|
||||
});
|
||||
const runtime = makeRuntime();
|
||||
|
||||
modelRegistryState.models = [
|
||||
{
|
||||
provider: "openrouter",
|
||||
id: "openrouter/hunter-alpha",
|
||||
name: "Hunter Alpha",
|
||||
input: ["text"],
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
contextWindow: 1048576,
|
||||
},
|
||||
];
|
||||
modelRegistryState.available = modelRegistryState.models;
|
||||
await modelsListCommand({ plain: true }, runtime);
|
||||
|
||||
expect(runtime.log).toHaveBeenCalledTimes(1);
|
||||
expect(runtime.log.mock.calls[0]?.[0]).toBe("openrouter/hunter-alpha");
|
||||
});
|
||||
|
||||
it.each(["z.ai", "Z.AI", "z-ai"] as const)(
|
||||
"models list provider filter normalizes %s alias",
|
||||
async (provider) => {
|
||||
|
||||
@@ -110,6 +110,45 @@ describe("models set + fallbacks", () => {
|
||||
expectWrittenPrimaryModel("zai/glm-4.7");
|
||||
});
|
||||
|
||||
it("keeps canonical OpenRouter native ids in models set", async () => {
|
||||
mockConfigSnapshot({});
|
||||
const runtime = makeRuntime();
|
||||
|
||||
await modelsSetCommand("openrouter/hunter-alpha", runtime);
|
||||
|
||||
expectWrittenPrimaryModel("openrouter/hunter-alpha");
|
||||
});
|
||||
|
||||
it("migrates legacy duplicated OpenRouter keys on write", async () => {
|
||||
mockConfigSnapshot({
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openrouter/openrouter/hunter-alpha": {
|
||||
params: { thinking: "high" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const runtime = makeRuntime();
|
||||
|
||||
await modelsSetCommand("openrouter/hunter-alpha", runtime);
|
||||
|
||||
expect(writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
const written = getWrittenConfig();
|
||||
expect(written.agents).toEqual({
|
||||
defaults: {
|
||||
model: { primary: "openrouter/hunter-alpha" },
|
||||
models: {
|
||||
"openrouter/hunter-alpha": {
|
||||
params: { thinking: "high" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrites string defaults.model to object form when setting primary", async () => {
|
||||
mockConfigSnapshot({ agents: { defaults: { model: "openai/gpt-4.1-mini" } } });
|
||||
const runtime = makeRuntime();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/mo
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { logConfigUpdated } from "../../config/logging.js";
|
||||
import { resolveAgentModelFallbackValues, toAgentModelListLike } from "../../config/model-input.js";
|
||||
import type { AgentModelEntryConfig } from "../../config/types.agent-defaults.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { loadModelsConfig } from "./load-config.js";
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
modelKey,
|
||||
resolveModelTarget,
|
||||
resolveModelKeysFromEntries,
|
||||
upsertCanonicalModelConfigEntry,
|
||||
updateConfig,
|
||||
} from "./shared.js";
|
||||
|
||||
@@ -79,11 +81,10 @@ export async function addFallbackCommand(
|
||||
) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const targetKey = modelKey(resolved.provider, resolved.model);
|
||||
const nextModels = { ...cfg.agents?.defaults?.models } as Record<string, unknown>;
|
||||
if (!nextModels[targetKey]) {
|
||||
nextModels[targetKey] = {};
|
||||
}
|
||||
const nextModels = {
|
||||
...cfg.agents?.defaults?.models,
|
||||
} as Record<string, AgentModelEntryConfig>;
|
||||
const targetKey = upsertCanonicalModelConfigEntry(nextModels, resolved);
|
||||
const existing = getFallbacks(cfg, params.key);
|
||||
const existingKeys = resolveModelKeysFromEntries({ cfg, entries: existing });
|
||||
if (existingKeys.includes(targetKey)) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { listAgentIds } from "../../agents/agent-scope.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../agents/defaults.js";
|
||||
import {
|
||||
buildModelAliasIndex,
|
||||
legacyModelKey,
|
||||
modelKey,
|
||||
parseModelRef,
|
||||
resolveModelRefFromString,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
} from "../../config/config.js";
|
||||
import { formatConfigIssueLines } from "../../config/issue-format.js";
|
||||
import { toAgentModelListLike } from "../../config/model-input.js";
|
||||
import type { AgentModelEntryConfig } from "../../config/types.agent-defaults.js";
|
||||
import type { AgentModelConfig } from "../../config/types.agents-shared.js";
|
||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
|
||||
@@ -163,6 +165,25 @@ export function resolveKnownAgentId(params: {
|
||||
|
||||
export type PrimaryFallbackConfig = { primary?: string; fallbacks?: string[] };
|
||||
|
||||
export function upsertCanonicalModelConfigEntry(
|
||||
models: Record<string, AgentModelEntryConfig>,
|
||||
params: { provider: string; model: string },
|
||||
) {
|
||||
const key = modelKey(params.provider, params.model);
|
||||
const legacyKey = legacyModelKey(params.provider, params.model);
|
||||
if (!models[key]) {
|
||||
if (legacyKey && models[legacyKey]) {
|
||||
models[key] = models[legacyKey];
|
||||
} else {
|
||||
models[key] = {};
|
||||
}
|
||||
}
|
||||
if (legacyKey) {
|
||||
delete models[legacyKey];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export function mergePrimaryFallbackConfig(
|
||||
existing: PrimaryFallbackConfig | undefined,
|
||||
patch: { primary?: string; fallbacks?: string[] },
|
||||
@@ -184,12 +205,10 @@ export function applyDefaultModelPrimaryUpdate(params: {
|
||||
field: "model" | "imageModel";
|
||||
}): OpenClawConfig {
|
||||
const resolved = resolveModelTarget({ raw: params.modelRaw, cfg: params.cfg });
|
||||
const key = `${resolved.provider}/${resolved.model}`;
|
||||
|
||||
const nextModels = { ...params.cfg.agents?.defaults?.models };
|
||||
if (!nextModels[key]) {
|
||||
nextModels[key] = {};
|
||||
}
|
||||
const nextModels = {
|
||||
...params.cfg.agents?.defaults?.models,
|
||||
} as Record<string, AgentModelEntryConfig>;
|
||||
const key = upsertCanonicalModelConfigEntry(nextModels, resolved);
|
||||
|
||||
const defaults = params.cfg.agents?.defaults ?? {};
|
||||
const existing = toAgentModelListLike(
|
||||
|
||||
Reference in New Issue
Block a user