fix: roll up the open deflake fixes for the MCP logging queue, PTU rollup, license gate, and pricing test isolation - #37833
Conversation
…global The rollup read litellm.proxy.proxy_server.llm_router out of sys.modules, so a run priced and swept whatever deployments anything else in the process had left on that module. Under xdist the shard's module-to-worker assignment varies per run, which made three rollup tests fail or pass on the same commit depending on ordering. Callers now hand the router in, and the proxy's scheduled job passes its own. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 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:
|
|
|
Greptile SummaryThis PR consolidates four CI deflake fixes while preserving the affected production behavior.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/proxy/proxy_server.py | Passes the current proxy router into the scheduled PTU rollup job. |
| litellm/proxy/spend_tracking/ptu_flat_cost_rollup.py | Replaces ambient module-global router discovery with explicit router propagation across rollup and backfill paths. |
| tests/code_coverage_tests/check_licenses.py | Adds bounded retries for transient PyPI connection, timeout, throttling, and server failures. |
| tests/mcp_tests/conftest.py | Adds per-test teardown that drains queued logging callbacks before the next MCP test. |
| tests/test_litellm/conftest.py | Invalidates cached model information before and after each isolated unit test. |
| tests/test_litellm/proxy/spend_tracking/test_ptu_flat_cost_rollup.py | Updates PTU tests to inject routers explicitly and verifies ambient proxy state is ignored. |
| tests/test_litellm/proxy/test_proxy_server.py | Verifies the scheduled rollup receives the proxy’s active router. |
| tests/test_litellm/test_check_licenses.py | Covers successful transient retries, retry exhaustion, and non-retryable 404 behavior. |
Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/lit..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…itellm_deflake_20260821
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…itellm_deflake_20260821
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…itellm_deflake_20260821
|
bugbot run |
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 09ff5bf. Configure here.
TLDR
Problem this solves:
How it solves it:
User Flow
Before: a contributor pushes a commit that touches none of this, and Code Quality, the MCP folder job, or the unit shards go red on someone else's leftover state
check_licensesstep, PyPI resets one connection, and the step reportsbotocore-stubs (1.43.14) - Unknown license, tornado (6.5.7) - Unknown licenseassert 1.35e-05 == 1.2, on a commit that never touched MCPAfter: the same push only goes red on something the commit actually did
All dependencies have acceptable licensesRelevant issues
Linear ticket
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).
CI evidence, mechanism, and fix per flake
2026-08-27: the license gate flaked again on a PyPI connection reset, same mechanism this PR already fixes
Flaky gate: the
check_licensesstep of Code Quality ChecksEvidence: https://github.com/BerriAI/litellm/actions/runs/33026421099 on
devin_ai_fix_gemini_stream_billing_36042ate8ec34c4, attempt 1 (job 98368723006) failed withFailed to fetch license for keyring 25.6.0: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))thenkeyring (25.6.0) - Unknown license; attempt 2 on the identical commit passed with no change in betweenMechanism and fix: identical to the 2026-08-20 entry below (single unretried
requests.getto PyPI). The retry fix already on this branch covers it; that branch ran without this PR's fix. No new code was needed, this is additional evidence the flake is live until this PR merges2026-08-24 and 2026-08-25: MCP cost assertions read another test's logging payload
Flaky tests:
tests/mcp_tests/test_mcp_logging.py::test_mcp_cost_tracking(8 job failures in the window),::test_mcp_cost_tracking_per_tool(6)Evidence,
LiteLLM MCP Tests (folder - tests/mcp_tests), same commit red on attempt 1 and green on the re-run: https://github.com/BerriAI/litellm/actions/runs/32813031488 at312abf26a0(attempt 1 job 97695992376 failed, attempt 2 passed), https://github.com/BerriAI/litellm/actions/runs/32799057588 at735195ae66(attempt 1 job 97656213844 failed, attempt 2 passed), https://github.com/BerriAI/litellm/actions/runs/32795604643 ata5c81e525b(attempts 1 and 2 failed, jobs 97646133301 and 97648109127, attempt 3 passed). Same failure across other branches in the window: jobs 97642202286, 97629426228, 97628673139, 97629416408, 97625705387, 97622992754, 97620683608, 97614424367, 97615597485, and https://github.com/BerriAI/litellm/actions/runs/32829113666 attempt 1The attempt-1 log of run 32813031488 names the mechanism outright: the payload the MCP logger asserted on has
'call_type': 'acompletion','model': 'gpt-4o-mini'and'response_cost': 1.35e-05, a chat-completion payload fromtest_mcp_chat_completions.py, while the MCP payload with'response_cost': 1.2is logged in the same runMechanism, async teardown race across event loops. Callbacks are handed to the process-wide logging worker, whose queue is bound to the loop that created it, and the MCP conftest gives every test a fresh loop. Anything a test leaves queued is carried onto the next test's loop and dispatched there, so it reaches the callbacks that test registered.
tests/mcp_testsruns on 4 xdist workers, so whether the earlier chat-completion callback landed inside a cost test depended on the splitFix: the MCP conftest drains the worker's queue when each test ends, so a test's own callbacks run in its own loop and nothing is left to carry. No assertion changed, and no marker, rerun, skip, or sleep was added
2026-08-21: PTU rollup read the proxy's router off a module global
Flaky tests:
tests/test_litellm/proxy/spend_tracking/test_ptu_flat_cost_rollup.py::test_a_database_only_run_sweeps_exactly_as_it_did_before,::test_every_deployment_that_prices_is_inside_the_set_that_bounds_the_prune,::test_rollup_prunes_stale_row_when_config_is_goneEvidence, same branch and adjacent commits with opposite results: https://github.com/BerriAI/litellm/actions/runs/32352053117 on
litellm_internal_stagingat6fcdea03b0, attempt 1,proxy-infra, all three failed, and that merge touched only the pricing JSON and an anthropic passthrough handler. The staging pushes either side of it passed: https://github.com/BerriAI/litellm/actions/runs/32338671277 at5290150a05and https://github.com/BerriAI/litellm/actions/runs/32357044632 at952c6d5675. The same three also failed on unrelated branches, https://github.com/BerriAI/litellm/actions/runs/32352326757 onlitellm_cli_refresh_tokensatb6fef179ffand https://github.com/BerriAI/litellm/actions/runs/32451279739 onlitellm_fix_raw_key_log_persistenceatfb417a5563attempts 1 and 2, where the output shows a scanned deployment id no test in that file creates,8792e271b8b561315c5c5471556a09088dde51606187906acdb3c3f8be10e275, a router-generated hash left on the proxy module by something else in the workerMechanism, shared module-level state between tests.
_running_router()readlitellm.proxy.proxy_server.llm_routerout ofsys.modules, so a rollup priced and swept every deployment any earlier test in the same xdist worker had left there.proxy-infraruns--dist loadscope, which assigns modules to workers dynamically, so the same commit passed or failed depending on what ran in front of the rollup testsFix: callers hand the rollup its router, and the proxy's nightly job passes its own. Nothing reads the module global
2026-08-20: license gate treated a PyPI connection reset as a missing license
Flaky gate: the
check_licensesstep of Code Quality ChecksEvidence: https://github.com/BerriAI/litellm/actions/runs/32353318790 on
litellm_vertex_live_db_credentials, attempt 1 (job 96377074542) failed the step, attempt 2 on the identical commit passed with no change in between. Attempt 1's log gives the cause,Failed to fetch license for tornado 6.5.7: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')), thenFound packages that need verification: botocore-stubs (1.43.14) - Unknown license, tornado (6.5.7) - Unknown licenseMechanism, real network dependence with no retry.
get_package_license_from_pypidid a singlerequests.getand caught every exception the same way, so a transient TCP reset was reclassified as "no license metadata" and failed a compliance gate meant to catch real license problemsFix: classify the failure before deciding. Connection errors, timeouts, 429 and 5xx get up to 3 attempts with a short backoff, and every other outcome, including a 404 for a version that is not on PyPI, still resolves on the first answer. Exhausting the retries keeps today's behaviour, so a real outage or an unlicensed package still fails the gate. The HTTP getter and the sleep are injected, and the tests pin attempt counts rather than only return values
2026-08-19: pricing memo survived a cost-map swap between tests
Flaky test:
tests/test_litellm/batches/test_batch_utils.py::test_handle_completed_vertex_batch_computes_cost_usage_and_modelsEvidence: failed with
assert 3.9375e-05 == 7.875e-05 ± 7.9e-11on https://github.com/BerriAI/litellm/actions/runs/32213874586 (attempt 2, SHA2e1d4077) and again on https://github.com/BerriAI/litellm/actions/runs/32235776332, while the same test at the same and adjacent SHAs passed elsewhere in the window and passes locally with no editsMechanism, cache state leaking across tests. The misc unit workflow does not set
LITELLM_LOCAL_MODEL_COST_MAP, solitellm.model_costis fetched over the network at import.litellm.get_model_infomemoizesModelInfokeyed only on model, provider and api_base, and its only invalidation hook was never called when a test reboundlitellm.model_costitself. About a dozen tests in those paths do exactly that, so they primed the memo from the packaged map and left it pointing at that map after the fixture restored the fetched one. The 2x gap is the Vertex batch price halving that had landed on the branch but not on the map served frommainFix:
isolate_litellm_stateinvalidates the memo on setup and on teardown, so every test starts and ends with memoized model info consistent with the currentlitellm.model_cost. No assertion was relaxed, and no rerun, marker, skip, or sleep was addedRoll-up notes
Absorbed and closed as superseded earlier: #37614 (license gate) and #37461 (pricing memo). Nothing else from those two branches was dropped, and no other
litellm_deflake_PR was open for this run2026-08-27 run: no new fixable test flakes in the 24h window, so nothing new was added. The window's only red runs besides the license gate above were infrastructure, detailed in the automation report: a staging Unit Tests job whose runner lost communication with the server (run 33003396465), a zizmor job that could not reach the GitHub advisories API (run 33014750380 attempt 1), and a CodSpeed run that failed on a 500 from its upload endpoint (run 33026389842). No other
litellm_deflake_PR was open, so nothing was absorbed or closedDropped from this branch's own original diff: the
config_sourcedflag on_LoadedDeploymentsand the unbounded prune path it gated. Staging #37793 landed a superseding change that always bounds the prune by the ids the run scanned, so the flag had no remaining reader and its assertion intest_a_router_left_on_the_proxy_module_is_not_scannedwas removed. The router injection this PR is about is unaffectedScreenshots / Proof of Fix
None of the four has a proxy route or UI surface: three are test-isolation fixes and one is a CI gate script, so the honest proof is the CI evidence above plus mutation checks that the coverage still bites. Verification on this tip, not offered as proof of the flakes:
Before
Every fix has a mutation check
tests/mcp_tests/test_mcp_logging.py::test_mcp_cost_trackingand::test_mcp_cost_tracking_per_tooltest_a_router_left_on_the_proxy_module_is_not_scannedwith the ambient deployment pricedtest_get_license_retries_connection_error_then_resolves_licenseandtest_get_license_returns_none_after_connection_retry_limitget_model_infostill returns the doubled batch rateAfter (09ff5bf, staging merged in on 2026-08-27)
tests/mcp_tests/test_mcp_logging.pywith the two sibling files that leak into it, 20 consecutive runs: 0 failures, 20 passed each. The wholetests/mcp_testsfolder on 4 workers: 147 passed, 2 skipped, 1 xpassed. Before this commit the same sibling combination failed on every attempttests/test_litellm/proxy/spend_tracking/test_ptu_flat_cost_rollup.pyandtests/test_litellm/test_check_licenses.py, 20 consecutive runs together on this tip: 0 failures, 163 passed eachtests/test_litellm/proxy/test_proxy_server.py(-k ptu), 20 consecutive runs on this tip: 0 failures, 3 passed eachmake lintandmake checkon this tip: clean (earlier fuller runs at 54ea379:tests/mcp_testsfolder on 4 workers 147 passed,test_proxy_server.py328 passed,spend_tracking602 passed)Type
🐛 Bug Fix
✅ Test
🚄 Infrastructure
Caveats (if any)
Final Attestation
Link to Devin session: https://app.devin.ai/sessions/f9aaff213ca44ace81d0b20716c154ee
Open in Devin Desktop: https://app.devin.ai/desktop/session/f9aaff213ca44ace81d0b20716c154ee?variant=devin
Note
Medium Risk
PTU rollup still writes team spend rows and now depends on the proxy passing its router for config-sourced deployments; mis-wiring would under-attribute PTU, though tests lock the cron call site.
Overview
This PR bundles four CI/test-isolation fixes plus the small production wiring needed for PTU rollup to stay correct after the router change.
PTU flat-cost rollup no longer reads
llm_routerofflitellm.proxy.proxy_serverviasys.modules. Callers pass an optionalrouterthrough_load_ptu_models, daily rollup, backfill, and the scheduled job; the proxy cron now passesllm_router. That stops xdist workers from pricing/pruning against another test’s leftover router while keeping config.yaml PTU deployments in scope.License checking retries transient PyPI failures (connection/timeout, 429, 5xx) up to three times with backoff, with injectable HTTP/sleep for tests. 404s still fail fast.
MCP tests add an autouse fixture that drains
GLOBAL_LOGGING_WORKERafter each test so queued callbacks don’t run on the next test’s event loop (fixing wrong cost assertions).Unit test isolation clears the
get_model_info/ model_cost memo via_invalidate_model_cost_lowercase_map()at the start and end ofisolate_litellm_state, so rebindinglitellm.model_costin one test doesn’t skew pricing in later tests.Reviewed by Cursor Bugbot for commit 09ff5bf. Bugbot is set up for automated code reviews on this repo. Configure here.