Repository navigation
fix(license): backport #41684 to stable/1.101.x so a wildcard license grants auto_router - #41698
Merged
Merged
Conversation
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Contributor
|
Contributor
|
bugbot run |
Contributor
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c9ed764. Configure here.
5 of 7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Problem this solves:
auto_routerentry inallowed_featuresallowed_features: ["*"], so a wildcard license keeps the one-router-per-capability limitHow it solves it:
stable/1.101.xLicenseCheck.grants_featuretreats a*entry as granting every featureauto_router_capability_limitgoes through it, so["*"]lifts the limitBackport notes
tests/test_litellm/proxy/auth/test_litellm_license.pyapplied cleanlitellm/proxy/auth/litellm_license.pyconflicted only in the docstring ofauto_router_capability_limit, whose wording on this line predates main's; the resolution takes fix(license): let a wildcard allowed_features license grant the auto_router feature #41684's docstring, and the whole region fromAUTO_ROUTER_LICENSE_FEATUREdown to the method'sreturn 1is identical to mainstable/1.100.xauto_router_capability_limiton this line (the boot-time validation, the twoRouterconstructions, andPOST /model/new) is also a caller on main, so the main-side risk check applies hereUser Flow
Before: an enterprise operator upgrading to v1.101.0 with two custom auto-routers cannot start the proxy at all, even though their license covers every feature
LITELLM_LICENSEto their enterprise key, whose decoded payload reads"allowed_features": ["*"], and keep a config.yaml with twoauto_router/complexity_routerdeployments that each define their owntier_definitionslitellm --config config.yaml)ValueError: config.yaml model_list: At most 1 auto-router(s) with operator-defined tier_definitions or an operator-written classifier prompt can be registered but this would make 2. ... A LiteLLM license with the 'auto_router' feature lifts the limit.and the process exitsAfter: the same operator starts the proxy built from this line and both routers serve traffic
LITELLM_LICENSEto the same enterprise key ("allowed_features": ["*"]) and keep the same config.yaml with two custom auto-routerslitellm --config config.yaml)"I'm alive!""allowed_features": ["*"]"model": "support-router"and again with"model": "engineering-router"each return 200 with a real completionRelevant issues
Backport of #41684 (main)
Affected release
regression in v1.101.0 (first in v1.101.0-rc.1, from #39674 on top of #39468); this PR carries the fix onto
stable/1.101.xLinear ticket
Resolves LIT-8019
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Every leg is a live proxy booted from a detached worktree at the named commit with its own venv and 2 uvicorn workers on a random free port, no database. The license is read from the
LITELLM_LICENSEenv var by each worker on its own, so there is no state shared between workers to cross-check, and each chat request is sent twice. The routers call OpenAI for real (gpt-5.4-miniclassifies and answers the easy tier,gpt-5.6the hard one). The three license keys are offline-signed test keys that differ only inallowed_features; their payloads carrymax_users: 100,max_teams: 5, and an expiry of 2027-07-06. Chat responses are trimmed with jq to the model, the content, and the token countconfig.yaml, the same file on every leg:Before is the tip of
stable/1.101.x(18243cd, the commit tagged v1.101.0); After is this PR's tip (c9ed764). Each row is a separate live proxy:["*"]["auto_router"]["sso", "audit_logs"]["*"]["auto_router"]["sso", "audit_logs"]Before (18243cd)
Wildcard license (
"allowed_features": ["*"])Start the proxy; startup aborts in both workers and nothing ever serves
GET /health/liveliness gets no answer
License naming the feature (
"allowed_features": ["auto_router"])Start the proxy; both workers come up and it serves traffic
GET /health/liveliness returns 200
GET /health/license returns 200
POST /v1/chat/completions with
"model": "support-router", sent twice; both return 200 with a real completionPOST /v1/chat/completions with
"model": "engineering-router", sent twice; both return 200 with a real completionLicense with other features only (
"allowed_features": ["sso", "audit_logs"])Start the proxy; startup aborts in both workers and nothing ever serves
GET /health/liveliness gets no answer
After (c9ed764)
Wildcard license (
"allowed_features": ["*"])Start the proxy; both workers come up and it serves traffic
GET /health/liveliness returns 200
GET /health/license returns 200
POST /v1/chat/completions with
"model": "support-router", sent twice; both return 200 with a real completionPOST /v1/chat/completions with
"model": "engineering-router", sent twice; both return 200 with a real completionLicense naming the feature (
"allowed_features": ["auto_router"])Start the proxy; both workers come up and it serves traffic
GET /health/liveliness returns 200
GET /health/license returns 200
POST /v1/chat/completions with
"model": "support-router", sent twice; both return 200 with a real completionPOST /v1/chat/completions with
"model": "engineering-router", sent twice; both return 200 with a real completionLicense with other features only (
"allowed_features": ["sso", "audit_logs"])Start the proxy; startup aborts in both workers and nothing ever serves
GET /health/liveliness gets no answer
Notes from the run:
/health/licenseshows["*"]verbatim on every leg; unchanged herePOST /model/newshares the gate; proven on fix(license): let a wildcard allowed_features license grant the auto_router feature #41684, not re-run hereType
🐛 Bug Fix
Caveats (if any)
Low
*entry is a wildcard; glob patterns likeauto_*still match nothing*, so nothing in the field uses patternsallowed_featuresverbatim, so a wildcard license still shows["*"]thereallowed_featuresis read as a one-item list, the same way GET /health/license reports itPOST /model/newpath of the same gate was proven on fix(license): let a wildcard allowed_features license grant the auto_router feature #41684 (second router 403 before, 200 after) and not re-run on this line; the gate's code region is identical to main'slocal_testing_part1(job),local_testing_part2(job), andllm_translation_testing(job) are red only on Together AI tests that call the serverlessopenai/gpt-oss-20bthe provider has since withdrawn (Unable to access non-serverless model); main moved those tests off that model in 1aa2e19, 8c046e1, and b478131 after this line branched, main's latest pipeline passes all three jobs, this PR touches no Together AI path, and CircleCI is not a required check onstable/1.101.xFinal Attestation
Note
Medium Risk
Changes signed-license feature gating used at proxy startup and model registration; mis-handling could block valid enterprise deployments or over-grant features, though scope is limited to auto_router limit logic.
Overview
Backports the #41684 fix so enterprise licenses with
allowed_features: ["*"](the generator default) are treated as grantingauto_router, not only licenses that name that feature explicitly.LicenseCheck.grants_featureis added with aLICENSE_ALL_FEATURES("*") wildcard;auto_router_capability_limitnow delegates to it instead of inlining list checks. Licenses with"*"in the list (or as a bare string) return unlimited auto-router capacity; behavior for namedauto_router, missing features, and API-only verification is unchanged.Tests cover wildcard list/string forms,
grants_feature, and signed-license verification paths.Reviewed by Cursor Bugbot for commit c9ed764. Bugbot is set up for automated code reviews on this repo. Configure here.