This commit is contained in:
Brendan Smith-Elion 2026-11-06 15:52:44 -05:00 • committed by GitHub
commit 32cdac652f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 2 deletions

View file

@ -586,7 +586,7 @@ last_fetched_at_keys: Final = None
############################################################
def print_verbose(
print_statement,
print_statement: object,
logger_only: bool = False,
log_level: Literal["DEBUG", "INFO", "ERROR"] = "DEBUG",
):
@ -598,7 +598,12 @@ def print_verbose(
elif log_level == "ERROR":
verbose_logger.error(print_statement)
if litellm.set_verbose is True and logger_only is False:
print(print_statement) # noqa: T201
stdout_statement: Final = (
redact_credentials_in_payload(print_statement)
if isinstance(print_statement, Mapping)
else print_statement
)
print(stdout_statement) # noqa: T201
except Exception:
pass

View file

@ -116,6 +116,7 @@ from litellm.utils import (
get_valid_models,
is_cached_message,
is_prompt_caching_valid_prompt,
print_verbose,
trim_messages,
validate_environment,
validate_chat_completion_tool_choice,
@ -7195,6 +7196,39 @@ class TestFinalOptionalParamsLineRedaction:
assert "'temperature': 0.25" in printed
class TestPrintVerboseRedaction:
"""print_verbose() must not leak secrets to stdout when callers pass dicts like
model_call_details that contain api_key fields."""
SECRET_VALUE: Final = "sk-ant-test-DO-NOT-LEAK-12345"
def _payload(self) -> Mapping[str, object]:
return {
"model": "claude-3-5-sonnet-20241022",
"api_key": self.SECRET_VALUE,
"messages": [{"role": "user", "content": "hi"}],
}
def test_utils_print_verbose_redacts_stdout_when_set_verbose(
self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(litellm, "set_verbose", True)
print_verbose(self._payload())
out: Final = capsys.readouterr().out
assert self.SECRET_VALUE not in out, f"api_key leaked to stdout: {out!r}"
assert "'api_key': 'REDACTED'" in out
assert "'model': 'claude-3-5-sonnet-20241022'" in out
def test_utils_print_verbose_routes_through_logger_redacted(
self, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(litellm, "set_verbose", False)
caplog.set_level(logging.DEBUG, logger="LiteLLM")
print_verbose(self._payload())
leaked: Final = [r.getMessage() for r in caplog.records if self.SECRET_VALUE in r.getMessage()]
assert not leaked, f"api_key leaked through verbose_logger: {leaked!r}"
class TestDropParamsStringCoercion:
@pytest.mark.parametrize("drop_params", ["true", "True", True])
def test_truthy_drop_params_drops_unsupported_temperature(self, drop_params, monkeypatch):