fix(logging): surface persistent SLP rebuild failure

Per review: if the empty-response retry also returns None, log a second
explicit error so the double failure is observable instead of masked by
the first message, and stop promising success in the first log line.
This commit is contained in:
Rodrigo-Palma 2026-07-03 09:16:15 -03:00
parent 327d4e20e1
commit 4d2713e8b0
2 changed files with 47 additions and 1 deletions

View file

@ -3356,7 +3356,7 @@ class Logging(LiteLLMLoggingBaseClass):
# fallback. See #32019.
verbose_logger.error(
"standard_logging_object build failed for streaming call_id=%s call_type=%s; "
"rebuilding a degraded payload so success loggers still fire",
"retrying with an empty response object",
self.litellm_call_id,
self.call_type,
)
@ -3364,6 +3364,13 @@ class Logging(LiteLLMLoggingBaseClass):
self.model_call_details["standard_logging_object"] = self._build_standard_logging_payload(
{}, start_time, end_time
)
if self.model_call_details["standard_logging_object"] is None:
verbose_logger.error(
"standard_logging_object rebuild failed for streaming call_id=%s call_type=%s; "
"loggers gated on standard_logging_object will skip this request",
self.litellm_call_id,
self.call_type,
)
except Exception: # noqa: BLE001 # same rule as above: never block later callbacks
verbose_logger.exception(
"LiteLLM.LoggingError: [Non-Blocking] Exception rebuilding the degraded standard "

View file

@ -8,6 +8,7 @@ exception and returns ``None``, spend tracking still succeeds via the
``response_cost`` fallback, and no error surfaces to the caller.
"""
import logging
from datetime import datetime
import pytest
@ -92,3 +93,41 @@ async def test_streaming_slo_build_success_does_not_rebuild(monkeypatch):
assert logging_obj.model_call_details["standard_logging_object"] == healthy_payload
assert len(build_calls) == 1
@pytest.mark.asyncio
async def test_streaming_slo_double_build_failure_is_logged(monkeypatch, caplog):
"""If the empty-response rebuild also fails, the persistent failure must be
surfaced with a second error log instead of silently leaving the payload None."""
logging_obj = Logging(
model="claude-3-5-sonnet-20240620",
messages=[{"role": "user", "content": "hi"}],
stream=True,
call_type="anthropic_messages",
start_time=datetime.now(),
litellm_call_id="test-32019-double",
function_id="test-32019-double",
)
build_calls = []
def fake_build(self, init_response_obj, start_time, end_time):
build_calls.append(init_response_obj)
return None # both the primary build and the empty-response retry fail
monkeypatch.setattr(Logging, "_build_standard_logging_payload", fake_build)
monkeypatch.setattr(litellm, "_async_success_callback", [])
monkeypatch.setattr(litellm, "success_callback", [])
result = ModelResponse(model="claude-3-5-sonnet-20240620")
with caplog.at_level(logging.ERROR, logger="LiteLLM"):
await logging_obj.async_success_handler(
result,
start_time=datetime.now(),
end_time=datetime.now(),
cache_hit=False,
)
assert logging_obj.model_call_details["standard_logging_object"] is None
assert build_calls == [result, {}]
assert "rebuild failed" in caplog.text