diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 9ddc7ce2caf..6514d4e1e8c 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -457,12 +457,20 @@ def is_claude_code_user_agent(user_agent: str) -> bool: return user_agent.startswith("claude-cli/") -def should_auto_drop_params_for_claude_code(user_agent: str, data: dict, proxy_config: ProxyConfig) -> bool: - """drop_params defaults to on for Claude Code so its Anthropic-specific - params (e.g. thinking) don't fail requests routed to non-Anthropic - providers. An explicit drop_params from the caller or in the operator's - ``litellm_settings`` always wins over this default.""" - if not is_claude_code_user_agent(user_agent): +def is_codex_user_agent(user_agent: str) -> bool: + """Codex identifies itself as ``codex_cli_rs/ ...`` (TUI), + ``codex_exec/ ...`` (exec mode), or ``codex_vscode/ ...`` + (IDE extension); all share the ``codex_`` prefix.""" + return user_agent.startswith("codex_") + + +def should_auto_drop_params_for_agentic_cli(user_agent: str, data: dict, proxy_config: ProxyConfig) -> bool: + """drop_params defaults to on for agentic CLIs so their client-specific + params (e.g. Claude Code's thinking, Codex's service_tier) don't fail + requests routed to providers that reject them. An explicit drop_params + from the caller or in the operator's ``litellm_settings`` always wins + over this default.""" + if not (is_claude_code_user_agent(user_agent) or is_codex_user_agent(user_agent)): return False if "drop_params" in data: return False @@ -1687,7 +1695,7 @@ async def add_litellm_data_to_request( user_agent = request.headers["user-agent"] data[_metadata_variable_name]["user_agent"] = user_agent - if should_auto_drop_params_for_claude_code(user_agent, data, proxy_config): + if should_auto_drop_params_for_agentic_cli(user_agent, data, proxy_config): data["drop_params"] = True # Merge caller-supplied tags (x-litellm-tags header, data["tags"] root-level) diff --git a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py index 47879ee96ad..8bee7e9f33b 100644 --- a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py +++ b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py @@ -5094,17 +5094,23 @@ def _make_request_mock(path: str, headers: dict) -> MagicMock: ("claude-cli/2.0.69 (external, cli)", False, None, False), ("claude-cli/2.0.69 (external, cli)", None, False, None), ("claude-cli/2.0.69 (external, cli)", None, True, None), + ("codex_cli_rs/0.144.5 (Mac OS 26.4.0; arm64) WezTerm", None, None, True), + ("codex_exec/0.144.5 (Mac OS 26.4.0; arm64) WarpTerminal (codex_exec; 0.144.5)", None, None, True), + ("codex_vscode/0.144.5 (Mac OS 26.4.0; arm64) vscode/1.104.1", None, None, True), + ("codex_exec/0.144.5 (Mac OS 26.4.0; arm64)", False, None, False), + ("codex_exec/0.144.5 (Mac OS 26.4.0; arm64)", None, True, None), ("PostmanRuntime/7.53.0", None, None, None), (None, None, None, None), ], ) -async def test_add_litellm_data_to_request_claude_code_drop_params( +async def test_add_litellm_data_to_request_agentic_cli_drop_params( user_agent, request_drop_params, operator_drop_params, expected_drop_params ): - """Claude Code sends Anthropic-specific params that fail on non-Anthropic - providers, so its user agent must turn on drop_params automatically, - without overriding an explicit caller value, an explicit operator-level - litellm_settings value, or affecting other clients. + """Claude Code sends Anthropic-specific params and Codex sends + service_tier, both of which fail on providers that reject them, so those + user agents must turn on drop_params automatically, without overriding an + explicit caller value, an explicit operator-level litellm_settings value, + or affecting other clients. """ headers = {"Content-Type": "application/json"} if user_agent is not None: