fix(codex): normalize native cliproxy tuning aliases (#1254)

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-05-15 09:51:42 -04:00
1 parent 53730c0165
commit 609bed0c39
5 files changed
+174 -14

No files matched your search

@@ -12,6 +12,7 @@ import {
normalizeModelIdForProvider,
normalizeModelIdForRouting,
normalizeModelEnvVarsForProvider,
parseCodexModelTuningAlias,
} from '../model-id-normalizer';
describe('model-id-normalizer', () => {
@@ -135,6 +136,20 @@ describe('model-id-normalizer', () => {
'gpt-5.4-high-fast'
);
});
it('parses codex model tuning suffixes', () => {
expect(parseCodexModelTuningAlias('gpt-5.5-high')).toEqual({
baseModel: 'gpt-5.5',
effort: 'high',
serviceTier: null,
});
expect(parseCodexModelTuningAlias('gpt-5.5-fast-high')).toEqual({
baseModel: 'gpt-5.5',
effort: 'high',
serviceTier: 'fast',
});
expect(parseCodexModelTuningAlias('gpt-5.5[1m]')).toBeNull();
});
});
describe('env normalization', () => {
@@ -45,6 +45,15 @@ const IFLOW_LEGACY_MODEL_ALIASES: Readonly<Record<string, string>> = Object.free
'minimax-m2.5': 'qwen3-coder-plus',
});
export type CodexModelTuningEffort = 'medium' | 'high' | 'xhigh';
export type CodexModelServiceTier = 'fast';
export interface CodexModelTuningAlias {
baseModel: string;
effort: CodexModelTuningEffort | null;
serviceTier: CodexModelServiceTier | null;
}
function trimModelId(model: string): string {
return model.trim();
}
@@ -93,6 +102,23 @@ function splitCodexTuningSuffix(model: string): { baseModel: string; suffix: str
};
}
export function parseCodexModelTuningAlias(model: string): CodexModelTuningAlias | null {
const trimmed = trimModelId(model);
const { baseModel, suffix } = splitBaseModelAndSuffix(trimmed);
if (suffix) return null;
const parsed = splitCodexTuningSuffix(baseModel);
if (!parsed.suffix) return null;
const tokens = parsed.suffix.slice(1).split('-');
const effort = tokens.find((token) => token !== 'fast') as CodexModelTuningEffort | undefined;
return {
baseModel: parsed.baseModel.trim(),
effort: effort ?? null,
serviceTier: tokens.includes('fast') ? 'fast' : null,
};
}
/**
* Extract provider segment from `/api/provider/{provider}` request paths.
*
+42 -14
View File
@@ -6,11 +6,14 @@ import {
stringifyTomlObject,
writeTomlFileAtomic,
} from '../web-server/services/compatible-cli-toml-file-service';
import { getModelMaxLevel } from '../cliproxy/model-catalog';
import { parseCodexModelTuningAlias } from '../cliproxy/ai-providers/model-id-normalizer';
export const CCSXP_CLIPROXY_SHORTCUT_ENV = 'CCSXP_CLIPROXY_SHORTCUT';
export const CODEX_CLIPROXY_PROVIDER_ID = 'cliproxy';
export const CODEX_CLIPROXY_PROVIDER_ENV_KEY = 'CLIPROXY_API_KEY';
export const CODEX_CLIPROXY_PROVIDER_NAME = 'CLIProxy Codex';
const CODEX_FAST_SERVICE_TIER = 'priority';
export interface CodexCliproxyProviderRepairResult {
changed: boolean;
@@ -99,18 +102,28 @@ function buildProviderConfig(baseUrl: string, envKey: string): Record<string, un
};
}
function buildProviderBlock(baseUrl: string): string {
return stringifyTomlObject({
function appendProviderBlock(rawText: string, baseUrl: string): string {
const prefix = rawText.trimEnd();
const providerBlock = stringifyTomlObject({
model_providers: {
[CODEX_CLIPROXY_PROVIDER_ID]: buildProviderConfig(baseUrl, CODEX_CLIPROXY_PROVIDER_ENV_KEY),
},
});
}).trimEnd();
return prefix ? `${prefix}\n\n${providerBlock}\n` : `${providerBlock}\n`;
}
function appendProviderBlock(rawText: string, baseUrl: string): string {
const prefix = rawText.trimEnd();
const providerBlock = buildProviderBlock(baseUrl).trimEnd();
return prefix ? `${prefix}\n\n${providerBlock}\n` : `${providerBlock}\n`;
function normalizeTopLevelCodexModelAlias(config: Record<string, unknown>): boolean {
const model = config.model;
if (typeof model !== 'string') return false;
const parsed = parseCodexModelTuningAlias(model);
if (!parsed || (!parsed.effort && !parsed.serviceTier)) return false;
if (!parsed.baseModel || getModelMaxLevel('codex', parsed.baseModel) === undefined) return false;
config.model = parsed.baseModel;
if (parsed.effort) config.model_reasoning_effort = parsed.effort;
if (parsed.serviceTier) config.service_tier = CODEX_FAST_SERVICE_TIER;
return true;
}
export async function ensureCodexCliproxyProviderConfig(
@@ -131,6 +144,7 @@ export async function ensureCodexCliproxyProviderConfig(
const modelProvidersValue = config.model_providers;
const providers = asObject(modelProvidersValue);
const expectedBaseUrl = buildCodexCliproxyProviderBaseUrl(port);
const normalizedModelAlias = normalizeTopLevelCodexModelAlias(config);
if (modelProvidersValue !== undefined && !providers) {
throw new Error(`Cannot repair ${displayPath}: [model_providers] must be a table.`);
@@ -139,7 +153,18 @@ export async function ensureCodexCliproxyProviderConfig(
if (!providers || !Object.prototype.hasOwnProperty.call(providers, CODEX_CLIPROXY_PROVIDER_ID)) {
await writeTomlFileAtomic({
filePath: configPath,
rawText: appendProviderBlock(fileProbe.rawText, expectedBaseUrl),
rawText: normalizedModelAlias
? stringifyTomlObject({
...config,
model_providers: {
...(providers ?? {}),
[CODEX_CLIPROXY_PROVIDER_ID]: buildProviderConfig(
expectedBaseUrl,
CODEX_CLIPROXY_PROVIDER_ENV_KEY
),
},
})
: appendProviderBlock(fileProbe.rawText, expectedBaseUrl),
expectedMtime: fileProbe.diagnostics.mtimeMs ?? undefined,
fileLabel: 'config.toml',
});
@@ -154,15 +179,18 @@ export async function ensureCodexCliproxyProviderConfig(
}
const envKey = resolveProviderEnvKey(currentProvider);
const providerReady = isProviderReady(currentProvider, expectedBaseUrl, envKey);
if (isProviderReady(currentProvider, expectedBaseUrl, envKey)) {
return { changed: false, configPath, displayPath, envKey };
if (!providerReady) {
providers[CODEX_CLIPROXY_PROVIDER_ID] = {
...currentProvider,
...buildProviderConfig(expectedBaseUrl, envKey),
};
}
providers[CODEX_CLIPROXY_PROVIDER_ID] = {
...currentProvider,
...buildProviderConfig(expectedBaseUrl, envKey),
};
if (providerReady && !normalizedModelAlias) {
return { changed: false, configPath, displayPath, envKey };
}
await writeTomlFileAtomic({
filePath: configPath,