diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 9b6ec8d28d6..750f45ed0fc 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -7538,7 +7538,7 @@ class StandardLoggingPayloadSetup: return None user_agent_tags: list[str] | None = None headers: Final = proxy_server_request.get("headers", {}) - if headers is not None and isinstance(headers, dict): + if headers is not None and isinstance(headers, Mapping): request_headers: Final = cast( # cast-ok: request headers are untyped framework data dict[str, str | None], headers ) @@ -7565,7 +7565,7 @@ class StandardLoggingPayloadSetup: return None headers: Final = proxy_server_request.get("headers", {}) - if not isinstance(headers, dict): + if not isinstance(headers, Mapping): return None request_headers: Final = cast( # cast-ok: request headers are untyped framework data dict[str, str], headers diff --git a/tests/unit/litellm_core_utils/test_litellm_logging.py b/tests/unit/litellm_core_utils/test_litellm_logging.py index e8ea0c04f6f..311826c1f61 100644 --- a/tests/unit/litellm_core_utils/test_litellm_logging.py +++ b/tests/unit/litellm_core_utils/test_litellm_logging.py @@ -3989,6 +3989,46 @@ def test_get_request_tags_includes_extra_header_tag_with_user_agent_tags( ) == ["User-Agent: litellm", "User-Agent: litellm/1.0", "X-Org-Id: org_1"] +def test_header_derived_spend_tags_accept_starlette_headers_mapping(monkeypatch): + """Pass-through stores Starlette Headers (a Mapping, not dict); tags must still extract. + + Regression for https://github.com/BerriAI/litellm/issues/44032 + """ + import litellm + from starlette.datastructures import Headers + + from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup + + monkeypatch.setattr(litellm, "disable_add_user_agent_to_request_tags", False) + monkeypatch.setattr( + litellm, "extra_spend_tag_headers", ["x-custom-header"], raising=False + ) + proxy_server_request = { + "headers": Headers( + { + "user-agent": "curl/8.0.0", + "x-custom-header": "tenant-a", + } + ) + } + + ua_tags = StandardLoggingPayloadSetup._get_user_agent_tags(proxy_server_request) + assert ua_tags is not None + assert "User-Agent: curl" in ua_tags + assert "User-Agent: curl/8.0.0" in ua_tags + + extra_tags = StandardLoggingPayloadSetup._get_extra_header_tags(proxy_server_request) + assert extra_tags is not None + assert "x-custom-header: tenant-a" in extra_tags + + request_tags = StandardLoggingPayloadSetup.get_request_tags( + litellm_params={}, + proxy_server_request=proxy_server_request, + ) + assert "User-Agent: curl" in request_tags + assert "x-custom-header: tenant-a" in request_tags + + def test_response_cost_calculator_with_response_cost_in_hidden_params(logging_obj): from litellm import Router