mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
feat(openai): default new setups to GPT-5.6 (#103581)
* feat(openai): default fresh setup to GPT-5.6 * test(crestodian): expect GPT-5.6 Codex defaults * test(crestodian): expect GPT-5.6 bootstrap default
This commit is contained in:
committed by
GitHub
parent
fc60a8e66b
commit
ab5d143d59
@@ -28,7 +28,7 @@ const HIGH_SIGNAL_LIVE_MODEL_PRIORITY = [
|
||||
"deepseek/deepseek-v4-flash",
|
||||
"deepseek/deepseek-v4-pro",
|
||||
"minimax/minimax-m3",
|
||||
"openai/gpt-5.5",
|
||||
"openai/gpt-5.6",
|
||||
"openrouter/openai/gpt-5.2-chat",
|
||||
"openrouter/minimax/minimax-m2.7",
|
||||
"opencode-go/glm-5",
|
||||
@@ -56,6 +56,12 @@ export const DEFAULT_HIGH_SIGNAL_LIVE_MODEL_LIMIT = HIGH_SIGNAL_LIVE_MODEL_PRIOR
|
||||
/** Default cap for the small-model live smoke lane. */
|
||||
export const DEFAULT_SMALL_LIVE_MODEL_LIMIT = SMALL_LIVE_MODEL_PRIORITY.length;
|
||||
const DEFAULT_HIGH_SIGNAL_LIVE_EXCLUDED_PROVIDERS = new Set(["codex", "codex-cli"]);
|
||||
const DIRECT_OPENAI_HIGH_SIGNAL_LIVE_MODEL_IDS = new Set([
|
||||
"gpt-5.6",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
]);
|
||||
const CURATED_ONLY_HIGH_SIGNAL_LIVE_PROVIDERS = new Set([
|
||||
"fireworks",
|
||||
"google",
|
||||
@@ -157,7 +163,7 @@ function isUnsupportedOpenAiLiveModelRef(provider: string, id: string): boolean
|
||||
}
|
||||
const modelName = normalizeLowercaseStringOrEmpty(id).split("/").pop() ?? "";
|
||||
if (provider === "openai") {
|
||||
return modelName !== "gpt-5.5";
|
||||
return !DIRECT_OPENAI_HIGH_SIGNAL_LIVE_MODEL_IDS.has(modelName);
|
||||
}
|
||||
return !modelName.startsWith("gpt-5.2");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveVisibleModelCatalog } from "./model-catalog-visibility.js";
|
||||
import {
|
||||
isCodexRoutableOpenAIPlatformCatalogEntry,
|
||||
resolveVisibleModelCatalog,
|
||||
} from "./model-catalog-visibility.js";
|
||||
import type { ModelCatalogEntry } from "./model-catalog.types.js";
|
||||
|
||||
const normalizeProviderModelIdWithRuntimeMock = vi.hoisted(() => vi.fn());
|
||||
@@ -19,6 +22,20 @@ describe("resolveVisibleModelCatalog", () => {
|
||||
normalizeProviderModelIdWithRuntimeMock.mockReset();
|
||||
});
|
||||
|
||||
it("recognizes exact GPT-5.6 Codex ids without treating the API alias as routable", () => {
|
||||
const entry = (id: string): ModelCatalogEntry => ({
|
||||
provider: "openai",
|
||||
id,
|
||||
name: id,
|
||||
api: "openai-responses",
|
||||
});
|
||||
|
||||
expect(isCodexRoutableOpenAIPlatformCatalogEntry(entry("gpt-5.6"))).toBe(false);
|
||||
expect(isCodexRoutableOpenAIPlatformCatalogEntry(entry("gpt-5.6-sol"))).toBe(true);
|
||||
expect(isCodexRoutableOpenAIPlatformCatalogEntry(entry("gpt-5.6-terra"))).toBe(true);
|
||||
expect(isCodexRoutableOpenAIPlatformCatalogEntry(entry("gpt-5.6-luna"))).toBe(true);
|
||||
});
|
||||
|
||||
it("can use static auth checks for gateway read-only model lists", async () => {
|
||||
const authChecker = vi.fn((provider: string) => provider === "openai");
|
||||
const catalog: ModelCatalogEntry[] = [
|
||||
|
||||
@@ -20,6 +20,9 @@ type ProviderAuthChecker = (provider: string, modelApi?: string) => boolean | Pr
|
||||
const OPENAI_PROVIDER_ID = "openai";
|
||||
const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
|
||||
const OPENAI_CODEX_ROUTABLE_MODEL_IDS = new Set([
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
"gpt-5.5",
|
||||
"gpt-5.5-pro",
|
||||
"gpt-5.4",
|
||||
|
||||
@@ -393,9 +393,18 @@ describe("isModernModelRef", () => {
|
||||
it("includes plugin-advertised modern models", () => {
|
||||
providerRuntimeMocks.resolveProviderModernModelRef.mockImplementation(({ provider, context }) =>
|
||||
provider === "openai" &&
|
||||
["gpt-5.5", "gpt-5.5-pro", "gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini", "gpt-5.4-nano"].includes(
|
||||
context.modelId,
|
||||
)
|
||||
[
|
||||
"gpt-5.6",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
"gpt-5.5",
|
||||
"gpt-5.5-pro",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-pro",
|
||||
"gpt-5.4-mini",
|
||||
"gpt-5.4-nano",
|
||||
].includes(context.modelId)
|
||||
? true
|
||||
: provider === "openai" &&
|
||||
["gpt-5.5", "gpt-5.5-pro", "gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini"].includes(
|
||||
@@ -409,6 +418,8 @@ describe("isModernModelRef", () => {
|
||||
: undefined,
|
||||
);
|
||||
|
||||
expect(isModernModelRef({ provider: "openai", id: "gpt-5.6" })).toBe(true);
|
||||
expect(isModernModelRef({ provider: "openai", id: "gpt-5.6-sol" })).toBe(true);
|
||||
expect(isModernModelRef({ provider: "openai", id: "gpt-5.5" })).toBe(true);
|
||||
expect(isModernModelRef({ provider: "openai", id: "gpt-5.5-pro" })).toBe(true);
|
||||
expect(isModernModelRef({ provider: "openai", id: "gpt-5.4" })).toBe(true);
|
||||
@@ -495,7 +506,7 @@ describe("isHighSignalLiveModelRef", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps only the current direct OpenAI-family model in the default live matrix", () => {
|
||||
it("keeps only the current direct OpenAI-family models in the default live matrix", () => {
|
||||
providerRuntimeMocks.resolveProviderModernModelRef.mockReturnValue(true);
|
||||
|
||||
expect(isHighSignalLiveModelRef({ provider: "openrouter", id: "openai/gpt-3.5-turbo" })).toBe(
|
||||
@@ -510,7 +521,10 @@ describe("isHighSignalLiveModelRef", () => {
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5" })).toBe(false);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.1" })).toBe(false);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.4" })).toBe(false);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.5" })).toBe(true);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.5" })).toBe(false);
|
||||
for (const id of ["gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]) {
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id })).toBe(true);
|
||||
}
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.2-codex" })).toBe(false);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openai", id: "gpt-5.2-chat-latest" })).toBe(false);
|
||||
expect(isHighSignalLiveModelRef({ provider: "openrouter", id: "openai/gpt-5.1-chat" })).toBe(
|
||||
@@ -680,7 +694,7 @@ describe("isPrioritizedHighSignalLiveModelRef", () => {
|
||||
{ provider: "deepseek", id: "deepseek-v4-flash" },
|
||||
{ provider: "deepseek", id: "deepseek-v4-pro" },
|
||||
{ provider: "minimax", id: "minimax-m3" },
|
||||
{ provider: "openai", id: "gpt-5.5" },
|
||||
{ provider: "openai", id: "gpt-5.6" },
|
||||
{ provider: "openrouter", id: "openai/gpt-5.2-chat" },
|
||||
{ provider: "openrouter", id: "minimax/minimax-m2.7" },
|
||||
{ provider: "opencode-go", id: "glm-5" },
|
||||
|
||||
@@ -17,6 +17,11 @@ function probeDeps(found: Record<string, boolean>) {
|
||||
}
|
||||
|
||||
describe("detectInferenceBackends", () => {
|
||||
it("uses route-specific GPT-5.6 defaults for direct API and Codex", () => {
|
||||
expect(OPENAI_API_DEFAULT_MODEL_REF).toBe("openai/gpt-5.6");
|
||||
expect(CODEX_APP_SERVER_DEFAULT_MODEL_REF).toBe("openai/gpt-5.6-sol");
|
||||
});
|
||||
|
||||
it("returns nothing when no backend exists", async () => {
|
||||
const candidates = await detectInferenceBackends({
|
||||
env: {},
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
readGeminiCliCredentialsCached,
|
||||
} from "../agents/cli-credentials.js";
|
||||
// Inference backend detection shared by onboarding bootstrap and Crestodian setup.
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { probeLocalCommand, type LocalCommandProbe } from "../crestodian/probes.js";
|
||||
@@ -16,10 +15,10 @@ import { probeLocalCommand, type LocalCommandProbe } from "../crestodian/probes.
|
||||
* asking the user anything. The ladder order is a documented contract
|
||||
* (docs/cli/crestodian.md "Setup bootstrap") — change docs when changing it.
|
||||
*/
|
||||
export const OPENAI_API_DEFAULT_MODEL_REF = `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}`;
|
||||
export const OPENAI_API_DEFAULT_MODEL_REF = "openai/gpt-5.6";
|
||||
export const ANTHROPIC_API_DEFAULT_MODEL_REF = "anthropic/claude-opus-4-8";
|
||||
export const CLAUDE_CLI_DEFAULT_MODEL_REF = "claude-cli/claude-opus-4-8";
|
||||
export const CODEX_APP_SERVER_DEFAULT_MODEL_REF = OPENAI_API_DEFAULT_MODEL_REF;
|
||||
export const CODEX_APP_SERVER_DEFAULT_MODEL_REF = "openai/gpt-5.6-sol";
|
||||
export const GEMINI_CLI_DEFAULT_MODEL_REF = "google-gemini-cli/gemini-3.1-pro-preview";
|
||||
|
||||
export type InferenceBackendKind =
|
||||
|
||||
@@ -211,7 +211,7 @@ describe("Crestodian assistant", () => {
|
||||
const codexAppServerEntries = requireRecord(codexAppServerPlugins.entries);
|
||||
const codexAppServerCodexEntry = requireRecord(codexAppServerEntries.codex);
|
||||
expect(codexAppServerDefaults.workspace).toBe("/tmp/workspace");
|
||||
expect(codexAppServerModel.primary).toBe("openai/gpt-5.5");
|
||||
expect(codexAppServerModel.primary).toBe("openai/gpt-5.6-sol");
|
||||
expect(codexAppServerCodexEntry.enabled).toBe(true);
|
||||
});
|
||||
|
||||
@@ -246,12 +246,12 @@ describe("Crestodian assistant", () => {
|
||||
}
|
||||
expect(result.command).toBe("gateway status");
|
||||
expect(result.reply).toBe("Codex planner online.");
|
||||
expect(result.modelLabel).toBe("openai/gpt-5.5 via codex");
|
||||
expect(result.modelLabel).toBe("openai/gpt-5.6-sol via codex");
|
||||
|
||||
expect(runEmbeddedAgent).toHaveBeenCalledTimes(1);
|
||||
const firstEmbeddedCall = firstMockArg(runEmbeddedAgent);
|
||||
expect(firstEmbeddedCall.provider).toBe("openai");
|
||||
expect(firstEmbeddedCall.model).toBe("gpt-5.5");
|
||||
expect(firstEmbeddedCall.model).toBe("gpt-5.6-sol");
|
||||
expect(firstEmbeddedCall.agentHarnessId).toBe("codex");
|
||||
expect(firstEmbeddedCall.disableTools).toBe(true);
|
||||
expect(firstEmbeddedCall.toolsAllow).toEqual([]);
|
||||
@@ -262,7 +262,7 @@ describe("Crestodian assistant", () => {
|
||||
const embeddedPlugins = requireRecord(embeddedConfig.plugins);
|
||||
const embeddedEntries = requireRecord(embeddedPlugins.entries);
|
||||
const embeddedCodexEntry = requireRecord(embeddedEntries.codex);
|
||||
expect(embeddedModel.primary).toBe("openai/gpt-5.5");
|
||||
expect(embeddedModel.primary).toBe("openai/gpt-5.6-sol");
|
||||
expect(embeddedCodexEntry.enabled).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -737,7 +737,7 @@ describe("parseCrestodianOperation", () => {
|
||||
const { runtime, lines } = createCrestodianTestRuntime();
|
||||
const applySetup = vi.fn(async () => ({
|
||||
configPath: path.join(tempDir, "openclaw.json"),
|
||||
lines: ["Workspace: /tmp/work", "Default model: openai/gpt-5.5"],
|
||||
lines: ["Workspace: /tmp/work", "Default model: openai/gpt-5.6"],
|
||||
}));
|
||||
|
||||
const plan = await executeCrestodianOperation(
|
||||
@@ -748,7 +748,7 @@ describe("parseCrestodianOperation", () => {
|
||||
expectRecordFields(plan as unknown as Record<string, unknown>, {
|
||||
applied: false,
|
||||
});
|
||||
expect(lines.join("\n")).toContain("Model choice: openai/gpt-5.5 (OPENAI_API_KEY).");
|
||||
expect(lines.join("\n")).toContain("Model choice: openai/gpt-5.6 (OPENAI_API_KEY).");
|
||||
expect(applySetup).not.toHaveBeenCalled();
|
||||
|
||||
const result = await executeCrestodianOperation(
|
||||
@@ -765,7 +765,7 @@ describe("parseCrestodianOperation", () => {
|
||||
expect(lines.join("\n")).toContain("[crestodian] done: crestodian.setup");
|
||||
expect(applySetup).toHaveBeenCalledWith({
|
||||
workspace: "/tmp/work",
|
||||
model: "openai/gpt-5.5",
|
||||
model: "openai/gpt-5.6",
|
||||
surface: "cli",
|
||||
runtime,
|
||||
});
|
||||
@@ -775,12 +775,12 @@ describe("parseCrestodianOperation", () => {
|
||||
audit,
|
||||
{
|
||||
operation: "crestodian.setup",
|
||||
summary: "Bootstrapped setup with openai/gpt-5.5",
|
||||
summary: "Bootstrapped setup with openai/gpt-5.6",
|
||||
},
|
||||
{
|
||||
rescue: true,
|
||||
workspace: "/tmp/work",
|
||||
model: "openai/gpt-5.5",
|
||||
model: "openai/gpt-5.6",
|
||||
modelSource: "OPENAI_API_KEY",
|
||||
},
|
||||
);
|
||||
|
||||
@@ -751,9 +751,10 @@ describe("activateSetupInference", () => {
|
||||
async (params: { transform: (config: OpenClawConfig) => { nextConfig: OpenClawConfig } }) => {
|
||||
const transformed = params.transform(persistedConfig).nextConfig;
|
||||
const configuredRuntime =
|
||||
transformed.agents?.defaults?.models?.["openai/gpt-5.5"]?.agentRuntime?.id ??
|
||||
transformed.agents?.list?.find((agent) => agent.id === "ops")?.models?.["openai/gpt-5.5"]
|
||||
?.agentRuntime?.id;
|
||||
transformed.agents?.defaults?.models?.["openai/gpt-5.6-sol"]?.agentRuntime?.id ??
|
||||
transformed.agents?.list?.find((agent) => agent.id === "ops")?.models?.[
|
||||
"openai/gpt-5.6-sol"
|
||||
]?.agentRuntime?.id;
|
||||
events.push(
|
||||
configuredRuntime === "codex" ? "persist-plugin-config" : "persist-plugin-install",
|
||||
);
|
||||
@@ -798,10 +799,13 @@ describe("activateSetupInference", () => {
|
||||
expect.objectContaining({
|
||||
id: "ops",
|
||||
model: {
|
||||
primary: "openai/gpt-5.5",
|
||||
primary: "openai/gpt-5.6-sol",
|
||||
fallbacks: ["google/gemini-3.1-pro-preview"],
|
||||
},
|
||||
models: { "openai/gpt-5.5": { agentRuntime: { id: "codex" } } },
|
||||
models: {
|
||||
"openai/gpt-5.5": { agentRuntime: { id: "openclaw" } },
|
||||
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -811,7 +815,7 @@ describe("activateSetupInference", () => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
model: "openai/gpt-5.5",
|
||||
model: "openai/gpt-5.6-sol",
|
||||
agentId: "ops",
|
||||
}),
|
||||
);
|
||||
@@ -852,10 +856,13 @@ describe("activateSetupInference", () => {
|
||||
expect.objectContaining({
|
||||
id: "ops",
|
||||
model: {
|
||||
primary: "openai/gpt-5.5",
|
||||
primary: "openai/gpt-5.6-sol",
|
||||
fallbacks: ["google/gemini-3.1-pro-preview"],
|
||||
},
|
||||
models: { "openai/gpt-5.5": { agentRuntime: { id: "codex" } } },
|
||||
models: {
|
||||
"openai/gpt-5.5": { agentRuntime: { id: "openclaw" } },
|
||||
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -879,10 +886,13 @@ describe("activateSetupInference", () => {
|
||||
expect.objectContaining({
|
||||
id: "ops",
|
||||
model: {
|
||||
primary: "openai/gpt-5.5",
|
||||
primary: "openai/gpt-5.6-sol",
|
||||
fallbacks: ["google/gemini-3.1-pro-preview"],
|
||||
},
|
||||
models: { "openai/gpt-5.5": { agentRuntime: { id: "codex" } } },
|
||||
models: {
|
||||
"openai/gpt-5.5": { agentRuntime: { id: "openclaw" } },
|
||||
"openai/gpt-5.6-sol": { agentRuntime: { id: "codex" } },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { normalizeUniqueStringEntries } from "@openclaw/normalization-core/string-normalization";
|
||||
import { upsertAuthProfileWithLock } from "../agents/auth-profiles/profiles.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { SecretInput } from "../config/types.secrets.js";
|
||||
import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js";
|
||||
@@ -26,6 +27,7 @@ type ProviderApiKeyAuthMethodOptions = {
|
||||
profileIds?: string[];
|
||||
allowProfile?: boolean;
|
||||
defaultModel?: string;
|
||||
preserveExistingPrimary?: boolean;
|
||||
expectedProviders?: string[];
|
||||
metadata?: Record<string, string>;
|
||||
noteMessage?: string;
|
||||
@@ -74,6 +76,7 @@ async function applyApiKeyConfig(params: {
|
||||
providerId: string;
|
||||
profileIds: string[];
|
||||
defaultModel?: string;
|
||||
preserveExistingPrimary?: boolean;
|
||||
applyConfig?: (cfg: OpenClawConfig) => OpenClawConfig;
|
||||
}) {
|
||||
const { applyAuthProfileConfig, applyPrimaryModel } = await loadProviderApiKeyAuthRuntime();
|
||||
@@ -88,7 +91,16 @@ async function applyApiKeyConfig(params: {
|
||||
if (params.applyConfig) {
|
||||
next = params.applyConfig(next);
|
||||
}
|
||||
return params.defaultModel ? applyPrimaryModel(next, params.defaultModel) : next;
|
||||
if (!params.defaultModel) {
|
||||
return next;
|
||||
}
|
||||
if (
|
||||
params.preserveExistingPrimary === true &&
|
||||
resolveAgentModelPrimaryValue(next.agents?.defaults?.model) !== undefined
|
||||
) {
|
||||
return next;
|
||||
}
|
||||
return applyPrimaryModel(next, params.defaultModel);
|
||||
}
|
||||
|
||||
/** Creates a provider auth method that captures, stores, and configures API-key credentials. */
|
||||
@@ -204,6 +216,7 @@ export function createProviderApiKeyAuthMethod(
|
||||
providerId: params.providerId,
|
||||
profileIds,
|
||||
defaultModel: params.defaultModel,
|
||||
preserveExistingPrimary: params.preserveExistingPrimary,
|
||||
applyConfig: params.applyConfig,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -31,6 +31,28 @@ describe("applyProviderAuthConfigPatch", () => {
|
||||
expect(next.agents?.defaults?.model).toEqual(base.agents.defaults.model);
|
||||
});
|
||||
|
||||
it("keeps configured primary and fallback refs in a newly introduced allowlist", () => {
|
||||
const next = applyProviderAuthConfigPatch(
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "openai/gpt-5.5",
|
||||
fallbacks: ["anthropic/claude-opus-4-6"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ agents: { defaults: { models: { "openai/gpt-5.6-sol": {} } } } },
|
||||
);
|
||||
|
||||
expect(next.agents?.defaults?.models).toEqual({
|
||||
"openai/gpt-5.6-sol": {},
|
||||
"openai/gpt-5.5": {},
|
||||
"anthropic/claude-opus-4-6": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces the allowlist only when replaceDefaultModels is set", () => {
|
||||
const patch = {
|
||||
agents: {
|
||||
@@ -176,6 +198,7 @@ describe("applyProviderAuthConfigPatch", () => {
|
||||
alias: "gemini",
|
||||
params: { thinking: "high", maxTokens: 12_000 },
|
||||
},
|
||||
"openai/gpt-5.5": {},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -327,6 +350,10 @@ describe("applyDefaultModel", () => {
|
||||
expect(next.agents?.defaults?.model).toEqual({
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
});
|
||||
expect(next.agents?.defaults?.models).toEqual({
|
||||
"openrouter/auto": {},
|
||||
"anthropic/claude-opus-4-6": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes a preserved retired Google Gemini primary", () => {
|
||||
@@ -363,6 +390,11 @@ describe("applyDefaultModel", () => {
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
fallbacks: ["openai/gpt-5.4"],
|
||||
});
|
||||
expect(next.agents?.defaults?.models).toEqual({
|
||||
"openrouter/auto": {},
|
||||
"anthropic/claude-opus-4-6": {},
|
||||
"openai/gpt-5.4": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("adds the model to the allowlist", () => {
|
||||
|
||||
@@ -276,6 +276,38 @@ function normalizeConfigModelRefsForWrite(
|
||||
};
|
||||
}
|
||||
|
||||
/** Keep a restrictive model allowlist consistent with the configured primary and fallbacks. */
|
||||
function ensureConfiguredDefaultModelsAllowed(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const defaults = cfg.agents?.defaults;
|
||||
if (!defaults?.models) {
|
||||
return cfg;
|
||||
}
|
||||
const model = defaults.model;
|
||||
const refs = [
|
||||
typeof model === "string" ? model : model?.primary,
|
||||
...(typeof model === "object" ? (model.fallbacks ?? []) : []),
|
||||
].filter((ref): ref is string => typeof ref === "string" && ref.trim().length > 0);
|
||||
const models = normalizeAgentModelMapForConfig(defaults.models);
|
||||
let changed = false;
|
||||
for (const ref of refs) {
|
||||
const normalizedRef = normalizeAgentModelRefForConfig(ref);
|
||||
if (!models[normalizedRef]) {
|
||||
models[normalizedRef] = {};
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
return cfg;
|
||||
}
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: { ...defaults, models },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyProviderAuthConfigPatch(
|
||||
cfg: OpenClawConfig,
|
||||
patch: unknown,
|
||||
@@ -291,7 +323,7 @@ export function applyProviderAuthConfigPatch(
|
||||
providerConfigNormalizer,
|
||||
);
|
||||
if (!options?.replaceDefaultModels || !isPlainRecord(patch)) {
|
||||
return merged;
|
||||
return ensureConfiguredDefaultModelsAllowed(merged);
|
||||
}
|
||||
|
||||
const patchModels = (patch.agents as { defaults?: { models?: unknown } } | undefined)?.defaults
|
||||
@@ -369,7 +401,7 @@ export function applyDefaultModel(
|
||||
normalizeAgentModelRefForConfig(fallback),
|
||||
)
|
||||
: undefined;
|
||||
return {
|
||||
return ensureConfiguredDefaultModelsAllowed({
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
@@ -385,5 +417,5 @@ export function applyDefaultModel(
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user