fix: custom OPENAI_BASE_URL always wins over Codex model alias detection (#222)
* feat: add --provider CLI flag for multi-provider support Adds a --provider flag that maps friendly provider names to the environment variables the codebase uses for provider detection. No more manual env-var configuration — users can now simply run: openclaude --provider openai --model gpt-4o openclaude --provider gemini --model gemini-2.0-flash openclaude --provider ollama --model llama3.2 openclaude --provider bedrock openclaude --provider vertex Implementation details: - providerFlag.ts: core logic — maps provider names to env vars, uses ??= so explicit env vars always win over the flag defaults - providerFlag.test.ts: 18 tests covering all 7 providers, error messages, model passthrough, and env-var precedence - cli.tsx: early fast-path (mirrors --bare pattern) — sets env vars before Commander option-building and module constants run - main.tsx: adds --provider to Commander option chain for --help Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: custom OPENAI_BASE_URL always wins over Codex model alias detection When OPENAI_MODEL=gpt-5.4 (or gpt-5.4-mini) and a custom OPENAI_BASE_URL is set (Azure, OpenRouter, etc), the transport was incorrectly forced to codex_responses because gpt-5.4 is in CODEX_ALIAS_MODELS. This caused requests to be sent with Codex auth instead of the user's API key, resulting in 401 Unauthorized errors. Fix: only use codex_responses when the base URL is explicitly the Codex endpoint, OR when no custom base URL is set and the model is a Codex alias. An explicit OPENAI_BASE_URL always takes priority over model-name based Codex detection. Verified locally: gpt-5.4 via OpenRouter now correctly shows Provider=OpenRouter, Endpoint=https://openrouter.ai/api/v1 instead of routing to chatgpt.com/backend-api/codex. Fixes #200, #203 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
107
src/utils/providerFlag.ts
Normal file
107
src/utils/providerFlag.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* --provider CLI flag support.
|
||||
*
|
||||
* Maps the user-friendly provider name to the environment variables
|
||||
* that the rest of the codebase uses for provider detection.
|
||||
*
|
||||
* Usage:
|
||||
* openclaude --provider openai --model gpt-4o
|
||||
* openclaude --provider gemini --model gemini-2.0-flash
|
||||
* openclaude --provider ollama --model llama3.2
|
||||
* openclaude --provider anthropic (default, no-op)
|
||||
*/
|
||||
|
||||
export const VALID_PROVIDERS = [
|
||||
'anthropic',
|
||||
'openai',
|
||||
'gemini',
|
||||
'github',
|
||||
'bedrock',
|
||||
'vertex',
|
||||
'ollama',
|
||||
] as const
|
||||
|
||||
export type ProviderFlagName = (typeof VALID_PROVIDERS)[number]
|
||||
|
||||
/**
|
||||
* Extract the value of --provider from argv.
|
||||
* Returns null if the flag is absent or has no value.
|
||||
*/
|
||||
export function parseProviderFlag(args: string[]): string | null {
|
||||
const idx = args.indexOf('--provider')
|
||||
if (idx === -1) return null
|
||||
const value = args[idx + 1]
|
||||
if (!value || value.startsWith('--')) return null
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the value of --model from argv.
|
||||
* Returns null if absent.
|
||||
*/
|
||||
function parseModelFlag(args: string[]): string | null {
|
||||
const idx = args.indexOf('--model')
|
||||
if (idx === -1) return null
|
||||
const value = args[idx + 1]
|
||||
if (!value || value.startsWith('--')) return null
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a provider name to process.env.
|
||||
* Sets the required CLAUDE_CODE_USE_* flag and any provider-specific
|
||||
* defaults (Ollama base URL, model routing). Does NOT overwrite values
|
||||
* that are already set — explicit env vars always win.
|
||||
*
|
||||
* Returns { error } if the provider name is not recognized.
|
||||
*/
|
||||
export function applyProviderFlag(
|
||||
provider: string,
|
||||
args: string[],
|
||||
): { error?: string } {
|
||||
if (!(VALID_PROVIDERS as readonly string[]).includes(provider)) {
|
||||
return {
|
||||
error: `Unknown provider "${provider}". Valid providers: ${VALID_PROVIDERS.join(', ')}`,
|
||||
}
|
||||
}
|
||||
|
||||
const model = parseModelFlag(args)
|
||||
|
||||
switch (provider as ProviderFlagName) {
|
||||
case 'anthropic':
|
||||
// Default — no env vars needed
|
||||
break
|
||||
|
||||
case 'openai':
|
||||
process.env.CLAUDE_CODE_USE_OPENAI = '1'
|
||||
if (model) process.env.OPENAI_MODEL ??= model
|
||||
break
|
||||
|
||||
case 'gemini':
|
||||
process.env.CLAUDE_CODE_USE_GEMINI = '1'
|
||||
if (model) process.env.GEMINI_MODEL ??= model
|
||||
break
|
||||
|
||||
case 'github':
|
||||
process.env.CLAUDE_CODE_USE_GITHUB = '1'
|
||||
if (model) process.env.OPENAI_MODEL ??= model
|
||||
break
|
||||
|
||||
case 'bedrock':
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = '1'
|
||||
break
|
||||
|
||||
case 'vertex':
|
||||
process.env.CLAUDE_CODE_USE_VERTEX = '1'
|
||||
break
|
||||
|
||||
case 'ollama':
|
||||
process.env.CLAUDE_CODE_USE_OPENAI = '1'
|
||||
process.env.OPENAI_BASE_URL ??= 'http://localhost:11434/v1'
|
||||
process.env.OPENAI_API_KEY ??= 'ollama'
|
||||
if (model) process.env.OPENAI_MODEL ??= model
|
||||
break
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
Reference in New Issue
Block a user