litellm - ✅(Solved) Fix [Feature]: Enable response logging after proxy response hooks fire [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#23462Fetched 2026-04-08 00:44:05
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
cross-referenced ×2labeled ×2

Fix Action

Fixed

PR fix notes

PR #23597: [23462][Feature]: Enable response logging after proxy response hooks fire

Description (problem / solution / changelog)

feat(proxy): add async_post_guardrail_log_success_event hook (issue 23462)

  • Add CustomLogger.async_post_guardrail_log_success_event() so callbacks can log the final response after post-call hooks (e.g. guardrails, tool blocking, response moderation). async_log_success_event still runs before post-call hooks and sees the unmodified response.
  • ProxyLogging.async_post_guardrail_log_success_event() invokes the hook on all CustomLogger callbacks (skips CustomGuardrails).
  • Call the new hook after post_call_success_hook in common_request_processing (non-streaming) and after the stream completes in async_data_generator (streaming), using stream_chunk_builder to assemble the post-guardrail response for streaming.
  • Add tests in test_async_post_guardrail_log_success_event.py following test_post_call_success_hook_integration patterns.

Relevant issues

Fixes #23462

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:
  • CI run for the last commit
    Link:
  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature

<!-- 🐛 Bug Fix --> <!-- 🧹 Refactoring --> <!-- 📖 Documentation --> <!-- 🚄 Infrastructure --> <!-- ✅ Test -->

Changes

  • litellm/integrations/custom_logger.py: Added async_post_guardrail_log_success_event(kwargs, response_obj, start_time, end_time) to CustomLogger with default no-op and docstring.
  • litellm/proxy/utils.py: Added ProxyLogging.async_post_guardrail_log_success_event(data, response, user_api_key_dict, logging_obj) to invoke the hook on all CustomLogger callbacks (excluding CustomGuardrails), with exception handling per callback.
  • litellm/proxy/common_request_processing.py: Call async_post_guardrail_log_success_event after post_call_success_hook for non-streaming chat completions.
  • litellm/proxy/proxy_server.py: For streaming, collect post-guardrail chunks in async_data_generator, assemble with stream_chunk_builder after the stream ends, and call async_post_guardrail_log_success_event; extracted _async_data_generator_fire_post_guardrail_log helper.
  • tests/test_litellm/proxy/hooks/test_async_post_guardrail_log_success_event.py: New integration tests (hook called with response, callbacks without hook skipped, exception in one callback does not block others, timing from logging_obj, CustomGuardrails skipped).

Changed files

  • litellm/integrations/custom_logger.py (modified, +12/-0)
  • litellm/proxy/common_request_processing.py (modified, +8/-0)
  • litellm/proxy/proxy_server.py (modified, +49/-1)
  • litellm/proxy/utils.py (modified, +65/-1)
  • tests/test_litellm/proxy/hooks/test_async_post_guardrail_log_success_event.py (added, +277/-0)

PR #23600: [Issue#23462]: Enable response logging after proxy response hooks fire

Description (problem / solution / changelog)

Relevant issues

Fixes #23462

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature

<!-- 🐛 Bug Fix --> <!-- 🧹 Refactoring --> <!-- 📖 Documentation --> <!-- 🚄 Infrastructure --> <!-- ✅ Test -->

Changes

  • litellm/integrations/custom_logger.py

    • Added async_post_guardrail_log_success_event(kwargs, response_obj, start_time, end_time) on CustomLogger with default no-op and docstring. Allows callbacks to log the final response after post-call hooks (guardrails, tool blocking, etc.); async_log_success_event still runs before hooks.
  • litellm/proxy/utils.py

    • Added ProxyLogging.async_post_guardrail_log_success_event(data, response, user_api_key_dict, logging_obj=None). Resolves callbacks from litellm.callbacks, skips CustomGuardrail, invokes the new hook on each CustomLogger that implements it. Builds kwargs from data and optional logging_obj.model_call_details; passes through start_time/end_time. Per-callback exceptions are caught and logged with verbose_proxy_logger.exception.
  • litellm/proxy/common_request_processing.py

    • After post_call_success_hook and _override_openai_response_model, calls async_post_guardrail_log_success_event so non-streaming callbacks see the final response and client-requested model name.
  • litellm/proxy/proxy_server.py

    • Streaming path: module-level _post_guardrail_log_tasks set to keep strong refs to background tasks. New helper _async_data_generator_fire_post_guardrail_log(...) builds a complete response with litellm.stream_chunk_builder, calls async_post_guardrail_log_success_event, logs failures with verbose_proxy_logger.exception. In async_data_generator, chunks are collected after _restamp_streaming_chunk_model with same serialization as client (exclude_none=True, exclude_unset=True); after the stream loop, post-guardrail log is scheduled via asyncio.create_task, task is added to _post_guardrail_log_tasks and removed in a done_callback so [DONE] is not delayed and the task is not GC’d. Uses cast(LLMResponseTypes, complete_response) for mypy.
  • tests/test_litellm/proxy/hooks/test_async_post_guardrail_log_success_event.py (new)

    • Five tests: hook called with correct response and kwargs; no-op and custom implementations both run; exception in one callback does not block others; timing from logging_obj; CustomGuardrail callbacks are not invoked for this hook.

Changed files

  • enterprise/litellm_enterprise/enterprise_callbacks/callback_controls.py (modified, +85/-54)
  • enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py (modified, +3/-3)
  • enterprise/litellm_enterprise/enterprise_callbacks/send_emails/sendgrid_email.py (modified, +1/-1)
  • enterprise/litellm_enterprise/proxy/auth/__init__.py (modified, +1/-1)
  • enterprise/litellm_enterprise/proxy/auth/custom_sso_handler.py (modified, +22/-15)
  • enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py (modified, +45/-20)
  • enterprise/litellm_enterprise/proxy/common_utils/check_responses_cost.py (modified, +29/-13)
  • enterprise/litellm_enterprise/proxy/hooks/managed_files.py (modified, +156/-107)
  • enterprise/litellm_enterprise/proxy/hooks/managed_vector_stores.py (modified, +44/-48)
  • enterprise/litellm_enterprise/proxy/management_endpoints/key_management_endpoints.py (modified, +0/-1)
  • enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py (modified, +3/-3)
  • enterprise/litellm_enterprise/types/enterprise_callbacks/send_emails.py (modified, +11/-1)
  • litellm/completion_extras/litellm_responses_transformation/transformation.py (modified, +4/-1)
  • litellm/integrations/custom_logger.py (modified, +12/-0)
  • litellm/litellm_core_utils/prompt_templates/factory.py (modified, +3/-1)
  • litellm/llms/anthropic/files/transformation.py (modified, +3/-1)
  • litellm/llms/base_llm/base_model_iterator.py (modified, +2/-6)
  • litellm/llms/black_forest_labs/image_edit/transformation.py (modified, +3/-1)
  • litellm/llms/perplexity/responses/transformation.py (modified, +3/-1)
  • litellm/proxy/_experimental/mcp_server/rest_endpoints.py (modified, +12/-12)
  • litellm/proxy/auth/user_api_key_auth.py (modified, +5/-1)
  • litellm/proxy/common_request_processing.py (modified, +8/-0)
  • litellm/proxy/guardrails/guardrail_hooks/presidio.py (modified, +6/-2)
  • litellm/proxy/management_endpoints/key_management_endpoints.py (modified, +2/-6)
  • litellm/proxy/management_endpoints/ui_sso.py (modified, +17/-7)
  • litellm/proxy/proxy_server.py (modified, +63/-4)
  • litellm/proxy/utils.py (modified, +65/-1)
  • litellm/responses/litellm_completion_transformation/transformation.py (modified, +3/-1)
  • poetry.lock (modified, +37/-28)
  • tests/test_litellm/proxy/hooks/test_async_post_guardrail_log_success_event.py (added, +273/-0)
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

The Feature

I would like to have the ability to log LLM responses after post-call hooks fire, since those may rewrite the response. Currently async_log_success_event fires before the post call hooks.

I'd be happy if async_log_success_event fired after the existing hooks, since it seems more correct - but that's a behavior change and we might not want that. An alternative is to add an entirely new logging hook, async_post_guardrail_log_success_event or whatever, that fires after the post success hooks.

Is one of these possible?

Motivation, pitch

I am writing a plugin that uses the async_log_success_event hook for logging, and async_post_call_success_hook and async_post_call_streaming_iterator_hook to do tool blocking/response moderation. With the current implementation of litellm and litellm-proxy, the logging hook is fired before the post call hooks. This means that if the post call hooks rewrite the response, we are not logging that, we're logging the unmodified response. But for my purposes the post-rewritten response is actually what I need logged.

What part of LiteLLM is this about?

Proxy

LiteLLM is hiring a founding backend engineer, are you interested in joining us and shipping to all our users?

No

Twitter / LinkedIn details

No response

extent analysis

Fix Plan

To address the issue, we can introduce a new logging hook, async_post_guardrail_log_success_event, that fires after the post-success hooks. Here are the steps:

  • Add a new hook to the litellm-proxy codebase:
    • Define the hook signature: async_post_guardrail_log_success_event(response: Response) -> None
    • Call this hook after the post-success hooks in the relevant code path
  • Update the plugin to use the new hook for logging:
    • Import the new hook and register a callback function
    • Implement the callback function to log the response

Example code snippet:

# In litellm-proxy
def async_post_call_success_hook(response: Response) -> Response:
    # ... existing implementation ...
    response = async_post_call_success_hook_impl(response)
    # Call the new hook after post-success hooks
    await async_post_guardrail_log_success_event(response)
    return response

# In the plugin
from litellm_proxy import async_post_guardrail_log_success_event

async def log_response(response: Response) -> None:
    # Log the response
    print(f"Logged response: {response}")

# Register the callback function
async_post_guardrail_log_success_event(log_response)

Verification

To verify the fix, test the plugin with the new hook and ensure that it logs the post-rewritten response correctly.

Extra Tips

  • Document the new hook and its usage in the litellm-proxy documentation.
  • Consider adding tests for the new hook to ensure it is called correctly.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - ✅(Solved) Fix [Feature]: Enable response logging after proxy response hooks fire [2 pull requests, 1 participants]