diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 4d7e164136d..51d9d34c956 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -103,7 +103,9 @@ _CONFIG_CONNECTION_FIELDS: Final[frozenset[str]] = frozenset( def _config_base_for_health_check( - config_params: Mapping[str, object], request_params: Mapping[str, object] + config_params: Mapping[str, object], + request_params: Mapping[str, object], + allow_client_side_credentials: bool = False, ) -> dict[str, object]: """Return the configured parameters to merge under a connection-test request. @@ -112,7 +114,14 @@ def _config_base_for_health_check( to the endpoint the configuration names. Anything the request does not set still comes from the configuration, which is what lets a request name a configured model and test it as configured. + + ``general_settings.allow_client_side_credentials`` is the existing proxy-wide + opt-in for callers supplying their own connection parameters. Where an admin + has enabled it, a request may pair its own endpoint with the configured + credentials, as it could before. """ + if allow_client_side_credentials: + return dict(config_params) if not any(param in request_params for param in _BANNED_REQUEST_BODY_PARAMS): return dict(config_params) return {key: value for key, value in config_params.items() if key not in _CONFIG_CONNECTION_FIELDS} @@ -1830,7 +1839,12 @@ async def test_model_connection( from litellm.proxy.management_endpoints.model_management_endpoints import ( ModelManagementAuthChecks, ) - from litellm.proxy.proxy_server import llm_router, premium_user, prisma_client + from litellm.proxy.proxy_server import ( + general_settings, + llm_router, + premium_user, + prisma_client, + ) from litellm.types.router import Deployment, LiteLLM_Params try: @@ -1900,7 +1914,11 @@ async def test_model_connection( # Merge: config params (from proxy config) as base, request params override litellm_params = { - **_config_base_for_health_check(config_litellm_params, request_litellm_params), + **_config_base_for_health_check( + config_litellm_params, + request_litellm_params, + allow_client_side_credentials=general_settings.get("allow_client_side_credentials") is True, + ), **request_litellm_params, } diff --git a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py index 21a3e80de02..cef2e643194 100644 --- a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py +++ b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py @@ -2379,12 +2379,14 @@ class TestConfigBaseForHealthCheck: "rpm": 100, } - def _base(self, config, request): + def _base(self, config, request, allow_client_side_credentials=False): from litellm.proxy.health_endpoints._health_endpoints import ( _config_base_for_health_check, ) - return _config_base_for_health_check(config, request) + return _config_base_for_health_check( + config, request, allow_client_side_credentials=allow_client_side_credentials + ) def test_request_without_connection_fields_inherits_config(self): base = self._base(self.CONFIG, {"model": "openai/gpt-4o"}) @@ -2426,3 +2428,13 @@ class TestConfigBaseForHealthCheck: ) assert "api_key" not in base assert "aws_secret_access_key" not in base + + def test_opt_in_restores_configured_credentials_under_a_request_endpoint(self): + """With general_settings.allow_client_side_credentials enabled, a request + may pair its own endpoint with the configured credentials, as before.""" + base = self._base( + self.CONFIG, + {"api_base": "https://caller.example/v1"}, + allow_client_side_credentials=True, + ) + assert base["api_key"] == "sk-configured"