mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(guardrails): skip None fields in in-memory guardrail updates so constructor defaults survive
This commit is contained in:
parent
8c72342ad5
commit
2286091a9a
3 changed files with 27 additions and 5 deletions
|
|
@ -1298,6 +1298,9 @@ class CustomGuardrail(CustomLogger):
|
|||
resync ``event_hook`` when the update carries a new ``mode``. The new
|
||||
mode is validated against ``supported_event_hooks`` before any state
|
||||
is mutated, so a rejected update leaves the guardrail untouched.
|
||||
``None`` values are skipped because both sources serialize every unset
|
||||
LitellmParams field as ``None``; applying them would clobber
|
||||
constructor-derived state (e.g. dict defaults) with ``None``.
|
||||
"""
|
||||
updated_params: Final[Mapping[str, object]] = (
|
||||
litellm_params if isinstance(litellm_params, Mapping) else vars(litellm_params)
|
||||
|
|
@ -1307,7 +1310,8 @@ class CustomGuardrail(CustomLogger):
|
|||
if new_event_hook is not None and self.supported_event_hooks:
|
||||
self._validate_or_warn_event_hook(new_event_hook, self.supported_event_hooks)
|
||||
for key, value in updated_params.items():
|
||||
setattr(self, key, value)
|
||||
if value is not None:
|
||||
setattr(self, key, value)
|
||||
if new_event_hook is not None:
|
||||
self.event_hook = new_event_hook
|
||||
|
||||
|
|
|
|||
|
|
@ -176,10 +176,10 @@ class ToolPermissionGuardrail(CustomGuardrail):
|
|||
super().update_in_memory_litellm_params(litellm_params)
|
||||
|
||||
# The generic update above sets ``self.rules`` from the incoming value
|
||||
# (None on a partial update that omits rules), but never rebuilds the
|
||||
# compiled maps. Rebuild them when rules are provided; otherwise restore
|
||||
# the previous ruleset so a partial update doesn't silently wipe it. An
|
||||
# explicit empty list still clears the rules.
|
||||
# (skipping None) but never rebuilds the compiled maps. Rebuild them
|
||||
# when a rules list is provided; otherwise restore the previous ruleset
|
||||
# so a non-list value can't silently wipe it. An explicit empty list
|
||||
# still clears the rules.
|
||||
rules: Final = params.get("rules")
|
||||
if isinstance(rules, list):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -2281,6 +2281,24 @@ class TestUpdateInMemoryLitellmParams:
|
|||
assert getattr(guardrail, "api_base", None) == "https://guardrail.example.com"
|
||||
assert guardrail.should_run_guardrail(data={}, event_type=GuardrailEventHooks.post_call) is True
|
||||
|
||||
def test_none_values_do_not_clobber_constructor_state(self):
|
||||
guardrail = self._guardrail()
|
||||
guardrail.additional_provider_specific_params = {"team": "security"}
|
||||
guardrail.api_base = "https://guardrail.example.com"
|
||||
|
||||
guardrail.update_in_memory_litellm_params(
|
||||
{
|
||||
"mode": "post_call",
|
||||
"api_base": None,
|
||||
"additional_provider_specific_params": None,
|
||||
"extra_headers": None,
|
||||
}
|
||||
)
|
||||
|
||||
assert guardrail.additional_provider_specific_params == {"team": "security"}
|
||||
assert guardrail.api_base == "https://guardrail.example.com"
|
||||
assert guardrail.event_hook is GuardrailEventHooks.post_call
|
||||
|
||||
def test_strict_mode_rejects_unsupported_mode_without_mutating(self, monkeypatch):
|
||||
monkeypatch.delenv("LITELLM_STRICT_GUARDRAIL_MODES", raising=False)
|
||||
guardrail = self._guardrail()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue