mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
Merge pull request #41701 from BerriAI/litellm_cherrypick_rc_1_102_0
fix(license): backport the wildcard license auto_router grant to rc/1.102.0 (#41684)
This commit is contained in:
commit
4fd5bc6b22
2 changed files with 44 additions and 12 deletions
|
|
@ -17,6 +17,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
AUTO_ROUTER_LICENSE_FEATURE: Final = "auto_router"
|
||||
LICENSE_ALL_FEATURES: Final = "*"
|
||||
AUTO_ROUTER_LICENSE_REMEDY: Final = "A LiteLLM license with the 'auto_router' feature lifts the limit."
|
||||
|
||||
|
||||
|
|
@ -153,17 +154,21 @@ class LicenseCheck:
|
|||
return False
|
||||
return team_count > _max_teams_in_license
|
||||
|
||||
def grants_feature(self, feature: str) -> bool:
|
||||
if self.airgapped_license_data is None:
|
||||
return False
|
||||
allowed_features: Final = self.airgapped_license_data.get("allowed_features")
|
||||
granted: Final = allowed_features if isinstance(allowed_features, list) else (allowed_features,)
|
||||
return feature in granted or LICENSE_ALL_FEATURES in granted
|
||||
|
||||
def auto_router_capability_limit(self) -> int | None:
|
||||
"""
|
||||
How many auto-routers may claim each licensed capability (heuristic_v2, operator-defined
|
||||
tier_definitions): unlimited (None) only when the signed license lists the auto_router
|
||||
feature, otherwise one per capability. A license verified through the API carries no
|
||||
feature list, so it does not lift the limit either.
|
||||
How many auto-routers may claim each gated classifier or customization capability:
|
||||
unlimited (None) only when the signed license lists the auto_router feature or the
|
||||
"*" wildcard that grants every feature, otherwise one per capability. A license verified
|
||||
through the API carries no feature list, so it does not lift the limit either.
|
||||
"""
|
||||
if self.airgapped_license_data is None:
|
||||
return 1
|
||||
allowed_features: Final = self.airgapped_license_data.get("allowed_features")
|
||||
if isinstance(allowed_features, list) and AUTO_ROUTER_LICENSE_FEATURE in allowed_features:
|
||||
if self.grants_feature(AUTO_ROUTER_LICENSE_FEATURE):
|
||||
return None
|
||||
return 1
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ def test_is_over_limit():
|
|||
|
||||
|
||||
def test_auto_router_capability_limit() -> None:
|
||||
"""Only the signed license's auto_router feature lifts the one-router limit; an API-verified
|
||||
license (no airgapped data) and an airgapped license without the feature keep it."""
|
||||
"""The signed license's auto_router feature or its "*" wildcard lifts the one-router limit; an
|
||||
API-verified license (no airgapped data) and an airgapped license without either keep it."""
|
||||
license_check = LicenseCheck()
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01", "allowed_features": ["auto_router"]}
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
|
@ -47,9 +47,18 @@ def test_auto_router_capability_limit() -> None:
|
|||
}
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01", "allowed_features": ["*"]}
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01", "allowed_features": ["sso", "*"]}
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01", "allowed_features": ["sso"]}
|
||||
assert license_check.auto_router_capability_limit() == 1
|
||||
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01", "allowed_features": "*"}
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
license_check.airgapped_license_data = {"expiration_date": "2999-01-01"}
|
||||
assert license_check.auto_router_capability_limit() == 1
|
||||
|
||||
|
|
@ -57,7 +66,9 @@ def test_auto_router_capability_limit() -> None:
|
|||
assert license_check.auto_router_capability_limit() == 1
|
||||
|
||||
|
||||
def _signed_license(expiration_date: str) -> tuple[RSAPublicKey, str]:
|
||||
def _signed_license(
|
||||
expiration_date: str, allowed_features: tuple[str, ...] = ("auto_router",)
|
||||
) -> tuple[RSAPublicKey, str]:
|
||||
import base64
|
||||
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
|
@ -65,7 +76,7 @@ def _signed_license(expiration_date: str) -> tuple[RSAPublicKey, str]:
|
|||
|
||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
message = json.dumps(
|
||||
{"expiration_date": expiration_date, "user_id": "u", "allowed_features": ["auto_router"]}
|
||||
{"expiration_date": expiration_date, "user_id": "u", "allowed_features": list(allowed_features)}
|
||||
).encode()
|
||||
signature = private_key.sign(
|
||||
message,
|
||||
|
|
@ -99,3 +110,19 @@ def test_valid_signed_license_with_auto_router_lifts_the_limit() -> None:
|
|||
|
||||
assert license_check.verify_license_without_api_request(public_key=public_key, license_key=license_key) is True
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
|
||||
def test_valid_signed_wildcard_license_lifts_the_limit() -> None:
|
||||
"""The license generator defaults allowed_features to ["*"], meaning every feature, so a wildcard
|
||||
license grants auto_router the same way a license that names it does."""
|
||||
license_check = LicenseCheck()
|
||||
public_key, license_key = _signed_license("2999-01-01", allowed_features=("*",))
|
||||
|
||||
assert license_check.verify_license_without_api_request(public_key=public_key, license_key=license_key) is True
|
||||
assert license_check.grants_feature("auto_router") is True
|
||||
assert license_check.auto_router_capability_limit() is None
|
||||
|
||||
named_public_key, named_key = _signed_license("2999-01-01", allowed_features=("sso", "audit_logs"))
|
||||
assert license_check.verify_license_without_api_request(public_key=named_public_key, license_key=named_key) is True
|
||||
assert license_check.grants_feature("auto_router") is False
|
||||
assert license_check.auto_router_capability_limit() == 1
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue