Repository navigation
fix(usage): rank cache leakage keys server-side by uncached prompt tokens - #43139
devin-ai-integration[bot] wants to merge 1 commit into
Conversation
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
|
1 similar comment
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
|
@greptileai please review the current head 30a957a (merged main into the branch). |
|
@greptileai please review the current head a7196b0 |
a7196b0 to
676772f
Compare
…kens Resolves LIT-8620 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
676772f to
7eca00c
Compare
|
@greptileai please review the current head 7eca00c (rebased onto main, no code change versus a7196b0 beyond formatting) |
TLDR
Problem this solves:
How it solves it:
Intentional product change: the "only the N highest-spend keys are loaded" note under the key ranking is gone because the ranking is no longer spend-capped. When the server truncates, the card says "Showing the top N of M keys, ranked by uncached prompt tokens" instead
User Flow
Before: an admin wants to know which virtual key wastes the most uncached prompt tokens, and the worst offender is missing
mid-cachedandwhale-cached, with 30,000 and 10,000 uncached tokensleaky-low-spend, which burned 900,000 uncached tokens on $5 of spend, is nowhere in the listUSAGE_TOP_API_KEYS_LIMITAfter: the same card puts the real leaker first
leaky-low-spendis first with 900,000 uncached tokens and 0.0% cache hit rate, thenmid-cachedwith 30,000user_idstill gets 403Linear ticket
Resolves LIT-8620
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)Screenshots / Proof of Fix
Setup shared by both legs: two proxies on the same real PostgreSQL, each launched with
PYTHONPATH=<its checkout>:<its checkout>/enterpriseandUSAGE_TOP_API_KEYS_LIMIT=2. The Before proxy (port 4001) runs the merge base, the After proxy (port 4000) runs the PR branch. Three control requests toanthropic-haiku-4-5through POST /v1/chat/completions returned 200 with real Anthropic usage. The same fixture SQL (sha2565f2dccee9c7b49442b8ee2b0c55be23af6611a6e0bdfafcd8c1e63e34649375d) seeds three keys forD=2026-09-25under userlit8620-user:whale-cached($90 spend, 1,000,000 prompt, 990,000 cached),mid-cached($40, 300,000 prompt, 270,000 cached),leaky-low-spend($5, 900,000 prompt, 0 cached). The Admin UI screenshots above are the UI proof for both legs, taken at http://localhost:3000/cost-optimization/ on the same rows (the dashboard's default range also includes the three control keys, hence "of 6 keys"). The After proxy's runtime code is identical between 30a957a, a7196b0 and the current head 7eca00c (rebase onto main plus test formatting only); the new route was re-verified on 7eca00c with the same fixture:leaky-low-spendfirst with 900,000 uncached tokens, thenmid-cachedwith 30,000,total_api_keys: 3, limit: 2, and the non-admin cross-user call returned 403Before (1e6c983)
Existing aggregated route, which the card ranks today
curl -s "localhost:4001/user/daily/activity/aggregated?start_date=$D&end_date=$D" -H "Authorization: Bearer $MK"piped through a one-liner printingapi_key_limit,total_api_keys, and alias/spend/uncached per keyleaky-low-spend(900,000 uncached) is dropped before the card can rank it:New cache leakage route
curl -s -w " HTTP %{http_code}" "localhost:4001/user/daily/activity/cache_leakage?start_date=$D&end_date=$D" -H "Authorization: Bearer $MK"{"detail":"Not Found"} HTTP 404Non-admin scoping
Admin UI
mid-cached30,000 andwhale-cached10,000, plus the note "Only the 2 highest-spend keys of 6 are loaded, so a lower-spend key that leaks more is not listed here" (Before screenshot in User Flow)After (a7196b0, re-run on 7eca00c)
Existing aggregated route, which the card ranks today
New cache leakage route
curl -s -w " HTTP %{http_code}" "localhost:4000/user/daily/activity/cache_leakage?start_date=$D&end_date=$D" -H "Authorization: Bearer $MK"leaky-low-spendfirst,whale-cached(10,000 uncached) correctly cut by the limit of 2, totals over all 3 keys (api_key hashes elided):{ "results": [ {"key_alias": "leaky-low-spend", "prompt_tokens": 900000, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, "uncached_prompt_tokens": 900000, "cache_hit_ratio": 0.0, "prompt_caching_savings_spend": 0.0}, {"key_alias": "mid-cached", "prompt_tokens": 300000, "cache_read_input_tokens": 250000, "cache_creation_input_tokens": 20000, "uncached_prompt_tokens": 30000, "cache_hit_ratio": 0.8333333333333334, "prompt_caching_savings_spend": 20.0} ], "metadata": {"total_api_keys": 3, "limit": 2, "total_cached_tokens": 1260000, "total_prompt_caching_savings_spend": 80.0} } HTTP 200Non-admin scoping
curl -s localhost:4000/key/generate -H "Authorization: Bearer $MK" -d '{"user_id":"lit8620-internal"}'for an internal_user key$UKcurl -s -w " HTTP %{http_code}" "localhost:4000/user/daily/activity/cache_leakage?start_date=$D&end_date=$D&user_id=some-other-user" -H "Authorization: Bearer $UK"{"detail":{"error":"Non-admin users can only view their own spend data."}} HTTP 403curl -s -w " HTTP %{http_code}" "localhost:4000/user/daily/activity/cache_leakage?start_date=$D&end_date=$D" -H "Authorization: Bearer $UK"{"results":[],"metadata":{"total_api_keys":0,"limit":2,"total_cached_tokens":0,"total_prompt_caching_savings_spend":0.0}} HTTP 200Admin UI
leaky-low-spend900,000 / 0.0% / $57.14,mid-cached30,000 / 83.3% / $1.90 (After screenshot in User Flow)Type
🐛 Bug Fix
Caveats (if any)
Low
limit(default 10, capped atUSAGE_TOP_API_KEYS_LIMIT)tsc --noEmit -p .in ui/litellm-dashboard reports 957 pre-existing errors outside this diff; the changed files type-check clean and ESLint reports 0 errors on themAnalyze (python)(CodeQL) fails withLogInjection.ql: Result set is larger than the limit of 2GiB, an analyzer-wide result-size failure, not an alert on any line: the latest CodeQL run on main (f4308bc) fails on the same query with the same error; theCodeQLcheck itself passes and there are zerogithub-advanced-security[bot]review threads on this PR.oauthsits in waiting because its workflow is gated on thee2e-changedenvironment and its path filters (MCP/OAuth files) do not match this diff; recently merged main PRs show it in the same statenpm run gen:api; T18 useCacheLeakageKeys.test.ts calls the hook as a plain function with the query hook mocked, judged cosmetic since React hook semantics are not under testFinal Attestation
Link to Devin session: https://app.devin.ai/sessions/565648bb3887474eb6fbd05c68e4cd6f
Open in Devin Desktop: https://app.devin.ai/desktop/session/565648bb3887474eb6fbd05c68e4cd6f?variant=devin
Requested by: @yassin-berriai
Link to Devin session: https://app.devin.ai/sessions/a7f364da79df46aa9a2873f31b955e65
Open in Devin Desktop: https://app.devin.ai/desktop/session/a7f364da79df46aa9a2873f31b955e65?variant=devin