mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-02 08:21:53 +00:00
Merge pull request #27441 from BerriAI/litellm_remove_tool_call_from_guardrails
feat(guardrails): optional skip tool message in unified guardrail inputs
This commit is contained in:
@@ -206,6 +206,7 @@ add_user_information_to_llm_headers: Optional[bool] = (
|
||||
)
|
||||
store_audit_logs = False # Enterprise feature, allow users to see audit logs
|
||||
skip_system_message_in_guardrail: bool = False
|
||||
skip_tool_message_in_guardrail: bool = False
|
||||
### end of callbacks #############
|
||||
|
||||
email: Optional[str] = (
|
||||
|
||||
@@ -23,7 +23,9 @@ from litellm.llms.anthropic.experimental_pass_through.adapters.transformation im
|
||||
from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation
|
||||
from litellm.llms.base_llm.guardrail_translation.utils import (
|
||||
effective_skip_system_message_for_guardrail,
|
||||
effective_skip_tool_message_for_guardrail,
|
||||
openai_messages_without_system,
|
||||
openai_messages_without_tool,
|
||||
)
|
||||
from litellm.proxy.pass_through_endpoints.llm_provider_handlers.anthropic_passthrough_logging_handler import (
|
||||
AnthropicPassthroughLoggingHandler,
|
||||
@@ -108,6 +110,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
||||
return data
|
||||
|
||||
skip_system = effective_skip_system_message_for_guardrail(guardrail_to_apply)
|
||||
skip_tool = effective_skip_tool_message_for_guardrail(guardrail_to_apply)
|
||||
|
||||
chat_completion_compatible_request = self._translate_to_openai(data)
|
||||
|
||||
@@ -117,6 +120,8 @@ class AnthropicMessagesHandler(BaseTranslation):
|
||||
)
|
||||
if skip_system:
|
||||
structured_messages = openai_messages_without_system(structured_messages)
|
||||
if skip_tool:
|
||||
structured_messages = openai_messages_without_tool(structured_messages)
|
||||
|
||||
texts_to_check: List[str] = []
|
||||
images_to_check: List[str] = []
|
||||
@@ -134,6 +139,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
||||
images_to_check=images_to_check,
|
||||
task_mappings=task_mappings,
|
||||
skip_system_message=skip_system,
|
||||
skip_tool_message=skip_tool,
|
||||
)
|
||||
|
||||
# Step 2: Apply guardrail to all texts in batch
|
||||
@@ -198,13 +204,17 @@ class AnthropicMessagesHandler(BaseTranslation):
|
||||
images_to_check: List[str],
|
||||
task_mappings: List[Tuple[int, Optional[int]]],
|
||||
skip_system_message: bool = False,
|
||||
skip_tool_message: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Extract text content and images from a message.
|
||||
|
||||
Override this method to customize text/image extraction logic.
|
||||
"""
|
||||
if skip_system_message and str(message.get("role") or "").lower() == "system":
|
||||
role = str(message.get("role") or "").lower()
|
||||
if skip_system_message and role == "system":
|
||||
return
|
||||
if skip_tool_message and role == "tool":
|
||||
return
|
||||
|
||||
content = message.get("content", None)
|
||||
|
||||
@@ -14,7 +14,22 @@ def effective_skip_system_message_for_guardrail(guardrail_to_apply: Any) -> bool
|
||||
return bool(getattr(litellm, "skip_system_message_in_guardrail", False))
|
||||
|
||||
|
||||
def effective_skip_tool_message_for_guardrail(guardrail_to_apply: Any) -> bool:
|
||||
per = getattr(guardrail_to_apply, "skip_tool_message_in_guardrail", None)
|
||||
if per is not None:
|
||||
return bool(per)
|
||||
import litellm
|
||||
|
||||
return bool(getattr(litellm, "skip_tool_message_in_guardrail", False))
|
||||
|
||||
|
||||
def openai_messages_without_system(
|
||||
messages: List[AllMessageValues],
|
||||
) -> List[AllMessageValues]:
|
||||
return [m for m in messages if str((m or {}).get("role") or "").lower() != "system"]
|
||||
|
||||
|
||||
def openai_messages_without_tool(
|
||||
messages: List[AllMessageValues],
|
||||
) -> List[AllMessageValues]:
|
||||
return [m for m in messages if str((m or {}).get("role") or "").lower() != "tool"]
|
||||
|
||||
@@ -21,7 +21,9 @@ from litellm._logging import verbose_proxy_logger
|
||||
from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation
|
||||
from litellm.llms.base_llm.guardrail_translation.utils import (
|
||||
effective_skip_system_message_for_guardrail,
|
||||
effective_skip_tool_message_for_guardrail,
|
||||
openai_messages_without_system,
|
||||
openai_messages_without_tool,
|
||||
)
|
||||
from litellm.main import stream_chunk_builder
|
||||
from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolParam
|
||||
@@ -73,6 +75,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
||||
return data
|
||||
|
||||
skip_system = effective_skip_system_message_for_guardrail(guardrail_to_apply)
|
||||
skip_tool = effective_skip_tool_message_for_guardrail(guardrail_to_apply)
|
||||
|
||||
texts_to_check: List[str] = []
|
||||
images_to_check: List[str] = []
|
||||
@@ -91,6 +94,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
||||
text_task_mappings=text_task_mappings,
|
||||
tool_call_task_mappings=tool_call_task_mappings,
|
||||
skip_system_message=skip_system,
|
||||
skip_tool_message=skip_tool,
|
||||
)
|
||||
|
||||
# Step 2: Apply guardrail to all texts and tool calls in batch
|
||||
@@ -102,11 +106,15 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
||||
inputs["tool_calls"] = tool_calls_to_check # type: ignore
|
||||
structured_messages = self.get_structured_messages(data)
|
||||
if structured_messages:
|
||||
inputs["structured_messages"] = (
|
||||
openai_messages_without_system(structured_messages)
|
||||
if skip_system
|
||||
else structured_messages
|
||||
)
|
||||
if skip_system:
|
||||
structured_messages = openai_messages_without_system(
|
||||
structured_messages
|
||||
)
|
||||
if skip_tool:
|
||||
structured_messages = openai_messages_without_tool(
|
||||
structured_messages
|
||||
)
|
||||
inputs["structured_messages"] = structured_messages
|
||||
# Pass tools (function definitions) to the guardrail
|
||||
tools = data.get("tools")
|
||||
if tools:
|
||||
@@ -176,13 +184,17 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
||||
text_task_mappings: List[Tuple[int, Optional[int]]],
|
||||
tool_call_task_mappings: List[Tuple[int, int]],
|
||||
skip_system_message: bool = False,
|
||||
skip_tool_message: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Extract text content, images, and tool calls from a message.
|
||||
|
||||
Override this method to customize text/image/tool call extraction logic.
|
||||
"""
|
||||
if skip_system_message and str(message.get("role") or "").lower() == "system":
|
||||
role = str(message.get("role") or "").lower()
|
||||
if skip_system_message and role == "system":
|
||||
return
|
||||
if skip_tool_message and role == "tool":
|
||||
return
|
||||
|
||||
content = message.get("content", None)
|
||||
|
||||
@@ -482,6 +482,11 @@ class InMemoryGuardrailHandler:
|
||||
"skip_system_message_in_guardrail",
|
||||
getattr(litellm_params, "skip_system_message_in_guardrail", None),
|
||||
)
|
||||
setattr(
|
||||
custom_guardrail_callback,
|
||||
"skip_tool_message_in_guardrail",
|
||||
getattr(litellm_params, "skip_tool_message_in_guardrail", None),
|
||||
)
|
||||
|
||||
parsed_guardrail = Guardrail(
|
||||
guardrail_id=guardrail.get("guardrail_id"),
|
||||
|
||||
@@ -633,6 +633,16 @@ class BaseLitellmParams(
|
||||
),
|
||||
)
|
||||
|
||||
skip_tool_message_in_guardrail: Optional[bool] = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"When True, unified guardrails skip tool-role messages when building "
|
||||
"evaluation inputs (texts and structured_messages). When False, tool "
|
||||
"messages are included even if litellm_settings sets a global skip. When "
|
||||
"None, use the global litellm.skip_tool_message_in_guardrail setting."
|
||||
),
|
||||
)
|
||||
|
||||
# Lakera specific params
|
||||
category_thresholds: Optional[LakeraCategoryThresholds] = Field(
|
||||
default=None,
|
||||
|
||||
+132
@@ -8,7 +8,9 @@ from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation
|
||||
from litellm.llms.base_llm.guardrail_translation.utils import (
|
||||
effective_skip_system_message_for_guardrail,
|
||||
effective_skip_tool_message_for_guardrail,
|
||||
openai_messages_without_system,
|
||||
openai_messages_without_tool,
|
||||
)
|
||||
from litellm.llms.openai.chat.guardrail_translation.handler import (
|
||||
OpenAIChatCompletionsHandler,
|
||||
@@ -180,6 +182,136 @@ class TestUnifiedLLMGuardrails:
|
||||
}
|
||||
assert "system" in roles
|
||||
|
||||
class TestSkipToolMessageForChatCompletions:
|
||||
def test_openai_messages_without_tool(self):
|
||||
msgs = [
|
||||
{"role": "user", "content": "hi"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "f", "arguments": "{}"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{"role": "tool", "content": "tool result", "tool_call_id": "call_1"},
|
||||
]
|
||||
out = openai_messages_without_tool(msgs)
|
||||
assert len(out) == 2
|
||||
assert all(m["role"] != "tool" for m in out)
|
||||
assert msgs[2]["content"] == "tool result"
|
||||
|
||||
def test_effective_skip_tool_respects_per_guardrail_over_global(
|
||||
self, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
litellm, "skip_tool_message_in_guardrail", True, raising=False
|
||||
)
|
||||
|
||||
class G:
|
||||
skip_tool_message_in_guardrail = False
|
||||
|
||||
assert effective_skip_tool_message_for_guardrail(G()) is False
|
||||
|
||||
class G2:
|
||||
skip_tool_message_in_guardrail = None
|
||||
|
||||
assert effective_skip_tool_message_for_guardrail(G2()) is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_handler_skips_tool_in_guardrail_inputs(self, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
litellm, "skip_tool_message_in_guardrail", True, raising=False
|
||||
)
|
||||
|
||||
captured = {}
|
||||
|
||||
class MockGuardrail:
|
||||
skip_tool_message_in_guardrail = None
|
||||
|
||||
async def apply_guardrail(
|
||||
self, inputs, request_data, input_type, logging_obj=None
|
||||
):
|
||||
captured["inputs"] = inputs
|
||||
return inputs
|
||||
|
||||
data = {
|
||||
"messages": [
|
||||
{"role": "user", "content": "hello"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "f", "arguments": "{}"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "secret tool result",
|
||||
"tool_call_id": "call_1",
|
||||
},
|
||||
],
|
||||
"model": "gpt-4o",
|
||||
}
|
||||
|
||||
handler = OpenAIChatCompletionsHandler()
|
||||
await handler.process_input_messages(
|
||||
data=data,
|
||||
guardrail_to_apply=MockGuardrail(),
|
||||
litellm_logging_obj=None,
|
||||
)
|
||||
|
||||
assert "secret tool result" not in captured["inputs"]["texts"]
|
||||
sm = captured["inputs"].get("structured_messages") or []
|
||||
assert all(m.get("role") != "tool" for m in sm)
|
||||
assert data["messages"][2]["content"] == "secret tool result"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_handler_per_guardrail_skip_tool_false_overrides_global(
|
||||
self, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
litellm, "skip_tool_message_in_guardrail", True, raising=False
|
||||
)
|
||||
|
||||
captured = {}
|
||||
|
||||
class MockGuardrail:
|
||||
skip_tool_message_in_guardrail = False
|
||||
|
||||
async def apply_guardrail(
|
||||
self, inputs, request_data, input_type, logging_obj=None
|
||||
):
|
||||
captured["inputs"] = inputs
|
||||
return inputs
|
||||
|
||||
data = {
|
||||
"messages": [
|
||||
{"role": "user", "content": "u"},
|
||||
{"role": "tool", "content": "tr", "tool_call_id": "call_1"},
|
||||
],
|
||||
}
|
||||
|
||||
await OpenAIChatCompletionsHandler().process_input_messages(
|
||||
data=data,
|
||||
guardrail_to_apply=MockGuardrail(),
|
||||
litellm_logging_obj=None,
|
||||
)
|
||||
|
||||
assert "tr" in captured["inputs"]["texts"]
|
||||
roles = {
|
||||
m.get("role")
|
||||
for m in (captured["inputs"].get("structured_messages") or [])
|
||||
}
|
||||
assert "tool" in roles
|
||||
|
||||
class TestAsyncPreCallHook:
|
||||
@pytest.mark.asyncio
|
||||
async def test_uses_mcp_event_type(self):
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createGuardrailCall, getGuardrailProviderSpecificParams, getGuardrailUI
|
||||
import ContentFilterConfiguration from "./content_filter/ContentFilterConfiguration";
|
||||
import {
|
||||
choiceToSkipSystemForCreate,
|
||||
choiceToSkipToolForCreate,
|
||||
getGuardrailProviders,
|
||||
guardrail_provider_map,
|
||||
guardrailLogoMap,
|
||||
@@ -188,6 +189,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
||||
mode: preset.mode,
|
||||
default_on: preset.defaultOn,
|
||||
skip_system_message_choice: "inherit",
|
||||
skip_tool_message_choice: "inherit",
|
||||
};
|
||||
if (preset.provider === "BlockCodeExecution") {
|
||||
baseValues.confidence_threshold = 0.5;
|
||||
@@ -433,6 +435,11 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
||||
guardrailData.litellm_params.skip_system_message_in_guardrail = skipForCreate;
|
||||
}
|
||||
|
||||
const skipToolForCreate = choiceToSkipToolForCreate(values.skip_tool_message_choice);
|
||||
if (skipToolForCreate !== undefined) {
|
||||
guardrailData.litellm_params.skip_tool_message_in_guardrail = skipToolForCreate;
|
||||
}
|
||||
|
||||
// For Presidio PII, add the entity and action configurations
|
||||
if (values.provider === "PresidioPII" && selectedEntities.length > 0) {
|
||||
const piiEntitiesConfig: { [key: string]: string } = {};
|
||||
@@ -804,6 +811,18 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="skip_tool_message_choice"
|
||||
label="Skip tool messages in guardrail"
|
||||
tooltip="Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail."
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="inherit">Use global default</Select.Option>
|
||||
<Select.Option value="yes">Yes — exclude from guardrail scan</Select.Option>
|
||||
<Select.Option value="no">No — always include in scan</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{/* Use the GuardrailProviderFields component to render provider-specific fields */}
|
||||
{!isToolPermissionProvider && !shouldRenderContentFilterConfigSettings(selectedProvider) && !shouldRenderLLMJudgeFields(selectedProvider) && (
|
||||
<GuardrailProviderFields
|
||||
@@ -1155,6 +1174,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
||||
mode: "pre_call",
|
||||
default_on: false,
|
||||
skip_system_message_choice: "inherit",
|
||||
skip_tool_message_choice: "inherit",
|
||||
}}
|
||||
>
|
||||
{stepConfigs.map((step, index) => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
guardrailLogoMap,
|
||||
getGuardrailProviders,
|
||||
type SkipSystemMessageChoice,
|
||||
type SkipToolMessageChoice,
|
||||
} from "./guardrail_info_helpers";
|
||||
import { getGuardrailUISettings, getGlobalLitellmHeaderName } from "../networking";
|
||||
import PiiConfiguration from "./pii_configuration";
|
||||
@@ -29,6 +30,7 @@ interface EditGuardrailFormProps {
|
||||
default_on: boolean;
|
||||
pii_entities_config?: { [key: string]: string };
|
||||
skip_system_message_choice?: SkipSystemMessageChoice;
|
||||
skip_tool_message_choice?: SkipToolMessageChoice;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
@@ -138,6 +140,15 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
||||
delete litellm_params.skip_system_message_in_guardrail;
|
||||
}
|
||||
|
||||
const skipToolChoice = values.skip_tool_message_choice as SkipToolMessageChoice | undefined;
|
||||
if (skipToolChoice === "yes") {
|
||||
litellm_params.skip_tool_message_in_guardrail = true;
|
||||
} else if (skipToolChoice === "no") {
|
||||
litellm_params.skip_tool_message_in_guardrail = false;
|
||||
} else {
|
||||
delete litellm_params.skip_tool_message_in_guardrail;
|
||||
}
|
||||
|
||||
let guardrail_info: any = {};
|
||||
|
||||
// For Presidio PII, add the entity and action configurations
|
||||
@@ -432,6 +443,18 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="skip_tool_message_choice"
|
||||
label="Skip tool messages in guardrail"
|
||||
tooltip="Unified guardrails only: whether role: tool content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail."
|
||||
>
|
||||
<Select>
|
||||
<Option value="inherit">Use global default</Option>
|
||||
<Option value="yes">Yes — exclude from guardrail scan</Option>
|
||||
<Option value="no">No — always include in scan</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{renderProviderSpecificFields()}
|
||||
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
|
||||
@@ -29,7 +29,9 @@ import {
|
||||
getGuardrailLogoAndName,
|
||||
guardrail_provider_map,
|
||||
skipSystemMessageToChoice,
|
||||
skipToolMessageToChoice,
|
||||
type SkipSystemMessageChoice,
|
||||
type SkipToolMessageChoice,
|
||||
} from "./guardrail_info_helpers";
|
||||
import GuardrailOptionalParams from "./guardrail_optional_params";
|
||||
import GuardrailProviderFields from "./guardrail_provider_fields";
|
||||
@@ -214,12 +216,16 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
|
||||
if (guardrailData && form) {
|
||||
const lp = { ...(guardrailData.litellm_params || {}) };
|
||||
delete lp.skip_system_message_in_guardrail;
|
||||
delete lp.skip_tool_message_in_guardrail;
|
||||
form.setFieldsValue({
|
||||
guardrail_name: guardrailData.guardrail_name,
|
||||
...lp,
|
||||
skip_system_message_choice: skipSystemMessageToChoice(
|
||||
guardrailData.litellm_params?.skip_system_message_in_guardrail,
|
||||
),
|
||||
skip_tool_message_choice: skipToolMessageToChoice(
|
||||
guardrailData.litellm_params?.skip_tool_message_in_guardrail,
|
||||
),
|
||||
guardrail_info: guardrailData.guardrail_info ? JSON.stringify(guardrailData.guardrail_info, null, 2) : "",
|
||||
// Include any optional_params if they exist
|
||||
...(guardrailData.litellm_params?.optional_params && {
|
||||
@@ -302,6 +308,20 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
|
||||
}
|
||||
}
|
||||
|
||||
const prevSkipToolChoice = skipToolMessageToChoice(
|
||||
guardrailData.litellm_params?.skip_tool_message_in_guardrail,
|
||||
);
|
||||
const nextSkipToolChoice = values.skip_tool_message_choice as SkipToolMessageChoice | undefined;
|
||||
if (nextSkipToolChoice !== undefined && nextSkipToolChoice !== prevSkipToolChoice) {
|
||||
if (nextSkipToolChoice === "inherit") {
|
||||
updateData.litellm_params.skip_tool_message_in_guardrail = null;
|
||||
} else if (nextSkipToolChoice === "yes") {
|
||||
updateData.litellm_params.skip_tool_message_in_guardrail = true;
|
||||
} else {
|
||||
updateData.litellm_params.skip_tool_message_in_guardrail = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only include guardrail_info if it has changed
|
||||
const originalGuardrailInfo = guardrailData.guardrail_info;
|
||||
const newGuardrailInfo = values.guardrail_info ? JSON.parse(values.guardrail_info) : undefined;
|
||||
@@ -674,11 +694,15 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
|
||||
...(() => {
|
||||
const lp = { ...(guardrailData.litellm_params || {}) };
|
||||
delete lp.skip_system_message_in_guardrail;
|
||||
delete lp.skip_tool_message_in_guardrail;
|
||||
return lp;
|
||||
})(),
|
||||
skip_system_message_choice: skipSystemMessageToChoice(
|
||||
guardrailData.litellm_params?.skip_system_message_in_guardrail,
|
||||
),
|
||||
skip_tool_message_choice: skipToolMessageToChoice(
|
||||
guardrailData.litellm_params?.skip_tool_message_in_guardrail,
|
||||
),
|
||||
guardrail_info: guardrailData.guardrail_info
|
||||
? JSON.stringify(guardrailData.guardrail_info, null, 2)
|
||||
: "",
|
||||
@@ -716,6 +740,18 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Skip tool messages in guardrail"
|
||||
name="skip_tool_message_choice"
|
||||
tooltip="Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail."
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="inherit">Use global default</Select.Option>
|
||||
<Select.Option value="yes">Yes — exclude from guardrail scan</Select.Option>
|
||||
<Select.Option value="no">No — always include in scan</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{guardrailData.litellm_params?.guardrail === "presidio" && (
|
||||
<>
|
||||
<Divider orientation="left">PII Protection</Divider>
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
GuardrailProviders,
|
||||
skipSystemMessageToChoice,
|
||||
choiceToSkipSystemForCreate,
|
||||
skipToolMessageToChoice,
|
||||
choiceToSkipToolForCreate,
|
||||
} from "./guardrail_info_helpers";
|
||||
|
||||
describe("guardrail_info_helpers", () => {
|
||||
@@ -215,4 +217,18 @@ describe("guardrail_info_helpers", () => {
|
||||
expect(choiceToSkipSystemForCreate("no")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("skipToolMessageToChoice / choiceToSkipToolForCreate", () => {
|
||||
it("maps API values to form choices and back for create", () => {
|
||||
expect(skipToolMessageToChoice(undefined)).toBe("inherit");
|
||||
expect(skipToolMessageToChoice(null)).toBe("inherit");
|
||||
expect(skipToolMessageToChoice(true)).toBe("yes");
|
||||
expect(skipToolMessageToChoice(false)).toBe("no");
|
||||
|
||||
expect(choiceToSkipToolForCreate("inherit")).toBeUndefined();
|
||||
expect(choiceToSkipToolForCreate(undefined)).toBeUndefined();
|
||||
expect(choiceToSkipToolForCreate("yes")).toBe(true);
|
||||
expect(choiceToSkipToolForCreate("no")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,3 +179,19 @@ export function choiceToSkipSystemForCreate(choice: SkipSystemMessageChoice | un
|
||||
if (choice === "no") return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Tri-state UI value for `litellm_params.skip_tool_message_in_guardrail` (inherit = use global). */
|
||||
export type SkipToolMessageChoice = "inherit" | "yes" | "no";
|
||||
|
||||
export function skipToolMessageToChoice(v: boolean | null | undefined): SkipToolMessageChoice {
|
||||
if (v === true) return "yes";
|
||||
if (v === false) return "no";
|
||||
return "inherit";
|
||||
}
|
||||
|
||||
/** Create flow: omit key when inheriting global default. */
|
||||
export function choiceToSkipToolForCreate(choice: SkipToolMessageChoice | undefined): boolean | undefined {
|
||||
if (choice === "yes") return true;
|
||||
if (choice === "no") return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,12 @@ import {
|
||||
SortingState,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { getGuardrailLogoAndName, guardrail_provider_map, skipSystemMessageToChoice } from "./guardrail_info_helpers";
|
||||
import {
|
||||
getGuardrailLogoAndName,
|
||||
guardrail_provider_map,
|
||||
skipSystemMessageToChoice,
|
||||
skipToolMessageToChoice,
|
||||
} from "./guardrail_info_helpers";
|
||||
import EditGuardrailForm from "./edit_guardrail_form";
|
||||
import { Guardrail, GuardrailDefinitionLocation } from "./types";
|
||||
|
||||
@@ -304,6 +309,9 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
|
||||
skip_system_message_choice: skipSystemMessageToChoice(
|
||||
selectedGuardrail.litellm_params?.skip_system_message_in_guardrail,
|
||||
),
|
||||
skip_tool_message_choice: skipToolMessageToChoice(
|
||||
selectedGuardrail.litellm_params?.skip_tool_message_in_guardrail,
|
||||
),
|
||||
...selectedGuardrail.guardrail_info,
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user