fix(router): isolate all per-deployment pricing overrides from sibling deployments - #31003
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR fixes a pricing override leak between sibling router deployments that share the same backend model. When two deployments point at the same backend (e.g.
Confidence Score: 5/5Safe to merge — the change is additive (new fields on an existing Pydantic model) and is fully covered by both a structural guard test and two regression tests. The fix is minimal and targeted: 13 Optional fields are appended to CustomPricingLiteLLMParams with None defaults, which is non-breaking. All newly added fields are verified to be present in ModelInfoBase and their absence was the root cause of the described leak. The guard test using typing.get_type_hints(ModelInfoBase) will catch any future drift. The regression tests reproduce the exact scenario from the ticket and verify both the per-deployment custom key and the shared backend key simultaneously. No files require special attention. litellm/types/utils.py is the only production file changed and the modification is purely additive.
|
| Filename | Overview |
|---|---|
| litellm/types/utils.py | Adds 13 missing pricing fields to CustomPricingLiteLLMParams so the denylist fully matches ModelInfoBase, closing the per-deployment override leak for tiered, regional-uplift, and OCR cost fields. |
| tests/test_litellm/test_router_model_cost_isolation.py | Adds a guard test that uses typing.get_type_hints to ensure CustomPricingLiteLLMParams always covers every pricing field in ModelInfoBase, plus two regression tests validating tiered-override isolation via model_cost lookup and the proxy /model/info path. |
Reviews (1): Last reviewed commit: "fix(router): isolate all per-deployment ..." | Re-trigger Greptile
…g deployments CustomPricingLiteLLMParams is the authoritative set of per-deployment pricing fields, used to strip overrides from the shared backend-alias key so one deployment cannot pollute a sibling that shares the same backend model. It had drifted from ModelInfoBase: tiered and per-unit cost fields such as input_cost_per_token_above_272k_tokens, cache_read_input_token_cost_above_*, output_vector_size, ocr_cost_per_*, and the regional uplift multipliers were absent, so a deployment overriding any of them leaked the override into litellm.model_cost under the shared key and every sibling read the wrong rate via /model/info (LIT-3897). Add the missing fields so the denylist covers every ModelInfoBase pricing field, and guard against future drift with a test asserting the two stay in sync, plus a regression test that a tiered override stays isolated to its own deployment model_id key.
5a5b4dc to
d78d6c8
Compare
|
Superseded by in-repo PR #31021 (pushed to BerriAI/litellm directly so full CI runs; cross-fork PRs skip the secret-gated jobs). |

Relevant issues
Linear ticket
LIT-3897
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
make test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
CI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Screenshots / Proof of Fix
Run a proxy with two deployments on the same backend, where one overrides a tiered rate that the previous denylist did not strip. Save this as
lit3897.yamlThen
Expected output after the fix, where the override stays on
custom-priced-flashonly and the sibling keeps the canonical gemini rateBefore the fix the sibling
gemini-2.5-flashreportedinput_above_272k=0.000999, the leaked overrideType
🐛 Bug Fix
Changes
Two router deployments that share the same backend model (for example
gemini/gemini-2.5-flash) are supposed to get isolated custom pricing. The router already strips per-deployment pricing fields from the shared backend-alias key before registering it intolitellm.model_cost, usingCustomPricingLiteLLMParamsas the list of fields to strip. That denylist had drifted fromModelInfoBase, so tiered and per-unit cost fields likeinput_cost_per_token_above_272k_tokens,input_cost_per_token_above_512k_tokens,cache_read_input_token_cost_above_272k_tokens,output_vector_size,ocr_cost_per_page, and the regional uplift multipliers were never stripped. A deployment overriding any of them leaked the override into the shared key, and every sibling then read the wrong rate via/model/infoThe fix adds the missing cost fields to
CustomPricingLiteLLMParamsso it covers every pricing field inModelInfoBase, which closes the leak at all the sites that consume this single source of truth (router stripping, thelitellm_paramstomodel_infocopy, pre-call utils, auth, and logging). A guard test asserts the denylist never drifts fromModelInfoBaseagain, and a regression test confirms a tiered override stays isolated to its own deploymentmodel_idkey and does not pollute the shared backend key a sibling resolves through