mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(guardrails): stop sibling defaults leaking into Azure Content Safety
LitellmParams mixes in every guardrail's config model, so dumping it with exclude_none handed the Azure guardrails 37 defaults belonging to other providers. One of them broke config-driven setups outright: Javelin's api_version of "v1" replaced the Content Safety default, and Azure answers api-version=v1 with 404 Resource not found Forward only the params the config actually set, the way the Prisma AIRS initializer already does. A config that omits api_version now gets the Content Safety default of 2024-09-01, and an explicit value still wins The Admin UI was unaffected, because its form submits the api_version it reads from the provider schema. Any other caller that omits api_version hits this, which is why the documented config.yaml example never worked Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b40a7b5b53
commit
19d8737998
3 changed files with 104 additions and 2 deletions
|
|
@ -32,7 +32,7 @@ def initialize_guardrail(
|
|||
) = AzureContentSafetyPromptShieldGuardrail(
|
||||
guardrail_name=guardrail_name,
|
||||
**{
|
||||
**litellm_params.model_dump(exclude_none=True),
|
||||
**litellm_params.model_dump(exclude_unset=True),
|
||||
"api_key": litellm_params.api_key,
|
||||
"api_base": litellm_params.api_base,
|
||||
"entra_token_provider": entra_token_provider,
|
||||
|
|
@ -44,7 +44,7 @@ def initialize_guardrail(
|
|||
azure_content_safety_guardrail = AzureContentSafetyTextModerationGuardrail(
|
||||
guardrail_name=guardrail_name,
|
||||
**{
|
||||
**litellm_params.model_dump(exclude_none=True),
|
||||
**litellm_params.model_dump(exclude_unset=True),
|
||||
"api_key": litellm_params.api_key,
|
||||
"api_base": litellm_params.api_base,
|
||||
"entra_token_provider": entra_token_provider,
|
||||
|
|
|
|||
|
|
@ -690,3 +690,62 @@ async def test_clearing_api_key_at_runtime_switches_to_entra(api_base, capturing
|
|||
assert "authorization" not in sent[0].headers
|
||||
assert sent[1].headers["Authorization"] == "Bearer entra-token"
|
||||
assert "ocp-apim-subscription-key" not in sent[1].headers
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_without_api_version_uses_the_content_safety_default(api_base, capturing_handler):
|
||||
"""A config that omits api_version gets the Content Safety default, not another guardrail's."""
|
||||
handler, sent = capturing_handler
|
||||
|
||||
guardrail = initialize_guardrail(
|
||||
LitellmParams(guardrail="azure/prompt_shield", mode="pre_call", api_base=api_base),
|
||||
{"guardrail_name": "azure-prompt-shield"},
|
||||
entra_token_provider=lambda: "entra-token",
|
||||
)
|
||||
guardrail.async_handler = handler
|
||||
|
||||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data={}, input_type="request")
|
||||
|
||||
assert sent[0].url.params["api-version"] == "2024-09-01"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_api_version_is_honoured(api_base, capturing_handler):
|
||||
"""Pinning an older Content Safety version stays possible."""
|
||||
handler, sent = capturing_handler
|
||||
|
||||
guardrail = initialize_guardrail(
|
||||
LitellmParams(guardrail="azure/prompt_shield", mode="pre_call", api_base=api_base, api_version="2023-10-01"),
|
||||
{"guardrail_name": "azure-prompt-shield"},
|
||||
entra_token_provider=lambda: "entra-token",
|
||||
)
|
||||
guardrail.async_handler = handler
|
||||
|
||||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data={}, input_type="request")
|
||||
|
||||
assert sent[0].url.params["api-version"] == "2023-10-01"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_pricing_extras_survive_the_forwarded_params(api_base, capturing_handler):
|
||||
"""cost_tier and price_per_1000_text_records arrive as pydantic extras rather than declared
|
||||
fields, so forwarding only the params the config set must still carry them through."""
|
||||
handler, _ = capturing_handler
|
||||
|
||||
guardrail = initialize_guardrail(
|
||||
LitellmParams(
|
||||
guardrail="azure/prompt_shield",
|
||||
mode="pre_call",
|
||||
api_base=api_base,
|
||||
cost_tier="paid",
|
||||
price_per_1000_text_records=0.38,
|
||||
),
|
||||
{"guardrail_name": "azure-prompt-shield"},
|
||||
entra_token_provider=lambda: "entra-token",
|
||||
)
|
||||
guardrail.async_handler = handler
|
||||
request_data = {"metadata": {}}
|
||||
|
||||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data=request_data, input_type="request")
|
||||
|
||||
assert _recorded_guardrail_info(request_data)["guardrail_cost"] == pytest.approx(0.38 / 1000)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -486,3 +487,45 @@ async def test_initialize_guardrail_without_api_key_authenticates_with_entra(api
|
|||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data={}, input_type="request")
|
||||
|
||||
assert sent[0].headers["Authorization"] == "Bearer entra-token"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_without_api_version_uses_the_content_safety_default(api_base, capturing_handler):
|
||||
"""A config that omits api_version gets the Content Safety default, not another guardrail's."""
|
||||
handler, sent = capturing_handler
|
||||
|
||||
guardrail = initialize_guardrail(
|
||||
LitellmParams(guardrail="azure/text_moderations", mode="pre_call", api_base=api_base),
|
||||
{"guardrail_name": "azure-text-moderation"},
|
||||
entra_token_provider=lambda: "entra-token",
|
||||
)
|
||||
guardrail.async_handler = handler
|
||||
|
||||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data={}, input_type="request")
|
||||
|
||||
assert sent[0].url.params["api-version"] == "2024-09-01"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_moderation_options_still_reach_the_request(api_base, capturing_handler):
|
||||
"""Forwarding only params the config set must not drop the guardrail's own options."""
|
||||
handler, sent = capturing_handler
|
||||
|
||||
guardrail = initialize_guardrail(
|
||||
LitellmParams(
|
||||
guardrail="azure/text_moderations",
|
||||
mode="pre_call",
|
||||
api_base=api_base,
|
||||
outputType="EightSeverityLevels",
|
||||
blocklistNames=["my-blocklist"],
|
||||
),
|
||||
{"guardrail_name": "azure-text-moderation"},
|
||||
entra_token_provider=lambda: "entra-token",
|
||||
)
|
||||
guardrail.async_handler = handler
|
||||
|
||||
await guardrail.apply_guardrail(inputs={"texts": ["hello"]}, request_data={}, input_type="request")
|
||||
|
||||
body = json.loads(sent[0].content)
|
||||
assert body["outputType"] == "EightSeverityLevels"
|
||||
assert body["blocklistNames"] == ["my-blocklist"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue