diff --git a/litellm/utils.py b/litellm/utils.py index a231644addf..97df6b25c96 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -11079,9 +11079,7 @@ def get_requester_metadata(metadata: Mapping[str, object]): requester_metadata: Final = metadata.get("requester_metadata") if isinstance(requester_metadata, dict): - cleaned_metadata = add_openai_metadata(requester_metadata) - if cleaned_metadata: - return cleaned_metadata + return add_openai_metadata(requester_metadata) or None cleaned_metadata = add_openai_metadata(metadata) if cleaned_metadata: diff --git a/tests/unit/proxy/test_litellm_pre_call_utils.py b/tests/unit/proxy/test_litellm_pre_call_utils.py index 81a196af676..c79f8dd6749 100644 --- a/tests/unit/proxy/test_litellm_pre_call_utils.py +++ b/tests/unit/proxy/test_litellm_pre_call_utils.py @@ -62,6 +62,7 @@ from litellm.proxy.litellm_pre_call_utils import ( from litellm.proxy.spend_tracking.spend_tracking_utils import get_logging_payload from litellm.types.litellm_params import AGENTIC_LOOP_KWARG_NAMES from litellm.types.utils import CredentialItem +from litellm.utils import get_requester_metadata def test_check_if_token_is_service_account(): @@ -276,6 +277,43 @@ async def test_add_litellm_data_to_request_keeps_requester_ip_address_for_guardr assert updated_data["metadata"]["requester_ip_address"] == "203.0.113.9" +@pytest.mark.asyncio +@pytest.mark.parametrize( + "data, expected", + ( + ({"model": "gpt-4o"}, None), + ({"model": "gpt-4o", "metadata": {"team": "search"}}, {"team": "search"}), + ), +) +async def test_proxy_forwards_only_caller_metadata_to_openai( + monkeypatch: pytest.MonkeyPatch, data: dict[str, object], expected: dict[str, str] | None +) -> None: + from litellm.proxy import proxy_server + + settings: Final = {} + monkeypatch.setattr(proxy_server, "general_settings", settings) + request_mock: Final = MagicMock(spec=Request) + request_mock.url = MagicMock() + request_mock.url.path = "/v1/chat/completions" + request_mock.url.__str__.return_value = "http://localhost/v1/chat/completions" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = {"Content-Type": "application/json", "user-agent": "test-agent"} + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + + updated_data: Final = await add_litellm_data_to_request( + data=data, + request=request_mock, + user_api_key_dict=UserAPIKeyAuth(api_key="hashed-key", user_id="u1"), + proxy_config=MagicMock(), + general_settings=settings, + version="test-version", + ) + + assert get_requester_metadata(updated_data["metadata"]) == expected, f"metadata={updated_data['metadata']!r}" + + @pytest.mark.asyncio async def test_key_otel_service_name_outranks_team_metadata_merge(): from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request diff --git a/tests/unit/test_utils_get_optional_params.py b/tests/unit/test_utils_get_optional_params.py index 13a64577efb..3881cda8931 100644 --- a/tests/unit/test_utils_get_optional_params.py +++ b/tests/unit/test_utils_get_optional_params.py @@ -57,6 +57,20 @@ def test_get_requester_metadata_returns_none_for_empty(): assert get_requester_metadata(metadata) is None +@pytest.mark.parametrize( + ("metadata", "expected"), + [ + ({"user_api_key_hash": "h", "requester_metadata": {"headers": {"host": "proxy"}}}, None), + ({"user_api_key_hash": "h", "requester_metadata": {"team": "a"}}, {"team": "a"}), + ({"team": "a"}, {"team": "a"}), + ], +) +def test_get_requester_metadata_forwards_only_caller_metadata( + metadata: dict[str, object], expected: dict[str, str] | None +) -> None: + assert get_requester_metadata(metadata) == expected + + @patch("litellm.main.openai_chat_completions.completion") def test_requester_metadata_forwarded_to_openai(mock_completion): mock_completion.return_value = MagicMock()