mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
Co-authored-by: Sameer Kankute <sameer@berri.ai>
This commit is contained in:
parent
6e9c0b0dd2
commit
d4915766d6
4 changed files with 79 additions and 9 deletions
|
|
@ -397,7 +397,7 @@ def _set_object_metadata_field(
|
|||
field_name: Name of the metadata field to set
|
||||
value: Value to set for the field
|
||||
"""
|
||||
if field_name in LiteLLM_ManagementEndpoint_MetadataFields_Premium:
|
||||
if field_name in LiteLLM_ManagementEndpoint_MetadataFields_Premium and value:
|
||||
_premium_user_check(field_name)
|
||||
|
||||
object_data.metadata = object_data.metadata or {}
|
||||
|
|
@ -563,13 +563,11 @@ def _update_metadata_field(updated_kv: dict, field_name: str) -> None:
|
|||
field_name: Name of the metadata field being updated
|
||||
"""
|
||||
if field_name in LiteLLM_ManagementEndpoint_MetadataFields_Premium:
|
||||
value = updated_kv.get(field_name)
|
||||
# Skip the premium check for empty collections ([] or {}).
|
||||
# The UI sends these as defaults even when the user hasn't configured
|
||||
# any enterprise features (see issue #20304). However, we still
|
||||
# proceed with the update so that users can intentionally clear a
|
||||
# previously-set field by sending an empty list/dict.
|
||||
if value is not None and value != [] and value != {}:
|
||||
# The UI sends falsy defaults (False, [], {}) even when the user has not
|
||||
# enabled any enterprise feature (see #20304, #30285); require a license
|
||||
# only for a truthy value. The falsy value is still persisted below so a
|
||||
# previously-set field can be cleared.
|
||||
if updated_kv.get(field_name):
|
||||
_premium_user_check()
|
||||
|
||||
if field_name in updated_kv and updated_kv[field_name] is not None:
|
||||
|
|
|
|||
|
|
@ -1793,7 +1793,8 @@ def prepare_metadata_fields(
|
|||
if k in LiteLLM_ManagementEndpoint_MetadataFields_Premium:
|
||||
from litellm.proxy.utils import _premium_user_check
|
||||
|
||||
_premium_user_check(k)
|
||||
if v:
|
||||
_premium_user_check(k)
|
||||
casted_metadata[k] = v
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -157,6 +157,32 @@ class TestUpdateMetadataFieldsEmptyCollections:
|
|||
assert "guardrails" not in updated_kv
|
||||
assert updated_kv["metadata"]["guardrails"] == ["my-guardrail"]
|
||||
|
||||
@patch("litellm.proxy.management_endpoints.common_utils._premium_user_check")
|
||||
def test_false_boolean_does_not_trigger_premium_check(self, mock_premium_check):
|
||||
"""
|
||||
Regression #30285: /team/update sends disable_global_guardrails=False
|
||||
(the UI's unchanged default). A falsy boolean must not trigger the
|
||||
premium check, so non-premium users are not wrongly 403'd.
|
||||
"""
|
||||
updated_kv = {"team_id": "test-team", "disable_global_guardrails": False}
|
||||
_update_metadata_fields(updated_kv=updated_kv)
|
||||
mock_premium_check.assert_not_called()
|
||||
|
||||
@patch("litellm.proxy.management_endpoints.common_utils._premium_user_check")
|
||||
def test_false_boolean_still_updates_metadata(self, mock_premium_check):
|
||||
"""A falsy boolean must still be moved into metadata so it persists."""
|
||||
updated_kv = {"team_id": "test-team", "disable_global_guardrails": False}
|
||||
_update_metadata_fields(updated_kv=updated_kv)
|
||||
assert "disable_global_guardrails" not in updated_kv
|
||||
assert updated_kv["metadata"]["disable_global_guardrails"] is False
|
||||
|
||||
@patch("litellm.proxy.management_endpoints.common_utils._premium_user_check")
|
||||
def test_true_boolean_triggers_premium_check(self, mock_premium_check):
|
||||
"""Control: enabling the premium feature (True) still requires a license."""
|
||||
updated_kv = {"team_id": "test-team", "disable_global_guardrails": True}
|
||||
_update_metadata_fields(updated_kv=updated_kv)
|
||||
mock_premium_check.assert_called()
|
||||
|
||||
@patch("litellm.proxy.management_endpoints.common_utils._premium_user_check")
|
||||
def test_ui_typical_payload_does_not_trigger_premium_check(
|
||||
self, mock_premium_check
|
||||
|
|
|
|||
|
|
@ -1547,6 +1547,51 @@ async def test_prepare_key_update_data_budget_limits_serializes_windows():
|
|||
assert windows[0]["reset_at"] is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_key_update_data_disable_global_guardrails_false_no_premium(
|
||||
monkeypatch,
|
||||
):
|
||||
"""
|
||||
Regression #30285: editing a key via the UI sends disable_global_guardrails=False
|
||||
(unchanged default). A non-premium user must NOT get a 403, and False must persist.
|
||||
"""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", False)
|
||||
data = UpdateKeyRequest(key="sk-1", disable_global_guardrails=False)
|
||||
existing_key = LiteLLM_VerificationToken(token="hashed")
|
||||
|
||||
result = await prepare_key_update_data(data=data, existing_key_row=existing_key)
|
||||
|
||||
assert result["metadata"]["disable_global_guardrails"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_key_update_data_disable_global_guardrails_true_requires_premium(
|
||||
monkeypatch,
|
||||
):
|
||||
"""Control: enabling the premium feature (True) without a license still 403s."""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", False)
|
||||
data = UpdateKeyRequest(key="sk-1", disable_global_guardrails=True)
|
||||
existing_key = LiteLLM_VerificationToken(token="hashed")
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await prepare_key_update_data(data=data, existing_key_row=existing_key)
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_key_update_data_disable_global_guardrails_true_premium_persists(
|
||||
monkeypatch,
|
||||
):
|
||||
"""A premium user enabling the feature (True) succeeds and the value persists."""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True)
|
||||
data = UpdateKeyRequest(key="sk-1", disable_global_guardrails=True)
|
||||
existing_key = LiteLLM_VerificationToken(token="hashed")
|
||||
|
||||
result = await prepare_key_update_data(data=data, existing_key_row=existing_key)
|
||||
|
||||
assert result["metadata"]["disable_global_guardrails"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_team_id_used_in_service_account_request_requires_team_id():
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue