From 4a5137044e1ea4b35773065b339da1b502afa934 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:58:54 +0000 Subject: [PATCH 1/7] feat(translation): semgrep effects-boundary rules; ambient clock/ids/env/fs/network must enter via TranslationDeps --- .../translation/translation-v2-effects.yml | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 .semgrep/rules/python/translation/translation-v2-effects.yml diff --git a/.semgrep/rules/python/translation/translation-v2-effects.yml b/.semgrep/rules/python/translation/translation-v2-effects.yml new file mode 100644 index 0000000000..2c42df0967 --- /dev/null +++ b/.semgrep/rules/python/translation/translation-v2-effects.yml @@ -0,0 +1,169 @@ +# litellm/translation v2 effects boundary, encoded as deterministic checks +# (06-quality-team.md, "algebraic effects for I/O and side effects"). Scoped +# to the translation package; the rest of the codebase is unaffected. +# +# The translation core is pure: every ambient input (clock, ids, env, fs, +# network, randomness) must arrive as a value through deps.TranslationDeps or +# the injected engine.http.HttpPort. The two boundary modules that DEFINE +# those ports are whitelisted by path; they declare the window, they do not +# get to reach through it either today (both are pure), but a future port +# default would live there and nowhere else. + +rules: + - id: translation-no-ambient-clock + message: > + ambient effect: reading the clock (time.*, datetime.now/utcnow/today) + inside litellm/translation. Inject the timestamp via TranslationDeps + (tenets: algebraic effects for I/O). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + # boundary modules: the only place a port/default may live + - litellm/translation/deps.py + - litellm/translation/engine/http.py + pattern-either: + - pattern: time.time(...) + - pattern: time.time_ns(...) + - pattern: time.monotonic(...) + - pattern: time.monotonic_ns(...) + - pattern: time.perf_counter(...) + - pattern: time.perf_counter_ns(...) + - pattern: time.sleep(...) + - pattern: datetime.datetime.now(...) + - pattern: datetime.datetime.utcnow(...) + - pattern: datetime.datetime.today(...) + - pattern: datetime.date.today(...) + - pattern: datetime.now(...) + - pattern: datetime.utcnow(...) + - pattern: datetime.today(...) + - pattern: date.today(...) + metadata: + category: correctness + tags: [python, translation-v2, algebraic-effects] + confidence: HIGH + + - id: translation-no-ambient-ids-or-randomness + message: > + ambient effect: generating ids or randomness (uuid.*, fastuuid, + random.*, secrets.*, os.urandom) inside litellm/translation. Inject the + generator via TranslationDeps (tenets: algebraic effects for I/O). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + - litellm/translation/deps.py + - litellm/translation/engine/http.py + pattern-either: + - pattern: uuid.uuid1(...) + - pattern: uuid.uuid3(...) + - pattern: uuid.uuid4(...) + - pattern: uuid.uuid5(...) + - pattern: import fastuuid + - pattern: from fastuuid import $X + - pattern: import random + - pattern: from random import $X + - pattern: import secrets + - pattern: from secrets import $X + - pattern: os.urandom(...) + metadata: + category: correctness + tags: [python, translation-v2, algebraic-effects] + confidence: HIGH + + - id: translation-no-ambient-env + message: > + ambient effect: reading process environment (os.environ, os.getenv) + inside litellm/translation. Env-derived flags enter as values on + TranslationDeps, built once at the dispatch seam (tenets: algebraic + effects for I/O; dependency injection, no global singletons). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + - litellm/translation/deps.py + - litellm/translation/engine/http.py + pattern-either: + - pattern: os.environ + - pattern: os.environb + - pattern: os.getenv(...) + - pattern: os.putenv(...) + - pattern: os.unsetenv(...) + metadata: + category: correctness + tags: [python, translation-v2, algebraic-effects] + confidence: HIGH + + - id: translation-no-ambient-filesystem + message: > + ambient effect: touching the filesystem (open(), io.open(), pathlib + read/write) inside litellm/translation. The package translates values; + file content must arrive pre-read via TranslationDeps (tenets: + algebraic effects for I/O). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + - litellm/translation/deps.py + - litellm/translation/engine/http.py + pattern-either: + - pattern: open(...) + - pattern: io.open(...) + - pattern: $P.read_text(...) + - pattern: $P.write_text(...) + - pattern: $P.read_bytes(...) + - pattern: $P.write_bytes(...) + - pattern: $P.open(...) + - pattern: os.remove(...) + - pattern: os.unlink(...) + - pattern: os.makedirs(...) + - pattern: os.mkdir(...) + - pattern: shutil.$F(...) + - pattern: tempfile.$F(...) + metadata: + category: correctness + tags: [python, translation-v2, algebraic-effects] + confidence: HIGH + + - id: translation-no-ambient-network + message: > + ambient effect: a network client (requests/httpx/aiohttp/urllib/socket) + inside litellm/translation. The ONLY I/O surface is the injected + engine.http.HttpPort; the seam owns clients (tenets: algebraic effects + for I/O; dependency injection). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + - litellm/translation/deps.py + - litellm/translation/engine/http.py + pattern-either: + - pattern: import requests + - pattern: from requests import $X + - pattern: from requests.$M import $X + - pattern: import httpx + - pattern: from httpx import $X + - pattern: from httpx.$M import $X + - pattern: import aiohttp + - pattern: from aiohttp import $X + - pattern: from aiohttp.$M import $X + - pattern: import urllib + - pattern: import urllib.request + - pattern: from urllib import $X + - pattern: from urllib.$M import $X + - pattern: import socket + - pattern: from socket import $X + metadata: + category: correctness + tags: [python, translation-v2, algebraic-effects] + confidence: HIGH From 76df0b0ffd68758f2b6a5bc61c1b6316e1251f07 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:59:30 +0000 Subject: [PATCH 2/7] feat(translation): file line-count cap gate (720 = 1.3x current max) for the no-monster-files tenet --- scripts/check_translation_file_sizes.py | 50 +++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 scripts/check_translation_file_sizes.py diff --git a/scripts/check_translation_file_sizes.py b/scripts/check_translation_file_sizes.py new file mode 100644 index 0000000000..39813361e5 --- /dev/null +++ b/scripts/check_translation_file_sizes.py @@ -0,0 +1,50 @@ +"""No-monster-files gate for litellm/translation (06-quality-team.md tenet: +no monster files or god objects). + +Hard line cap per .py file. 720 = 1.3x today's largest file (ir.py, 555 +lines, the deliberate home of every IR union) so the current tree passes +with ~30% headroom; everything else is under 360. Raising the cap is a +reviewed decision, not a drive-by edit. + +Run: python scripts/check_translation_file_sizes.py +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +MAX_LINES = 720 +PACKAGE = Path(__file__).resolve().parent.parent / "litellm" / "translation" + + +def line_count(path: Path) -> int: + with path.open("r", encoding="utf-8") as handle: + return sum(1 for _ in handle) + + +def main() -> int: + counts = sorted( + ((line_count(p), p) for p in PACKAGE.rglob("*.py")), reverse=True + ) + offenders = [(n, p) for n, p in counts if n > MAX_LINES] + if not offenders: + biggest, where = counts[0] + print( + f"translation file sizes OK: {len(counts)} files, " + f"largest {where.relative_to(PACKAGE.parent.parent)} at {biggest} " + f"lines (cap {MAX_LINES})" + ) + return 0 + print( + f"monster file: {len(offenders)} file(s) over the {MAX_LINES}-line cap " + "(tenets: no monster files or god objects). Split along the package " + "map in litellm/translation/CLAUDE.md:" + ) + for n, p in offenders: + print(f" {p.relative_to(PACKAGE.parent.parent)}: {n} lines") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) From 234914217cf44fd51ad5d129be3c00f53dd7aae8 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:00:28 +0000 Subject: [PATCH 3/7] feat(translation): CLAUDE.md structural freshness gate (all dirs mapped, no phantom tree entries) --- scripts/check_translation_claude_md.py | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 scripts/check_translation_claude_md.py diff --git a/scripts/check_translation_claude_md.py b/scripts/check_translation_claude_md.py new file mode 100644 index 0000000000..52d581921e --- /dev/null +++ b/scripts/check_translation_claude_md.py @@ -0,0 +1,79 @@ +"""CLAUDE.md structural-freshness gate for litellm/translation +(06-quality-team.md tenet: deliberate file/folder structure with a +hand-written CLAUDE.md mapping it). + +Structural only, no prose judgment: +- litellm/translation/CLAUDE.md must exist +- every real directory under the package (any depth) must be mentioned as + ``/`` somewhere in CLAUDE.md +- every ``/`` token in CLAUDE.md's tree code block must be a real + directory (no phantom dirs) + +Run: python scripts/check_translation_claude_md.py +""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + +PACKAGE = Path(__file__).resolve().parent.parent / "litellm" / "translation" +CLAUDE_MD = PACKAGE / "CLAUDE.md" +DIR_TOKEN = re.compile(r"([A-Za-z_][\w.]*)/") + + +def real_dirs() -> frozenset[str]: + return frozenset( + p.name + for p in PACKAGE.rglob("*") + if p.is_dir() and p.name != "__pycache__" + ) + + +def tree_block_lines(text: str) -> tuple[str, ...]: + blocks = re.findall(r"^```\n(.*?)^```$", text, re.DOTALL | re.MULTILINE) + if not blocks: + return () + return tuple(line.split("#", 1)[0] for line in blocks[0].splitlines()) + + +def main() -> int: + if not CLAUDE_MD.is_file(): + print( + "CLAUDE.md freshness: litellm/translation/CLAUDE.md is missing " + "(tenets: deliberate structure, hand-written CLAUDE.md at the root)" + ) + return 1 + text = CLAUDE_MD.read_text(encoding="utf-8") + dirs = real_dirs() + + unmentioned = sorted(d for d in dirs if f"{d}/" not in text) + claimed = frozenset( + m for line in tree_block_lines(text) for m in DIR_TOKEN.findall(line) + ) + phantoms = sorted(claimed - dirs - {"translation"}) + + if not unmentioned and not phantoms: + print( + f"CLAUDE.md freshness OK: {len(dirs)} directories all mapped, " + "no phantom entries" + ) + return 0 + for d in unmentioned: + print( + f"CLAUDE.md freshness: directory '{d}/' exists but is not in " + "litellm/translation/CLAUDE.md; map it (tenets: deliberate " + "structure, CLAUDE.md kept true)" + ) + for d in phantoms: + print( + f"CLAUDE.md freshness: CLAUDE.md's tree names '{d}/' but no such " + "directory exists under litellm/translation; prune it (tenets: " + "deliberate structure, CLAUDE.md kept true)" + ) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) From 9f2416a0d4990555f68c5fb809b21ad717e4ec95 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:02:44 +0000 Subject: [PATCH 4/7] feat(translation): semgrep composition-over-inheritance rules (declarative-base whitelist, no multiple inheritance), 0 FP on current tree --- .../translation-v2-inheritance.yml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 .semgrep/rules/python/translation/translation-v2-inheritance.yml diff --git a/.semgrep/rules/python/translation/translation-v2-inheritance.yml b/.semgrep/rules/python/translation/translation-v2-inheritance.yml new file mode 100644 index 0000000000..bd3624e082 --- /dev/null +++ b/.semgrep/rules/python/translation/translation-v2-inheritance.yml @@ -0,0 +1,91 @@ +# litellm/translation composition-over-inheritance, encoded (06-quality-team.md +# tenet: composition over inheritance). Scoped to the translation package. +# +# A provider is a module of pure functions; behavior is composed, never +# inherited. The only legitimate bases are structural/declarative ones: +# Protocol ports, ABC port definitions, pydantic wire models (BaseModel and +# the package's single-level WireModel base), NamedTuple/TypedDict records, +# and the Enum family. errors.py is excluded by path: it is the one module +# allowed to define error machinery, including Exception subclasses if the +# seam contract ever needs them. +# +# Engine semantics this encoding relies on (verified against semgrep +# 1.157.0): `class $C($B): ...` matches any class with at least one +# positional base and binds $B to the FIRST base only. So the first rule +# fully checks single-base classes, and the second rule closes the +# multi-base hole by banning multiple inheritance outright -- which is what +# the tenet wants anyway. A justified exception (e.g. a str+Enum mixin on +# py3.10) carries an inline `# nosemgrep: ` with a reason. +# +# First-run measurement (2026-06-12): 25 classes with bases in the tree +# (HttpPort(Protocol); WireModel(BaseModel); 23x single-base WireModel +# subclasses), zero findings -- false-positive rate 0, rule enabled. + +rules: + - id: translation-no-inheritance + message: > + inheritance: class with a non-declarative base inside + litellm/translation. Compose pure functions instead of subclassing; + allowed bases are Protocol/ABC ports, pydantic wire models + (BaseModel/WireModel), NamedTuple/TypedDict, and Enum kinds (tenets: + composition over inheritance). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + # the one module allowed to define error machinery (Exception bases) + - litellm/translation/errors.py + patterns: + - pattern: | + class $C($B): + ... + - metavariable-pattern: + metavariable: $B + patterns: + - pattern-not: Protocol + - pattern-not: typing.Protocol + - pattern-not: Protocol[...] + - pattern-not: typing.Protocol[...] + - pattern-not: ABC + - pattern-not: abc.ABC + - pattern-not: BaseModel + - pattern-not: pydantic.BaseModel + - pattern-not: WireModel + - pattern-not: NamedTuple + - pattern-not: typing.NamedTuple + - pattern-not: TypedDict + - pattern-not: typing.TypedDict + - pattern-not: Enum + - pattern-not: enum.Enum + - pattern-not: StrEnum + - pattern-not: enum.StrEnum + - pattern-not: IntEnum + - pattern-not: enum.IntEnum + - pattern-not: IntFlag + - pattern-not: enum.IntFlag + metadata: + category: correctness + tags: [python, translation-v2, composition-over-inheritance] + confidence: HIGH + + - id: translation-no-multiple-inheritance + message: > + multiple inheritance inside litellm/translation. One declarative base + at most; compose behavior with functions instead of mixins (tenets: + composition over inheritance). + severity: ERROR + languages: [python] + paths: + include: + - litellm/translation + exclude: + - litellm/translation/errors.py + pattern: | + class $C($B1, $B2): + ... + metadata: + category: correctness + tags: [python, translation-v2, composition-over-inheritance] + confidence: HIGH From bc2c5869f6513470624fc0384d5197a45d6e98bb Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:03:44 +0000 Subject: [PATCH 5/7] feat(translation): ruff match/union hygiene rules (E721, SIM101, FURB168) for the tagged-unions-plus-match tenet --- litellm/translation/ruff.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/litellm/translation/ruff.toml b/litellm/translation/ruff.toml index f2789f2f42..b63ba2bc74 100644 --- a/litellm/translation/ruff.toml +++ b/litellm/translation/ruff.toml @@ -4,6 +4,12 @@ # module-global rebinding (PLW0603). Inherits the repo config; ruff's # hierarchical discovery applies this file to this subtree automatically, # so the repo-wide `ruff check` enforces it with no extra wiring. +# +# Match/union hygiene (tenet: tagged unions + match over branching on +# types): ruff 0.15 has no use-match-over-isinstance rule, so exhaustiveness +# stays with pyright (reportMatchNotExhaustive + assert_never). What IS +# deterministic here: E721 (no `type(x) ==` branching), SIM101 (no +# duplicated isinstance chains), FURB168 (no isinstance None checks). extend = "../../ruff.toml" preview = true @@ -15,6 +21,9 @@ extend-select = [ "PLR0915", "ANN", "PLW0603", + "E721", + "SIM101", + "FURB168", ] [lint.mccabe] From 76db738c7f0a803f838be0ac498dd36ef3bf9cb0 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:05:30 +0000 Subject: [PATCH 6/7] fix(translation): freshness gate ignores tool-cache dirs (.ruff_cache and friends) --- scripts/check_translation_claude_md.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/check_translation_claude_md.py b/scripts/check_translation_claude_md.py index 52d581921e..060eccc1d5 100644 --- a/scripts/check_translation_claude_md.py +++ b/scripts/check_translation_claude_md.py @@ -23,11 +23,16 @@ CLAUDE_MD = PACKAGE / "CLAUDE.md" DIR_TOKEN = re.compile(r"([A-Za-z_][\w.]*)/") +def is_tooling_dir(path: Path) -> bool: + parts = path.relative_to(PACKAGE).parts + return any(part == "__pycache__" or part.startswith(".") for part in parts) + + def real_dirs() -> frozenset[str]: return frozenset( p.name for p in PACKAGE.rglob("*") - if p.is_dir() and p.name != "__pycache__" + if p.is_dir() and not is_tooling_dir(p) ) From 6948efc49d6dd10225381ecccc6184dfbda3c8ef Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:05:36 +0000 Subject: [PATCH 7/7] feat(translation): wire file-size and CLAUDE.md freshness gates into make lint-translation --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 600908588a..57740edb9c 100644 --- a/Makefile +++ b/Makefile @@ -167,6 +167,8 @@ lint-translation: install-test-deps uv tool run --from 'pyright==1.1.406' pyright --project litellm/translation uv tool run --from 'semgrep==1.157.0' semgrep scan --config .semgrep/rules litellm/translation --error -q $(UV_RUN) lint-imports + $(UV_RUN) python scripts/check_translation_file_sizes.py + $(UV_RUN) python scripts/check_translation_claude_md.py LITELLM_LOCAL_MODEL_COST_MAP=True $(UV_RUN) pytest tests/test_litellm/translation --tb=short -q test-unit-root: install-test-deps