From 88beed905ea9a37d2ae67a021a754936500ff12b Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Sat, 11 Apr 2026 13:59:59 -0700 Subject: [PATCH] feat(guardrails): per-team opt-out for specific global guardrails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add disabled_global_guardrails list field to team metadata that selectively skips named globals at request time. Coexists with the existing disable_global_guardrails boolean kill switch — the boolean kills all globals (including future ones), the new list selectively skips named ones (new globals auto-apply). The field lives in the team metadata JSON column; no schema migration. - litellm/proxy/litellm_pre_call_utils.py: propagate the field from team_metadata into per-request data.metadata - litellm/integrations/custom_guardrail.py: new get_disabled_global_guardrails_from_metadata helper plus a scoped-to-globals early return at the top of should_run_guardrail --- litellm/integrations/custom_guardrail.py | 13 +++++++++++++ litellm/proxy/litellm_pre_call_utils.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py index c95010e7f3..fafa084d21 100644 --- a/litellm/integrations/custom_guardrail.py +++ b/litellm/integrations/custom_guardrail.py @@ -266,6 +266,15 @@ class CustomGuardrail(CustomLogger): return metadata["disable_global_guardrails"] return False + def get_disabled_global_guardrails_from_metadata(self, data: dict) -> List[str]: + """ + Returns the list of global guardrail names the team/key has opted out of. + """ + if "disabled_global_guardrails" in data: + return data["disabled_global_guardrails"] or [] + metadata = data.get("litellm_metadata") or data.get("metadata", {}) + return metadata.get("disabled_global_guardrails") or [] + def _is_valid_response_type(self, result: Any) -> bool: """ Check if result is a valid LLMResponseTypes instance. @@ -406,6 +415,7 @@ class CustomGuardrail(CustomLogger): """ requested_guardrails = self.get_guardrail_from_metadata(data) disable_global_guardrail = self.get_disable_global_guardrail(data) + disabled_global_guardrails = self.get_disabled_global_guardrails_from_metadata(data) verbose_logger.debug( "inside should_run_guardrail for guardrail=%s event_type= %s guardrail_supported_event_hooks= %s requested_guardrails= %s self.default_on= %s", self.guardrail_name, @@ -414,6 +424,9 @@ class CustomGuardrail(CustomLogger): requested_guardrails, self.default_on, ) + if self.default_on is True and self.guardrail_name in disabled_global_guardrails: + return False + if self.default_on is True and disable_global_guardrail is not True: if self._event_hook_is_event_type(event_type): if isinstance(self.event_hook, Mode): diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 2b8c16ed12..4d893112a3 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -1103,6 +1103,12 @@ async def add_litellm_data_to_request( # noqa: PLR0915 data[_metadata_variable_name]["disable_global_guardrails"] = team_metadata[ "disable_global_guardrails" ] + if "disabled_global_guardrails" in team_metadata and isinstance( + team_metadata["disabled_global_guardrails"], list + ): + data[_metadata_variable_name]["disabled_global_guardrails"] = team_metadata[ + "disabled_global_guardrails" + ] if "spend_logs_metadata" in team_metadata and isinstance( team_metadata["spend_logs_metadata"], dict ):