fix(logging): keep the request's redaction opt-in when another callback param is rejected

This commit is contained in:
yucheng 2026-10-31 14:11:44 -07:00
parent 690890398a
commit 9d973b0caf
2 changed files with 43 additions and 4 deletions

View file

@ -8,6 +8,7 @@
# Thank you users! We ❤️ you! - Krrish & Ishaan
import asyncio
import contextlib
import copy
import inspect
import itertools
@ -28,6 +29,7 @@ from litellm.litellm_core_utils.core_helpers import (
)
from litellm.litellm_core_utils.initialize_dynamic_callback_params import (
initialize_standard_callback_dynamic_params,
iter_client_callback_metadata_dicts,
)
from litellm.litellm_core_utils.served_output_texts import SERVED_OUTPUT_TEXTS_KEY
from litellm.llms.base_llm.guardrail_translation.utils import stream_item_field, stream_item_items
@ -896,12 +898,12 @@ def should_redact_failed_request(request_data: Mapping[str, object]) -> bool:
"""
The redaction decision for proxy failure hooks, which get the request body but not its logging
object: the same metadata, headers and dynamic params the logging object would read. When the
logging object would reject the request's callback params, the global setting decides.
logging object would reject another of the request's callback params, the request's own
``turn_off_message_logging`` still counts, and when it would reject that too the global setting decides.
"""
body: Final = dict(request_data)
try:
dynamic_params: Final = initialize_standard_callback_dynamic_params(body)
except ValueError:
dynamic_params: Final = _request_callback_params(body)
if dynamic_params is None:
return litellm.turn_off_message_logging is True
return should_redact_failure_logging(
{
@ -911,6 +913,27 @@ def should_redact_failed_request(request_data: Mapping[str, object]) -> bool:
)
def _request_callback_params(body: Mapping[str, object]) -> StandardCallbackDynamicParams | None:
"""
The callback params the logging object reads from ``body``. When it would reject one of them, only the
request's own ``turn_off_message_logging``, and ``None`` when it would reject that too.
"""
with contextlib.suppress(ValueError):
return initialize_standard_callback_dynamic_params(dict(body))
sources: Final[tuple[Mapping[str, object], ...]] = (
body,
*(metadata for _, metadata in iter_client_callback_metadata_dicts(body)),
)
redaction_choice: Final = next(
(source["turn_off_message_logging"] for source in sources if "turn_off_message_logging" in source), None
)
with contextlib.suppress(ValueError):
return initialize_standard_callback_dynamic_params(
{} if redaction_choice is None else {"turn_off_message_logging": redaction_choice}
)
return None
def scrubbed_error_information_for_request(
error_information: StandardLoggingPayloadErrorInformation, request_data: Mapping[str, object]
) -> StandardLoggingPayloadErrorInformation:

View file

@ -2357,6 +2357,22 @@ def test_should_redact_failed_request_falls_back_to_the_global_setting_for_an_un
assert should_redact_failed_request(request_data) is global_redaction
@pytest.mark.parametrize(
"request_data",
[
{"turn_off_message_logging": True, "langfuse_host": "os.environ/LANGFUSE_HOST"},
{"metadata": {"turn_off_message_logging": True, "langfuse_host": "os.environ/LANGFUSE_HOST"}},
],
ids=["body_flag", "metadata_flag"],
)
def test_should_redact_failed_request_keeps_the_request_opt_in_past_a_rejected_callback_param(
monkeypatch: pytest.MonkeyPatch, request_data: dict
) -> None:
monkeypatch.setattr(litellm, "turn_off_message_logging", False)
assert should_redact_failed_request(request_data) is True
def test_should_redact_failed_request_reads_the_request_setting_past_non_mapping_headers(
monkeypatch: pytest.MonkeyPatch,
) -> None: