From 566a323e4706c072fa3013611f3eb7a1f27aca50 Mon Sep 17 00:00:00 2001 From: yucheng Date: Mon, 14 Sep 2026 19:41:15 +0000 Subject: [PATCH] fix(logging): redact raw callback request payloads Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/litellm_core_utils/litellm_logging.py | 26 ++++++++++--------- litellm/litellm_core_utils/redact_messages.py | 22 ++++++++++++++++ .../test_litellm_logging.py | 15 +++++++++++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 8e01c6793f8..da805ce77e1 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -3194,25 +3194,27 @@ class Logging(LiteLLMLoggingBaseClass): ) if isinstance(callback, CustomLogger): # custom logger class - model_call_details: dict = self.model_call_details + callback_model_call_details: Final[dict] = self.model_call_details ################################## # call redaction hook for custom logger - model_call_details = callback.redact_standard_logging_payload_from_model_call_details( - model_call_details=model_call_details + standard_redacted_model_call_details: Final[dict] = ( + callback.redact_standard_logging_payload_from_model_call_details( + model_call_details=callback_model_call_details + ) ) - model_call_details = redact_streaming_responses_for_custom_logger( - model_call_details=model_call_details, custom_logger=callback + streaming_redacted_model_call_details: Final[dict] = redact_streaming_responses_for_custom_logger( + model_call_details=standard_redacted_model_call_details, custom_logger=callback ) - model_call_details = redact_model_call_details_for_custom_logger( - model_call_details=model_call_details, custom_logger=callback + redacted_model_call_details: Final[dict] = redact_model_call_details_for_custom_logger( + model_call_details=streaming_redacted_model_call_details, custom_logger=callback ) ################################## if self.stream is True: - if "async_complete_streaming_response" in model_call_details: + if "async_complete_streaming_response" in redacted_model_call_details: await callback.async_log_success_event( - kwargs=model_call_details, + kwargs=redacted_model_call_details, response_obj=redact_response_for_custom_logger( - result=model_call_details["async_complete_streaming_response"], + result=redacted_model_call_details["async_complete_streaming_response"], custom_logger=callback, ), start_time=start_time, @@ -3220,14 +3222,14 @@ class Logging(LiteLLMLoggingBaseClass): ) else: await callback.async_log_stream_event( # [TODO]: move this to being an async log stream event function - kwargs=model_call_details, + kwargs=redacted_model_call_details, response_obj=redact_response_for_custom_logger(result=result, custom_logger=callback), start_time=start_time, end_time=end_time, ) else: await callback.async_log_success_event( - kwargs=model_call_details, + kwargs=redacted_model_call_details, response_obj=redact_response_for_custom_logger(result=result, custom_logger=callback), start_time=start_time, end_time=end_time, diff --git a/litellm/litellm_core_utils/redact_messages.py b/litellm/litellm_core_utils/redact_messages.py index 89eb6fcc38c..e2bdeb2cb17 100644 --- a/litellm/litellm_core_utils/redact_messages.py +++ b/litellm/litellm_core_utils/redact_messages.py @@ -88,6 +88,23 @@ def redact_model_call_details_for_custom_logger( if not opted_out or custom_logger.redacts_messages_itself(): return model_call_details response_keys: Final = ("response", "original_response", "complete_response") + raw_request_typed_dict: Final = model_call_details.get("raw_request_typed_dict") + raw_request_body: Final = ( + raw_request_typed_dict.get("raw_request_body") if isinstance(raw_request_typed_dict, dict) else None + ) + redacted_raw_request_typed_dict: Final = ( + { + **raw_request_typed_dict, + "raw_request_body": { + **raw_request_body, + "messages": [{"role": "user", "content": REDACTED_BY_LITELLM}], + "input": "", + "prompt": "", + }, + } + if isinstance(raw_request_typed_dict, dict) and isinstance(raw_request_body, dict) + else raw_request_typed_dict + ) return { **model_call_details, **{ @@ -95,6 +112,11 @@ def redact_model_call_details_for_custom_logger( for key in response_keys if model_call_details.get(key) is not None }, + **( + {"raw_request_typed_dict": redacted_raw_request_typed_dict} + if redacted_raw_request_typed_dict is not raw_request_typed_dict + else {} + ), } diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index c5d7ba9f08a..bc840ff517b 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -1054,6 +1054,9 @@ def test_success_handler_redacts_custom_logger_payload_per_callback(logging_obj) logging_obj.model_call_details["original_response"] = { "choices": [{"message": {"content": "original response"}}] } + logging_obj.model_call_details["raw_request_typed_dict"] = { + "raw_request_body": {"messages": [{"role": "user", "content": "original request"}]}, + } standard_logging_object = { "messages": [{"role": "user", "content": "original message"}], "response": {"choices": []}, @@ -1076,14 +1079,26 @@ def test_success_handler_redacts_custom_logger_payload_per_callback(logging_obj) assert ( logging_obj.model_call_details["standard_logging_object"]["messages"][0]["content"] == "original message" ) + assert ( + logging_obj.model_call_details["raw_request_typed_dict"]["raw_request_body"]["messages"][0]["content"] + == "original request" + ) assert redacting_logger.received_kwargs[0]["messages"] == [{"role": "user", "content": "redacted-by-litellm"}] assert ( redacting_logger.received_kwargs[0]["original_response"]["choices"][0]["message"]["content"] == "redacted-by-litellm" ) + assert ( + redacting_logger.received_kwargs[0]["raw_request_typed_dict"]["raw_request_body"]["messages"][0]["content"] + == "redacted-by-litellm" + ) assert ( plain_logger.received_kwargs[0]["original_response"]["choices"][0]["message"]["content"] == "original response" ) + assert ( + plain_logger.received_kwargs[0]["raw_request_typed_dict"]["raw_request_body"]["messages"][0]["content"] + == "original request" + ) assert redacting_logger.received_response_objs[0] == {"text": "redacted-by-litellm"} assert plain_logger.received_response_objs[0] == {"id": "response"}