diff --git a/src/cliproxy/config/env-builder.ts b/src/cliproxy/config/env-builder.ts index f3d5dfae..11a74b2a 100644 --- a/src/cliproxy/config/env-builder.ts +++ b/src/cliproxy/config/env-builder.ts @@ -415,6 +415,14 @@ export function getRemoteEnvVars( return env; } +/** Remote config for composite variant (passed from env-resolver) */ +export interface CompositeRemoteConfig { + host: string; + port: number; + protocol: 'http' | 'https'; + authToken?: string; +} + /** * Get environment variables for composite variant. * Uses root URL (no /api/provider/ path) for model-based routing. @@ -422,14 +430,16 @@ export function getRemoteEnvVars( * * @param tiers Per-tier provider+model mappings * @param defaultTier Which tier ANTHROPIC_MODEL equals - * @param port Local CLIProxy port + * @param port Local CLIProxy port (ignored if remoteConfig provided) * @param customSettingsPath Optional path to user's custom settings file + * @param remoteConfig Optional remote proxy config (overrides localhost URL/auth) */ export function getCompositeEnvVars( tiers: { opus: CompositeTierConfig; sonnet: CompositeTierConfig; haiku: CompositeTierConfig }, defaultTier: 'opus' | 'sonnet' | 'haiku', port: number = CLIPROXY_DEFAULT_PORT, - customSettingsPath?: string + customSettingsPath?: string, + remoteConfig?: CompositeRemoteConfig ): Record { const globalEnv = getGlobalEnvVars(); @@ -473,12 +483,26 @@ export function getCompositeEnvVars( throw new Error(`Missing model for default tier '${defaultTier}'`); } + // Determine base URL and auth token based on remote vs local mode + const baseUrl = remoteConfig + ? (() => { + const normalizedProtocol = normalizeProtocol(remoteConfig.protocol); + const effectivePort = + validateRemotePort(remoteConfig.port) ?? getRemoteDefaultPort(normalizedProtocol); + const standardWebPort = normalizedProtocol === 'https' ? 443 : 80; + const portSuffix = effectivePort === standardWebPort ? '' : `:${effectivePort}`; + return `${normalizedProtocol}://${remoteConfig.host}${portSuffix}`; + })() + : `http://127.0.0.1:${validPort}`; + + const authToken = remoteConfig?.authToken ?? getEffectiveApiKey(); + const env: Record = { ...globalEnv, ...additionalEnvVars, // Root URL — CLIProxyAPI routes based on model name in request body - ANTHROPIC_BASE_URL: `http://127.0.0.1:${validPort}`, - ANTHROPIC_AUTH_TOKEN: getEffectiveApiKey(), + ANTHROPIC_BASE_URL: baseUrl, + ANTHROPIC_AUTH_TOKEN: authToken, ANTHROPIC_MODEL: defaultModel, }; diff --git a/src/cliproxy/config/thinking-config.ts b/src/cliproxy/config/thinking-config.ts index c7e1ad02..e0cda2eb 100644 --- a/src/cliproxy/config/thinking-config.ts +++ b/src/cliproxy/config/thinking-config.ts @@ -67,14 +67,22 @@ export function getThinkingValueForTier( return thinkingConfig.tier_defaults?.[tier] ?? DEFAULT_THINKING_TIER_DEFAULTS[tier]; } +/** + * Composite tier config for provider lookup (subset of full CompositeTierConfig) + */ +interface CompositeTierProvider { + provider?: CLIProxyProvider; +} + /** * Apply thinking configuration to env vars. * Modifies ANTHROPIC_MODEL and tier models with thinking suffixes. * * @param envVars - Environment variables to modify - * @param provider - CLIProxy provider + * @param provider - CLIProxy provider (default provider for base model) * @param thinkingOverride - Optional CLI override (takes priority over config) * @param compositeTierThinking - Optional per-tier thinking overrides for composite variants + * @param compositeTiers - Optional per-tier provider config for composite variants * @returns Modified env vars with thinking suffixes applied */ export function applyThinkingConfig( @@ -85,6 +93,11 @@ export function applyThinkingConfig( opus?: string; sonnet?: string; haiku?: string; + }, + compositeTiers?: { + opus?: CompositeTierProvider; + sonnet?: CompositeTierProvider; + haiku?: CompositeTierProvider; } ): NodeJS.ProcessEnv { const thinkingConfig = getThinkingConfig(); @@ -158,7 +171,7 @@ export function applyThinkingConfig( for (const tierVar of tierModels) { const model = result[tierVar]; - if (model && supportsThinking(provider, model)) { + if (model) { // Get tier-specific thinking value const tier = tierVar.includes('OPUS') ? 'opus' @@ -166,6 +179,15 @@ export function applyThinkingConfig( ? 'sonnet' : 'haiku'; + // P2 FIX: Use tier-specific provider from compositeTiers for mixed-provider composites + // Falls back to the default provider if not a composite or tier not specified + const tierProvider = compositeTiers?.[tier]?.provider ?? provider; + + // Check if this tier's model supports thinking (using tier-specific provider) + if (!supportsThinking(tierProvider, model)) { + continue; + } + // Priority chain: CLI --thinking > per-tier config > global config > defaults let tierThinkingValue: string | number; if (thinkingOverride !== undefined) { @@ -177,8 +199,8 @@ export function applyThinkingConfig( // Per-tier config from composite variant tierThinkingValue = perTierValue; } else { - // Global config or defaults - tierThinkingValue = getThinkingValueForTier(tier, provider, thinkingConfig); + // Global config or defaults (use tier-specific provider for provider overrides) + tierThinkingValue = getThinkingValueForTier(tier, tierProvider, thinkingConfig); } } diff --git a/src/cliproxy/executor/env-resolver.ts b/src/cliproxy/executor/env-resolver.ts index 40daf8f5..2f15749e 100644 --- a/src/cliproxy/executor/env-resolver.ts +++ b/src/cliproxy/executor/env-resolver.ts @@ -92,7 +92,13 @@ export function buildClaudeEnvironment(config: ProxyChainConfig): Record