mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
refactor(openai): simplify GPT-5.6 canonicalization (#122467)
Behavior is unchanged. Remove duplicate policy and test scaffolding while preserving canonical Sol identity and direct bare-alias compatibility.
This commit is contained in:
committed by
GitHub
parent
127facd39f
commit
baefefa815
@@ -24,11 +24,7 @@ export function applyOpenAIProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
(next, modelRef) => ensureModelAllowlistEntry({ cfg: next, modelRef }),
|
||||
cfg,
|
||||
);
|
||||
const next = ensureModelAllowlistEntry({
|
||||
cfg: withConfiguredRefs,
|
||||
modelRef: OPENAI_DEFAULT_MODEL,
|
||||
});
|
||||
const models = { ...next.agents?.defaults?.models };
|
||||
const models = { ...withConfiguredRefs.agents?.defaults?.models };
|
||||
const gptAliasClaimed = Object.entries(models).some(
|
||||
([modelRef, model]) =>
|
||||
modelRef !== OPENAI_DEFAULT_MODEL && model?.alias?.trim().toLowerCase() === "gpt",
|
||||
@@ -41,11 +37,11 @@ export function applyOpenAIProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
};
|
||||
|
||||
return {
|
||||
...next,
|
||||
...withConfiguredRefs,
|
||||
agents: {
|
||||
...next.agents,
|
||||
...withConfiguredRefs.agents,
|
||||
defaults: {
|
||||
...next.agents?.defaults,
|
||||
...withConfiguredRefs.agents?.defaults,
|
||||
models,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { OPENAI_API_BASE_URL, OPENAI_CODEX_RESPONSES_BASE_URL } from "./base-url.js";
|
||||
import { OPENAI_CODEX_DEFAULT_MODEL, OPENAI_DEFAULT_MODEL } from "./default-models.js";
|
||||
import { OPENAI_DEFAULT_MODEL } from "./default-models.js";
|
||||
import { buildOpenAIProvider } from "./openai-provider.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { resolveModelRoutes } from "./provider-policy-api.js";
|
||||
@@ -459,8 +459,6 @@ describe("buildOpenAIProvider", () => {
|
||||
cost: { input: 0.2, output: 1.25, cacheRead: 0.02, cacheWrite: 0 },
|
||||
},
|
||||
]);
|
||||
expect(OPENAI_DEFAULT_MODEL).toBe("openai/gpt-5.6-sol");
|
||||
expect(OPENAI_CODEX_DEFAULT_MODEL).toBe("openai/gpt-5.6-sol");
|
||||
});
|
||||
|
||||
it("scopes the OpenAI API-key catalog to the OpenAI provider id", async () => {
|
||||
|
||||
@@ -3667,74 +3667,37 @@ describe("legacy model compat migrate", () => {
|
||||
});
|
||||
|
||||
it("canonicalizes persisted OpenAI GPT-5.6 aliases without affecting GitHub Copilot", () => {
|
||||
const legacy = "openai/gpt-5.6";
|
||||
const canonical = "openai/gpt-5.6-sol";
|
||||
const copilot = "github-copilot/gpt-5.6";
|
||||
const res = migrateLegacyConfigForTest({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: `${legacy}@openai:work`,
|
||||
fallbacks: [legacy, "github-copilot/gpt-5.6"],
|
||||
},
|
||||
modelPolicy: { allow: [legacy, "github-copilot/gpt-5.6"] },
|
||||
model: { primary: "openai/gpt-5.6@openai:work" },
|
||||
modelPolicy: { allow: ["openai/gpt-5.6", copilot] },
|
||||
models: {
|
||||
[legacy]: {
|
||||
alias: "GPT",
|
||||
agentRuntime: { id: "openclaw" },
|
||||
params: { temperature: 0.2, nested: { fromAlias: true } },
|
||||
},
|
||||
[canonical]: {
|
||||
params: { serviceTier: "priority", nested: { fromCanonical: true } },
|
||||
},
|
||||
"github-copilot/gpt-5.6": { alias: "Copilot GPT" },
|
||||
"openai/gpt-5.6": { alias: "GPT" },
|
||||
"openai/gpt-5.6-sol": { agentRuntime: { id: "openclaw" } },
|
||||
[copilot]: { alias: "Copilot GPT" },
|
||||
},
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
models: [
|
||||
{ id: "gpt-5.6", name: "GPT alias", maxTokens: 64_000 },
|
||||
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", contextWindow: 1_050_000 },
|
||||
],
|
||||
},
|
||||
openai: { models: [{ id: "gpt-5.6", name: "GPT alias" }] },
|
||||
"github-copilot": { models: [{ id: "gpt-5.6", name: "Copilot GPT" }] },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.config?.agents?.defaults).toMatchObject({
|
||||
model: {
|
||||
primary: `${canonical}@openai:work`,
|
||||
fallbacks: [canonical, "github-copilot/gpt-5.6"],
|
||||
},
|
||||
modelPolicy: { allow: [canonical, "github-copilot/gpt-5.6"] },
|
||||
models: {
|
||||
[canonical]: {
|
||||
alias: "GPT",
|
||||
agentRuntime: { id: "openclaw" },
|
||||
params: {
|
||||
serviceTier: "priority",
|
||||
temperature: 0.2,
|
||||
nested: { fromAlias: true, fromCanonical: true },
|
||||
},
|
||||
},
|
||||
"github-copilot/gpt-5.6": { alias: "Copilot GPT" },
|
||||
},
|
||||
const defaults = res.config?.agents?.defaults;
|
||||
expect(defaults).toMatchObject({
|
||||
model: { primary: "openai/gpt-5.6-sol@openai:work" },
|
||||
modelPolicy: { allow: ["openai/gpt-5.6-sol", copilot] },
|
||||
});
|
||||
expect(res.config?.agents?.defaults?.models).not.toHaveProperty(legacy);
|
||||
expect(res.config?.models?.providers?.openai?.models).toEqual([
|
||||
{
|
||||
id: "gpt-5.6-sol",
|
||||
name: "GPT-5.6 Sol",
|
||||
contextWindow: 1_050_000,
|
||||
maxTokens: 64_000,
|
||||
},
|
||||
]);
|
||||
expect(res.config?.models?.providers?.["github-copilot"]?.models).toEqual([
|
||||
{ id: "gpt-5.6", name: "Copilot GPT" },
|
||||
]);
|
||||
expect(migrateLegacyConfigForTest(res.config)).toEqual({ config: null, changes: [] });
|
||||
expect(defaults?.models).toEqual({
|
||||
"openai/gpt-5.6-sol": { alias: "GPT", agentRuntime: { id: "openclaw" } },
|
||||
[copilot]: { alias: "Copilot GPT" },
|
||||
});
|
||||
expect(res.config?.models?.providers?.openai?.models?.[0]?.id).toBe("gpt-5.6-sol");
|
||||
expect(res.config?.models?.providers?.["github-copilot"]?.models?.[0]?.id).toBe("gpt-5.6");
|
||||
});
|
||||
|
||||
it("merges provider catalog rows that normalize to an explicitly canonical id", () => {
|
||||
|
||||
@@ -74,15 +74,14 @@ const RETIRED_CODEX_MODEL_OVERRIDES = modelTable({
|
||||
});
|
||||
|
||||
function applyRetiredModelTable(
|
||||
model: string,
|
||||
normalizedModel: string,
|
||||
table: Readonly<Record<string, string>>,
|
||||
overrides?: Readonly<Record<string, string>>,
|
||||
): string | null {
|
||||
const normalized = normalizeString(model);
|
||||
if (overrides && Object.hasOwn(overrides, normalized)) {
|
||||
return overrides[normalized] ?? null;
|
||||
if (overrides && Object.hasOwn(overrides, normalizedModel)) {
|
||||
return overrides[normalizedModel] ?? null;
|
||||
}
|
||||
return Object.hasOwn(table, normalized) ? (table[normalized] ?? null) : null;
|
||||
return Object.hasOwn(table, normalizedModel) ? (table[normalizedModel] ?? null) : null;
|
||||
}
|
||||
|
||||
function hasRetiredVersionPrefix(normalized: string, prefix: string): boolean {
|
||||
@@ -232,14 +231,14 @@ function canonicalizeKnownModelRef(value: string): string | null {
|
||||
}
|
||||
const retiredOwnerModel =
|
||||
normalizedProvider === "groq"
|
||||
? applyRetiredModelTable(model, RETIRED_GROQ_MODELS)
|
||||
? applyRetiredModelTable(normalizedModel, RETIRED_GROQ_MODELS)
|
||||
: normalizedProvider === "xai"
|
||||
? applyRetiredModelTable(model, RETIRED_XAI_MODELS)
|
||||
? applyRetiredModelTable(normalizedModel, RETIRED_XAI_MODELS)
|
||||
: normalizedProvider === "openai" ||
|
||||
normalizedProvider === "openai-codex" ||
|
||||
normalizedProvider === "github-copilot"
|
||||
? applyRetiredModelTable(
|
||||
model,
|
||||
normalizedModel,
|
||||
RETIRED_OPENAI_MODELS,
|
||||
normalizedProvider === "openai-codex" ? RETIRED_CODEX_MODEL_OVERRIDES : undefined,
|
||||
)
|
||||
|
||||
@@ -4,8 +4,6 @@ import type { LocalCommandProbe } from "../system-agent/probes.js";
|
||||
import {
|
||||
ANTHROPIC_API_DEFAULT_MODEL_REF,
|
||||
CLAUDE_CLI_DEFAULT_MODEL_REF,
|
||||
CODEX_APP_SERVER_DEFAULT_MODEL_REF,
|
||||
OPENAI_API_DEFAULT_MODEL_REF,
|
||||
detectInferenceBackends,
|
||||
} from "./onboard-inference.js";
|
||||
|
||||
@@ -17,11 +15,6 @@ function probeDeps(found: Record<string, boolean>) {
|
||||
}
|
||||
|
||||
describe("detectInferenceBackends", () => {
|
||||
it("uses canonical GPT-5.6 Sol defaults for direct API and Codex", () => {
|
||||
expect(OPENAI_API_DEFAULT_MODEL_REF).toBe("openai/gpt-5.6-sol");
|
||||
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: {},
|
||||
@@ -84,8 +77,8 @@ describe("detectInferenceBackends", () => {
|
||||
expect(candidates[0]?.modelRef).toBe("zai/glm-5.2");
|
||||
expect(candidates[0]?.detail).toBe("zai/glm-5.2 — already configured");
|
||||
expect(candidates[1]?.modelRef).toBe(CLAUDE_CLI_DEFAULT_MODEL_REF);
|
||||
expect(candidates[2]?.modelRef).toBe(CODEX_APP_SERVER_DEFAULT_MODEL_REF);
|
||||
expect(candidates[3]?.modelRef).toBe(OPENAI_API_DEFAULT_MODEL_REF);
|
||||
expect(candidates[2]?.modelRef).toBe("openai/gpt-5.6-sol");
|
||||
expect(candidates[3]?.modelRef).toBe("openai/gpt-5.6-sol");
|
||||
expect(candidates[4]?.modelRef).toBe(ANTHROPIC_API_DEFAULT_MODEL_REF);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { migrateLegacyConfig } from "../../commands/doctor/shared/legacy-config-migrate.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { withEnvAsync } from "../../test-utils/env.js";
|
||||
import {
|
||||
catalogEntry,
|
||||
listModels,
|
||||
WITHOUT_OPENAI_ENV_AUTH,
|
||||
} from "./models-list-result.openai-routes.test-support.js";
|
||||
|
||||
describe("models.list OpenAI picker", () => {
|
||||
it("does not expose a configured GPT-5.6 alias beside named variants after doctor normalization", async () => {
|
||||
const staleConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openai/gpt-5.6" },
|
||||
models: {
|
||||
"openai/gpt-5.6": { alias: "GPT" },
|
||||
"openai/gpt-5.6-sol": {},
|
||||
"openai/gpt-5.6-terra": {},
|
||||
"openai/gpt-5.6-luna": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const cfg = migrateLegacyConfig(staleConfig).config ?? staleConfig;
|
||||
const catalog = [
|
||||
{ ...catalogEntry("gpt-5.6-sol", "openai-responses"), providerOrder: 0 },
|
||||
{ ...catalogEntry("gpt-5.6-terra", "openai-responses"), providerOrder: 1 },
|
||||
{ ...catalogEntry("gpt-5.6-luna", "openai-responses"), providerOrder: 2 },
|
||||
];
|
||||
|
||||
await withEnvAsync({ ...WITHOUT_OPENAI_ENV_AUTH, OPENAI_API_KEY: "test-key" }, async () => {
|
||||
const result = await listModels({ catalog, cfg, view: "configured" });
|
||||
expect(result.models.map((entry) => entry.id)).toEqual([
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { vi } from "vitest";
|
||||
import type { ModelCatalogEntry } from "../../agents/model-catalog.types.js";
|
||||
import type { createOpenAIModelRoutesResolver } from "../../agents/openai-model-routes.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
@@ -32,17 +31,14 @@ export async function listModels(params: {
|
||||
const config = params.cfg ?? ({} as OpenClawConfig);
|
||||
const context = {
|
||||
getRuntimeConfig: () => config,
|
||||
loadGatewayModelCatalog: vi.fn(() => Promise.resolve(params.catalog)),
|
||||
loadGatewayModelCatalogSnapshot: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/models-list-openai-agent",
|
||||
config,
|
||||
entries: params.catalog,
|
||||
routeVariants: params.catalog,
|
||||
}),
|
||||
),
|
||||
logGateway: { debug: vi.fn() },
|
||||
loadGatewayModelCatalogSnapshot: async () => ({
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/models-list-openai-agent",
|
||||
config,
|
||||
entries: params.catalog,
|
||||
routeVariants: params.catalog,
|
||||
}),
|
||||
logGateway: { debug: () => {} },
|
||||
} as unknown as GatewayRequestContext;
|
||||
return await buildModelsListResult({
|
||||
context,
|
||||
|
||||
Reference in New Issue
Block a user