From c5878c3474ba58109e6c89f73fa1be899edd6cfa Mon Sep 17 00:00:00 2001 From: devarakondasrikanth Date: Fri, 13 Mar 2026 20:06:12 -0700 Subject: [PATCH] fix(proxy): model_call_details wins for timing; skip post-guardrail task when no chunks - utils: let model_call_details override start_time/end_time in kwargs merge; add comment - proxy_server: only create post-guardrail log task when _streaming_chunks_for_log is non-empty Made-with: Cursor --- litellm/proxy/proxy_server.py | 4 ++-- litellm/proxy/utils.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b3ed5354382..7f1c06e32a0 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5609,8 +5609,8 @@ async def async_data_generator( except Exception as e: yield f"data: {str(e)}\n\n" - # Post-guardrail log: only for successful stream completion, not when we got an SSE error - if error_message is None: + # Post-guardrail log: only for successful stream completion with chunks to process + if error_message is None and _streaming_chunks_for_log: def _discard_task(t: asyncio.Task[None]) -> None: _post_guardrail_log_tasks.discard(t) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 4e20ac532c3..4771fbd9691 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1994,7 +1994,12 @@ class ProxyLogging: if logging_obj is not None and getattr( logging_obj, "model_call_details", None ): + # Merge so request data wins overall, but model_call_details wins for + # LLM-lifecycle timing fields so callbacks get accurate start/end times. kwargs = {**logging_obj.model_call_details, **kwargs} + for key in ("start_time", "end_time"): + if key in logging_obj.model_call_details: + kwargs[key] = logging_obj.model_call_details[key] kwargs["user_api_key_dict"] = user_api_key_dict start_time = None if logging_obj is not None: