mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(guardrails): lower tool permission guardrail log noise
The tool permission guardrail emitted expected, non-actionable events at WARNING; the no-tools skip path and every denied-by-rule message now log at DEBUG and INFO respectively so normal operation stops flooding logs
This commit is contained in:
parent
bf02a4a47f
commit
7596f48779
2 changed files with 71 additions and 4 deletions
|
|
@ -665,7 +665,7 @@ class ToolPermissionGuardrail(CustomGuardrail):
|
|||
|
||||
new_tools = self._collect_request_tools(data)
|
||||
if not new_tools:
|
||||
verbose_proxy_logger.warning(
|
||||
verbose_proxy_logger.debug(
|
||||
"Tool Permission Guardrail: not running guardrail. No tools or functions in data"
|
||||
)
|
||||
return data
|
||||
|
|
@ -676,7 +676,7 @@ class ToolPermissionGuardrail(CustomGuardrail):
|
|||
is_allowed, _, message = self._check_tool_permission(tool_name, tool_type)
|
||||
|
||||
if not is_allowed and message is not None:
|
||||
verbose_proxy_logger.warning(f"Tool Permission Guardrail: {message}")
|
||||
verbose_proxy_logger.info(f"Tool Permission Guardrail: {message}")
|
||||
if self.on_disallowed_action == "block":
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
|
|
@ -734,7 +734,7 @@ class ToolPermissionGuardrail(CustomGuardrail):
|
|||
is_allowed, rule_id, message = self._get_permission_for_tool_call(tool_call)
|
||||
|
||||
if not is_allowed and message is not None:
|
||||
verbose_proxy_logger.warning(f"Tool Permission Guardrail: {message}")
|
||||
verbose_proxy_logger.info(f"Tool Permission Guardrail: {message}")
|
||||
|
||||
if self.on_disallowed_action == "block":
|
||||
raise GuardrailRaisedException(
|
||||
|
|
@ -813,7 +813,7 @@ class ToolPermissionGuardrail(CustomGuardrail):
|
|||
is_allowed, rule_id, message = self._get_permission_for_tool_call(tool_call)
|
||||
|
||||
if not is_allowed and message is not None:
|
||||
verbose_proxy_logger.warning(f"Tool Permission Guardrail: {message}")
|
||||
verbose_proxy_logger.info(f"Tool Permission Guardrail: {message}")
|
||||
|
||||
if self.on_disallowed_action == "block":
|
||||
raise GuardrailRaisedException(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ Unit tests for Tool Permission Guardrail (OpenAI tool_calls semantics)
|
|||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
|
@ -612,6 +613,72 @@ class TestToolPermissionGuardrail:
|
|||
assert excinfo.value.status_code == 400
|
||||
assert excinfo.value.detail.get("detection_message") == "blocked Read by policy"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_pre_call_hook_no_tools_logs_at_debug(self, caplog):
|
||||
user_api_key_dict = UserAPIKeyAuth()
|
||||
cache = DualCache(default_in_memory_ttl=1)
|
||||
|
||||
with patch.object(self.guardrail, "should_run_guardrail", return_value=True):
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
await self.guardrail.async_pre_call_hook(
|
||||
user_api_key_dict=user_api_key_dict,
|
||||
cache=cache,
|
||||
data={},
|
||||
call_type="completion",
|
||||
)
|
||||
|
||||
matching = [r for r in caplog.records if "not running guardrail" in r.message]
|
||||
assert matching, "expected the no-tools message to be logged"
|
||||
assert all(r.levelno == logging.DEBUG for r in matching)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_pre_call_hook_denied_tool_logs_at_info(self, caplog):
|
||||
data = {"tools": [{"type": "function", "function": {"name": "Read"}}]}
|
||||
user_api_key_dict = UserAPIKeyAuth()
|
||||
cache = DualCache(default_in_memory_ttl=1)
|
||||
|
||||
with patch.object(self.guardrail, "should_run_guardrail", return_value=True):
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
with pytest.raises(HTTPException):
|
||||
await self.guardrail.async_pre_call_hook(
|
||||
user_api_key_dict=user_api_key_dict,
|
||||
cache=cache,
|
||||
data=data,
|
||||
call_type="completion",
|
||||
)
|
||||
|
||||
matching = [
|
||||
r
|
||||
for r in caplog.records
|
||||
if r.message.startswith("Tool Permission Guardrail:")
|
||||
and "denied by rule" in r.message
|
||||
]
|
||||
assert matching, "expected the denied-by-rule message to be logged"
|
||||
assert all(r.levelno == logging.INFO for r in matching)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_post_call_success_hook_denied_tool_logs_at_info(self, caplog):
|
||||
tool_call = {"function": {"name": "Read", "arguments": "{}"}, "type": "function"}
|
||||
response = ModelResponse(choices=[Choices(message={"tool_calls": [tool_call]})])
|
||||
user_api_key_dict = UserAPIKeyAuth()
|
||||
data = {"guardrails": ["test-tool-permission"]}
|
||||
|
||||
with patch.object(self.guardrail, "should_run_guardrail", return_value=True):
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
with pytest.raises(GuardrailRaisedException):
|
||||
await self.guardrail.async_post_call_success_hook(
|
||||
data=data, user_api_key_dict=user_api_key_dict, response=response
|
||||
)
|
||||
|
||||
matching = [
|
||||
r
|
||||
for r in caplog.records
|
||||
if r.message.startswith("Tool Permission Guardrail:")
|
||||
and "denied by rule" in r.message
|
||||
]
|
||||
assert matching, "expected the denied-by-rule message to be logged"
|
||||
assert all(r.levelno == logging.INFO for r in matching)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_pre_call_hook_rewrite_mode(self):
|
||||
guardrail = ToolPermissionGuardrail(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue