Skip to content

fix(headroom guardrail): log real token/compression stats instead of "allow" - #32158

Merged
krrish-berri-2 merged 3 commits into
litellm_internal_stagingfrom
litellm_fix_headroom_guardrail_token_logging
Jul 5, 2026
Merged

fix(headroom guardrail): log real token/compression stats instead of "allow"#32158
krrish-berri-2 merged 3 commits into
litellm_internal_stagingfrom
litellm_fix_headroom_guardrail_token_logging

Conversation

@krrish-berri-2

@krrish-berri-2 krrish-berri-2 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

(Replaces #32153 - that PR's head branch lived in a fork, which meant CircleCI never ran against it. This PR uses the same branch pushed directly into this repo instead.)

Summary

  • The headroom guardrail already fetches tokens_before/tokens_after/compression_ratio/transforms_applied from Headroom's /v1/compress response, but only logged them via a debug-level verbose_proxy_logger.debug(...) call - never persisted anywhere.
  • As a result, spend_logs.guardrail_information.guardrail_response always showed the generic "allow", with no way to tell whether compression actually ran or by how much.
  • _call_compress now returns the token/compression stats alongside the compressed messages and success flag, and apply_guardrail logs them via add_standard_logging_guardrail_information_to_request_data when compression succeeds (the same mechanism other guardrails like block_code_execution and cisco_ai_defense use for rich logging).
  • Raw message content is intentionally excluded from what's logged - only token counts, compression ratio, and applied transform names, so no request/response text lands in spend logs.
  • Rebased on top of litellm_internal_staging's existing fail_open/fail_closed unreachable-fallback change to the same function - stats are only logged on the success path.

Before:

"guardrail_response": "allow"

After:

"guardrail_response": {
  "tokens_before": 1833,
  "tokens_after": 1429,
  "tokens_saved": 404,
  "compression_ratio": 0.78,
  "transforms_applied": ["router:mixed:0.44"]
}

Test plan

  • Verified locally against a running headroom-ai 0.28.0 proxy + litellm proxy with the headroom guardrail configured
  • Ran real /v1/chat/completions requests through litellm with small/medium/large conversation payloads (plain, JSON tool output, log dumps, diffs, code) and confirmed guardrail_information in LiteLLM_SpendLogs now shows real token stats instead of "allow" for compressed requests
  • Confirmed small/uncompressed requests still log correctly (tokens_saved: 0, compression_ratio: 1.0)
  • On fix(headroom guardrail): log real token/compression stats instead of "allow" #32153: full CI green (75 pass / 1 skip), Greptile 5/5 with all comments resolved

Note

Low Risk
Observability-only change on the compression success path; no auth or request-payload behavior changes beyond existing guardrail logging.

Overview
Headroom guardrail now writes real compression metrics into spend_logs / standard guardrail logging instead of the generic "allow" string.

_call_compress returns a third value: a dict of token/compression fields from Headroom’s /v1/compress JSON (tokens_before, tokens_after, tokens_saved, compression_ratio, transforms_applied). Failure paths return an empty stats dict. On a successful compress in apply_guardrail, the hook records timing and passes that dict through add_standard_logging_guardrail_information_to_request_data (same pattern as other guardrails with rich payloads), so guardrail_response reflects actual compression outcomes without logging message bodies.

Reviewed by Cursor Bugbot for commit 60a6162. Bugbot is set up for automated code reviews on this repo. Configure here.

…"allow"

The headroom guardrail fetched tokens_before/tokens_after/compression_ratio
from Headroom's /v1/compress response but only surfaced them via a debug-level
log line, so spend_logs.guardrail_information showed guardrail_response:
"allow" with no way to tell whether compression actually ran or by how much.

_call_compress now returns the token/compression stats alongside the
compressed messages and success flag, and apply_guardrail logs them via
add_standard_logging_guardrail_information_to_request_data when compression
succeeds. Raw message content is intentionally excluded from what's logged -
only token counts, compression ratio, and applied transform names.
…ntion

Addresses codex review feedback - CLAUDE.md says not to add comments
unless explicitly asked; the sensitive-logging guarantee is already
expressed by the stats dict only pulling specific keys, not messages.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the headroom guardrail's spend-log output so that real token/compression stats (tokens_before, tokens_after, tokens_saved, compression_ratio, transforms_applied) are persisted in guardrail_information instead of the generic "allow" string.

  • _call_compress return type is extended to a 3-tuple, adding a stats dict built from whichever keys the Headroom /v1/compress response includes; all failure paths return {} and False so call-sites are unambiguous.
  • apply_guardrail captures wall-clock timing around _call_compress and calls add_standard_logging_guardrail_information_to_request_data on the success path, relying on the @log_guardrail_information decorator's entry-count guard to suppress the "allow" fallback — the same mechanism used by block_code_execution and cisco_ai_defense.

Confidence Score: 4/5

Safe to merge; the change is additive and isolated to the headroom guardrail's logging path, with no behavioural change to the compression or routing logic.

The implementation correctly mirrors the double-logging prevention pattern established by other guardrails, and failure paths all return empty stats so no data is lost. The only gap is that event_type is not forwarded to the logging helper, which could produce an inaccurate guardrail_mode in spend logs when the guardrail is configured for multiple hooks — the request still compresses and routes correctly, but the log metadata may be wrong.

Only headroom.py is changed; the event_type omission in the add_standard_logging_guardrail_information_to_request_data call is worth a second look.

Important Files Changed

Filename Overview
litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py Extends _call_compress to return a 3-tuple with token stats, and calls add_standard_logging_guardrail_information_to_request_data on the success path — following the same pattern as block_code_execution and cisco_ai_defense. The double-logging guard in @log_guardrail_information correctly detects the manual call and skips the "allow" default. The event_type parameter is not passed to the logging helper, which may cause an inaccurate guardrail_mode in spend logs.

Reviews (1): Last reviewed commit: "fix(review): remove comment per repo's n..." | Re-trigger Greptile

Comment on lines +478 to +486
self.add_standard_logging_guardrail_information_to_request_data(
guardrail_json_response=stats,
request_data=request_data,
guardrail_status="success",
guardrail_provider="headroom",
start_time=start_time,
end_time=end_time,
duration=end_time - start_time,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The event_type parameter is not passed to add_standard_logging_guardrail_information_to_request_data, so guardrail_mode in the spend-log entry falls back to self.event_hook, which may be a list or an unexpected value when the guardrail is configured for multiple hooks. Since apply_guardrail only executes on input_type == "request", the correct type is always pre_call. Other guardrails like block_code_execution explicitly pass this field.

Suggested change
self.add_standard_logging_guardrail_information_to_request_data(
guardrail_json_response=stats,
request_data=request_data,
guardrail_status="success",
guardrail_provider="headroom",
start_time=start_time,
end_time=end_time,
duration=end_time - start_time,
)
self.add_standard_logging_guardrail_information_to_request_data(
guardrail_json_response=stats,
request_data=request_data,
guardrail_status="success",
guardrail_provider="headroom",
start_time=start_time,
end_time=end_time,
duration=end_time - start_time,
event_type=GuardrailEventHooks.pre_call,
)

@krrish-berri-2

Copy link
Copy Markdown
Contributor Author

@BugBot please review

@yucheng-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 60a6162. Configure here.

@krrish-berri-2
krrish-berri-2 enabled auto-merge (squash) July 5, 2026 00:21
@krrish-berri-2
krrish-berri-2 merged commit 26c0c93 into litellm_internal_staging Jul 5, 2026
202 of 203 checks passed
@krrish-berri-2
krrish-berri-2 deleted the litellm_fix_headroom_guardrail_token_logging branch July 5, 2026 00:25
EkkoG pushed a commit to EkkoG/litellm that referenced this pull request Jul 7, 2026
…"allow" (BerriAI#32158)

* fix(headroom guardrail): log real token/compression stats instead of "allow"

The headroom guardrail fetched tokens_before/tokens_after/compression_ratio
from Headroom's /v1/compress response but only surfaced them via a debug-level
log line, so spend_logs.guardrail_information showed guardrail_response:
"allow" with no way to tell whether compression actually ran or by how much.

_call_compress now returns the token/compression stats alongside the
compressed messages and success flag, and apply_guardrail logs them via
add_standard_logging_guardrail_information_to_request_data when compression
succeeds. Raw message content is intentionally excluded from what's logged -
only token counts, compression ratio, and applied transform names.

* fix(ci): apply ruff format to headroom.py

* fix(review): remove comment per repo's no-comments-unless-asked convention

Addresses codex review feedback - CLAUDE.md says not to add comments
unless explicitly asked; the sensitive-logging guarantee is already
expressed by the stats dict only pulling specific keys, not messages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants