feat: support minimal codex effort aliases

This commit is contained in:
Tam Nhu Tran committed 2026-05-20 11:11:46 -04:00
1 parent f0ad68d446
commit 6569eed15b
18 files changed
+204 -29

No files matched your search

@@ -228,9 +228,10 @@ export function ModelConfigSection({
) : null}
{provider === 'codex' && (
<p className="text-[11px] text-muted-foreground mb-3 rounded-md border bg-muted/30 px-2.5 py-2">
Codex tip: suffixes <code>-medium</code>, <code>-high</code>, and <code>-xhigh</code>{' '}
pin reasoning effort. <code>-fast</code> enables the Codex fast service tier on models
that support it, including combined forms such as <code>gpt-5.4-high-fast</code>.
Codex tip: suffixes <code>-minimal</code>, <code>-low</code>, <code>-medium</code>,{' '}
<code>-high</code>, and <code>-xhigh</code> pin reasoning effort. <code>-fast</code>{' '}
enables the Codex fast service tier on models that support it, including combined forms
such as <code>gpt-5.4-high-fast</code>.
</p>
)}
<div className="space-y-4">
@@ -16,6 +16,7 @@ import type { CodexEffort, CodexServiceTier } from '@/lib/codex-effort';
import {
getCodexEffortDisplay,
getCodexEffortVariants,
parseCodexEffort,
parseCodexServiceTier,
} from '@/lib/codex-effort';
import { getResolvedCatalogModels, getSupplementalCatalogModels } from '@/lib/model-catalogs';
@@ -344,6 +345,13 @@ function getModelOptionValues(
: [optionValue];
}
function getRecommendedGroupKey(optionValue: string, isCodexProvider: boolean): string {
if (!isCodexProvider) return 'recommended';
if (parseCodexServiceTier(optionValue)) return 'codex-fast';
if (parseCodexEffort(optionValue)) return 'codex-reasoning';
return 'recommended';
}
export function FlexibleModelSelector({
label,
description,
@@ -400,7 +408,7 @@ export function FlexibleModelSelector({
return optionValues.map((optionValue) => ({
value: optionValue,
groupKey: 'recommended',
groupKey: getRecommendedGroupKey(optionValue, isCodexProvider),
searchText: `${optionValue} ${model.id} ${model.name} ${routingHint?.recommendedModelId ?? ''}`,
keywords: [model.tier ?? '', catalog?.provider ?? ''],
triggerContent: (
@@ -555,6 +563,26 @@ export function FlexibleModelSelector({
<span className="text-xs text-primary">{t('providerModelSelector.recommended')}</span>
),
},
...(isCodexProvider
? [
{
key: 'codex-reasoning',
label: (
<span className="text-xs text-muted-foreground">
{t('providerModelSelector.codexReasoningVariants')}
</span>
),
},
{
key: 'codex-fast',
label: (
<span className="text-xs text-amber-600">
{t('providerModelSelector.codexFastVariants')}
</span>
),
},
]
: []),
...(allModelOptions.length > 0
? [
{
+9 -3
View File
@@ -1,8 +1,14 @@
export type CodexEffort = 'medium' | 'high' | 'xhigh';
export type CodexEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
export type CodexServiceTier = 'fast';
const CODEX_TUNING_SUFFIX_TOKEN_REGEX = /-(medium|high|xhigh|fast)$/i;
const CODEX_EFFORTS_IN_ORDER: readonly CodexEffort[] = ['medium', 'high', 'xhigh'];
const CODEX_TUNING_SUFFIX_TOKEN_REGEX = /-(minimal|low|medium|high|xhigh|fast)$/i;
const CODEX_EFFORTS_IN_ORDER: readonly CodexEffort[] = [
'minimal',
'low',
'medium',
'high',
'xhigh',
];
function trimModelId(modelId: string | undefined): string {
return modelId?.trim() ?? '';
+10
View File
@@ -225,6 +225,8 @@ const resources = {
pinnedRouteStatus: 'Pinned route status:',
pinnedModelNotAdvertised:
'Pinned model is not currently advertised by the proxy: {{model}}',
codexReasoningVariants: 'Reasoning variants',
codexFastVariants: 'Fast variants',
},
createAuthProfileDialog: {
title: 'Create New Account',
@@ -2929,6 +2931,8 @@ const resources = {
preferredPinnedModel: '偏好的固定模型:',
pinnedRouteStatus: '固定路由状态:',
pinnedModelNotAdvertised: '固定模型当前未被代理广播:{{model}}',
codexReasoningVariants: '推理变体',
codexFastVariants: '快速变体',
},
createAuthProfileDialog: {
title: '创建新账号',
@@ -5517,6 +5521,8 @@ const resources = {
preferredPinnedModel: 'Mô hình ghim ưu tiên:',
pinnedRouteStatus: 'Trạng thái tuyến ghim:',
pinnedModelNotAdvertised: 'Mô hình ghim hiện không được proxy quảng bá: {{model}}',
codexReasoningVariants: 'Biến thể lý luận',
codexFastVariants: 'Biến thể nhanh',
},
createAuthProfileDialog: {
title: 'Tạo tài khoản mới',
@@ -8220,6 +8226,8 @@ const resources = {
preferredPinnedModel: '優先ピンモデル:',
pinnedRouteStatus: 'ピンルートの状態:',
pinnedModelNotAdvertised: 'ピンモデルは現在プロキシから通知されていません: {{model}}',
codexReasoningVariants: '推論バリアント',
codexFastVariants: '高速バリアント',
},
createAuthProfileDialog: {
title: '新しいアカウントを作成',
@@ -10946,6 +10954,8 @@ const resources = {
preferredPinnedModel: '선호하는 고정 모델:',
pinnedRouteStatus: '고정된 라우트 상태:',
pinnedModelNotAdvertised: '고정된 모델이 현재 프록시에서 알리지 않고 있습니다: {{model}}',
codexReasoningVariants: '추론 변형',
codexFastVariants: '빠른 변형',
},
createAuthProfileDialog: {
title: '새 계정 생성',
@@ -121,6 +121,10 @@ describe('FlexibleModelSelector', () => {
await userEvent.click(screen.getByRole('button', { name: /select model/i }));
expect(screen.getByText('Reasoning variants')).toBeInTheDocument();
expect(screen.getByText('Fast variants')).toBeInTheDocument();
expect(screen.getByText('gpt-5.5-minimal')).toBeInTheDocument();
expect(screen.getByText('gpt-5.5-low')).toBeInTheDocument();
expect(screen.getByText('gpt-5.3-codex-high')).toBeInTheDocument();
expect(screen.getByText('gpt-5.3-codex-xhigh')).toBeInTheDocument();
expect(screen.getByText('gpt-5.4-high-fast')).toBeInTheDocument();
+10 -1
View File
@@ -15,6 +15,8 @@ describe('parseCodexEffort', () => {
expect(parseCodexEffort('gpt-5.4-fast-high')).toBe('high');
expect(parseCodexEffort('gpt-5.3-codex-xhigh')).toBe('xhigh');
expect(parseCodexEffort('gpt-5-mini-medium')).toBe('medium');
expect(parseCodexEffort('gpt-5.5-low')).toBe('low');
expect(parseCodexEffort('gpt-5.5-minimal')).toBe('minimal');
});
it('parses mixed-case suffixes', () => {
@@ -23,7 +25,6 @@ describe('parseCodexEffort', () => {
it('returns undefined for unsuffixed or unsupported values', () => {
expect(parseCodexEffort('gpt-5.3-codex')).toBeUndefined();
expect(parseCodexEffort('gpt-5.3-codex-low')).toBeUndefined();
expect(parseCodexEffort('gpt-5.4-fast')).toBeUndefined();
expect(parseCodexEffort(undefined)).toBeUndefined();
});
@@ -69,12 +70,16 @@ describe('codex effort helpers', () => {
it('builds ordered codex effort variants up to the supported max level', () => {
expect(getCodexEffortVariants('gpt-5.3-codex', 'xhigh')).toEqual([
'gpt-5.3-codex',
'gpt-5.3-codex-minimal',
'gpt-5.3-codex-low',
'gpt-5.3-codex-medium',
'gpt-5.3-codex-high',
'gpt-5.3-codex-xhigh',
]);
expect(getCodexEffortVariants('gpt-5.4-mini', 'high')).toEqual([
'gpt-5.4-mini',
'gpt-5.4-mini-minimal',
'gpt-5.4-mini-low',
'gpt-5.4-mini-medium',
'gpt-5.4-mini-high',
]);
@@ -84,6 +89,10 @@ describe('codex effort helpers', () => {
expect(getCodexEffortVariants('gpt-5.4', 'high', ['fast'])).toEqual([
'gpt-5.4',
'gpt-5.4-fast',
'gpt-5.4-minimal',
'gpt-5.4-minimal-fast',
'gpt-5.4-low',
'gpt-5.4-low-fast',
'gpt-5.4-medium',
'gpt-5.4-medium-fast',
'gpt-5.4-high',