diff --git a/litellm/__init__.py b/litellm/__init__.py index c8df4394a06..9e83e7efd88 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -377,7 +377,9 @@ prompt_name_config_map: Dict[str, PromptSpec] = {} enable_preview_features: bool = False return_response_headers: bool = False # get response headers from LLM Api providers - example x-remaining-requests, enable_json_schema_validation: bool = False -enable_model_config_credential_overrides: bool = False +enable_model_config_credential_overrides: bool = ( + os.getenv("LITELLM_ENABLE_MODEL_CONFIG_CREDENTIAL_OVERRIDES", "false").lower() == "true" +) enable_key_alias_format_validation: bool = ( False # opt-in validation of key_alias format on /key/generate and /key/update ) diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 90bba82aa84..234fe1b55dc 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -2667,7 +2667,6 @@ def _apply_credential_overrides_from_model_config( 5. Team default override (defaultconfig) 6. Deployment default (no action needed) """ - # Feature flag gate — disabled by default, opt in with litellm.enable_model_config_credential_overrides = True if not litellm.enable_model_config_credential_overrides: return 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 0d4b9e8d21f..b6454533972 100644 --- a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py +++ b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py @@ -2,9 +2,13 @@ import asyncio import copy import json import os +import subprocess +import sys +import textwrap import time from datetime import datetime, timezone from types import SimpleNamespace +from typing import Final from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -5152,6 +5156,36 @@ def test_apply_overrides_feature_flag_disabled_by_default(): assert "api_key" not in data +@pytest.mark.parametrize( + "env_value, expected", + [("true", True), ("True", True), ("false", False), (None, False)], +) +def test_credential_overrides_flag_reads_env_var_at_import(env_value: str | None, expected: bool): + """LITELLM_ENABLE_MODEL_CONFIG_CREDENTIAL_OVERRIDES is read at import, so check it in a fresh interpreter.""" + base_env: Final = {k: v for k, v in os.environ.items() if k != "LITELLM_ENABLE_MODEL_CONFIG_CREDENTIAL_OVERRIDES"} + env: Final = ( + base_env if env_value is None else {**base_env, "LITELLM_ENABLE_MODEL_CONFIG_CREDENTIAL_OVERRIDES": env_value} + ) + result: Final = subprocess.run( + [ + sys.executable, + "-c", + textwrap.dedent( + """ + import litellm + print(litellm.enable_model_config_credential_overrides) + """ + ), + ], + capture_output=True, + text=True, + env=env, + timeout=180, + ) + assert result.returncode == 0, result.stderr + assert result.stdout.strip() == str(expected) + + def test_extract_credential_provider_hint_prefers_exact_match(): """Provider hint selects the correct provider in a multi-provider entry.""" entry = {