fix(proxy): Bedrock guardrail spend logs - hook mode, match redaction, streaming request_data - #26266
Conversation
Restore guardrail spend/UI event_type wiring, request_data on streaming OUTPUT paths, and centralized match redaction after the upstream revert. Made-with: Cursor
Greptile SummaryThis PR fixes two proxy-level guardrail logging bugs for Bedrock: (1) The fix introduces Confidence Score: 5/5Safe to merge — all remaining findings are minor P2 suggestions with no correctness impact on the primary fix paths. The three core problems (wrong No files require special attention;
|
| Filename | Overview |
|---|---|
| litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py | Core fix: adds logging_event_type param to make_bedrock_api_request, sets use_native_during_call_hook=True, and refactors _redact_pii_matches to delegate to the shared utility. All call sites correctly thread logging_event_type through. |
| litellm/integrations/custom_guardrail.py | Adds use_native_during_call_hook: ClassVar[bool] = False and applies redact_nested_match_and_regex_keys on the already-deep-copied response in add_standard_logging_guardrail_information_to_request_data. Clean implementation. |
| litellm/litellm_core_utils/core_helpers.py | New redact_nested_match_and_regex_keys function with deep-copy + iterative walk. Exception fallback returns original unredacted payload — acceptable parity with old behaviour but worth knowing. |
| litellm/proxy/utils.py | Both _execute_guardrail_hook and during_call_hook correctly skip the unified apply_guardrail path when use_native_during_call_hook=True and hook_type=="during_call", routing to async_moderation_hook directly. |
| litellm/proxy/common_utils/callback_utils.py | Adds deduplication guard in add_guardrail_to_applied_guardrails_header to prevent the same guardrail name appearing twice in the response header. |
| tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py | New mock-only tests covering: use_native_during_call_hook flag, logging_event_type forwarding, during_call_hook routing, streaming request_data propagation, and assessment redaction in HTTP exceptions. No real network calls. |
| tests/test_litellm/integrations/test_custom_guardrail.py | Adds TestCustomGuardrailSpendLogMatchRedaction covering match and regex redaction via the shared logging path, and verifies the original dict is not mutated (deep-copy check). |
| tests/test_litellm/litellm_core_utils/test_core_helpers.py | Unit tests for redact_nested_match_and_regex_keys covering recursive dicts/lists, None/string passthrough. Straightforward and correct. |
| tests/guardrails_tests/test_bedrock_guardrails.py | Only adds logging_event_type to the mock signature so tests compile with the new parameter; assertions unchanged. |
Sequence Diagram
sequenceDiagram
participant PL as ProxyLogging
participant BG as BedrockGuardrail
participant AB as AWS Bedrock API
participant CG as CustomGuardrail
participant CH as core_helpers
note over PL,BG: during_call (use_native_during_call_hook=True — skip unified apply_guardrail)
PL->>BG: async_moderation_hook(data, call_type)
BG->>AB: POST ApplyGuardrail (source=INPUT)
AB-->>BG: response_json with raw match values
BG->>CG: add_standard_logging_guardrail_information_to_request_data(event_type=during_call)
CG->>CH: redact_nested_match_and_regex_keys(raw)
CH-->>CG: redacted copy
CG->>CG: StandardLoggingGuardrailInformation(guardrail_mode=during_call)
CG-->>PL: spend log shows DURING-CALL
note over PL,BG: pre_call (unified apply_guardrail path)
PL->>BG: apply_guardrail(input_type=request)
BG->>AB: POST ApplyGuardrail (source=INPUT)
AB-->>BG: response_json
BG->>CG: add_standard_logging_guardrail_information_to_request_data(event_type=pre_call)
CG->>CH: redact_nested_match_and_regex_keys(raw)
CH-->>CG: redacted copy
CG-->>PL: spend log shows PRE-CALL
Reviews (3): Last reviewed commit: "fix(core_helpers): make redact_nested_ma..." | Re-trigger Greptile
| if "regex" in node: | ||
| node["regex"] = "[REDACTED]" |
There was a problem hiding this comment.
Blanket
"regex" redaction may over-redact unrelated keys
_walk replaces every key literally named "regex" anywhere in the nested payload with "[REDACTED]". In the Bedrock context this is fine (the only regex key is the pattern text from sensitiveInformationPolicy.regexes[].regex). However, redact_nested_match_and_regex_keys is now called from the shared CustomGuardrail.add_standard_logging_guardrail_information_to_request_data path for all guardrail providers. If another provider's response contains a "regex" key that is not sensitive (e.g. a debug field or an OpenAI-style filter criterion), it will be silently replaced with "[REDACTED]", making logs harder to interpret.
- Dedupe names in add_guardrail_to_applied_guardrails_header (matches policies). - Inline unified during_call condition so mypy narrows UserAPIKeyAuth. - Extend bedrock guardrails test mock for logging_event_type. Made-with: Cursor
91485a0 to
9577d87
Compare
Replace recursive `_walk` helper with a stack-based traversal so the recursive_detector CI check passes without adding to the ignore list, and avoid Python recursion limits on deeply nested payloads. Made-with: Cursor
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…il_spend_logging_reapply fix(proxy): Bedrock guardrail spend logs - hook mode, match redaction, streaming request_data
Relevant issues
Fixes:
guardrail_mode/ spend-log labels for Bedrock when proxy hooks areduring_call/post_callbut Bedrock usesINPUT/OUTPUTmatch/regexvalues in spend/compliance metadata,Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit(CI will confirm; locally:test_bedrock_guardrails.py,test_custom_guardrail.py,test_core_helpers.py,test_litellm/proxy/test_proxy_utils.py, and targetedproxy_unit_tests/test_proxy_utils.pyguardrail/during tests were run green.)@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Screenshots / Proof of Fix
Hook mode: Spend / evaluation UI should show PRE-CALL / DURING-CALL / POST-CALL aligned with proxy hooks, not inferred only from Bedrock

INPUT/OUTPUT.Match redaction: Compliance exports / metadata should not contain raw Bedrock



matchspans;"[REDACTED]"where applicable.Type
🐛 Bug Fix
✅ Test
Changes
Problem
Wrong
guardrail_modein spend logs: Bedrock ApplyGuardrailINPUT/OUTPUTwas used to infer logging hook phase, soduring_call(and somepost_call) runs could appear as PRE-CALL in spend/UI.Sensitive
match/regexin logs: Raw match-like fields could appear in standard guardrail logging (spend/compliance) and related paths.Fix
logging_event_typeonmake_bedrock_api_request— When provided, drivesevent_typeforadd_standard_logging_guardrail_information_to_request_data. If omitted, keep legacy mapping fromsource.BedrockGuardrail.use_native_during_call_hook—during_calluses nativeasync_moderation_hookso spend logs recordduring_callinstead of unifiedapply_guardrailalways looking like pre_call.ProxyLogging/_execute_guardrail_hookrespect the flag (CustomGuardraildefaultFalse).Centralized redaction —
redact_nested_match_and_regex_keysinlitellm_core_utils/core_helpers.py; applied inCustomGuardrail.add_standard_logging_guardrail_information_to_request_data. Bedrock passes raw JSON into that path (single redaction pass). Bedrock_redact_pii_matches/ HTTPdetail["assessments"]helpers delegate to the same logic.Tests
tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py: hook /logging_event_type/ redaction / streamingrequest_dataassertions.tests/test_litellm/integrations/test_custom_guardrail.py: standard logging redactsmatch/regex.tests/test_litellm/litellm_core_utils/test_core_helpers.py:redact_nested_match_and_regex_keysunit tests.Files (high level)
litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.pylitellm/proxy/utils.pylitellm/integrations/custom_guardrail.pylitellm/litellm_core_utils/core_helpers.pytests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.pytests/test_litellm/integrations/test_custom_guardrail.pytests/test_litellm/litellm_core_utils/test_core_helpers.py