diff --git a/.github/workflows/test-code-quality.yml b/.github/workflows/test-code-quality.yml index 872a1799d98..3a966385674 100644 --- a/.github/workflows/test-code-quality.yml +++ b/.github/workflows/test-code-quality.yml @@ -23,6 +23,7 @@ jobs: steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: + fetch-depth: 0 persist-credentials: false - name: Checkout litellm-docs into docs/my-website (for documentation_tests) @@ -37,6 +38,11 @@ jobs: with: python-version: "3.12" + - name: Check root-size budget + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: python scripts/check_root_budget.py --base "$BASE_SHA" + - name: Set up uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: diff --git a/ci_cd/root-size-budget.json b/ci_cd/root-size-budget.json new file mode 100644 index 00000000000..d199c658b54 --- /dev/null +++ b/ci_cd/root-size-budget.json @@ -0,0 +1,6 @@ +{ + "max_tracked_root_dirs": 24, + "max_tracked_root_entries": 63, + "max_tracked_root_file_bytes": 3488525, + "max_tracked_root_files": 39 +} diff --git a/cookbook/nova_sonic_realtime.py b/cookbook/nova_sonic_realtime.py index ab510556254..ce9cec6085e 100644 --- a/cookbook/nova_sonic_realtime.py +++ b/cookbook/nova_sonic_realtime.py @@ -1,3 +1,4 @@ +# ruff: noqa: T201 """ Client script to test Nova Sonic realtime API through LiteLLM proxy. @@ -281,7 +282,7 @@ async def main(): if __name__ == "__main__": print("\nMake sure:") print("1. LiteLLM proxy is running on port 4000") - print("2. Bedrock is configured in proxy_server_config.yaml") + print("2. Bedrock is configured in docker/proxy_server_config.yaml") print("3. AWS credentials are set") print() diff --git a/docker-compose.hardened.yml b/docker-compose.hardened.yml index 31d0c2e9ef2..fc5a35f2793 100644 --- a/docker-compose.hardened.yml +++ b/docker-compose.hardened.yml @@ -22,7 +22,7 @@ services: - /app/cache:rw,noexec,nosuid,nodev,size=128m,uid=101,gid=101,mode=1777 - /app/migrations:rw,noexec,nosuid,nodev,size=64m,uid=101,gid=101,mode=1777 volumes: - - ./proxy_server_config.yaml:/app/config.yaml:ro + - ./docker/proxy_server_config.yaml:/app/config.yaml:ro environment: LITELLM_NON_ROOT: "true" PRISMA_BINARY_CACHE_DIR: "/app/cache/prisma-python/binaries" diff --git a/docker-compose.yml b/docker-compose.yml index 80e1f289aad..d0beabf96d6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,7 +57,7 @@ services: image: prom/prometheus volumes: - prometheus_data:/prometheus - - ./prometheus.yml:/etc/prometheus/prometheus.yml + - ./docker/prometheus.yml:/etc/prometheus/prometheus.yml ports: - "9090:9090" command: diff --git a/prometheus.yml b/docker/prometheus.yml similarity index 100% rename from prometheus.yml rename to docker/prometheus.yml diff --git a/proxy_server_config.yaml b/docker/proxy_server_config.yaml similarity index 100% rename from proxy_server_config.yaml rename to docker/proxy_server_config.yaml diff --git a/mcp_servers.json b/litellm/proxy/example_config_yaml/mcp_servers.json similarity index 100% rename from mcp_servers.json rename to litellm/proxy/example_config_yaml/mcp_servers.json diff --git a/scripts/check_root_budget.py b/scripts/check_root_budget.py new file mode 100644 index 00000000000..7130278c85a --- /dev/null +++ b/scripts/check_root_budget.py @@ -0,0 +1,123 @@ +import argparse +import json +import subprocess +import sys +from pathlib import Path +from typing import Any + + +BUDGET_PATH = Path("ci_cd/root-size-budget.json") + + +def _git(args: list[str], *, check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["git", *args], + check=check, + capture_output=True, + text=True, + ) + + +def _tracked_files() -> list[str]: + result = _git(["ls-files"]) + return [line for line in result.stdout.splitlines() if line] + + +def current_root_metrics() -> dict[str, int]: + root_files: list[str] = [] + root_dirs: set[str] = set() + + for file_path in _tracked_files(): + parts = Path(file_path).parts + if len(parts) == 1: + root_files.append(file_path) + else: + root_dirs.add(parts[0]) + + root_file_bytes = sum(Path(file_path).stat().st_size for file_path in root_files) + return { + "max_tracked_root_dirs": len(root_dirs), + "max_tracked_root_entries": len(root_files) + len(root_dirs), + "max_tracked_root_file_bytes": root_file_bytes, + "max_tracked_root_files": len(root_files), + } + + +def load_budget() -> dict[str, int]: + with BUDGET_PATH.open() as budget_file: + budget = json.load(budget_file) + return {key: int(value) for key, value in budget.items()} + + +def load_base_budget(base_ref: str | None) -> dict[str, int] | None: + if not base_ref: + return None + + result = _git(["show", f"{base_ref}:{BUDGET_PATH}"], check=False) + if result.returncode != 0: + return None + parsed: dict[str, Any] = json.loads(result.stdout) + return {key: int(value) for key, value in parsed.items()} + + +def check_current_budget(metrics: dict[str, int], budget: dict[str, int]) -> list[str]: + errors: list[str] = [] + for key, actual_value in metrics.items(): + budget_value = budget.get(key) + if budget_value is None: + errors.append(f"{key}: missing from {BUDGET_PATH}") + continue + if actual_value > budget_value: + errors.append(f"{key}: actual {actual_value} exceeds budget {budget_value}") + return errors + + +def check_budget_ratchet( + budget: dict[str, int], base_budget: dict[str, int] | None +) -> list[str]: + if base_budget is None: + return [] + + errors: list[str] = [] + for key, budget_value in budget.items(): + base_value = base_budget.get(key) + if base_value is None: + continue + if budget_value > base_value: + errors.append( + f"{key}: budget increased from {base_value} to {budget_value}" + ) + return errors + + +def emit(message: str = "") -> None: + sys.stdout.write(f"{message}\n") + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Ensure tracked repo-root file/dir counts do not grow." + ) + parser.add_argument("--base", help="Optional base ref for budget ratcheting") + args = parser.parse_args() + + metrics = current_root_metrics() + budget = load_budget() + base_budget = load_base_budget(args.base) + + errors = check_current_budget(metrics, budget) + errors.extend(check_budget_ratchet(budget, base_budget)) + + emit("Current root metrics:") + for key in sorted(metrics): + emit(f" {key}: {metrics[key]} / {budget.get(key, 'missing')}") + + if errors: + emit("\nRoot budget check failed:") + for error in errors: + emit(f" - {error}") + raise SystemExit(1) + + +if __name__ == "__main__": + main()