mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 14:14:47 +00:00
1 parent
da6c99e3df
commit
24eae8ae8f
8 files changed
+111
-4
No files matched your search
@@ -247,6 +247,9 @@ describe('Model Catalog', () => {
|
|||||||
const { MODEL_CATALOG } = modelCatalog;
|
const { MODEL_CATALOG } = modelCatalog;
|
||||||
const ids = MODEL_CATALOG.codex.models.map((m) => m.id);
|
const ids = MODEL_CATALOG.codex.models.map((m) => m.id);
|
||||||
assert.deepStrictEqual(ids, [
|
assert.deepStrictEqual(ids, [
|
||||||
|
'gpt-5.6-sol',
|
||||||
|
'gpt-5.6-terra',
|
||||||
|
'gpt-5.6-luna',
|
||||||
'gpt-5.5',
|
'gpt-5.5',
|
||||||
'gpt-5.4',
|
'gpt-5.4',
|
||||||
'gpt-5.4-mini',
|
'gpt-5.4-mini',
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ describe('codex plan compatibility', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not rewrite cross-plan or already-safe Codex models', () => {
|
it('does not rewrite cross-plan or already-safe Codex models', () => {
|
||||||
|
expect(getFreePlanFallbackCodexModel('gpt-5.6-sol')).toBeNull();
|
||||||
|
expect(getFreePlanFallbackCodexModel('gpt-5.6-terra-high')).toBeNull();
|
||||||
|
expect(getFreePlanFallbackCodexModel('gpt-5.6-luna-fast')).toBeNull();
|
||||||
expect(getFreePlanFallbackCodexModel('gpt-5.4')).toBeNull();
|
expect(getFreePlanFallbackCodexModel('gpt-5.4')).toBeNull();
|
||||||
expect(getFreePlanFallbackCodexModel('gpt-5.4-mini')).toBeNull();
|
expect(getFreePlanFallbackCodexModel('gpt-5.4-mini')).toBeNull();
|
||||||
expect(getFreePlanFallbackCodexModel('gpt-5.2')).toBeNull();
|
expect(getFreePlanFallbackCodexModel('gpt-5.2')).toBeNull();
|
||||||
@@ -94,6 +97,9 @@ describe('codex plan compatibility', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('tracks Codex thinking caps for current safe defaults, paid models, and legacy aliases', () => {
|
it('tracks Codex thinking caps for current safe defaults, paid models, and legacy aliases', () => {
|
||||||
|
expect(getModelMaxLevel('codex', 'gpt-5.6-sol')).toBe('xhigh');
|
||||||
|
expect(getModelMaxLevel('codex', 'gpt-5.6-terra')).toBe('xhigh');
|
||||||
|
expect(getModelMaxLevel('codex', 'gpt-5.6-luna')).toBe('xhigh');
|
||||||
expect(getModelMaxLevel('codex', 'gpt-5.5')).toBe('xhigh');
|
expect(getModelMaxLevel('codex', 'gpt-5.5')).toBe('xhigh');
|
||||||
expect(getModelMaxLevel('codex', 'gpt-5.4')).toBe('xhigh');
|
expect(getModelMaxLevel('codex', 'gpt-5.4')).toBe('xhigh');
|
||||||
expect(getModelMaxLevel('codex', 'gpt-5.4-mini')).toBe('high');
|
expect(getModelMaxLevel('codex', 'gpt-5.4-mini')).toBe('high');
|
||||||
|
|||||||
@@ -190,6 +190,42 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
|||||||
displayName: 'Copilot Codex',
|
displayName: 'Copilot Codex',
|
||||||
defaultModel: 'gpt-5.4',
|
defaultModel: 'gpt-5.4',
|
||||||
models: [
|
models: [
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-sol',
|
||||||
|
name: 'GPT-5.6 Sol',
|
||||||
|
description: 'Latest frontier agentic coding model.',
|
||||||
|
thinking: {
|
||||||
|
type: 'levels',
|
||||||
|
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
maxLevel: 'xhigh',
|
||||||
|
dynamicAllowed: false,
|
||||||
|
},
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-terra',
|
||||||
|
name: 'GPT-5.6 Terra',
|
||||||
|
description: 'Balanced agentic coding model for everyday work.',
|
||||||
|
thinking: {
|
||||||
|
type: 'levels',
|
||||||
|
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
maxLevel: 'xhigh',
|
||||||
|
dynamicAllowed: false,
|
||||||
|
},
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-luna',
|
||||||
|
name: 'GPT-5.6 Luna',
|
||||||
|
description: 'Fast and affordable agentic coding model.',
|
||||||
|
thinking: {
|
||||||
|
type: 'levels',
|
||||||
|
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
maxLevel: 'xhigh',
|
||||||
|
dynamicAllowed: false,
|
||||||
|
},
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5.5',
|
id: 'gpt-5.5',
|
||||||
name: 'GPT-5.5',
|
name: 'GPT-5.5',
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ export interface ModelEntry {
|
|||||||
};
|
};
|
||||||
/** Highest codex reasoning-effort suffix this model supports in the dashboard UI. */
|
/** Highest codex reasoning-effort suffix this model supports in the dashboard UI. */
|
||||||
codexMaxEffort?: CodexEffort;
|
codexMaxEffort?: CodexEffort;
|
||||||
|
/** Exact Codex reasoning-effort suffixes supported by this model in the dashboard UI. */
|
||||||
|
codexEfforts?: CodexEffort[];
|
||||||
/** Additional Codex service-tier suffixes supported by this model in the dashboard UI. */
|
/** Additional Codex service-tier suffixes supported by this model in the dashboard UI. */
|
||||||
codexServiceTiers?: CodexServiceTier[];
|
codexServiceTiers?: CodexServiceTier[];
|
||||||
}
|
}
|
||||||
@@ -347,12 +349,13 @@ function getPreferredOptionValue(
|
|||||||
|
|
||||||
function getModelOptionValues(
|
function getModelOptionValues(
|
||||||
codexMaxEffort: CodexEffort | undefined,
|
codexMaxEffort: CodexEffort | undefined,
|
||||||
|
codexEfforts: readonly CodexEffort[] | undefined,
|
||||||
codexServiceTiers: readonly CodexServiceTier[] | undefined,
|
codexServiceTiers: readonly CodexServiceTier[] | undefined,
|
||||||
optionValue: string,
|
optionValue: string,
|
||||||
isCodexProvider: boolean
|
isCodexProvider: boolean
|
||||||
): string[] {
|
): string[] {
|
||||||
return isCodexProvider
|
return isCodexProvider
|
||||||
? getCodexEffortVariants(optionValue, codexMaxEffort, codexServiceTiers)
|
? getCodexEffortVariants(optionValue, codexMaxEffort, codexServiceTiers, codexEfforts)
|
||||||
: [optionValue];
|
: [optionValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +398,7 @@ export function FlexibleModelSelector({
|
|||||||
resolvedCatalogModels.flatMap((model) =>
|
resolvedCatalogModels.flatMap((model) =>
|
||||||
getModelOptionValues(
|
getModelOptionValues(
|
||||||
model.codexMaxEffort,
|
model.codexMaxEffort,
|
||||||
|
model.codexEfforts,
|
||||||
model.codexServiceTiers,
|
model.codexServiceTiers,
|
||||||
getPreferredOptionValue(model.id, routingHints.get(model.id.toLowerCase())),
|
getPreferredOptionValue(model.id, routingHints.get(model.id.toLowerCase())),
|
||||||
isCodexProvider
|
isCodexProvider
|
||||||
@@ -431,6 +435,7 @@ export function FlexibleModelSelector({
|
|||||||
const routingHint = routingHints.get(model.id.toLowerCase());
|
const routingHint = routingHints.get(model.id.toLowerCase());
|
||||||
const optionValues = getModelOptionValues(
|
const optionValues = getModelOptionValues(
|
||||||
model.codexMaxEffort,
|
model.codexMaxEffort,
|
||||||
|
model.codexEfforts,
|
||||||
model.codexServiceTiers,
|
model.codexServiceTiers,
|
||||||
getPreferredOptionValue(model.id, routingHint),
|
getPreferredOptionValue(model.id, routingHint),
|
||||||
isCodexProvider
|
isCodexProvider
|
||||||
@@ -485,6 +490,7 @@ export function FlexibleModelSelector({
|
|||||||
.flatMap((model) => {
|
.flatMap((model) => {
|
||||||
const routingHint = routingHints.get(model.id.toLowerCase());
|
const routingHint = routingHints.get(model.id.toLowerCase());
|
||||||
const optionValues = getModelOptionValues(
|
const optionValues = getModelOptionValues(
|
||||||
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
getPreferredOptionValue(model.id, routingHint),
|
getPreferredOptionValue(model.id, routingHint),
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ export function applyCodexEffortSuffix(
|
|||||||
export function getCodexEffortVariants(
|
export function getCodexEffortVariants(
|
||||||
modelId: string,
|
modelId: string,
|
||||||
maxEffort: CodexEffort | undefined,
|
maxEffort: CodexEffort | undefined,
|
||||||
serviceTiers: readonly CodexServiceTier[] = []
|
serviceTiers: readonly CodexServiceTier[] = [],
|
||||||
|
efforts?: readonly CodexEffort[]
|
||||||
): string[] {
|
): string[] {
|
||||||
if (!maxEffort) {
|
if (!maxEffort) {
|
||||||
const explicitEffort = parseCodexEffort(modelId);
|
const explicitEffort = parseCodexEffort(modelId);
|
||||||
@@ -91,9 +92,9 @@ export function getCodexEffortVariants(
|
|||||||
|
|
||||||
appendVariantsForEffort(undefined);
|
appendVariantsForEffort(undefined);
|
||||||
|
|
||||||
for (const effort of CODEX_EFFORTS_IN_ORDER) {
|
for (const effort of efforts ?? CODEX_EFFORTS_IN_ORDER) {
|
||||||
appendVariantsForEffort(effort);
|
appendVariantsForEffort(effort);
|
||||||
if (effort === maxEffort) {
|
if (!efforts && effort === maxEffort) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,48 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
|||||||
displayName: 'Codex',
|
displayName: 'Codex',
|
||||||
defaultModel: 'gpt-5.4',
|
defaultModel: 'gpt-5.4',
|
||||||
models: [
|
models: [
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-sol',
|
||||||
|
name: 'GPT-5.6 Sol',
|
||||||
|
description: 'Latest frontier agentic coding model.',
|
||||||
|
codexMaxEffort: 'xhigh',
|
||||||
|
codexEfforts: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
presetMapping: {
|
||||||
|
default: 'gpt-5.6-sol',
|
||||||
|
opus: 'gpt-5.6-sol',
|
||||||
|
sonnet: 'gpt-5.6-sol',
|
||||||
|
haiku: 'gpt-5.4-mini',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-terra',
|
||||||
|
name: 'GPT-5.6 Terra',
|
||||||
|
description: 'Balanced agentic coding model for everyday work.',
|
||||||
|
codexMaxEffort: 'xhigh',
|
||||||
|
codexEfforts: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
presetMapping: {
|
||||||
|
default: 'gpt-5.6-terra',
|
||||||
|
opus: 'gpt-5.6-terra',
|
||||||
|
sonnet: 'gpt-5.6-terra',
|
||||||
|
haiku: 'gpt-5.4-mini',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-5.6-luna',
|
||||||
|
name: 'GPT-5.6 Luna',
|
||||||
|
description: 'Fast and affordable agentic coding model.',
|
||||||
|
codexMaxEffort: 'xhigh',
|
||||||
|
codexEfforts: ['low', 'medium', 'high', 'xhigh'],
|
||||||
|
codexServiceTiers: ['fast'],
|
||||||
|
presetMapping: {
|
||||||
|
default: 'gpt-5.6-luna',
|
||||||
|
opus: 'gpt-5.6-luna',
|
||||||
|
sonnet: 'gpt-5.6-luna',
|
||||||
|
haiku: 'gpt-5.4-mini',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5.5',
|
id: 'gpt-5.5',
|
||||||
name: 'GPT-5.5',
|
name: 'GPT-5.5',
|
||||||
|
|||||||
@@ -191,6 +191,10 @@ describe('FlexibleModelSelector', () => {
|
|||||||
expect(screen.getByText('gpt-5.3-codex-high')).toBeInTheDocument();
|
expect(screen.getByText('gpt-5.3-codex-high')).toBeInTheDocument();
|
||||||
expect(screen.getByText('gpt-5.3-codex-xhigh')).toBeInTheDocument();
|
expect(screen.getByText('gpt-5.3-codex-xhigh')).toBeInTheDocument();
|
||||||
expect(screen.getByText('gpt-5.4-high-fast')).toBeInTheDocument();
|
expect(screen.getByText('gpt-5.4-high-fast')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('gpt-5.6-sol-low')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('gpt-5.6-terra-xhigh')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('gpt-5.6-luna-fast')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('gpt-5.6-sol-minimal')).not.toBeInTheDocument();
|
||||||
|
|
||||||
await userEvent.click(screen.getByText('gpt-5.3-codex-high'));
|
await userEvent.click(screen.getByText('gpt-5.3-codex-high'));
|
||||||
expect(onChange).toHaveBeenCalledWith('gpt-5.3-codex-high');
|
expect(onChange).toHaveBeenCalledWith('gpt-5.3-codex-high');
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { MODEL_CATALOGS } from '@/lib/model-catalogs';
|
|||||||
describe('codex model catalog defaults', () => {
|
describe('codex model catalog defaults', () => {
|
||||||
it('mirrors the current Codex runtime catalog and free-safe defaults', () => {
|
it('mirrors the current Codex runtime catalog and free-safe defaults', () => {
|
||||||
const codexCatalog = MODEL_CATALOGS.codex;
|
const codexCatalog = MODEL_CATALOGS.codex;
|
||||||
|
const codex56Sol = codexCatalog.models.find((model) => model.id === 'gpt-5.6-sol');
|
||||||
|
const codex56Terra = codexCatalog.models.find((model) => model.id === 'gpt-5.6-terra');
|
||||||
|
const codex56Luna = codexCatalog.models.find((model) => model.id === 'gpt-5.6-luna');
|
||||||
const codex55 = codexCatalog.models.find((model) => model.id === 'gpt-5.5');
|
const codex55 = codexCatalog.models.find((model) => model.id === 'gpt-5.5');
|
||||||
const codex53 = codexCatalog.models.find((model) => model.id === 'gpt-5.3-codex');
|
const codex53 = codexCatalog.models.find((model) => model.id === 'gpt-5.3-codex');
|
||||||
const codex52 = codexCatalog.models.find((model) => model.id === 'gpt-5.2');
|
const codex52 = codexCatalog.models.find((model) => model.id === 'gpt-5.2');
|
||||||
@@ -11,6 +14,12 @@ describe('codex model catalog defaults', () => {
|
|||||||
const codexMini = codexCatalog.models.find((model) => model.id === 'gpt-5.4-mini');
|
const codexMini = codexCatalog.models.find((model) => model.id === 'gpt-5.4-mini');
|
||||||
|
|
||||||
expect(codexCatalog.defaultModel).toBe('gpt-5.4');
|
expect(codexCatalog.defaultModel).toBe('gpt-5.4');
|
||||||
|
for (const model of [codex56Sol, codex56Terra, codex56Luna]) {
|
||||||
|
expect(model?.tier).toBeUndefined();
|
||||||
|
expect(model?.codexEfforts).toEqual(['low', 'medium', 'high', 'xhigh']);
|
||||||
|
expect(model?.codexServiceTiers).toEqual(['fast']);
|
||||||
|
expect(model?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
|
}
|
||||||
expect(codex55?.tier).toBe('paid');
|
expect(codex55?.tier).toBe('paid');
|
||||||
expect(codex55?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
expect(codex55?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
expect(codex55?.codexMaxEffort).toBe('xhigh');
|
expect(codex55?.codexMaxEffort).toBe('xhigh');
|
||||||
|
|||||||
Reference in new issue
Block a user