mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 18:15:48 +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,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
import type { CliproxyProviderRoutingHints } from '@/lib/api-client';
|
import type { CliproxyProviderRoutingHints } from '@/lib/api-client';
|
||||||
import type { CodexEffort, CodexServiceTier } from '@/lib/codex-effort';
|
import type { CodexEffort, CodexServiceTier } from '@/lib/codex-effort';
|
||||||
import {
|
import {
|
||||||
|
applyCodexServiceTierSuffix,
|
||||||
getCodexEffortDisplay,
|
getCodexEffortDisplay,
|
||||||
getCodexEffortVariants,
|
getCodexEffortVariants,
|
||||||
parseCodexEffort,
|
parseCodexEffort,
|
||||||
parseCodexServiceTier,
|
parseCodexServiceTier,
|
||||||
|
stripCodexEffortSuffix,
|
||||||
} from '@/lib/codex-effort';
|
} 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 { getReasoningAdapter } from '@/lib/reasoning-control';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@@ -586,6 +593,19 @@ export function FlexibleModelSelector({
|
|||||||
!selectableReasoningLevels.includes(currentReasoningLevel)
|
!selectableReasoningLevels.includes(currentReasoningLevel)
|
||||||
? [...selectableReasoningLevels, currentReasoningLevel]
|
? [...selectableReasoningLevels, currentReasoningLevel]
|
||||||
: selectableReasoningLevels;
|
: 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 (
|
return (
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
@@ -702,6 +722,19 @@ export function FlexibleModelSelector({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</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>
|
</div>
|
||||||
{selectedRoutingHint ? (
|
{selectedRoutingHint ? (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ export function applyCodexEffortSuffix(
|
|||||||
return [normalizedModelId, effort, parsed.serviceTier].filter(Boolean).join('-');
|
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(
|
export function getCodexEffortVariants(
|
||||||
modelId: string,
|
modelId: string,
|
||||||
maxEffort: CodexEffort | undefined,
|
maxEffort: CodexEffort | undefined,
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ const resources = {
|
|||||||
codexFastVariants: 'Fast variants',
|
codexFastVariants: 'Fast variants',
|
||||||
effortLabel: 'Effort',
|
effortLabel: 'Effort',
|
||||||
effortAuto: 'Auto',
|
effortAuto: 'Auto',
|
||||||
|
fastTier: 'Fast',
|
||||||
},
|
},
|
||||||
createAuthProfileDialog: {
|
createAuthProfileDialog: {
|
||||||
title: 'Create New Account',
|
title: 'Create New Account',
|
||||||
@@ -2958,6 +2959,7 @@ const resources = {
|
|||||||
codexFastVariants: '快速变体',
|
codexFastVariants: '快速变体',
|
||||||
effortLabel: '推理强度',
|
effortLabel: '推理强度',
|
||||||
effortAuto: '自动',
|
effortAuto: '自动',
|
||||||
|
fastTier: '快速',
|
||||||
},
|
},
|
||||||
createAuthProfileDialog: {
|
createAuthProfileDialog: {
|
||||||
title: '创建新账号',
|
title: '创建新账号',
|
||||||
@@ -5569,6 +5571,7 @@ const resources = {
|
|||||||
codexFastVariants: 'Biến thể nhanh',
|
codexFastVariants: 'Biến thể nhanh',
|
||||||
effortLabel: 'Mức suy luận',
|
effortLabel: 'Mức suy luận',
|
||||||
effortAuto: 'Tự động',
|
effortAuto: 'Tự động',
|
||||||
|
fastTier: 'Nhanh',
|
||||||
},
|
},
|
||||||
createAuthProfileDialog: {
|
createAuthProfileDialog: {
|
||||||
title: 'Tạo tài khoản mới',
|
title: 'Tạo tài khoản mới',
|
||||||
@@ -8296,6 +8299,7 @@ const resources = {
|
|||||||
codexFastVariants: '高速バリアント',
|
codexFastVariants: '高速バリアント',
|
||||||
effortLabel: '推論レベル',
|
effortLabel: '推論レベル',
|
||||||
effortAuto: '自動',
|
effortAuto: '自動',
|
||||||
|
fastTier: '高速',
|
||||||
},
|
},
|
||||||
createAuthProfileDialog: {
|
createAuthProfileDialog: {
|
||||||
title: '新しいアカウントを作成',
|
title: '新しいアカウントを作成',
|
||||||
@@ -11046,6 +11050,7 @@ const resources = {
|
|||||||
codexFastVariants: '빠른 변형',
|
codexFastVariants: '빠른 변형',
|
||||||
effortLabel: '추론 강도',
|
effortLabel: '추론 강도',
|
||||||
effortAuto: '자동',
|
effortAuto: '자동',
|
||||||
|
fastTier: '고속',
|
||||||
},
|
},
|
||||||
createAuthProfileDialog: {
|
createAuthProfileDialog: {
|
||||||
title: '새 계정 생성',
|
title: '새 계정 생성',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import {
|
import {
|
||||||
applyCodexEffortSuffix,
|
applyCodexEffortSuffix,
|
||||||
|
applyCodexServiceTierSuffix,
|
||||||
getCodexEffortDisplay,
|
getCodexEffortDisplay,
|
||||||
getCodexEffortVariants,
|
getCodexEffortVariants,
|
||||||
getSelectableCodexEfforts,
|
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', () => {
|
describe('getSelectableCodexEfforts', () => {
|
||||||
it('returns the explicit efforts list when provided', () => {
|
it('returns the explicit efforts list when provided', () => {
|
||||||
expect(getSelectableCodexEfforts('xhigh', ['low', 'medium', 'high', 'xhigh'])).toEqual([
|
expect(getSelectableCodexEfforts('xhigh', ['low', 'medium', 'high', 'xhigh'])).toEqual([
|
||||||
|
|||||||
Reference in new issue
Block a user