From ba097a7b8b0fe304525b048dad2b8a18cbb00f03 Mon Sep 17 00:00:00 2001 From: Numan Elahi Date: Fri, 21 Aug 2026 11:02:27 +0530 Subject: [PATCH] fix(cisco-ai-defense): preserve request direction for tool definitions --- .../cisco_ai_defense/cisco_ai_defense.py | 2 +- .../test_cisco_ai_defense_chat.py | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py b/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py index facb822d00d..6e8d242efa3 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py +++ b/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py @@ -1866,7 +1866,7 @@ class CiscoAIDefenseGuardrail(_CiscoAIDefenseMcpMixin, CustomGuardrail): tool_text: Final = CiscoAIDefenseGuardrail._extract_tool_definition_text(data) if tool_text: - messages.append({"role": "system", "content": tool_text}) + messages.append({"role": "tool", "content": tool_text}) return messages diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_cisco_ai_defense_chat.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_cisco_ai_defense_chat.py index 8974a18593b..3f0a4ff7835 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_cisco_ai_defense_chat.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_cisco_ai_defense_chat.py @@ -1833,6 +1833,31 @@ class TestCiscoAIDefenseToolDefinitionBypass: f"Sent: {sent!r}" ) + @pytest.mark.asyncio + async def test_pre_call_sends_tool_definitions_as_final_tool_message(self): + g = _make_guardrail(event_hook="pre_call") + data = self._tools_request("Fetch weather data") + data["messages"] = [ + {"role": "system", "content": "Follow the security policy."}, + {"role": "user", "content": "First question"}, + {"role": "assistant", "content": "First answer"}, + {"role": "user", "content": "Current question"}, + ] + post_mock = AsyncMock(return_value=_safe_response()) + + with _patch_inspection_post(g, post_mock): + await g.async_pre_call_hook( + user_api_key_dict=UserAPIKeyAuth(), + cache=DualCache(), + data=data, + call_type="completion", + ) + + sent_messages = post_mock.call_args.kwargs["json"]["messages"] + assert sent_messages[:-1] == data["messages"] + assert sent_messages[-1]["role"] == "tool" + assert "get_weather" in sent_messages[-1]["content"] + @pytest.mark.asyncio async def test_pre_call_scans_legacy_functions_definitions(self): g = _make_guardrail(event_hook="pre_call") @@ -1884,7 +1909,7 @@ class TestCiscoAIDefenseToolDefinitionBypass: cisco_resp = _redact_response( sanitized_messages=[ {"role": "user", "content": "what's the weather?"}, - {"role": "system", "content": "[REDACTED] tool description"}, + {"role": "tool", "content": "[REDACTED] tool description"}, ] )