mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-05 00:18:05 +00:00
fix(cliproxy): refresh upstream model and quota surfaces (#1158)
* fix(cliproxy): refresh upstream model and quota surfaces * test(cliproxy): cover codex legacy model fallback
This commit is contained in:
@@ -19,13 +19,13 @@ export const BACKEND_CONFIG = {
|
|||||||
repo: 'router-for-me/CLIProxyAPI',
|
repo: 'router-for-me/CLIProxyAPI',
|
||||||
binaryPrefix: 'CLIProxyAPI',
|
binaryPrefix: 'CLIProxyAPI',
|
||||||
executable: 'cli-proxy-api',
|
executable: 'cli-proxy-api',
|
||||||
fallbackVersion: '6.8.2',
|
fallbackVersion: '6.9.45',
|
||||||
},
|
},
|
||||||
plus: {
|
plus: {
|
||||||
repo: 'kaitranntt/CLIProxyAPIPlus',
|
repo: 'kaitranntt/CLIProxyAPIPlus',
|
||||||
binaryPrefix: 'CLIProxyAPIPlus',
|
binaryPrefix: 'CLIProxyAPIPlus',
|
||||||
executable: 'cli-proxy-api-plus',
|
executable: 'cli-proxy-api-plus',
|
||||||
fallbackVersion: '6.9.36-0',
|
fallbackVersion: '6.9.45-0',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -208,4 +208,111 @@ describe('Antigravity quota failure metadata', () => {
|
|||||||
fs.rmSync(tempHome, { recursive: true, force: true });
|
fs.rmSync(tempHome, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tries the daily loadCodeAssist host before falling back to prod', async () => {
|
||||||
|
const moduleId = Date.now() + Math.random();
|
||||||
|
const { fetchAccountQuota } = await import(`../quota-fetcher?agy-daily-host=${moduleId}`);
|
||||||
|
const { getProviderAuthDir } = await import(
|
||||||
|
`../../config/config-generator?agy-config=${moduleId}`
|
||||||
|
);
|
||||||
|
const fs = await import('node:fs');
|
||||||
|
const os = await import('node:os');
|
||||||
|
const path = await import('node:path');
|
||||||
|
|
||||||
|
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-agy-daily-host-'));
|
||||||
|
const originalCcsHome = process.env.CCS_HOME;
|
||||||
|
process.env.CCS_HOME = tempHome;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const authDir = getProviderAuthDir('agy');
|
||||||
|
fs.mkdirSync(authDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(authDir, 'antigravity-user@example.com.json'),
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'antigravity',
|
||||||
|
email: 'user@example.com',
|
||||||
|
access_token: 'token',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
const urls: string[] = [];
|
||||||
|
globalThis.fetch = (async (input, init) => {
|
||||||
|
const url = String(input);
|
||||||
|
urls.push(url);
|
||||||
|
|
||||||
|
if (url === 'https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist') {
|
||||||
|
const bodyText = typeof init?.body === 'string' ? init.body : '';
|
||||||
|
expect(init?.headers).toMatchObject({
|
||||||
|
'X-Goog-Api-Client': 'gl-node/22.21.1',
|
||||||
|
});
|
||||||
|
expect(bodyText).toBe(
|
||||||
|
JSON.stringify({
|
||||||
|
metadata: {
|
||||||
|
ide_name: 'antigravity',
|
||||||
|
ide_type: 'ANTIGRAVITY',
|
||||||
|
ide_version: '1.21.9',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return new Response('daily unavailable', { status: 503 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === 'https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist') {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
cloudaicompanionProject: { id: 'project-x' },
|
||||||
|
paidTier: { id: 'g1-pro-tier' },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === 'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels') {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
models: {
|
||||||
|
'gemini-3-pro-high': {
|
||||||
|
quotaInfo: {
|
||||||
|
remainingFraction: 0.75,
|
||||||
|
resetTime: '2026-05-01T10:00:00Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response('unexpected url', { status: 500 });
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await fetchAccountQuota('agy', 'user@example.com');
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.tier).toBe('pro');
|
||||||
|
expect(result.projectId).toBe('project-x');
|
||||||
|
expect(urls).toEqual([
|
||||||
|
'https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist',
|
||||||
|
'https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist',
|
||||||
|
'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels',
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
globalThis.fetch = originalFetch;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (originalCcsHome === undefined) {
|
||||||
|
delete process.env.CCS_HOME;
|
||||||
|
} else {
|
||||||
|
process.env.CCS_HOME = originalCcsHome;
|
||||||
|
}
|
||||||
|
fs.rmSync(tempHome, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,17 +82,20 @@ export interface QuotaResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Google Cloud Code API endpoints */
|
/** Google Cloud Code API endpoints */
|
||||||
|
const ANTIGRAVITY_DAILY_API_BASE = 'https://daily-cloudcode-pa.googleapis.com';
|
||||||
const ANTIGRAVITY_API_BASE = 'https://cloudcode-pa.googleapis.com';
|
const ANTIGRAVITY_API_BASE = 'https://cloudcode-pa.googleapis.com';
|
||||||
const ANTIGRAVITY_API_VERSION = 'v1internal';
|
const ANTIGRAVITY_API_VERSION = 'v1internal';
|
||||||
|
const ANTIGRAVITY_LOADCODEASSIST_BASE_URLS = [
|
||||||
|
ANTIGRAVITY_DAILY_API_BASE,
|
||||||
|
ANTIGRAVITY_API_BASE,
|
||||||
|
] as const;
|
||||||
const MANAGEMENT_API_TIMEOUT_MS = 5000;
|
const MANAGEMENT_API_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
/** Headers for loadCodeAssist (matches CLIProxyAPI antigravity.go) */
|
/** Headers for loadCodeAssist (matches current CLIProxyAPIPlus control-plane requests) */
|
||||||
const LOADCODEASSIST_HEADERS = {
|
const LOADCODEASSIST_HEADERS = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'User-Agent': 'google-api-nodejs-client/9.15.1',
|
'User-Agent': 'antigravity/1.21.9 darwin/arm64 google-api-nodejs-client/10.3.0',
|
||||||
'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',
|
'X-Goog-Api-Client': 'gl-node/22.21.1',
|
||||||
'Client-Metadata':
|
|
||||||
'{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Headers for fetchAvailableModels (matches CLIProxyAPI antigravity_executor.go) */
|
/** Headers for fetchAvailableModels (matches CLIProxyAPI antigravity_executor.go) */
|
||||||
@@ -543,6 +546,40 @@ async function performAntigravityRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function performAntigravityRequestWithBaseUrlFallback(
|
||||||
|
accountId: string,
|
||||||
|
accessToken: string,
|
||||||
|
baseUrls: readonly string[],
|
||||||
|
apiPath: string,
|
||||||
|
headers: Record<string, string>,
|
||||||
|
body: string
|
||||||
|
): Promise<ManagedResponse> {
|
||||||
|
let lastResponse: ManagedResponse | null = null;
|
||||||
|
|
||||||
|
for (const baseUrl of baseUrls) {
|
||||||
|
const response = await performAntigravityRequest(
|
||||||
|
accountId,
|
||||||
|
accessToken,
|
||||||
|
`${baseUrl}/${apiPath}`,
|
||||||
|
headers,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
lastResponse = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
lastResponse ?? {
|
||||||
|
status: 503,
|
||||||
|
bodyText: 'No Antigravity API endpoint available',
|
||||||
|
json: null,
|
||||||
|
viaManagement: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read auth data from auth file (access token, project_id, expiry status)
|
* Read auth data from auth file (access token, project_id, expiry status)
|
||||||
*/
|
*/
|
||||||
@@ -616,18 +653,18 @@ function readAuthData(provider: CLIProxyProvider, accountId: string): AuthData |
|
|||||||
* Uses paidTier.id for accurate tier detection (g1-ultra-tier, g1-pro-tier)
|
* Uses paidTier.id for accurate tier detection (g1-ultra-tier, g1-pro-tier)
|
||||||
*/
|
*/
|
||||||
async function getProjectId(accountId: string, accessToken: string): Promise<ProjectLookupResult> {
|
async function getProjectId(accountId: string, accessToken: string): Promise<ProjectLookupResult> {
|
||||||
const url = `${ANTIGRAVITY_API_BASE}/${ANTIGRAVITY_API_VERSION}:loadCodeAssist`;
|
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
metadata: {
|
metadata: {
|
||||||
ideType: 'IDE_UNSPECIFIED',
|
ide_name: 'antigravity',
|
||||||
platform: 'PLATFORM_UNSPECIFIED',
|
ide_type: 'ANTIGRAVITY',
|
||||||
pluginType: 'GEMINI',
|
ide_version: '1.21.9',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const response = await performAntigravityRequest(
|
const response = await performAntigravityRequestWithBaseUrlFallback(
|
||||||
accountId,
|
accountId,
|
||||||
accessToken,
|
accessToken,
|
||||||
url,
|
ANTIGRAVITY_LOADCODEASSIST_BASE_URLS,
|
||||||
|
`${ANTIGRAVITY_API_VERSION}:loadCodeAssist`,
|
||||||
LOADCODEASSIST_HEADERS,
|
LOADCODEASSIST_HEADERS,
|
||||||
body
|
body
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -257,130 +257,83 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
|||||||
codex: {
|
codex: {
|
||||||
provider: 'codex',
|
provider: 'codex',
|
||||||
displayName: 'Codex',
|
displayName: 'Codex',
|
||||||
defaultModel: 'gpt-5-codex',
|
defaultModel: 'gpt-5.4',
|
||||||
models: [
|
models: [
|
||||||
{
|
{
|
||||||
id: 'gpt-5.5',
|
id: 'gpt-5.5',
|
||||||
name: 'GPT-5.5',
|
name: 'GPT-5.5',
|
||||||
tier: 'paid',
|
tier: 'paid',
|
||||||
description: 'Newest Codex-only GPT-5 family model',
|
description:
|
||||||
|
'Newest Codex-released GPT-5 family model; falls back to GPT-5.4 on free plans',
|
||||||
codexMaxEffort: 'xhigh',
|
codexMaxEffort: 'xhigh',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5.5',
|
default: 'gpt-5.5',
|
||||||
opus: 'gpt-5.5',
|
opus: 'gpt-5.5',
|
||||||
sonnet: 'gpt-5.5',
|
sonnet: 'gpt-5.5',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5-codex',
|
id: 'gpt-5.4',
|
||||||
name: 'GPT-5 Codex',
|
name: 'GPT-5.4',
|
||||||
description: 'Cross-plan safe Codex default',
|
description: 'Recommended Codex default for most coding and agentic tasks',
|
||||||
codexMaxEffort: 'xhigh',
|
codexMaxEffort: 'xhigh',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5-codex',
|
default: 'gpt-5.4',
|
||||||
opus: 'gpt-5-codex',
|
opus: 'gpt-5.4',
|
||||||
sonnet: 'gpt-5-codex',
|
sonnet: 'gpt-5.4',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5-codex-mini',
|
id: 'gpt-5.4-mini',
|
||||||
name: 'GPT-5 Codex Mini',
|
name: 'GPT-5.4 Mini',
|
||||||
description: 'Faster and cheaper Codex option',
|
description: 'Fast, lower-cost Codex option for lighter tasks and haiku-tier routing',
|
||||||
codexMaxEffort: 'high',
|
codexMaxEffort: 'high',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5-codex-mini',
|
default: 'gpt-5.4-mini',
|
||||||
opus: 'gpt-5-codex',
|
opus: 'gpt-5.4',
|
||||||
sonnet: 'gpt-5-codex',
|
sonnet: 'gpt-5.4',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'gpt-5-mini',
|
|
||||||
name: 'GPT-5 Mini',
|
|
||||||
description: 'Legacy mini model ID kept for backwards compatibility',
|
|
||||||
codexMaxEffort: 'high',
|
|
||||||
presetMapping: {
|
|
||||||
default: 'gpt-5-mini',
|
|
||||||
opus: 'gpt-5-codex',
|
|
||||||
sonnet: 'gpt-5-mini',
|
|
||||||
haiku: 'gpt-5-mini',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'gpt-5.1-codex-mini',
|
|
||||||
name: 'GPT-5.1 Codex Mini',
|
|
||||||
description: 'Legacy fast Codex mini model',
|
|
||||||
codexMaxEffort: 'high',
|
|
||||||
presetMapping: {
|
|
||||||
default: 'gpt-5.1-codex-mini',
|
|
||||||
opus: 'gpt-5.1-codex-max',
|
|
||||||
sonnet: 'gpt-5.1-codex-max',
|
|
||||||
haiku: 'gpt-5.1-codex-mini',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'gpt-5.1-codex-max',
|
|
||||||
name: 'GPT-5.1 Codex Max',
|
|
||||||
description: 'Higher-effort Codex model with xhigh support',
|
|
||||||
codexMaxEffort: 'xhigh',
|
|
||||||
presetMapping: {
|
|
||||||
default: 'gpt-5.1-codex-max',
|
|
||||||
opus: 'gpt-5.1-codex-max',
|
|
||||||
sonnet: 'gpt-5.1-codex-max',
|
|
||||||
haiku: 'gpt-5.1-codex-mini',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'gpt-5.2-codex',
|
|
||||||
name: 'GPT-5.2 Codex',
|
|
||||||
description: 'Cross-plan Codex model with xhigh support',
|
|
||||||
codexMaxEffort: 'xhigh',
|
|
||||||
presetMapping: {
|
|
||||||
default: 'gpt-5.2-codex',
|
|
||||||
opus: 'gpt-5.2-codex',
|
|
||||||
sonnet: 'gpt-5.2-codex',
|
|
||||||
haiku: 'gpt-5-codex-mini',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5.3-codex',
|
id: 'gpt-5.3-codex',
|
||||||
name: 'GPT-5.3 Codex',
|
name: 'GPT-5.3 Codex',
|
||||||
tier: 'paid',
|
tier: 'paid',
|
||||||
description: 'Paid Codex plans only',
|
description: 'Previous flagship coding model whose capabilities now power GPT-5.4',
|
||||||
codexMaxEffort: 'xhigh',
|
codexMaxEffort: 'xhigh',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5.3-codex',
|
default: 'gpt-5.3-codex',
|
||||||
opus: 'gpt-5.3-codex',
|
opus: 'gpt-5.3-codex',
|
||||||
sonnet: 'gpt-5.3-codex',
|
sonnet: 'gpt-5.3-codex',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5.3-codex-spark',
|
id: 'gpt-5.3-codex-spark',
|
||||||
name: 'GPT-5.3 Codex Spark',
|
name: 'GPT-5.3 Codex Spark',
|
||||||
tier: 'paid',
|
tier: 'paid',
|
||||||
description: 'Paid Codex plans only, ultra-fast coding model',
|
description:
|
||||||
|
'Research preview model for ChatGPT Pro subscribers, optimized for near-instant coding iteration',
|
||||||
codexMaxEffort: 'xhigh',
|
codexMaxEffort: 'xhigh',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5.3-codex-spark',
|
default: 'gpt-5.3-codex-spark',
|
||||||
opus: 'gpt-5.3-codex',
|
opus: 'gpt-5.3-codex',
|
||||||
sonnet: 'gpt-5.3-codex',
|
sonnet: 'gpt-5.3-codex',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-5.4',
|
id: 'gpt-5.2',
|
||||||
name: 'GPT-5.4',
|
name: 'GPT-5.2',
|
||||||
tier: 'paid',
|
description: 'Previous general-purpose Codex model',
|
||||||
description: 'Paid Codex plans only, latest GPT-5 family model',
|
|
||||||
codexMaxEffort: 'xhigh',
|
codexMaxEffort: 'xhigh',
|
||||||
presetMapping: {
|
presetMapping: {
|
||||||
default: 'gpt-5.4',
|
default: 'gpt-5.2',
|
||||||
opus: 'gpt-5.4',
|
opus: 'gpt-5.2',
|
||||||
sonnet: 'gpt-5.4',
|
sonnet: 'gpt-5.2',
|
||||||
haiku: 'gpt-5-codex-mini',
|
haiku: 'gpt-5.4-mini',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe('ModelConfigSection presets', () => {
|
|||||||
ANTHROPIC_MODEL: 'gpt-5.4',
|
ANTHROPIC_MODEL: 'gpt-5.4',
|
||||||
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gpt-5.4',
|
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gpt-5.4',
|
||||||
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4',
|
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4',
|
||||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gpt-5-codex-mini',
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gpt-5.4-mini',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,27 @@ describe('FlexibleModelSelector', () => {
|
|||||||
expect(screen.getAllByText('gpt-5.3-codex-high').length).toBeGreaterThan(0);
|
expect(screen.getAllByText('gpt-5.3-codex-high').length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('preserves saved legacy codex model IDs under the current-value fallback', async () => {
|
||||||
|
render(
|
||||||
|
<FlexibleModelSelector
|
||||||
|
label="Primary model"
|
||||||
|
value="gpt-5-codex"
|
||||||
|
onChange={vi.fn()}
|
||||||
|
catalog={MODEL_CATALOGS.codex}
|
||||||
|
allModels={[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: /gpt-5-codex/i })).toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: /gpt-5-codex/i }));
|
||||||
|
|
||||||
|
expect(screen.getByText('Current value')).toBeInTheDocument();
|
||||||
|
expect(screen.getAllByText('gpt-5-codex').length).toBeGreaterThan(0);
|
||||||
|
expect(screen.getByText('gpt-5.4')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('gpt-5.4-mini')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('preserves explicit suffixes on supplemental codex models outside the static catalog', async () => {
|
it('preserves explicit suffixes on supplemental codex models outside the static catalog', async () => {
|
||||||
const onChange = vi.fn();
|
const onChange = vi.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,22 @@ import { describe, expect, it } from 'vitest';
|
|||||||
import { MODEL_CATALOGS } from '@/lib/model-catalogs';
|
import { MODEL_CATALOGS } from '@/lib/model-catalogs';
|
||||||
|
|
||||||
describe('codex model catalog defaults', () => {
|
describe('codex model catalog defaults', () => {
|
||||||
it('uses gpt-5-codex-mini as the haiku mapping for cross-plan codex presets', () => {
|
it('mirrors the current Codex runtime catalog and free-safe defaults', () => {
|
||||||
const codexCatalog = MODEL_CATALOGS.codex;
|
const codexCatalog = MODEL_CATALOGS.codex;
|
||||||
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-codex');
|
const codex52 = codexCatalog.models.find((model) => model.id === 'gpt-5.2');
|
||||||
const codexMini = codexCatalog.models.find((model) => model.id === 'gpt-5-codex-mini');
|
const codex54 = codexCatalog.models.find((model) => model.id === 'gpt-5.4');
|
||||||
|
const codexMini = codexCatalog.models.find((model) => model.id === 'gpt-5.4-mini');
|
||||||
|
|
||||||
expect(codex55?.presetMapping?.haiku).toBe('gpt-5-codex-mini');
|
expect(codexCatalog.defaultModel).toBe('gpt-5.4');
|
||||||
|
expect(codex55?.tier).toBe('paid');
|
||||||
|
expect(codex55?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
expect(codex55?.codexMaxEffort).toBe('xhigh');
|
expect(codex55?.codexMaxEffort).toBe('xhigh');
|
||||||
expect(codex53?.presetMapping?.haiku).toBe('gpt-5-codex-mini');
|
expect(codex54?.tier).toBeUndefined();
|
||||||
expect(codex52?.presetMapping?.haiku).toBe('gpt-5-codex-mini');
|
expect(codex54?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
|
expect(codex53?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
|
expect(codex52?.presetMapping?.haiku).toBe('gpt-5.4-mini');
|
||||||
expect(codex53?.codexMaxEffort).toBe('xhigh');
|
expect(codex53?.codexMaxEffort).toBe('xhigh');
|
||||||
expect(codexMini?.codexMaxEffort).toBe('high');
|
expect(codexMini?.codexMaxEffort).toBe('high');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user