Add ultra effort and refine model reasoning profiles

This commit is contained in:
musistudio
2026-07-16 11:23:24 +08:00
parent 5b8f64160e
commit f18759aa8f
6 changed files with 424 additions and 56 deletions
@@ -1488,8 +1488,8 @@ class OpenCodeBotWorker {
const entry = this.conversationEntry(key) || { sessionId: "", projectDirectory: this.projectDirectory(key), title: "" };
const value = String(args || "").trim();
if (!value) return "Current " + setting + ": " + (entry[setting] || "default") + ".";
const allowed = setting === "effort" ? new Set(["low", "medium", "high", "xhigh", "max", "reset"]) : null;
if (allowed && !allowed.has(value)) return "Supported effort values: low, medium, high, xhigh, max, reset.";
const allowed = setting === "effort" ? new Set(["low", "medium", "high", "xhigh", "max", "ultra", "reset"]) : null;
if (allowed && !allowed.has(value)) return "Supported effort values: low, medium, high, xhigh, max, ultra, reset.";
entry[setting] = value === "reset" ? "" : value;
entry.updatedAt = Date.now();
this.setConversationEntry(key, entry);
@@ -3194,7 +3194,7 @@ class ClaudeCodeAppServer {
? entry.effort || thread && thread.reasoningEffort
: entry[setting] || thread && thread[setting];
if (!value) return "Current " + setting + ": " + (current || "default") + ".";
if (setting === "effort" && !["low", "medium", "high", "xhigh", "max", "reset"].includes(value)) return "Supported effort values: low, medium, high, xhigh, max, reset.";
if (setting === "effort" && !["low", "medium", "high", "xhigh", "max", "ultra", "reset"].includes(value)) return "Supported effort values: low, medium, high, xhigh, max, ultra, reset.";
if (setting === "mode" && !["manual", "acceptEdits", "plan", "auto", "dontAsk", "reset"].includes(value)) return "Supported modes: manual, acceptEdits, plan, auto, dontAsk, reset.";
const next = value === "reset" ? "" : value;
if (setting === "mode") entry.permissionMode = next;
@@ -6068,7 +6068,7 @@ function sessionCommandHelpText(agentName) {
"/session archive <n> | restore <n> | delete <n> confirm",
"/session history [count] - show recent turns",
"/session model [selector|reset] - show or change model/provider",
"/session effort [low|medium|high|xhigh|max|reset]",
"/session effort [low|medium|high|xhigh|max|ultra|reset]",
"/session mode [manual|acceptEdits|plan|auto|dontAsk|reset]",
"/session usage - show the latest token and cost data",
"/session memory [list|add <text>|clear] - manage persistent session context",
+155 -31
View File
@@ -186,7 +186,13 @@ function codexModelCapabilityProfile(
const supportsFusionVision = codexVirtualModelSupportsFusionVision(model, config);
const supportsFusionWebSearch = codexVirtualModelSupportsFusionWebSearch(model, config);
const metadataReasoningLevels = normalizeProviderReasoningLevels(providerModelMetadata?.supportedReasoningLevels);
const supportsReasoning = providerModelMetadata?.supportsReasoningSummaries ?? (metadataReasoningLevels ? true : readCatalogCapability(capabilities, "reasoning"));
const documentedReasoning = documentedReasoningProfile(providerModel);
const resolvedReasoningLevels = metadataReasoningLevels
?? documentedReasoning?.levels
?? [];
const supportsReasoning = providerModelMetadata?.supportsReasoningSummaries
?? documentedReasoning?.supportsReasoning
?? (metadataReasoningLevels !== undefined || readCatalogCapability(capabilities, "reasoning"));
const supportsImageInput = supportsFusionVision || catalogEntrySupportsImageInput(catalogEntry);
const supportsParallelToolCalls = readCatalogCapability(capabilities, "parallelFunctionCalling");
const applyPatchToolType = providerSupportsResponses || catalogModelLooksLikeGpt(model, catalogEntry) || codexPatchBridgeApplies(model, catalogEntry, config)
@@ -208,17 +214,19 @@ function codexModelCapabilityProfile(
applyPatchToolType,
catalogEntry,
contextWindow: providerModelMetadata?.contextWindow,
defaultReasoningLevel: providerModelMetadata && providerModelMetadata.defaultReasoningLevel !== undefined
? providerModelMetadata.defaultReasoningLevel
: supportsReasoning
? "medium"
: null,
defaultReasoningLevel: resolveDefaultReasoningLevel(
providerModelMetadata?.defaultReasoningLevel !== undefined
? providerModelMetadata.defaultReasoningLevel
: documentedReasoning?.defaultLevel,
resolvedReasoningLevels,
providerModelMetadata?.defaultReasoningLevel !== undefined || documentedReasoning !== undefined
),
defaultReasoningSummary: providerModelMetadata?.defaultReasoningSummary ?? "none",
effectiveContextWindowPercent: providerModelMetadata?.effectiveContextWindowPercent,
inputModalities: supportsImageInput ? ["text", "image"] : ["text"],
serviceTiers: providerModelMetadata?.serviceTiers ?? [],
maxContextWindow: providerModelMetadata?.maxContextWindow,
supportedReasoningLevels: metadataReasoningLevels ?? (supportsReasoning ? supportedReasoningLevels(capabilities) : []),
supportedReasoningLevels: resolvedReasoningLevels,
supportsImageInput,
supportsParallelToolCalls,
supportsReasoning,
@@ -267,13 +275,51 @@ function providerApiKey(provider: GatewayProviderConfig): string {
}
function normalizeProviderReasoningLevels(levels: ProviderReasoningLevel[] | undefined): Array<{ description: string; effort: string }> | undefined {
const normalized = (levels ?? [])
.map((level) => ({
description: level.description.trim() || effortDescription(level.effort),
effort: level.effort.trim()
}))
.filter((level) => level.effort);
return normalized.length > 0 ? normalized : undefined;
if (levels === undefined) {
return undefined;
}
const seen = new Set<string>();
const normalized: Array<{ description: string; effort: string }> = [];
for (const level of levels) {
const effort = level.effort.trim().toLowerCase();
// Codex treats `none` as the absence of a reasoning selection and does not
// render it in the effort menu, so do not publish it as a selectable level.
if (!effort || effort === "none" || seen.has(effort)) {
continue;
}
seen.add(effort);
normalized.push({
description: level.description.trim() || effortDescription(effort),
effort
});
}
return normalized;
}
function resolveDefaultReasoningLevel(
configuredDefault: string | null | undefined,
levels: Array<{ effort: string }>,
hasConfiguredDefault: boolean
): string | null {
if (hasConfiguredDefault && configuredDefault === null) {
return null;
}
const normalizedDefault = configuredDefault?.trim().toLowerCase();
const configuredMatch = normalizedDefault
? levels.find((level) => level.effort.toLowerCase() === normalizedDefault)
: undefined;
if (configuredMatch) {
return configuredMatch.effort;
}
if (normalizedDefault === "none") {
return null;
}
return levels.find((level) => level.effort === "medium")?.effort
?? levels.find((level) => level.effort === "high")?.effort
?? levels[0]?.effort
?? null;
}
function effortDescription(effort: string): string {
@@ -281,6 +327,9 @@ function effortDescription(effort: string): string {
if (normalized === "xhigh") {
return "Extra high reasoning";
}
if (normalized === "ultra") {
return "Maximum reasoning with automatic task delegation";
}
return `${effort.slice(0, 1).toUpperCase()}${effort.slice(1)} reasoning`;
}
@@ -309,26 +358,100 @@ function catalogEntrySupportsImageInput(entry: ModelCatalogEntry | undefined): b
readCatalogCapability(capabilities, "multimodal");
}
function supportedReasoningLevels(capabilities: Record<string, unknown>): Array<{ description: string; effort: string }> {
const levels: Array<{ description: string; effort: string }> = [];
if (readCatalogCapability(capabilities, "noneReasoningEffort")) {
levels.push({ effort: "none", description: "No reasoning" });
type DocumentedReasoningProfile = {
defaultLevel: string | null;
levels: Array<{ description: string; effort: string }>;
supportsReasoning: boolean;
};
function documentedReasoningProfile(model: string): DocumentedReasoningProfile | undefined {
const name = model.trim().toLowerCase().split("/").at(-1) ?? "";
const profile = (efforts: string[], defaultLevel: string | null, supportsReasoning = true): DocumentedReasoningProfile => ({
defaultLevel,
levels: efforts
.filter((effort) => effort !== "none")
.map((effort) => ({ description: effortDescription(effort), effort })),
supportsReasoning
});
// OpenAI API model pages define the API effort values. Codex additionally
// exposes its client-only Ultra mode for Sol/Terra, matching gateway metadata.
if (/^gpt-5\.6(?:-(?:sol|terra|luna))?(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
const supportsUltra = !/^gpt-5\.6-luna(?:-|$)/.test(name);
return profile([
"low",
"medium",
"high",
"xhigh",
"max",
...(supportsUltra ? ["ultra"] : [])
], "medium");
}
if (readCatalogCapability(capabilities, "minimalReasoningEffort")) {
levels.push({ effort: "minimal", description: "Minimal reasoning" });
if (/^gpt-5\.5(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
return profile(["none", "low", "medium", "high", "xhigh"], "medium");
}
levels.push(
{ effort: "low", description: "Low reasoning" },
{ effort: "medium", description: "Medium reasoning" },
{ effort: "high", description: "High reasoning" }
);
if (readCatalogCapability(capabilities, "xhighReasoningEffort") || readCatalogCapability(capabilities, "maxReasoningEffort")) {
levels.push({ effort: "xhigh", description: "Extra high reasoning" });
if (/^gpt-5\.5-pro(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
return profile(["medium", "high", "xhigh"], "high");
}
if (readCatalogCapability(capabilities, "maxReasoningEffort")) {
levels.push({ effort: "max", description: "Maximum reasoning" });
if (/^gpt-5\.4(?:-(?:mini|nano))?(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
return profile(["none", "low", "medium", "high", "xhigh"], "none");
}
return levels;
if (/^gpt-5\.4-pro(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
return profile(["medium", "high", "xhigh"], "medium");
}
if (/^gpt-5\.3-codex(?:-\d{4}-\d{2}-\d{2})?$/.test(name)) {
return profile(["low", "medium", "high", "xhigh"], "medium");
}
// Anthropic effort is distinct from extended-thinking on/off or token budgets.
if (/^claude-(?:fable|mythos)-5(?:-|$)/.test(name)) {
return profile(["low", "medium", "high", "xhigh", "max"], "high");
}
if (/^claude-opus-4[.-](?:7|8)(?:-|$)/.test(name)) {
return profile(["low", "medium", "high", "xhigh", "max"], "high");
}
if (/^claude-(?:opus-4[.-]6|sonnet-4[.-]6)(?:-|$)/.test(name)) {
return profile(["low", "medium", "high", "max"], "high");
}
if (/^claude-opus-4[.-]5(?:-|$)/.test(name)) {
return profile(["low", "medium", "high"], "high");
}
if (/^claude-(?:sonnet|haiku)-4[.-]5(?:-|$)/.test(name)) {
return profile([], null);
}
// Gemini Interactions thinking levels. Gemini 2.5 defaults dynamically, so
// a null default preserves the provider default until the user selects a level.
if (/^gemini-3\.5-flash(?:-|$)/.test(name)) {
return profile(["minimal", "low", "medium", "high"], "medium");
}
if (/^gemini-3(?:\.0)?-flash(?:-|$)/.test(name)) {
return profile(["minimal", "low", "medium", "high"], "high");
}
if (/^gemini-3\.1-pro(?:-|$)/.test(name)) {
return profile(["low", "medium", "high"], "high");
}
if (/^gemini-2\.5-(?:pro|flash)(?:-|$)/.test(name)) {
return profile(["low", "medium", "high"], null);
}
if (/^deepseek-v4-(?:flash|pro)(?:-free)?$/.test(name)) {
return profile(["high", "max"], "high");
}
if (/^glm-5\.2(?:-|$)/.test(name)) {
return profile(["none", "minimal", "low", "medium", "high", "xhigh", "max"], "max");
}
if (/^glm-(?:5(?:\.1|-turbo)?|4\.7|4\.5-air|5v-turbo)(?:-|$)/.test(name)) {
return profile([], null);
}
if (/^kimi-(?:k2[.-](?:6|7)(?:-code)?|for-coding)(?:-|$)/.test(name)) {
return profile([], null);
}
if (/^grok-4[.-]5(?:-|$)/.test(name)) {
return profile(["low", "medium", "high"], "high");
}
return undefined;
}
function findConfiguredProvider(
@@ -651,7 +774,8 @@ function normalizeModelSelector(value: string | undefined): string {
function pushUniqueModel(models: string[], model: string | undefined): void {
const normalized = model?.trim();
if (normalized && !models.includes(normalized)) {
const dedupeKey = normalized?.toLowerCase();
if (normalized && dedupeKey && !models.some((candidate) => candidate.toLowerCase() === dedupeKey)) {
models.push(normalized);
}
}
@@ -71,6 +71,7 @@ function fallbackModelCatalogEntry(model: string): ModelCatalogEntry | undefined
capabilities: {
functionCalling: true,
imageInput: true,
lowReasoningEffort: true,
maxReasoningEffort: true,
noneReasoningEffort: true,
parallelFunctionCalling: true,
@@ -79,6 +80,7 @@ function fallbackModelCatalogEntry(model: string): ModelCatalogEntry | undefined
structuredOutput: true,
supports1MContext: true,
toolCalling: true,
ultraReasoningEffort: !/^gpt-5\.6-luna(?:-|$)/.test(modelName),
vision: true,
webSearch: true,
xhighReasoningEffort: true
+59 -13
View File
@@ -38,6 +38,7 @@ const managedProviderStart = "# BEGIN CCR managed Codex provider";
const managedProviderEnd = "# END CCR managed Codex provider";
const managedToolHubMcpStart = "# BEGIN CCR managed ToolHub MCP";
const managedToolHubMcpEnd = "# END CCR managed ToolHub MCP";
const managedConfiguredModelPrefix = "# CCR configured model = ";
const originalBackupSuffix = ".ccr-original";
const originalMissingSuffix = ".ccr-original-missing";
const globalProfileTakeoverFile = path.join(CONFIGDIR, "global-profile-takeover.json");
@@ -737,9 +738,14 @@ function buildCodexConfigToml(
toolHubMcp?: ToolHubMcpRuntimeConfig;
}
): string {
let content = removeManagedBlock(source, managedRootStart, managedRootEnd);
content = removeManagedBlock(content, managedProviderStart, managedProviderEnd);
content = removeManagedBlock(content, managedToolHubMcpStart, managedToolHubMcpEnd);
let content = removeManagedMarkerLines(source, [
managedRootStart,
managedRootEnd,
managedProviderStart,
managedProviderEnd,
managedToolHubMcpStart,
managedToolHubMcpEnd
]);
content = removeCodexProviderTable(content, values.providerId);
content = removeCodexMcpServerTable(content, TOOL_HUB_MCP_SERVER_NAME);
if (values.configFormat === "separate_profile_files") {
@@ -749,13 +755,20 @@ function buildCodexConfigToml(
const firstTableIndex = firstTomlTableIndex(content);
const rootSource = firstTableIndex === -1 ? content : content.slice(0, firstTableIndex);
const restSource = firstTableIndex === -1 ? "" : content.slice(firstTableIndex);
const cleanedRoot = removeRootTomlKeys(rootSource, ["model", "model_catalog_json", "model_provider", "profile", "show_all_sessions"]);
const modelAssignment = managedModelAssignment(rootSource, values.model);
const showAllSessionsAssignment = rootTomlAssignment(rootSource, "show_all_sessions")
?? (values.showAllSessions ? "show_all_sessions = true" : undefined);
const cleanedRoot = removeManagedConfiguredModelLine(removeRootTomlKeys(
rootSource,
["model", "model_catalog_json", "model_provider", "show_all_sessions"]
));
const rootBlock = [
managedRootStart,
`model_provider = ${tomlString(values.providerId)}`,
`model = ${tomlString(values.model)}`,
modelAssignment,
`model_catalog_json = ${tomlString(values.modelCatalogFile)}`,
...(values.showAllSessions ? ["show_all_sessions = true"] : []),
`${managedConfiguredModelPrefix}${tomlString(values.model)}`,
...(showAllSessionsAssignment ? [showAllSessionsAssignment] : []),
managedRootEnd,
""
].join("\n");
@@ -831,12 +844,18 @@ function buildSeparateCodexProfileToml(
const firstTableIndex = firstTomlTableIndex(source);
const rootSource = firstTableIndex === -1 ? source : source.slice(0, firstTableIndex);
const restSource = firstTableIndex === -1 ? "" : source.slice(firstTableIndex);
const cleanedRoot = removeRootTomlKeys(rootSource, ["model", "model_provider", "model_reasoning_effort", "show_all_sessions"]);
const modelAssignment = managedModelAssignment(rootSource, values.model);
const showAllSessionsAssignment = rootTomlAssignment(rootSource, "show_all_sessions")
?? (values.showAllSessions ? "show_all_sessions = true" : undefined);
const cleanedRoot = removeManagedConfiguredModelLine(removeRootTomlKeys(
rootSource,
["model", "model_provider", "show_all_sessions"]
));
const rootBlock = [
`model_provider = ${tomlString(values.providerId)}`,
`model = ${tomlString(values.model)}`,
`model_reasoning_effort = "xhigh"`,
...(values.showAllSessions ? ["show_all_sessions = true"] : []),
modelAssignment,
`${managedConfiguredModelPrefix}${tomlString(values.model)}`,
...(showAllSessionsAssignment ? [showAllSessionsAssignment] : []),
""
].join("\n");
return ensureTrailingNewline(`${rootBlock}${trimLeadingBlankLines(cleanedRoot)}${restSource}`.replace(/\n{4,}/g, "\n\n\n"));
@@ -1559,6 +1578,32 @@ function removeRootTomlKeys(source: string, keys: string[]): string {
return source.replace(pattern, "");
}
function rootTomlAssignment(source: string, key: string): string | undefined {
const rootEnd = firstTomlTableIndex(source);
const rootSource = rootEnd === -1 ? source : source.slice(0, rootEnd);
const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=.*$`, "m");
return rootSource.match(pattern)?.[0].trim();
}
function managedModelAssignment(source: string, configuredModel: string): string {
const currentAssignment = rootTomlAssignment(source, "model");
const previousConfiguredModel = managedConfiguredModel(source);
const configuredModelChanged = previousConfiguredModel !== undefined && previousConfiguredModel !== tomlString(configuredModel);
return currentAssignment && !configuredModelChanged
? currentAssignment
: `model = ${tomlString(configuredModel)}`;
}
function managedConfiguredModel(source: string): string | undefined {
const pattern = new RegExp(`^\\s*${escapeRegExp(managedConfiguredModelPrefix)}(.+?)\\s*$`, "m");
return source.match(pattern)?.[1];
}
function removeManagedConfiguredModelLine(source: string): string {
const pattern = new RegExp(`^\\s*${escapeRegExp(managedConfiguredModelPrefix)}.*(?:\\n|$)`, "gm");
return source.replace(pattern, "");
}
function removeCodexProviderTable(source: string, providerId: string): string {
return removeTomlTable(source, "model_providers", providerId);
}
@@ -1638,9 +1683,10 @@ function legacyCodexProfileTableBody(source: string, providerId: string): string
return lines.join("\n").trim();
}
function removeManagedBlock(source: string, start: string, end: string): string {
const pattern = new RegExp(`\\n?${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`, "g");
return source.replace(pattern, "\n");
function removeManagedMarkerLines(source: string, markers: string[]): string {
const markerPattern = markers.map(escapeRegExp).join("|");
const pattern = new RegExp(`^\\s*(?:${markerPattern})\\s*(?:\\n|$)`, "gm");
return source.replace(pattern, "");
}
function firstTomlTableIndex(source: string): number {
@@ -270,6 +270,7 @@ test("profile service injects ToolHub MCP into Codex config", { skip: !process.e
const configFile = path.join(CONFIGDIR, "profiles", profileId, "codex", "config.toml");
const content = readFileSync(configFile, "utf8");
assert.match(content, /# BEGIN CCR managed ToolHub MCP/);
assert.match(content, /# CCR configured model = "Provider\/model"/);
assert.match(content, /\[mcp_servers\.ccr-toolhub\]/);
assert.equal(content.includes(`command = ${JSON.stringify(process.execPath)}`), true);
assert.equal(content.includes(`args = [${JSON.stringify(path.join(CONFIGDIR, "bin", "toolhub-mcp.js"))}]`), true);
@@ -277,6 +278,64 @@ test("profile service injects ToolHub MCP into Codex config", { skip: !process.e
assert.match(content, /TOOLHUB_OPENAI_API_KEY = "ccr-codex-profile-test"/);
assert.match(content, new RegExp(`TOOLHUB_OPENAI_BASE_URL = "http://127\\.0\\.0\\.1:${config.gateway.port}/v1"`));
assert.match(content, /TOOLHUB_OPENAI_MODEL = "Provider\/model"/);
const separateProfileFile = path.join(path.dirname(configFile), "claude-code-router.config.toml");
const initialSeparateProfile = readFileSync(separateProfileFile, "utf8");
assert.equal(initialSeparateProfile.includes("model_reasoning_effort"), false);
const codexEditedConfig = content
.replace(
'model = "Provider/model"',
'model = "User/selected-in-codex"\nmodel_reasoning_effort = "max"'
)
.replace(
"# END CCR managed ToolHub MCP",
[
"[desktop]",
'followUpQueueMode = "steer"',
"",
'[plugins."browser@openai-bundled"]',
"enabled = false",
"",
"[features]",
"js_repl = true",
"# END CCR managed ToolHub MCP"
].join("\n")
);
writeFileSync(configFile, codexEditedConfig);
writeFileSync(
separateProfileFile,
initialSeparateProfile
.replace('model = "Provider/model"', 'model = "User/selected-in-cli"')
.replace(/\s*$/, '\nmodel_reasoning_effort = "ultra"\n')
);
await applyProfileConfig(config);
const preservedConfig = readFileSync(configFile, "utf8");
assert.match(preservedConfig, /model = "User\/selected-in-codex"/);
assert.match(preservedConfig, /model_reasoning_effort = "max"/);
assert.match(preservedConfig, /\[desktop\]\nfollowUpQueueMode = "steer"/);
assert.match(preservedConfig, /\[plugins\."browser@openai-bundled"\]\nenabled = false/);
assert.match(preservedConfig, /\[features\]\njs_repl = true/);
assert.equal((preservedConfig.match(/\[mcp_servers\.ccr-toolhub\]/g) ?? []).length, 1);
assert.equal((preservedConfig.match(/# BEGIN CCR managed ToolHub MCP/g) ?? []).length, 1);
const preservedSeparateProfile = readFileSync(separateProfileFile, "utf8");
assert.match(preservedSeparateProfile, /model = "User\/selected-in-cli"/);
assert.match(preservedSeparateProfile, /model_reasoning_effort = "ultra"/);
config.Providers[0].models.push("model-2");
config.profile.profiles[0].model = "Provider/model-2";
await applyProfileConfig(config);
const explicitlyUpdatedConfig = readFileSync(configFile, "utf8");
assert.match(explicitlyUpdatedConfig, /model = "Provider\/model-2"/);
assert.match(explicitlyUpdatedConfig, /model_reasoning_effort = "max"/);
assert.match(explicitlyUpdatedConfig, /\[desktop\]\nfollowUpQueueMode = "steer"/);
const explicitlyUpdatedSeparateProfile = readFileSync(separateProfileFile, "utf8");
assert.match(explicitlyUpdatedSeparateProfile, /model = "Provider\/model-2"/);
assert.match(explicitlyUpdatedSeparateProfile, /model_reasoning_effort = "ultra"/);
});
test("profile service writes a Grok CLI wrapper that points model discovery and inference to CCR", { skip: !process.env.CCR_INTERNAL_HOME_DIR }, async () => {
@@ -3,7 +3,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { buildCodexModelCatalog } from "@ccr/core/agents/codex/model-catalog.ts";
import { buildCodexModelCatalog, buildCodexModelCatalogIds } from "@ccr/core/agents/codex/model-catalog.ts";
function catalogModelFor(config, slug) {
const catalog = buildCodexModelCatalog(config, slug);
@@ -12,6 +12,16 @@ function catalogModelFor(config, slug) {
return model;
}
test("codex catalog removes duplicate model IDs case-insensitively without reordering", () => {
const ids = buildCodexModelCatalogIds({
Providers: [
{ name: "Provider", type: "openai_responses", models: ["Model-A", " model-a ", "MODEL-B"] }
]
}, "provider/model-a");
assert.deepEqual(ids, ["provider/model-a", "Provider/MODEL-B"]);
});
test("codex catalog treats unknown models as text-only without advanced tools", () => {
const model = catalogModelFor({
Providers: [
@@ -59,10 +69,12 @@ test("codex catalog enables multimodal reasoning and search when provider protoc
assert.equal(model.supports_reasoning_summaries, true);
assert.equal(model.supports_search_tool, true);
assert.equal(model.web_search_tool_type, "text_and_image");
for (const effort of ["low", "medium", "high"]) {
assert.ok(model.supported_reasoning_levels.some((level) => level.effort === effort));
}
assert.equal(model.default_reasoning_level, "medium");
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), [
"low",
"medium",
"high"
]);
assert.equal(model.default_reasoning_level, null);
assert.equal(model.apply_patch_tool_type, "freeform");
});
@@ -80,18 +92,143 @@ test("codex catalog exposes current capabilities for the GPT-5.6 family", () =>
assert.equal(model.supports_image_detail_original, true);
assert.equal(model.supports_parallel_tool_calls, true);
assert.equal(model.supports_reasoning_summaries, true);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), [
"none",
const efforts = model.supported_reasoning_levels.map((level) => level.effort);
assert.deepEqual(efforts, [
"low",
"medium",
"high",
"xhigh",
"max"
"max",
...(/-luna$/.test(modelName) ? [] : ["ultra"])
]);
assert.equal(new Set(efforts).size, efforts.length);
assert.equal(model.default_reasoning_level, "medium");
}
});
test("codex catalog uses documented reasoning levels instead of aggregate capability guesses", () => {
const cases = [
["abacus", "mimo-v2-pro", [], null],
["x-ai", "grok-4.5", ["low", "medium", "high"], "high"],
["z-ai", "glm-4.5-air", [], null],
["z-ai", "glm-5.2", ["minimal", "low", "medium", "high", "xhigh", "max"], "max"],
["google", "gemini-3.5-flash", ["minimal", "low", "medium", "high"], "medium"],
["deepseek", "deepseek-v4-flash", ["high", "max"], "high"],
["deepseek", "deepseek-v4-pro", ["high", "max"], "high"],
["opencode", "deepseek-v4-flash-free", ["high", "max"], "high"],
["kimi", "kimi-k2.7-code", [], null]
];
for (const [provider, modelName, expectedEfforts, expectedDefault] of cases) {
const model = catalogModelFor({
Providers: [
{ name: provider, type: "openai_responses", models: [modelName] }
]
}, `${provider}/${modelName}`);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), expectedEfforts);
assert.equal(new Set(expectedEfforts).size, expectedEfforts.length);
assert.equal(model.default_reasoning_level, expectedDefault);
}
});
test("codex catalog follows Anthropic's model-specific effort matrix", () => {
const cases = [
["claude-fable-5", ["low", "medium", "high", "xhigh", "max"]],
["claude-opus-4.8", ["low", "medium", "high", "xhigh", "max"]],
["claude-opus-4-7", ["low", "medium", "high", "xhigh", "max"]],
["claude-opus-4-6", ["low", "medium", "high", "max"]],
["claude-sonnet-4.6", ["low", "medium", "high", "max"]],
["claude-opus-4-5", ["low", "medium", "high"]],
["claude-sonnet-4-5-20250929", []],
["claude-haiku-4-5", []]
];
for (const [modelName, expectedEfforts] of cases) {
const model = catalogModelFor({
Providers: [
{ name: "anthropic", type: "anthropic_messages", models: [modelName] }
]
}, `anthropic/${modelName}`);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), expectedEfforts);
assert.equal(model.default_reasoning_level, expectedEfforts.length > 0 ? "high" : null);
}
});
test("codex catalog follows official OpenAI API reasoning levels for custom providers", () => {
const cases = [
["gpt-5.5", ["low", "medium", "high", "xhigh"], "medium"],
["gpt-5.4", ["low", "medium", "high", "xhigh"], null],
["gpt-5.4-mini", ["low", "medium", "high", "xhigh"], null],
["gpt-5.3-codex", ["low", "medium", "high", "xhigh"], "medium"]
];
for (const [modelName, expectedEfforts, expectedDefault] of cases) {
const model = catalogModelFor({
Providers: [
{ name: "custom", type: "openai_responses", models: [modelName] }
]
}, `custom/${modelName}`);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), expectedEfforts);
assert.equal(model.default_reasoning_level, expectedDefault);
}
});
test("codex catalog preserves explicit reasoning metadata while removing duplicate efforts", () => {
const model = catalogModelFor({
Providers: [
{
modelMetadata: {
"custom-reasoner": {
defaultReasoningLevel: "unsupported",
supportedReasoningLevels: [
{ description: "No reasoning", effort: "none" },
{ description: "First low", effort: " LOW " },
{ description: "Duplicate low", effort: "low" },
{ description: "High", effort: "HIGH" }
],
supportsReasoningSummaries: true
}
},
models: ["custom-reasoner"],
name: "custom",
type: "openai_responses"
}
]
}, "custom/custom-reasoner");
assert.deepEqual(model.supported_reasoning_levels, [
{ description: "First low", effort: "low" },
{ description: "High", effort: "high" }
]);
assert.equal(model.default_reasoning_level, "high");
});
test("codex catalog preserves an explicit empty provider reasoning-level list", () => {
const model = catalogModelFor({
Providers: [
{
modelMetadata: {
"gpt-5.6-sol": {
defaultReasoningLevel: null,
supportedReasoningLevels: [],
supportsReasoningSummaries: false
}
},
models: ["gpt-5.6-sol"],
name: "custom",
type: "openai_responses"
}
]
}, "custom/gpt-5.6-sol");
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
assert.equal(model.supports_reasoning_summaries, false);
});
test("codex catalog uses provider model metadata for reasoning effort and speed tiers", () => {
const model = catalogModelFor({
Providers: [