From ed22c1aa1ec9d3c3bf6e161b31326e603e299620 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 12 Feb 2026 04:24:50 +0700 Subject: [PATCH] feat(cliproxy): add per-tier thinking config for composite variants - Extend applyThinkingConfig() with compositeTierThinking parameter - Priority chain: CLI --thinking > per-tier config > global config > defaults - thinking: 'off' skips suffix for that tier - Wire compositeTierThinking extraction in buildClaudeEnvironment() - Add applyFallback() helper for tier-level env var modification --- src/cliproxy/config/thinking-config.ts | 40 +++++++++++++++++++++++--- src/cliproxy/executor/env-resolver.ts | 35 +++++++++++++++++++++- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/cliproxy/config/thinking-config.ts b/src/cliproxy/config/thinking-config.ts index 398e9dc5..c7e1ad02 100644 --- a/src/cliproxy/config/thinking-config.ts +++ b/src/cliproxy/config/thinking-config.ts @@ -74,12 +74,18 @@ export function getThinkingValueForTier( * @param envVars - Environment variables to modify * @param provider - CLIProxy provider * @param thinkingOverride - Optional CLI override (takes priority over config) + * @param compositeTierThinking - Optional per-tier thinking overrides for composite variants * @returns Modified env vars with thinking suffixes applied */ export function applyThinkingConfig( envVars: NodeJS.ProcessEnv, provider: CLIProxyProvider, - thinkingOverride?: string | number + thinkingOverride?: string | number, + compositeTierThinking?: { + opus?: string; + sonnet?: string; + haiku?: string; + } ): NodeJS.ProcessEnv { const thinkingConfig = getThinkingConfig(); const result = { ...envVars }; @@ -115,7 +121,13 @@ export function applyThinkingConfig( } else if (thinkingConfig.mode === 'auto') { // Auto mode: detect tier and apply default const tier = detectTierFromModel(baseModel); - thinkingValue = getThinkingValueForTier(tier, provider, thinkingConfig); + // Check per-tier config first if composite + const perTierValue = compositeTierThinking?.[tier]; + if (perTierValue !== undefined) { + thinkingValue = perTierValue; + } else { + thinkingValue = getThinkingValueForTier(tier, provider, thinkingConfig); + } } else { return result; // No thinking to apply } @@ -153,8 +165,28 @@ export function applyThinkingConfig( : tierVar.includes('SONNET') ? 'sonnet' : 'haiku'; - const tierThinkingValue = - thinkingOverride ?? getThinkingValueForTier(tier, provider, thinkingConfig); + + // Priority chain: CLI --thinking > per-tier config > global config > defaults + let tierThinkingValue: string | number; + if (thinkingOverride !== undefined) { + // CLI override takes priority + tierThinkingValue = thinkingOverride; + } else { + const perTierValue = compositeTierThinking?.[tier]; + if (perTierValue !== undefined) { + // Per-tier config from composite variant + tierThinkingValue = perTierValue; + } else { + // Global config or defaults + tierThinkingValue = getThinkingValueForTier(tier, provider, thinkingConfig); + } + } + + // If per-tier thinking is 'off', skip this tier + if (tierThinkingValue === 'off') { + continue; + } + result[tierVar] = applyThinkingSuffix(model, tierThinkingValue); } } diff --git a/src/cliproxy/executor/env-resolver.ts b/src/cliproxy/executor/env-resolver.ts index bb452cd2..1af44fa7 100644 --- a/src/cliproxy/executor/env-resolver.ts +++ b/src/cliproxy/executor/env-resolver.ts @@ -132,8 +132,20 @@ export function buildClaudeEnvironment(config: ProxyChainConfig): Record 0) { + compositeTierThinking = tierThinking; + } + } + // Apply thinking configuration to model (auto tier-based or manual override) - applyThinkingConfig(envVars, provider, thinkingOverride); + applyThinkingConfig(envVars, provider, thinkingOverride, compositeTierThinking); // Apply extended context suffix for 1M token context window // Auto-enabled for Gemini, opt-in for Claude (--1m flag) @@ -203,3 +215,24 @@ export function logEnvironment( log(`Claude env: Global env applied (telemetry/reporting disabled)`); } } + +/** Apply fallback provider config to env vars for a failed tier */ +export function applyFallback( + env: Record, + failedTier: 'opus' | 'sonnet' | 'haiku', + fallback: { provider: string; model: string } +): Record { + const tierEnvMap = { + opus: 'ANTHROPIC_DEFAULT_OPUS_MODEL', + sonnet: 'ANTHROPIC_DEFAULT_SONNET_MODEL', + haiku: 'ANTHROPIC_DEFAULT_HAIKU_MODEL', + } as const; + const result = { ...env }; + const originalModel = result[tierEnvMap[failedTier]]; + result[tierEnvMap[failedTier]] = fallback.model; + // If failed tier is default tier, also update ANTHROPIC_MODEL + if (result.ANTHROPIC_MODEL === originalModel) { + result.ANTHROPIC_MODEL = fallback.model; + } + return result; +}