refactor(any-gate): widen per-file Any headroom to ~50%

Bump HEADROOM_RATIO from 0.25 to 0.50 so a grandfathered file may hold up to
baseline + ceil(baseline * 0.50) Any-tainted values before the gate trips, and
update the docs and tests to match. The per-file baselines are raw counts, so
only the ceiling moves.
This commit is contained in:
mateo-berri 2026-06-17 00:28:20 +00:00
parent e005618bb5
commit 90f8220907
No known key found for this signature in database
5 changed files with 18 additions and 18 deletions

View file

@ -40,7 +40,7 @@ When you fix violations gated by `ruff-strict-budget.json`, `mypy-code-budget.js
If you're trying to create a new function that relies on untyped stuff, instead of adding more Any's and bringing it closer to the max, just validate it in the caller with Pydantic (a model or `TypeAdapter` that returns the typed thing or raises will do) and then pass the now typed variable in
The Any-discipline gate (`make lint-any`, also a CI job) grandfathers each file under `litellm/` at its current count of values typed `Any` (including the `X | Any`) in `any-discipline-budget.json` and gives it ~25% headroom; a changed file fails once it exceeds that ceiling, so a brand new file must be `Any`-free while a legacy file can absorb a little drift before it has to be cleaned. Ideally `# any-ok: <reason>` is never used; treat it as a last resort for a genuine typed/untyped boundary that Pydantic truly can't model
The Any-discipline gate (`make lint-any`, also a CI job) grandfathers each file under `litellm/` at its current count of values typed `Any` (including the `X | Any`) in `any-discipline-budget.json` and gives it ~50% headroom; a changed file fails once it exceeds that ceiling, so a brand new file must be `Any`-free while a legacy file can absorb a little drift before it has to be cleaned. Ideally `# any-ok: <reason>` is never used; treat it as a last resort for a genuine typed/untyped boundary that Pydantic truly can't model
Ask to commit and push your work when you're done (or if you're confident that your code is good and works, just do it)

View file

@ -155,7 +155,7 @@ Individual linting commands:
make format-check # Check Black formatting
make lint-ruff # Run Ruff linting
make lint-mypy # Run MyPy type checking
make lint-any # Fail if a changed file exceeds its Any-typed value budget (baseline + ~25%)
make lint-any # Fail if a changed file exceeds its Any-typed value budget (baseline + ~50%)
make check-circular-imports # Check for circular imports
make check-import-safety # Check import safety
```

View file

@ -32,7 +32,7 @@ help:
@echo " make lint-ruff-budget - Gate the codebase total of each strict ruff rule against its ceiling"
@echo " make lint-ruff-budget-update - Re-capture per-rule baselines in ruff-strict-budget.json (ratchet)"
@echo " make lint-budget-update - Re-capture all three ratchet budgets (ruff + mypy + basedpyright)"
@echo " make lint-any - Fail if a changed file exceeds its Any-typed value budget (baseline + ~25%)"
@echo " make lint-any - Fail if a changed file exceeds its Any-typed value budget (baseline + ~50%)"
@echo " make lint-any-budget-update - Re-capture the per-file Any baseline (ratchet)"
@echo " make check-circular-imports - Check for circular imports"
@echo " make check-import-safety - Check import safety"

View file

@ -13,8 +13,8 @@ litellm already contains a large amount of pre-existing `Any` (a single legacy
file can have >100 findings), and a whole-tree scan would have to re-export types
for litellm's entire import closure on every run (~2 min, ~3 GB). So the gate
grandfathers each file at its current count in `any-discipline-budget.json` and
gives it ~25% headroom: a file fails once its `Any`-tainted values exceed
`baseline + ceil(baseline * 0.25)`. A file with no entry is budgeted at zero, so
gives it ~50% headroom: a file fails once its `Any`-tainted values exceed
`baseline + ceil(baseline * 0.50)`. A file with no entry is budgeted at zero, so
a brand new file must be `Any`-free, and a legacy file may absorb a little drift
before it has to be cleaned. Cold files the change doesn't touch are left to the
ratchet (run `--update`); like the mypy/basedpyright budgets, the gate only
@ -113,7 +113,7 @@ PY_TAG = f"{sys.version_info.major}.{sys.version_info.minor}"
DEFAULT_BASE = "origin/litellm_internal_staging"
# Headroom each grandfathered file gets over its baseline before the gate trips.
HEADROOM_RATIO = 0.25
HEADROOM_RATIO = 0.50
# `--update` re-counts every file. Type-checking each module also preserves its
# AST and exports its types, so we re-check in bounded batches against a warmed
@ -161,7 +161,7 @@ class Violation(NamedTuple):
def cap_for(baseline: int) -> int:
"""The most `Any`-tainted values a file may hold: baseline plus ~25%."""
"""The most `Any`-tainted values a file may hold: baseline plus ~50%."""
return baseline + math.ceil(baseline * HEADROOM_RATIO)

View file

@ -16,7 +16,7 @@ def _v(path="litellm/x.py", line=10, code="LIT009"):
# --------------------------------------------------------------------------- #
# cap_for: ~25% headroom over the grandfathered baseline
# cap_for: ~50% headroom over the grandfathered baseline
# --------------------------------------------------------------------------- #
@ -25,16 +25,16 @@ def test_zero_baseline_gets_no_headroom():
assert mod.cap_for(0) == 0
def test_headroom_is_a_quarter_of_the_baseline():
assert mod.cap_for(100) == 125
assert mod.cap_for(40) == 50
def test_headroom_is_half_of_the_baseline():
assert mod.cap_for(100) == 150
assert mod.cap_for(40) == 60
def test_headroom_rounds_up_so_small_baselines_get_at_least_one_slot():
# ceil(1 * 0.25) == 1, ceil(4 * 0.25) == 1, ceil(8 * 0.25) == 2.
# ceil(1 * 0.5) == 1, ceil(3 * 0.5) == 2, ceil(5 * 0.5) == 3.
assert mod.cap_for(1) == 2
assert mod.cap_for(4) == 5
assert mod.cap_for(8) == 10
assert mod.cap_for(3) == 5
assert mod.cap_for(5) == 8
# --------------------------------------------------------------------------- #
@ -56,17 +56,17 @@ def test_counts_only_lit009_per_file():
# --------------------------------------------------------------------------- #
# budget_breaches: a file fails only above baseline + ~25%
# budget_breaches: a file fails only above baseline + ~50%
# --------------------------------------------------------------------------- #
def test_file_at_its_ceiling_does_not_breach():
assert mod.budget_breaches({"litellm/a.py": 125}, {"litellm/a.py": 100}) == []
assert mod.budget_breaches({"litellm/a.py": 150}, {"litellm/a.py": 100}) == []
def test_file_one_over_its_ceiling_breaches():
assert mod.budget_breaches({"litellm/a.py": 126}, {"litellm/a.py": 100}) == [
("litellm/a.py", 126, 125)
assert mod.budget_breaches({"litellm/a.py": 151}, {"litellm/a.py": 100}) == [
("litellm/a.py", 151, 150)
]