Repository navigation
fix(license): backport the wildcard license auto_router grant to rc/1.102.0 (#41684) - #41701
Merged
Merged
Conversation
Contributor
|
Contributor
Author
|
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 e71b78f. Configure here.
This was referenced Sep 18, 2026
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:
rc/1.102.0(v1.102.0-rc.2 plus the backports after it) carries the auto-router gate from feat(router): limit heuristic_v2 auto-routers to one without the auto_router license feature #39468 and feat(router): meter auto-router tier and prompt customization against the auto_router license feature #39674 but not the fix(license): let a wildcard allowed_features license grant the auto_router feature #41684 fix, so the v1.102.0 stable cut would ship the same startup failure v1.101.0 hasauto_routerentry inallowed_featuresallowed_features: ["*"], so a wildcard license keeps the one-router-per-capability limitHow it solves it:
rc/1.102.0LicenseCheck.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.102.0-rc.2 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), still present in v1.102.0-rc.2; this PR carries the fix onto
rc/1.102.0ahead of the v1.102.0 stable cutLinear 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 this PR's merge base on
rc/1.102.0(3f96566, the commit tagged v1.102.0-rc.2); After is this PR's tip (e71b78f). Each row is a separate live proxy:["*"]["auto_router"]["sso", "audit_logs"]["*"]["auto_router"]["sso", "audit_logs"]Before (3f96566)
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 (e71b78f)
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 the 22 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, none of which is onrc/1.102.0, main's latest pipeline passes all three jobs, this PR touches no Together AI path, andrc/1.102.0has no required status checksFinal Attestation
Note
Low Risk
Narrow change to offline license feature parsing for auto-router limits; API-verified licenses without feature lists behave as before.
Overview
Fixes enterprise proxies that fail startup when
allowed_featuresis["*"](the default) but the auto-router gate only recognized the literalauto_routerentry.Adds
LicenseCheck.grants_feature, which treats either the requested feature or theLICENSE_ALL_FEATURES(*) wildcard in signed (airgapped) license data as a grant, including whenallowed_featuresis a bare string.auto_router_capability_limitnow delegates to that helper instead of inlining list checks, so wildcard licenses lift the one-router-per-capability cap the same as explicitly licensedauto_router.Tests cover list and string wildcards, mixed feature lists, and signed licenses that name only other features (limit stays at 1).
Reviewed by Cursor Bugbot for commit e71b78f. Bugbot is set up for automated code reviews on this repo. Configure here.