mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-03 20:13:02 +00:00
feat(cliproxy): expose model context windows
This commit is contained in:
1 parent
236471100c
commit
b0732613a3
5 files changed
+103
-1
No files matched your search
@@ -38,6 +38,7 @@ import {
|
||||
} from '../../config/config-loader-facade';
|
||||
import { buildCliproxyProviderPath, buildLocalProviderBaseUrl } from './provider-route';
|
||||
import { ConfigError } from '../../errors/error-types';
|
||||
import { findModel } from '../model-catalog';
|
||||
|
||||
export { buildCliproxyProviderPath, buildLocalProviderBaseUrl } from './provider-route';
|
||||
|
||||
@@ -68,11 +69,38 @@ const CURSOR_LEGACY_ENV_OVERRIDE_KEYS = new Set([
|
||||
'ANTHROPIC_AUTH_TOKEN',
|
||||
'ANTHROPIC_API_KEY',
|
||||
]);
|
||||
const CLAUDE_AUTO_COMPACT_WINDOW_ENV = 'CLAUDE_CODE_AUTO_COMPACT_WINDOW';
|
||||
|
||||
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Claude Code's auto-compact window for a known effective model.
|
||||
* Existing env keys win by presence, including an explicit empty value.
|
||||
*/
|
||||
export function applyClaudeAutoCompactWindow(
|
||||
envVars: NodeJS.ProcessEnv,
|
||||
provider: CLIProxyProvider
|
||||
): NodeJS.ProcessEnv {
|
||||
if (Object.prototype.hasOwnProperty.call(envVars, CLAUDE_AUTO_COMPACT_WINDOW_ENV)) {
|
||||
return envVars;
|
||||
}
|
||||
|
||||
const modelId = envVars.ANTHROPIC_MODEL;
|
||||
if (typeof modelId !== 'string' || modelId.trim().length === 0) return envVars;
|
||||
|
||||
const contextWindow = findModel(provider, modelId)?.contextWindow;
|
||||
if (typeof contextWindow !== 'number' || !Number.isInteger(contextWindow) || contextWindow <= 0) {
|
||||
return envVars;
|
||||
}
|
||||
|
||||
return {
|
||||
...envVars,
|
||||
[CLAUDE_AUTO_COMPACT_WINDOW_ENV]: String(contextWindow),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate deprecated gemini-claude-* model names to upstream claude-* names in a settings file.
|
||||
* CLIProxyAPI registry no longer recognizes the gemini-claude-* prefix convention.
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
getRemoteEnvVars,
|
||||
getCompositeEnvVars,
|
||||
applyThinkingConfig,
|
||||
applyClaudeAutoCompactWindow,
|
||||
} from '../config/config-generator';
|
||||
import { applyExtendedContextConfig } from '../config/extended-context-config';
|
||||
import { CLIProxyProvider } from '../types';
|
||||
@@ -414,8 +415,17 @@ export function buildClaudeEnvironment(config: ProxyChainConfig): Record<string,
|
||||
CCS_PROFILE_TYPE: 'cliproxy', // Signal to WebSearch hook this is a third-party provider
|
||||
};
|
||||
|
||||
const effectiveProvider =
|
||||
isComposite && compositeTiers && compositeDefaultTier
|
||||
? compositeTiers[compositeDefaultTier].provider
|
||||
: provider;
|
||||
const autoCompactEnv = applyClaudeAutoCompactWindow(
|
||||
stripClaudeCodeEnv(mergedEnv),
|
||||
effectiveProvider
|
||||
);
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(stripClaudeCodeEnv(mergedEnv)).filter(([, v]) => v !== undefined)
|
||||
Object.entries(autoCompactEnv).filter(([, v]) => v !== undefined)
|
||||
) as Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ export interface ModelEntry {
|
||||
tier?: 'free' | 'pro' | 'ultra';
|
||||
/** Optional description for the model */
|
||||
description?: string;
|
||||
/** Total context window in tokens when authoritatively known */
|
||||
contextWindow?: number;
|
||||
/** Model has known issues - show warning when selected */
|
||||
broken?: boolean;
|
||||
/** Issue URL for broken models */
|
||||
@@ -95,6 +97,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-opus-4-6-thinking',
|
||||
name: 'Claude Opus 4.6 Thinking',
|
||||
description: 'Latest flagship, extended thinking',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -111,6 +114,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-sonnet-4-6',
|
||||
name: 'Claude Sonnet 4.6',
|
||||
description: 'Latest Sonnet with thinking budget support',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -194,6 +198,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'gpt-5.6-sol',
|
||||
name: 'GPT-5.6 Sol',
|
||||
description: 'Latest frontier agentic coding model.',
|
||||
contextWindow: 372000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -206,6 +211,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'gpt-5.6-terra',
|
||||
name: 'GPT-5.6 Terra',
|
||||
description: 'Balanced agentic coding model for everyday work.',
|
||||
contextWindow: 372000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -218,6 +224,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'gpt-5.6-luna',
|
||||
name: 'GPT-5.6 Luna',
|
||||
description: 'Fast and affordable agentic coding model.',
|
||||
contextWindow: 372000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -232,6 +239,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
tier: 'pro',
|
||||
description:
|
||||
'Newest Codex-released GPT-5 family model; falls back to GPT-5.4 on free plans',
|
||||
contextWindow: 272000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -244,6 +252,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'gpt-5.4',
|
||||
name: 'GPT-5.4',
|
||||
description: 'Recommended Codex default for most coding and agentic tasks',
|
||||
contextWindow: 1050000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -256,6 +265,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'gpt-5.4-mini',
|
||||
name: 'GPT-5.4 Mini',
|
||||
description: 'Fast, lower-cost Codex option for lighter tasks and haiku-tier routing',
|
||||
contextWindow: 400000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high'],
|
||||
@@ -281,6 +291,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
tier: 'pro',
|
||||
description:
|
||||
'Research preview model for ChatGPT Pro subscribers, optimized for near-instant coding iteration',
|
||||
contextWindow: 128000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh'],
|
||||
@@ -310,11 +321,13 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'grok-build-0.1',
|
||||
name: 'Grok Build 0.1',
|
||||
description: 'Fast coding model for agentic software engineering workflows',
|
||||
contextWindow: 256000,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.5',
|
||||
name: 'Grok 4.5',
|
||||
description: 'Frontier model for coding, engineering, and agentic workflows',
|
||||
contextWindow: 500000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high'],
|
||||
@@ -325,6 +338,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'grok-4.3',
|
||||
name: 'Grok 4.3',
|
||||
description: 'General-purpose Grok model with a one-million-token context window',
|
||||
contextWindow: 1000000,
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['none', 'low', 'medium', 'high'],
|
||||
@@ -335,34 +349,40 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'grok-4.20-0309-reasoning',
|
||||
name: 'Grok 4.20 0309 Reasoning',
|
||||
description: 'Reasoning model with a two-million-token context window',
|
||||
contextWindow: 2000000,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-0309-non-reasoning',
|
||||
name: 'Grok 4.20 0309 Non Reasoning',
|
||||
description: 'Non-reasoning model with a two-million-token context window',
|
||||
contextWindow: 2000000,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-multi-agent-0309',
|
||||
name: 'Grok 4.20 Multi Agent 0309',
|
||||
description: 'Multi-agent model with a two-million-token context window',
|
||||
contextWindow: 2000000,
|
||||
thinking: { type: 'levels', levels: ['low', 'medium', 'high'] },
|
||||
},
|
||||
{
|
||||
id: 'grok-3-mini',
|
||||
name: 'Grok 3 Mini',
|
||||
description: 'Compact reasoning model',
|
||||
contextWindow: 131072,
|
||||
thinking: { type: 'levels', levels: ['low', 'medium', 'high'] },
|
||||
},
|
||||
{
|
||||
id: 'grok-3-mini-fast',
|
||||
name: 'Grok 3 Mini Fast',
|
||||
description: 'Faster compact reasoning model',
|
||||
contextWindow: 131072,
|
||||
thinking: { type: 'levels', levels: ['low', 'medium', 'high'] },
|
||||
},
|
||||
{
|
||||
id: 'grok-composer-2.5-fast',
|
||||
name: 'Grok Composer 2.5 Fast',
|
||||
description: 'Fast Composer model for lightweight coding tasks',
|
||||
contextWindow: 200000,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -375,56 +395,67 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'qoder/auto',
|
||||
name: 'Qoder Auto',
|
||||
description: 'Auto selects the best Qoder model for your prompt',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/ultimate',
|
||||
name: 'Qoder Ultimate',
|
||||
description: 'Highest quality Qoder tier',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/performance',
|
||||
name: 'Qoder Performance',
|
||||
description: 'Balanced quality and speed',
|
||||
contextWindow: 272000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/efficient',
|
||||
name: 'Qoder Efficient',
|
||||
description: 'Cost-efficient Qoder tier',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/lite',
|
||||
name: 'Qoder Lite',
|
||||
description: 'Fastest and most affordable Qoder tier',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/qmodel',
|
||||
name: 'Qwen 3.6 Plus (via Qoder)',
|
||||
description: 'Qwen 3.6 Plus frontier model',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/dmodel',
|
||||
name: 'DeepSeek V4 Pro (via Qoder)',
|
||||
description: 'DeepSeek V4 Pro frontier model',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/dfmodel',
|
||||
name: 'DeepSeek V4 Flash (via Qoder)',
|
||||
description: 'DeepSeek V4 Flash frontier model',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/gm51model',
|
||||
name: 'GLM 5.1 (via Qoder)',
|
||||
description: 'GLM 5.1 frontier model',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/kmodel',
|
||||
name: 'Kimi K2.6 (via Qoder)',
|
||||
description: 'Kimi K2.6 frontier model',
|
||||
contextWindow: 256000,
|
||||
},
|
||||
{
|
||||
id: 'qoder/mmodel',
|
||||
name: 'MiniMax M2.7 (via Qoder)',
|
||||
description: 'MiniMax M2.7 frontier model',
|
||||
contextWindow: 180000,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -437,6 +468,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'kimi-k2.5',
|
||||
name: 'Kimi K2.5',
|
||||
description: 'Latest multimodal model (262K context)',
|
||||
contextWindow: 262144,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -450,6 +482,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'kimi-k2-thinking',
|
||||
name: 'Kimi K2 Thinking',
|
||||
description: 'Extended reasoning model',
|
||||
contextWindow: 131072,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
min: 1024,
|
||||
@@ -462,6 +495,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'kimi-k2',
|
||||
name: 'Kimi K2',
|
||||
description: 'Flagship coding model',
|
||||
contextWindow: 131072,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -474,6 +508,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-sonnet-5',
|
||||
name: 'Claude Sonnet 5',
|
||||
description: 'Latest Sonnet model',
|
||||
contextWindow: 1000000,
|
||||
nativeImageInput: true,
|
||||
// Sonnet 5 uses adaptive thinking; manual budget_tokens is rejected with 400.
|
||||
thinking: {
|
||||
@@ -488,6 +523,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-fable-5',
|
||||
name: 'Claude Fable 5',
|
||||
description: 'Most powerful model',
|
||||
contextWindow: 1000000,
|
||||
nativeImageInput: true,
|
||||
// New tier above Opus. Same adaptive-thinking surface as Opus 4.8:
|
||||
// Anthropic accepts only effort levels; manual budget_tokens is rejected with 400.
|
||||
@@ -520,6 +556,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-opus-4-8',
|
||||
name: 'Claude Opus 4.8',
|
||||
description: 'Latest flagship model',
|
||||
contextWindow: 1000000,
|
||||
nativeImageInput: true,
|
||||
// Mirrors 4.7: Anthropic accepts only adaptive thinking levels on the
|
||||
// current Opus generation; manual budget_tokens is rejected with 400.
|
||||
@@ -535,6 +572,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-opus-4-7',
|
||||
name: 'Claude Opus 4.7',
|
||||
description: 'Previous flagship model',
|
||||
contextWindow: 1000000,
|
||||
nativeImageInput: true,
|
||||
// Opus 4.7 only supports adaptive thinking on the Anthropic API; manual
|
||||
// thinking.type: "enabled" with budget_tokens is rejected with 400.
|
||||
@@ -552,6 +590,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-opus-4-6',
|
||||
name: 'Claude Opus 4.6',
|
||||
description: 'Older flagship model',
|
||||
contextWindow: 1000000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -566,6 +605,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-sonnet-4-6',
|
||||
name: 'Claude Sonnet 4.6',
|
||||
description: 'Balanced performance and speed',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -580,6 +620,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-opus-4-5-20251101',
|
||||
name: 'Claude Opus 4.5',
|
||||
description: 'Most capable Claude model',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -594,6 +635,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-sonnet-4-5-20250929',
|
||||
name: 'Claude Sonnet 4.5',
|
||||
description: 'Balanced performance and speed',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -608,6 +650,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-sonnet-4-20250514',
|
||||
name: 'Claude Sonnet 4',
|
||||
description: 'Previous generation Sonnet',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: {
|
||||
type: 'budget',
|
||||
@@ -622,6 +665,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
id: 'claude-haiku-4-5-20251001',
|
||||
name: 'Claude Haiku 4.5',
|
||||
description: 'Fast and efficient',
|
||||
contextWindow: 200000,
|
||||
nativeImageInput: true,
|
||||
thinking: { type: 'none' },
|
||||
},
|
||||
@@ -675,6 +719,16 @@ export function findModel(provider: CLIProxyProvider, modelId: string): ModelEnt
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const lookupCandidates = new Set([normalizedId, providerNormalizedId]);
|
||||
if (provider === 'codex') {
|
||||
for (const candidate of [...lookupCandidates]) {
|
||||
const tuningMatch = candidate.match(
|
||||
/^(.*?)(?:-(?:minimal|low|medium|high|xhigh)(?:-fast)?|-fast(?:-(?:minimal|low|medium|high|xhigh))?)$/i
|
||||
);
|
||||
if (tuningMatch?.[1]) {
|
||||
lookupCandidates.add(tuningMatch[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isAntigravityProvider(provider)) {
|
||||
const migratedRaw = migrateDeniedAntigravityModelAliases(normalizedId).trim().toLowerCase();
|
||||
const migratedProvider = migrateDeniedAntigravityModelAliases(providerNormalizedId)
|
||||
|
||||
@@ -221,6 +221,13 @@ function mapRemoteToModelEntry(provider: CLIProxyProvider, remote: RemoteModelIn
|
||||
name: remote.display_name || remote.id,
|
||||
};
|
||||
if (remote.description) entry.description = remote.description;
|
||||
if (
|
||||
typeof remote.context_length === 'number' &&
|
||||
Number.isInteger(remote.context_length) &&
|
||||
remote.context_length > 0
|
||||
) {
|
||||
entry.contextWindow = remote.context_length;
|
||||
}
|
||||
// xAI context length is inherent to the model ID; its API does not accept
|
||||
// Claude's [1m] model suffix.
|
||||
if (provider !== 'xai' && remote.context_length && remote.context_length >= 1_000_000) {
|
||||
@@ -286,6 +293,7 @@ export function mergeCatalog(
|
||||
issueUrl: staticEntry.issueUrl,
|
||||
deprecated: staticEntry.deprecated,
|
||||
deprecationReason: staticEntry.deprecationReason,
|
||||
contextWindow: remoteEntry.contextWindow ?? staticEntry.contextWindow,
|
||||
});
|
||||
} else {
|
||||
mergedModels.push(remoteEntry);
|
||||
|
||||
@@ -181,6 +181,7 @@ interface CatalogJsonModel {
|
||||
name: string;
|
||||
tier?: 'free' | 'pro' | 'ultra';
|
||||
description?: string;
|
||||
contextWindow?: number;
|
||||
deprecated?: boolean;
|
||||
deprecationReason?: string;
|
||||
broken?: boolean;
|
||||
@@ -206,6 +207,7 @@ export function handleCatalogJson(): void {
|
||||
const entry: CatalogJsonModel = { id: m.id, name: m.name };
|
||||
if (m.tier !== undefined) entry.tier = m.tier;
|
||||
if (m.description !== undefined) entry.description = m.description;
|
||||
if (m.contextWindow !== undefined) entry.contextWindow = m.contextWindow;
|
||||
if (m.deprecated !== undefined) entry.deprecated = m.deprecated;
|
||||
if (m.deprecationReason !== undefined) entry.deprecationReason = m.deprecationReason;
|
||||
if (m.broken !== undefined) entry.broken = m.broken;
|
||||
|
||||
Reference in new issue
Block a user