mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
fix(cli): normalize Gemini config mutation refs
This commit is contained in:
@@ -72,6 +72,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids returned by direct `openclaw models auth login --set-default` provider auth flows before writing config, so Gemini testing targets `google/gemini-3.1-pro-preview`.
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids in per-agent config defaults and auth patches, so agent-specific emitted config keeps targeting `google/gemini-3.1-pro-preview`.
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids in provider catalog rows when API-key onboarding only reapplies the agent default, so emitted config keeps testing `google/gemini-3.1-pro-preview`.
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids in `config set` mutation output for agent overrides and provider catalog rows, so current config emits `google/gemini-3.1-pro-preview`.
|
||||
- Google/Gemini: canonicalize provider-qualified retired Gemini 3 Pro Preview refs during Google forward-compatible model resolution, so emitted config uses `google/gemini-3.1-pro-preview` for Gemini 3.1 testing.
|
||||
- Google/Gemini: normalize proxy-prefixed retired Gemini 3 Pro Preview catalog rows, so emitted configs use `google/gemini-3.1-pro-preview` for Gemini 3.1 testing.
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids inside per-agent model overrides before writing config, so agent-specific config emits `google/gemini-3.1-pro-preview` for Gemini 3.1 testing.
|
||||
|
||||
@@ -479,6 +479,64 @@ describe("config cli", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes agent-list model refs before writing config mutations", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "tester",
|
||||
model: { primary: "google/gemini-3-pro-preview" },
|
||||
models: {
|
||||
"google/gemini-3-pro-preview": { alias: "gemini" },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand(["config", "set", "gateway.port", "18790"]);
|
||||
|
||||
expect(mockWriteConfigFile).toHaveBeenCalledTimes(1);
|
||||
const agent = firstWrittenConfig().agents?.list?.[0];
|
||||
expect(agent?.model).toEqual({ primary: "google/gemini-3.1-pro-preview" });
|
||||
expect(agent?.models).toEqual({
|
||||
"google/gemini-3.1-pro-preview": { alias: "gemini" },
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes provider catalog model refs before writing config mutations", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
models: {
|
||||
providers: {
|
||||
google: {
|
||||
api: "google-generative-ai",
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
models: [
|
||||
{
|
||||
id: "google/gemini-3-pro-preview",
|
||||
name: "Gemini 3 Pro",
|
||||
contextWindow: 1_048_576,
|
||||
maxTokens: 65_536,
|
||||
input: ["text", "image"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
reasoning: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand(["config", "set", "gateway.port", "18790"]);
|
||||
|
||||
expect(mockWriteConfigFile).toHaveBeenCalledTimes(1);
|
||||
expect(firstWrittenConfig().models?.providers?.google?.models?.[0]?.id).toBe(
|
||||
"google/gemini-3.1-pro-preview",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects plugin install record config updates", async () => {
|
||||
await expect(
|
||||
runConfigCommand([
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import type { Command } from "commander";
|
||||
import JSON5 from "json5";
|
||||
import { normalizeConfiguredProviderCatalogModelId } from "../agents/model-ref-shared.js";
|
||||
import { readConfigFileSnapshot, replaceConfigFile } from "../config/config.js";
|
||||
import { AUTO_MANAGED_CONFIG_META_PATHS } from "../config/io.meta.js";
|
||||
import { formatConfigIssueLines, normalizeConfigIssues } from "../config/issue-format.js";
|
||||
@@ -116,30 +117,136 @@ function normalizeAgentDefaultModelValueForConfigMutation(value: unknown): unkno
|
||||
return next;
|
||||
}
|
||||
|
||||
function normalizeAgentListModelRefsForConfigMutation(value: unknown): unknown {
|
||||
if (!Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
let mutated = false;
|
||||
const next = value.map((agent) => {
|
||||
if (!isPlainRecord(agent)) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
let nextAgent = agent;
|
||||
if (Object.prototype.hasOwnProperty.call(agent, "model")) {
|
||||
const model = normalizeAgentDefaultModelValueForConfigMutation(agent.model);
|
||||
if (model !== agent.model) {
|
||||
nextAgent = { ...nextAgent, model };
|
||||
mutated = true;
|
||||
}
|
||||
}
|
||||
if (isPlainRecord(agent.models)) {
|
||||
const models = normalizeAgentModelMapForConfig(agent.models);
|
||||
if (models !== agent.models) {
|
||||
nextAgent = { ...nextAgent, models };
|
||||
mutated = true;
|
||||
}
|
||||
}
|
||||
return nextAgent;
|
||||
});
|
||||
|
||||
return mutated ? next : value;
|
||||
}
|
||||
|
||||
function normalizeProviderCatalogModelsForConfigMutation(
|
||||
provider: string,
|
||||
models: unknown,
|
||||
): unknown {
|
||||
if (!Array.isArray(models)) {
|
||||
return models;
|
||||
}
|
||||
|
||||
let mutated = false;
|
||||
const next = models.map((model) => {
|
||||
if (!isPlainRecord(model) || typeof model.id !== "string") {
|
||||
return model;
|
||||
}
|
||||
const trimmed = model.id.trim();
|
||||
if (!trimmed) {
|
||||
return model;
|
||||
}
|
||||
const id = normalizeConfiguredProviderCatalogModelId(provider, trimmed);
|
||||
if (id === model.id) {
|
||||
return model;
|
||||
}
|
||||
mutated = true;
|
||||
return { ...model, id };
|
||||
});
|
||||
|
||||
return mutated ? next : models;
|
||||
}
|
||||
|
||||
function normalizeModelProviderRefsForConfigMutation(
|
||||
providers: NonNullable<OpenClawConfig["models"]>["providers"] | undefined,
|
||||
): unknown {
|
||||
if (!isPlainRecord(providers)) {
|
||||
return providers;
|
||||
}
|
||||
|
||||
let mutated = false;
|
||||
const nextProviders: Record<string, unknown> = { ...providers };
|
||||
for (const [provider, providerConfig] of Object.entries(providers)) {
|
||||
if (!isPlainRecord(providerConfig)) {
|
||||
continue;
|
||||
}
|
||||
const models = normalizeProviderCatalogModelsForConfigMutation(provider, providerConfig.models);
|
||||
if (models === providerConfig.models) {
|
||||
continue;
|
||||
}
|
||||
nextProviders[provider] = { ...providerConfig, models };
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
return mutated ? nextProviders : providers;
|
||||
}
|
||||
|
||||
function normalizeConfigMutationModelRefs(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const defaults = cfg.agents?.defaults;
|
||||
if (!defaults) {
|
||||
return cfg;
|
||||
}
|
||||
const agentList = cfg.agents?.list;
|
||||
const providers = cfg.models?.providers;
|
||||
const normalizedAgentList = normalizeAgentListModelRefsForConfigMutation(agentList);
|
||||
const normalizedProviders = normalizeModelProviderRefsForConfigMutation(providers) as
|
||||
| typeof providers
|
||||
| undefined;
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...defaults,
|
||||
...(defaults.model !== undefined
|
||||
? {
|
||||
model: normalizeAgentDefaultModelValueForConfigMutation(
|
||||
defaults.model,
|
||||
) as typeof defaults.model,
|
||||
}
|
||||
: undefined),
|
||||
...(defaults.models !== undefined
|
||||
? { models: normalizeAgentModelMapForConfig(defaults.models) }
|
||||
: undefined),
|
||||
},
|
||||
},
|
||||
...(defaults || normalizedAgentList !== agentList
|
||||
? {
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
...(defaults
|
||||
? {
|
||||
defaults: {
|
||||
...defaults,
|
||||
...(defaults.model !== undefined
|
||||
? {
|
||||
model: normalizeAgentDefaultModelValueForConfigMutation(
|
||||
defaults.model,
|
||||
) as typeof defaults.model,
|
||||
}
|
||||
: undefined),
|
||||
...(defaults.models !== undefined
|
||||
? { models: normalizeAgentModelMapForConfig(defaults.models) }
|
||||
: undefined),
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
...(normalizedAgentList !== agentList
|
||||
? { list: normalizedAgentList as typeof agentList }
|
||||
: undefined),
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
...(normalizedProviders !== providers
|
||||
? {
|
||||
models: {
|
||||
...cfg.models,
|
||||
providers: normalizedProviders,
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user