mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
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:
parent
e005618bb5
commit
90f8220907
5 changed files with 18 additions and 18 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue