From 9178af564d8a6d974180c8e95cecfa82eeb1731b Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:30:18 +0000 Subject: [PATCH] fix: revert dependency promotions, remove nodejs-wheel-binaries, fix Docker layer caching - Move google-generativeai, Pillow, tenacity back to ci group (they are lazily imported and bloat the base SDK install needlessly) - Remove nodejs-wheel-binaries from extra_proxy and proxy-dev (redundant in Docker where system Node.js is already installed via apk) - Remove all nodejs-wheel node replacement and venv npm patching blocks from Dockerfiles since the wheel is no longer installed - Add --no-default-groups to CodSpeed benchmark workflow so the benchmark environment matches the old minimal pip install footprint - Apply standard uv two-phase Docker pattern: copy metadata first, install deps (cached layer), then copy source and install project - Replace CircleCI enterprise no-op with proper uv sync command Co-Authored-By: Claude Opus 4.6 (1M context) --- .circleci/config.yml | 2 +- .github/workflows/codspeed.yml | 4 +--- Dockerfile | 40 ++++++++++++++-------------------- docker/Dockerfile.alpine | 18 ++++++++++++--- docker/Dockerfile.database | 37 ++++++++++++++----------------- docker/Dockerfile.dev | 37 ++++++++++++++----------------- docker/Dockerfile.non_root | 35 +++++++++++++---------------- pyproject.toml | 13 ++++++----- 8 files changed, 87 insertions(+), 99 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f5528d71e72..9e06debfc04 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ commands: steps: - run: name: "Install local version of litellm-enterprise" - command: "true" + command: uv sync --frozen --package litellm-enterprise --python "$(which python)" setup_litellm_test_deps: steps: - checkout diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 0e5695983a6..17efbf90339 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -45,12 +45,10 @@ jobs: mode: simulation run: > env PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 - uv run --frozen + uv run --frozen --no-default-groups --with pytest==8.3.5 - --with pytest-asyncio==1.2.0 --with pytest-codspeed==4.3.0 pytest - -p pytest_asyncio.plugin -p pytest_codspeed.plugin tests/benchmarks/ --codspeed diff --git a/Dockerfile b/Dockerfile index 80890d0b6e2..a2cd1cb3ed2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,11 +33,26 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ XDG_CACHE_HOME=/app/.cache \ PATH="/app/.venv/bin:${PATH}" +# Copy dependency metadata first for layer caching +COPY pyproject.toml uv.lock ./ +COPY enterprise/pyproject.toml enterprise/ +COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/ + +# Install third-party dependencies (cached unless pyproject.toml/uv.lock change) +RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 + +# Copy full source tree COPY . . -# Build Admin UI before syncing so the packaged assets are current. +# Build Admin UI before final sync RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh +# Install project and workspace packages (fast - deps already cached) RUN uv sync --frozen --no-default-groups --no-editable \ --extra proxy \ --extra proxy-runtime \ @@ -85,32 +100,9 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ COPY --from=builder /app /app -# Replace the nodejs-wheel-binaries bundled node with the system node. -RUN NODEJS_WHEEL_NODE=$(find /app/.venv -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi - RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \ find /app/.venv -type d -path "*/tornado/test" -delete -# Patch npm copies bundled inside nodejs-wheel-binaries as well. -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /app/.venv -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /app/.venv -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /app/.venv -type d -name "brace-expansion" -path "*/node_modules/@isaacs/brace-expansion" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/@isaacs/brace-expansion" "$d"; \ - done && \ - find /app/.venv -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /app/.venv -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - EXPOSE 4000/tcp COPY docker/supervisord.conf /etc/supervisord.conf diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 7399c60ba9c..1a85ee5c02b 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -22,8 +22,23 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ XDG_CACHE_HOME=/app/.cache \ PATH="/app/.venv/bin:${PATH}" +# Copy dependency metadata first for layer caching +COPY pyproject.toml uv.lock ./ +COPY enterprise/pyproject.toml enterprise/ +COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/ + +# Install third-party dependencies (cached unless pyproject.toml/uv.lock change) +RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 + +# Copy full source tree COPY . . +# Install project and workspace packages (fast - deps already cached) RUN uv sync --frozen --no-default-groups --no-editable \ --extra proxy \ --extra proxy-runtime \ @@ -47,9 +62,6 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ COPY --from=builder /app /app -RUN NODEJS_WHEEL_NODE=$(find /app/.venv -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi - EXPOSE 4000/tcp ENTRYPOINT ["docker/prod_entrypoint.sh"] diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index 245c0c1c532..57ecef81eb8 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -32,10 +32,26 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ XDG_CACHE_HOME=/app/.cache \ PATH="/app/.venv/bin:${PATH}" +# Copy dependency metadata first for layer caching +COPY pyproject.toml uv.lock ./ +COPY enterprise/pyproject.toml enterprise/ +COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/ + +# Install third-party dependencies (cached unless pyproject.toml/uv.lock change) +RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 + +# Copy full source tree COPY . . +# Build Admin UI before final sync RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh +# Install project and workspace packages (fast - deps already cached) RUN uv sync --frozen --no-default-groups --no-editable \ --extra proxy \ --extra proxy-runtime \ @@ -82,30 +98,9 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ COPY --from=builder /app /app -RUN NODEJS_WHEEL_NODE=$(find /app/.venv -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi - RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \ find /app/.venv -type d -path "*/tornado/test" -delete -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /app/.venv -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /app/.venv -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /app/.venv -type d -name "brace-expansion" -path "*/node_modules/@isaacs/brace-expansion" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/@isaacs/brace-expansion" "$d"; \ - done && \ - find /app/.venv -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /app/.venv -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - EXPOSE 4000/tcp COPY docker/supervisord.conf /etc/supervisord.conf diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 5ba480195a4..88be7a6980c 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -31,10 +31,26 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ XDG_CACHE_HOME=/app/.cache \ PATH="/app/.venv/bin:${PATH}" +# Copy dependency metadata first for layer caching +COPY pyproject.toml uv.lock ./ +COPY enterprise/pyproject.toml enterprise/ +COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/ + +# Install third-party dependencies (cached unless pyproject.toml/uv.lock change) +RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python + +# Copy full source tree COPY . . +# Build Admin UI before final sync RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh +# Install project and workspace packages (fast - deps already cached) RUN uv sync --frozen --no-default-groups --no-editable \ --extra proxy \ --extra proxy-runtime \ @@ -99,27 +115,6 @@ ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ COPY --from=builder /app /app -RUN NODEJS_WHEEL_NODE=$(find /app/.venv -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi - -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /app/.venv -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /app/.venv -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /app/.venv -type d -name "brace-expansion" -path "*/node_modules/@isaacs/brace-expansion" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/@isaacs/brace-expansion" "$d"; \ - done && \ - find /app/.venv -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /app/.venv -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - EXPOSE 4000/tcp ENTRYPOINT ["docker/prod_entrypoint.sh"] diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index f7729c5c7f1..5451bff808d 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -43,6 +43,20 @@ ENV UV_PROJECT_ENVIRONMENT=/app/.venv \ PRISMA_CLI_BINARY_TARGETS="debian-openssl-3.0.x" \ XDG_CACHE_HOME=/app/.cache +# Copy dependency metadata first for layer caching +COPY pyproject.toml uv.lock ./ +COPY enterprise/pyproject.toml enterprise/ +COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/ + +# Install third-party dependencies (cached unless pyproject.toml/uv.lock change) +RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 + +# Copy full source tree COPY . . # Set non-root flag for build time consistency @@ -169,26 +183,7 @@ ENV PATH="/app/.venv/bin:${PATH}" \ NPM_CONFIG_PREFER_OFFLINE=true \ PRISMA_OFFLINE_MODE=true -RUN NODEJS_WHEEL_NODE=$(find /app/.venv -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi && \ - GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /app/.venv -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /app/.venv -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /app/.venv -type d -name "brace-expansion" -path "*/node_modules/@isaacs/brace-expansion" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/@isaacs/brace-expansion" "$d"; \ - done && \ - find /app/.venv -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /app/.venv -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done && \ - sed -i 's/\r$//' docker/entrypoint.sh && \ +RUN sed -i 's/\r$//' docker/entrypoint.sh && \ sed -i 's/\r$//' docker/prod_entrypoint.sh && \ chmod +x docker/entrypoint.sh docker/prod_entrypoint.sh && \ mkdir -p /nonexistent /.npm /var/lib/litellm/assets /var/lib/litellm/ui /tmp/.npm && \ diff --git a/pyproject.toml b/pyproject.toml index e23bd04aa84..3474c90c8bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ dependencies = [ "openai==2.24.0", "python-dotenv==1.0.1", "tiktoken==0.12.0", - "tenacity==8.5.0", "importlib-metadata==8.5.0", "tokenizers==0.22.2", "click==8.1.8", @@ -23,9 +22,6 @@ dependencies = [ "aiohttp==3.13.3", "pydantic==2.12.5", "jsonschema==4.23.0", - "google-generativeai==0.8.6", - "Pillow==11.3.0; python_version < '3.10'", - "Pillow==12.1.1; python_version >= '3.10'", ] [project.urls] @@ -66,7 +62,6 @@ proxy = [ ] extra_proxy = [ "prisma==0.11.0", - "nodejs-wheel-binaries==24.13.1", "azure-identity==1.25.2; python_version >= '3.9'", "azure-keyvault-secrets==4.10.0", # Not in PyPI proxy extra. @@ -166,9 +161,15 @@ proxy-dev = [ "opentelemetry-exporter-otlp==1.28.0", "azure-identity==1.25.2; python_version >= '3.9'", "a2a-sdk==0.3.24; python_version >= '3.10'", - "nodejs-wheel-binaries==24.13.1", ] ci = [ + # These are lazily imported at call sites; keep them out of core deps to + # avoid bloating the base SDK install (google-generativeai pulls grpcio + + # protobuf, Pillow is a compiled C extension). + "tenacity==8.5.0", + "google-generativeai==0.8.6", + "Pillow==11.3.0; python_version < '3.10'", + "Pillow==12.1.1; python_version >= '3.10'", # Azure batch E2E tests still import psycopg2 directly. "psycopg2-binary==2.9.11", "pytest-codspeed==4.3.0",