mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-03 20:13:02 +00:00
feat(ui): add codex fast service-tier toggle
This commit is contained in:
1 parent
30b54a0966
commit
108ff843f1
4 files changed
+80
-1
No files matched your search
@@ -18,15 +18,22 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import type { CliproxyProviderRoutingHints } from '@/lib/api-client';
|
||||
import type { CodexEffort, CodexServiceTier } from '@/lib/codex-effort';
|
||||
import {
|
||||
applyCodexServiceTierSuffix,
|
||||
getCodexEffortDisplay,
|
||||
getCodexEffortVariants,
|
||||
parseCodexEffort,
|
||||
parseCodexServiceTier,
|
||||
stripCodexEffortSuffix,
|
||||
} from '@/lib/codex-effort';
|
||||
import { getResolvedCatalogModels, getSupplementalCatalogModels } from '@/lib/model-catalogs';
|
||||
import {
|
||||
findCatalogModel,
|
||||
getResolvedCatalogModels,
|
||||
getSupplementalCatalogModels,
|
||||
} from '@/lib/model-catalogs';
|
||||
import { getReasoningAdapter } from '@/lib/reasoning-control';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
@@ -586,6 +593,19 @@ export function FlexibleModelSelector({
|
||||
!selectableReasoningLevels.includes(currentReasoningLevel)
|
||||
? [...selectableReasoningLevels, currentReasoningLevel]
|
||||
: selectableReasoningLevels;
|
||||
const currentServiceTier = isCodexProvider ? parseCodexServiceTier(value) : undefined;
|
||||
const selectedCodexBaseModel =
|
||||
isCodexProvider && catalog && value
|
||||
? findCatalogModel(
|
||||
catalog.provider,
|
||||
stripCodexEffortSuffix(normalizeModelValue(value, routing)),
|
||||
catalog
|
||||
)
|
||||
: undefined;
|
||||
const showFastToggle =
|
||||
isCodexProvider &&
|
||||
(selectedCodexBaseModel?.codexServiceTiers?.includes('fast') === true ||
|
||||
currentServiceTier === 'fast');
|
||||
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
@@ -702,6 +722,19 @@ export function FlexibleModelSelector({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
{showFastToggle && (
|
||||
<label className="flex h-9 shrink-0 items-center gap-1.5">
|
||||
<Switch
|
||||
checked={currentServiceTier === 'fast'}
|
||||
onCheckedChange={(checked) => onChange(applyCodexServiceTierSuffix(value, checked))}
|
||||
disabled={disabled || !value}
|
||||
aria-label={t('providerModelSelector.fastTier')}
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{t('providerModelSelector.fastTier')}
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
{selectedRoutingHint ? (
|
||||
<div
|
||||
|
||||
@@ -66,6 +66,12 @@ export function applyCodexEffortSuffix(
|
||||
return [normalizedModelId, effort, parsed.serviceTier].filter(Boolean).join('-');
|
||||
}
|
||||
|
||||
export function applyCodexServiceTierSuffix(modelId: string | undefined, enabled: boolean): string {
|
||||
const parsed = parseCodexTuningSuffix(modelId);
|
||||
if (!parsed.baseModel) return parsed.baseModel;
|
||||
return [parsed.baseModel, parsed.effort, enabled ? 'fast' : undefined].filter(Boolean).join('-');
|
||||
}
|
||||
|
||||
export function getCodexEffortVariants(
|
||||
modelId: string,
|
||||
maxEffort: CodexEffort | undefined,
|
||||
|
||||
@@ -230,6 +230,7 @@ const resources = {
|
||||
codexFastVariants: 'Fast variants',
|
||||
effortLabel: 'Effort',
|
||||
effortAuto: 'Auto',
|
||||
fastTier: 'Fast',
|
||||
},
|
||||
createAuthProfileDialog: {
|
||||
title: 'Create New Account',
|
||||
@@ -2958,6 +2959,7 @@ const resources = {
|
||||
codexFastVariants: '快速变体',
|
||||
effortLabel: '推理强度',
|
||||
effortAuto: '自动',
|
||||
fastTier: '快速',
|
||||
},
|
||||
createAuthProfileDialog: {
|
||||
title: '创建新账号',
|
||||
@@ -5569,6 +5571,7 @@ const resources = {
|
||||
codexFastVariants: 'Biến thể nhanh',
|
||||
effortLabel: 'Mức suy luận',
|
||||
effortAuto: 'Tự động',
|
||||
fastTier: 'Nhanh',
|
||||
},
|
||||
createAuthProfileDialog: {
|
||||
title: 'Tạo tài khoản mới',
|
||||
@@ -8296,6 +8299,7 @@ const resources = {
|
||||
codexFastVariants: '高速バリアント',
|
||||
effortLabel: '推論レベル',
|
||||
effortAuto: '自動',
|
||||
fastTier: '高速',
|
||||
},
|
||||
createAuthProfileDialog: {
|
||||
title: '新しいアカウントを作成',
|
||||
@@ -11046,6 +11050,7 @@ const resources = {
|
||||
codexFastVariants: '빠른 변형',
|
||||
effortLabel: '추론 강도',
|
||||
effortAuto: '자동',
|
||||
fastTier: '고속',
|
||||
},
|
||||
createAuthProfileDialog: {
|
||||
title: '새 계정 생성',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
applyCodexEffortSuffix,
|
||||
applyCodexServiceTierSuffix,
|
||||
getCodexEffortDisplay,
|
||||
getCodexEffortVariants,
|
||||
getSelectableCodexEfforts,
|
||||
@@ -111,6 +112,40 @@ describe('codex effort helpers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyCodexServiceTierSuffix', () => {
|
||||
it('adds the fast tier while preserving the effort suffix', () => {
|
||||
expect(applyCodexServiceTierSuffix('gpt-5.4-high', true)).toBe('gpt-5.4-high-fast');
|
||||
});
|
||||
|
||||
it('removes only the fast tier', () => {
|
||||
expect(applyCodexServiceTierSuffix('gpt-5.4-high-fast', false)).toBe('gpt-5.4-high');
|
||||
expect(applyCodexServiceTierSuffix('gpt-5.4-fast', false)).toBe('gpt-5.4');
|
||||
});
|
||||
|
||||
it('preserves routing prefixes', () => {
|
||||
expect(applyCodexServiceTierSuffix('codex/gpt-5.6-sol', true)).toBe('codex/gpt-5.6-sol-fast');
|
||||
});
|
||||
|
||||
it('is idempotent and safe on empty input', () => {
|
||||
expect(applyCodexServiceTierSuffix('gpt-5.4-fast', true)).toBe('gpt-5.4-fast');
|
||||
expect(applyCodexServiceTierSuffix('gpt-5.4', false)).toBe('gpt-5.4');
|
||||
expect(applyCodexServiceTierSuffix(undefined, true)).toBe('');
|
||||
expect(applyCodexServiceTierSuffix('', true)).toBe('');
|
||||
});
|
||||
|
||||
it('composes with applyCodexEffortSuffix in both orders', () => {
|
||||
expect(applyCodexEffortSuffix(applyCodexServiceTierSuffix('gpt-5.4', true), 'high')).toBe(
|
||||
'gpt-5.4-high-fast'
|
||||
);
|
||||
expect(applyCodexServiceTierSuffix(applyCodexEffortSuffix('gpt-5.4', 'high'), true)).toBe(
|
||||
'gpt-5.4-high-fast'
|
||||
);
|
||||
expect(
|
||||
applyCodexEffortSuffix(applyCodexServiceTierSuffix('gpt-5.4-high-fast', false), 'low')
|
||||
).toBe('gpt-5.4-low');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSelectableCodexEfforts', () => {
|
||||
it('returns the explicit efforts list when provided', () => {
|
||||
expect(getSelectableCodexEfforts('xhigh', ['low', 'medium', 'high', 'xhigh'])).toEqual([
|
||||
|
||||
Reference in new issue
Block a user