diff --git a/.circleci/config.yml b/.circleci/config.yml index 2b0a6924cc..0c7a04d0f8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,40 +16,90 @@ commands: echo "nameserver 127.0.0.11" | sudo tee /etc/resolv.conf echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf echo "nameserver 8.8.4.4" | sudo tee -a /etc/resolv.conf + wait_for_service: + description: "Poll a TCP or HTTP endpoint until it responds (replaces dockerize -wait)" + parameters: + url: + type: string + timeout: + type: string + default: "60" + steps: + - run: + name: "Wait for << parameters.url >>" + command: | + TIMEOUT=<< parameters.timeout >> + URL="<< parameters.url >>" + ELAPSED=0 + echo "Waiting up to ${TIMEOUT}s for ${URL} ..." + if echo "$URL" | grep -q '^tcp://'; then + HOST=$(echo "$URL" | sed 's|tcp://||' | cut -d: -f1) + PORT=$(echo "$URL" | sed 's|tcp://||' | cut -d: -f2) + while ! bash -c "echo > /dev/tcp/$HOST/$PORT" 2>/dev/null; do + sleep 2; ELAPSED=$((ELAPSED+2)) + if [ "$ELAPSED" -ge "$TIMEOUT" ]; then echo "Timed out"; exit 1; fi + done + else + while ! curl -sf --max-time 5 "$URL" > /dev/null 2>&1; do + sleep 2; ELAPSED=$((ELAPSED+2)) + if [ "$ELAPSED" -ge "$TIMEOUT" ]; then echo "Timed out"; exit 1; fi + done + fi + echo "Service ready after ${ELAPSED}s" + install_helm: + steps: + - run: + name: Install Helm v3.17.3 + command: | + curl -sSLf -o /tmp/helm.tar.gz \ + https://get.helm.sh/helm-v3.17.3-linux-amd64.tar.gz + echo "ee88b3c851ae6466a3de507f7be73fe94d54cbf2987cbaa3d1a3832ea331f2cd /tmp/helm.tar.gz" | sha256sum -c - + sudo tar -C /usr/local/bin --strip-components=1 -xzf /tmp/helm.tar.gz linux-amd64/helm + rm -f /tmp/helm.tar.gz + install_kind: + steps: + - run: + name: Install Kind v0.20.0 + command: | + curl -sSLf -o /tmp/kind \ + https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 + echo "513a7213d6d3332dd9ef27c24dab35e5ef10a04fa27274fe1c14d8a246493ded /tmp/kind" | sha256sum -c - + chmod +x /tmp/kind + sudo mv /tmp/kind /usr/local/bin/kind setup_litellm_enterprise_pip: steps: - run: name: "Install local version of litellm-enterprise" command: | - pip install --force-reinstall --no-deps -e enterprise/ + # litellm-enterprise is a uv workspace member and is already installed + # by the main `uv sync --all-groups --all-extras`. Do NOT run + # `uv sync --package litellm-enterprise` here — that overwrites the + # shared .venv and strips out dev/test deps (pytest, prisma, etc.). + uv run --no-sync python -c "import litellm_enterprise; print('litellm-enterprise OK:', litellm_enterprise.__file__)" setup_litellm_test_deps: steps: - checkout - setup_google_dns - restore_cache: keys: - - v3-litellm-uv-deps-{{ checksum "requirements.txt" }}-{{ checksum ".circleci/config.yml" }} + - v3-litellm-uv-deps-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - # Use uv for the heavy requirements.txt (10-100x faster than pip) - uv pip install --system -r requirements.txt - # Use pip for test deps (small set, avoids uv strict-resolution - # conflicts with transitive dep pins like openai<2 and pydantic>=2.11.5) - pip install "pytest-mock==3.12.0" "pytest==7.3.1" "pytest-retry==1.6.3" \ - "pytest-asyncio==0.21.1" "respx==0.22.0" "hypercorn==0.17.3" \ - "pydantic==2.12.5" "mcp==1.26.0" "requests-mock>=1.12.1" \ - "responses==0.25.7" "pytest-xdist==3.6.1" "pytest-timeout==2.2.0" \ - "pytest-cov==5.0.0" "semantic_router==0.1.10" "fastapi-offline==1.7.3" \ - "a2a" "parameterized>=0.9.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - save_cache: paths: - ~/.local/lib - ~/.local/bin - ~/.cache/uv - key: v3-litellm-uv-deps-{{ checksum "requirements.txt" }}-{{ checksum ".circleci/config.yml" }} + key: v3-litellm-uv-deps-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} jobs: # Add Windows testing job @@ -71,13 +121,20 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - pip install pytest - pip install . + Invoke-RestMethod https://astral.sh/uv/0.10.9/install.ps1 | Invoke-Expression + $uvBin = Join-Path $HOME ".local\bin" + $env:Path = "$uvBin;$env:Path" + if (!(Test-Path $PROFILE)) { + New-Item -ItemType File -Force -Path $PROFILE | Out-Null + } + if (-not (Select-String -Path $PROFILE -SimpleMatch $uvBin -Quiet)) { + Add-Content -Path $PROFILE -Value "`$env:Path = `"$uvBin;`$env:Path`"" + } + uv sync --frozen --group dev --python (Get-Command python).Source - run: name: Run Windows-specific test command: | - python -m pytest tests/windows_tests/test_litellm_on_windows.py -v + uv run --no-sync python -m pytest tests/windows_tests/test_litellm_on_windows.py -v mypy_linting: docker: @@ -94,16 +151,19 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip uninstall fastuuid -y - pip install "mypy==1.18.2" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv sync --frozen --group dev --python "$(which python)" --no-install-package fastuuid - run: name: MyPy Type Checking command: | cd litellm # Use the same approach as GitHub Actions, explicitly exclude fastuuid to avoid segfaults - python -m mypy . + uv run --no-sync python -m mypy . cd .. no_output_timeout: 10m @@ -120,10 +180,19 @@ jobs: - setup_google_dns - run: name: Install Semgrep - command: pip install semgrep + command: | + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" - run: name: Run Semgrep (custom rules only) - command: semgrep scan --config .semgrep/rules . --error + command: | + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv tool run --from 'semgrep==1.157.0' semgrep scan --config .semgrep/rules . --error local_testing_part1: docker: @@ -143,31 +212,27 @@ jobs: - restore_cache: keys: - - v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-asyncio==0.21.1" "pytest-cov==5.0.0" \ - "mypy==1.18.2" "google-generativeai==0.3.2" "google-cloud-aiplatform==1.133.0" pyarrow \ - "boto3==1.42.80" langchain lunary==0.2.5 \ - "azure-identity==1.25.3" "langfuse==2.59.7" "logfire==0.29.0" numpydoc \ - traceloop-sdk==0.21.1 openai==1.100.1 prisma==0.11.0 \ - "detect_secrets==1.5.0" "respx==0.22.0" fastapi \ - "gunicorn==23.0.0" "aiodynamo==23.10.1" "asyncio==3.4.3" \ - "apscheduler==3.11.2" "PyGithub==1.59.1" argon2-cffi "pytest-mock==3.12.0" \ - python-multipart prometheus-client==0.20.0 "pydantic==2.12.5" \ - "diskcache==5.6.1" "Pillow==12.1.1" "jsonschema==4.23.0" \ - "pytest-xdist==3.6.1" "pytest-timeout==2.2.0" "websockets==15.0.1" - pip install semantic_router --no-deps - pip install aurelio_sdk --no-deps - pip uninstall posthog -y + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - save_cache: paths: - - ./venv - key: v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - ./.venv + key: v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Run prisma ./docker/entrypoint.sh command: | @@ -179,8 +244,7 @@ jobs: name: Black Formatting command: | cd litellm - python -m pip install black - python -m black . + uv run --no-sync python -m black . cd .. # Run pytest and generate JUnit XML report @@ -195,7 +259,7 @@ jobs: echo "$TEST_FILES" | circleci tests run \ --split-by=timings \ --verbose \ - --command="xargs python -m pytest \ + --command="xargs uv run --no-sync python -m pytest \ -vv \ --cov=litellm \ --cov-report=xml \ @@ -238,31 +302,27 @@ jobs: - restore_cache: keys: - - v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-asyncio==0.21.1" "pytest-cov==5.0.0" \ - "mypy==1.18.2" "google-generativeai==0.3.2" "google-cloud-aiplatform==1.133.0" pyarrow \ - "boto3==1.42.80" langchain lunary==0.2.5 \ - "azure-identity==1.25.3" "langfuse==2.59.7" "logfire==0.29.0" numpydoc \ - traceloop-sdk==0.21.1 openai==1.100.1 prisma==0.11.0 \ - "detect_secrets==1.5.0" "respx==0.22.0" fastapi \ - "gunicorn==23.0.0" "aiodynamo==23.10.1" "asyncio==3.4.3" \ - "apscheduler==3.11.2" "PyGithub==1.59.1" argon2-cffi "pytest-mock==3.12.0" \ - python-multipart prometheus-client==0.20.0 "pydantic==2.12.5" \ - "diskcache==5.6.1" "Pillow==12.1.1" "jsonschema==4.23.0" \ - "pytest-xdist==3.6.1" "pytest-timeout==2.2.0" "websockets==15.0.1" - pip install semantic_router --no-deps - pip install aurelio_sdk --no-deps - pip uninstall posthog -y + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - save_cache: paths: - - ./venv - key: v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - ./.venv + key: v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Run prisma ./docker/entrypoint.sh command: | @@ -274,8 +334,7 @@ jobs: name: Black Formatting command: | cd litellm - python -m pip install black - python -m black . + uv run --no-sync python -m black . cd .. # Run pytest and generate JUnit XML report @@ -290,7 +349,7 @@ jobs: echo "$TEST_FILES" | circleci tests run \ --split-by=timings \ --verbose \ - --command="xargs python -m pytest \ + --command="xargs uv run --no-sync python -m pytest \ -vv \ --cov=litellm \ --cov-report=xml \ @@ -334,58 +393,27 @@ jobs: - restore_cache: keys: - - v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-cov==5.0.0" - pip install "mypy==1.18.2" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install pyarrow - pip install "boto3==1.42.80" - pip install langchain - pip install lunary==0.2.5 - pip install "azure-identity==1.25.3" - pip install "langfuse==2.59.7" - pip install "logfire==0.29.0" - pip install numpydoc - pip install traceloop-sdk==0.21.1 - pip install opentelemetry-api==1.28.0 - pip install opentelemetry-sdk==1.28.0 - pip install opentelemetry-exporter-otlp==1.28.0 - pip install openai==1.100.1 - pip install prisma==0.11.0 - pip install "detect_secrets==1.5.0" - pip install "httpx==0.28.1" - pip install "respx==0.22.0" - pip install fastapi - pip install "gunicorn==23.0.0" - pip install "anyio==4.8.0" - pip install "aiodynamo==23.10.1" - pip install "asyncio==3.4.3" - pip install "apscheduler==3.11.2" - pip install "PyGithub==1.59.1" - pip install argon2-cffi - pip install "pytest-mock==3.12.0" - pip install python-multipart - pip install google-cloud-aiplatform - pip install prometheus-client==0.20.0 - pip install "pydantic==2.12.5" - pip install "diskcache==5.6.1" - pip install "Pillow==12.1.1" - pip install "jsonschema==4.23.0" - pip install "websockets==15.0.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - save_cache: paths: - - ./venv - key: v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - ./.venv + key: v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Run prisma ./docker/entrypoint.sh command: | @@ -400,7 +428,7 @@ jobs: command: | pwd ls - python -m pytest -v tests/local_testing -x --junitxml=test-results/junit.xml --durations=5 -k "langfuse" + uv run --no-sync python -m pytest -v tests/local_testing -x --junitxml=test-results/junit.xml --durations=5 -k "langfuse" no_output_timeout: 15m # Store test results - store_test_results: @@ -419,16 +447,22 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - save_cache: paths: - - ./venv - key: v1-dependencies-{{ checksum ".circleci/requirements.txt" }} + - ./.venv + key: v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Run prisma ./docker/entrypoint.sh command: | @@ -436,13 +470,16 @@ jobs: chmod +x docker/entrypoint.sh ./docker/entrypoint.sh set -e + - run: + name: Generate Prisma Client + command: uv run --no-sync python -m prisma generate # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -v tests/proxy_admin_ui_tests -x --junitxml=test-results/junit.xml --durations=5 -n 2 + uv run --no-sync python -m pytest -v tests/proxy_admin_ui_tests -x --junitxml=test-results/junit.xml --durations=5 -n 2 no_output_timeout: 15m # Store test results @@ -463,25 +500,27 @@ jobs: - setup_google_dns - restore_cache: keys: - - v1-router-testing-deps-{{ checksum "requirements.txt" }} + - v1-router-testing-deps-{{ checksum "uv.lock" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "respx==0.22.0" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-xdist==3.6.1" - pip install "pytest-timeout==2.2.0" - pip install semantic_router --no-deps - pip install aurelio_sdk --no-deps + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - save_cache: paths: - /home/circleci/.pyenv - /home/circleci/.local - key: v1-router-testing-deps-{{ checksum "requirements.txt" }} + key: v1-router-testing-deps-{{ checksum "uv.lock" }} # Run pytest and generate JUnit XML report - setup_litellm_enterprise_pip - run: @@ -494,7 +533,7 @@ jobs: echo "$TEST_FILES" | circleci tests run \ --split-by=timings \ --verbose \ - --command="xargs python -m pytest \ + --command="xargs uv run --no-sync python -m pytest \ -v \ -k 'router' \ -n 4 \ @@ -520,24 +559,27 @@ jobs: - setup_google_dns - restore_cache: keys: - - v1-router-unit-deps-{{ checksum "requirements.txt" }} + - v1-router-unit-deps-{{ checksum "uv.lock" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "respx==0.22.0" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install semantic_router --no-deps - pip install aurelio_sdk --no-deps - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - save_cache: paths: - /home/circleci/.pyenv - /home/circleci/.local - key: v1-router-unit-deps-{{ checksum "requirements.txt" }} + key: v1-router-unit-deps-{{ checksum "uv.lock" }} # Run pytest and generate JUnit XML report - setup_litellm_enterprise_pip - run: @@ -545,7 +587,7 @@ jobs: command: | pwd ls - python -m pytest -v tests/router_unit_tests -x --junitxml=test-results/junit.xml --durations=5 -n 4 + uv run --no-sync python -m pytest -v tests/router_unit_tests -x --junitxml=test-results/junit.xml --durations=5 -n 4 no_output_timeout: 15m # Store test results - store_test_results: @@ -565,13 +607,18 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - pip install wheel setuptools - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "respx==0.22.0" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - setup_litellm_enterprise_pip - run: @@ -579,7 +626,7 @@ jobs: command: | pwd ls - python -m pytest tests/local_testing/ -v -k "assistants" -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest tests/local_testing/ -v -k "assistants" -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Store test results - store_test_results: @@ -598,23 +645,27 @@ jobs: - setup_google_dns - restore_cache: keys: - - v1-llm-translation-deps-{{ checksum "requirements.txt" }} + - v1-llm-translation-deps-{{ checksum "uv.lock" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pytest-xdist==3.6.1" - pip install "pytest-timeout==2.2.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - save_cache: paths: - /home/circleci/.pyenv - /home/circleci/.local - key: v1-llm-translation-deps-{{ checksum "requirements.txt" }} + key: v1-llm-translation-deps-{{ checksum "uv.lock" }} # Run pytest and generate JUnit XML report - run: name: Run tests @@ -631,7 +682,7 @@ jobs: for dir in "${IGNORE_DIRS[@]}"; do IGNORE_ARGS="$IGNORE_ARGS --ignore=$dir" done - python -m pytest -v tests/llm_translation $IGNORE_ARGS --junitxml=test-results/junit.xml --durations=20 -n 8 --timeout=120 --timeout_method=thread --retries 2 --retry-delay 5 + uv run --no-sync python -m pytest -v tests/llm_translation $IGNORE_ARGS --junitxml=test-results/junit.xml --durations=20 -n 8 --timeout=120 --timeout_method=thread --retries 2 --retry-delay 5 no_output_timeout: 15m # Store test results @@ -651,9 +702,18 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-cov==5.0.0" "pytest-asyncio==0.21.1" "respx==0.22.0" "pytest-xdist==3.6.1" "pytest-timeout==2.2.0" "websockets" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run realtime tests @@ -662,7 +722,7 @@ jobs: ls # Add --timeout to kill hanging tests after 120s (2 min) # Add --durations=20 to show 20 slowest tests for debugging - python -m pytest -vv tests/llm_translation/realtime --cov=litellm --cov-report=xml -v --junitxml=test-results/junit.xml --durations=20 -n 4 --timeout=120 --timeout_method=thread + uv run --no-sync python -m pytest -vv tests/llm_translation/realtime --cov=litellm --cov-report=xml -v --junitxml=test-results/junit.xml --durations=20 -n 4 --timeout=120 --timeout_method=thread no_output_timeout: 15m - run: name: Rename the coverage files @@ -692,23 +752,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pydantic==2.12.5" - pip install "mcp==1.26.0" - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/mcp_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 + uv run --no-sync python -m pytest -vv tests/mcp_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 no_output_timeout: 15m - run: name: Rename the coverage files @@ -738,22 +800,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pydantic==2.12.5" - pip install "a2a-sdk" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/agent_tests --ignore=tests/agent_tests/local_only_agent_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/agent_tests --ignore=tests/agent_tests/local_only_agent_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m - run: name: Rename the coverage files @@ -783,26 +848,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pydantic==2.12.5" - pip install "boto3==1.42.80" - pip install "semantic_router==0.1.10" --no-deps - pip install aurelio_sdk - pip install "pytest-xdist==3.6.1" - pip install "pytest-timeout==2.2.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - LITELLM_LOG=WARNING python -m pytest tests/guardrails_tests -vv --cov=litellm --cov-report=xml --junitxml=test-results/junit.xml --durations=5 -n 2 --timeout=120 --timeout_method=thread + LITELLM_LOG=WARNING uv run --no-sync python -m pytest tests/guardrails_tests -vv --cov=litellm --cov-report=xml --junitxml=test-results/junit.xml --durations=5 -n 2 --timeout=120 --timeout_method=thread no_output_timeout: 15m - run: name: Rename the coverage files @@ -833,21 +897,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pydantic==2.12.5" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/unified_google_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 --retries 3 --retry-delay 5 + uv run --no-sync python -m pytest -vv tests/unified_google_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 --retries 3 --retry-delay 5 no_output_timeout: 15m - run: name: Rename the coverage files @@ -878,29 +946,34 @@ jobs: - setup_google_dns - restore_cache: keys: - - v1-llm-responses-deps-{{ checksum "requirements.txt" }} + - v1-llm-responses-deps-{{ checksum "uv.lock" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - save_cache: paths: - /home/circleci/.pyenv - /home/circleci/.local - key: v1-llm-responses-deps-{{ checksum "requirements.txt" }} + key: v1-llm-responses-deps-{{ checksum "uv.lock" }} # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -v tests/llm_responses_api_testing -x --junitxml=test-results/junit.xml --durations=5 -n 8 + uv run --no-sync python -m pytest -v tests/llm_responses_api_testing -x --junitxml=test-results/junit.xml --durations=5 -n 8 no_output_timeout: 15m # Store test results @@ -920,16 +993,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-cov==5.0.0" "pytest-asyncio==0.21.1" "respx==0.22.0" "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/ocr_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 + uv run --no-sync python -m pytest -vv tests/ocr_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 no_output_timeout: 15m - run: name: Rename the coverage files @@ -959,16 +1041,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-cov==5.0.0" "pytest-asyncio==0.21.1" "respx==0.22.0" "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/search_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 + uv run --no-sync python -m pytest -vv tests/search_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 no_output_timeout: 15m - run: name: Rename the coverage files @@ -998,9 +1089,9 @@ jobs: - run: name: Run proxy tests part 1 (high-volume directories) command: | - prisma generate + uv run --no-sync python -m prisma generate export PYTHONUNBUFFERED=1 - python -m pytest tests/test_litellm/proxy/guardrails tests/test_litellm/proxy/management_endpoints tests/test_litellm/proxy/_experimental tests/test_litellm/proxy/client tests/test_litellm/proxy/auth --junitxml=test-results/junit-proxy-part1.xml --durations=10 -n 4 --maxfail=5 --timeout=60 -vv --log-cli-level=WARNING -r A + uv run --no-sync python -m pytest tests/test_litellm/proxy/guardrails tests/test_litellm/proxy/management_endpoints tests/test_litellm/proxy/_experimental tests/test_litellm/proxy/client tests/test_litellm/proxy/auth --junitxml=test-results/junit-proxy-part1.xml --durations=10 -n 4 --maxfail=5 --timeout=60 -vv --log-cli-level=WARNING -r A no_output_timeout: 15m - store_test_results: path: test-results @@ -1017,9 +1108,9 @@ jobs: - run: name: Run proxy tests part 2 (all other tests) command: | - prisma generate + uv run --no-sync python -m prisma generate export PYTHONUNBUFFERED=1 - python -m pytest tests/test_litellm/proxy --ignore=tests/test_litellm/proxy/guardrails --ignore=tests/test_litellm/proxy/management_endpoints --ignore=tests/test_litellm/proxy/_experimental --ignore=tests/test_litellm/proxy/client --ignore=tests/test_litellm/proxy/auth --junitxml=test-results/junit-proxy-part2.xml --durations=10 -n 4 --maxfail=5 --timeout=120 -vv --log-cli-level=WARNING -r A + uv run --no-sync python -m pytest tests/test_litellm/proxy --ignore=tests/test_litellm/proxy/guardrails --ignore=tests/test_litellm/proxy/management_endpoints --ignore=tests/test_litellm/proxy/_experimental --ignore=tests/test_litellm/proxy/client --ignore=tests/test_litellm/proxy/auth --junitxml=test-results/junit-proxy-part2.xml --durations=10 -n 4 --maxfail=5 --timeout=120 -vv --log-cli-level=WARNING -r A no_output_timeout: 15m - store_test_results: path: test-results @@ -1038,31 +1129,26 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest-mock==3.12.0" - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "hypercorn==0.17.3" - pip install "pydantic==2.12.5" - pip install "mcp==1.26.0" - pip install "requests-mock>=1.12.1" - pip install "responses==0.25.7" - pip install "pytest-xdist==3.6.1" - pip install "semantic_router==0.1.10" --no-deps - pip install aurelio_sdk - pip install "fastapi-offline==1.7.3" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - run: name: Run enterprise tests command: | pwd ls - prisma generate - python -m pytest -v tests/enterprise -x --junitxml=test-results/junit-enterprise.xml --durations=10 -n 4 + uv run --no-sync python -m prisma generate + uv run --no-sync python -m pytest -v tests/enterprise -x --junitxml=test-results/junit-enterprise.xml --durations=10 -n 4 no_output_timeout: 15m # Store test results - store_test_results: @@ -1081,23 +1167,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "respx==0.22.0" - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-cov==5.0.0" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/batches_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 + uv run --no-sync python -m pytest -vv tests/batches_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 no_output_timeout: 15m - run: name: Rename the coverage files @@ -1127,25 +1215,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install numpydoc - pip install "respx==0.22.0" - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-cov==5.0.0" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install pytest-mock - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/litellm_utils_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 + uv run --no-sync python -m pytest -vv tests/litellm_utils_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 -n 2 no_output_timeout: 15m - run: name: Rename the coverage files @@ -1176,16 +1264,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" "pytest-retry==1.6.3" "pytest-cov==5.0.0" "pytest-asyncio==0.21.1" "respx==0.22.0" "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/pass_through_unit_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 + uv run --no-sync python -m pytest -vv tests/pass_through_unit_tests --cov=litellm --cov-report=xml -x -v --junitxml=test-results/junit.xml --durations=5 -n 4 no_output_timeout: 15m - run: name: Rename the coverage files @@ -1216,21 +1313,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" - pip install "pytest-xdist==3.6.1" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -v tests/image_gen_tests -n 4 -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -v tests/image_gen_tests -n 4 -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Store test results - store_test_results: @@ -1249,21 +1350,18 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install pytest-mock - pip install "respx==0.22.0" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install "mlflow==2.17.2" - pip install "anthropic==0.54.0" - pip install "blockbuster==1.5.24" - pip install "pytest-xdist==3.6.1" - pip install "pytest-timeout==2.2.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - setup_litellm_enterprise_pip - run: @@ -1271,7 +1369,7 @@ jobs: command: | pwd ls - LITELLM_LOG=WARNING python -m pytest tests/logging_callback_tests -vv --cov=litellm --cov-report=xml -n 4 --junitxml=test-results/junit.xml --durations=5 --timeout=120 --timeout_method=thread + LITELLM_LOG=WARNING uv run --no-sync python -m pytest tests/logging_callback_tests -vv --cov=litellm --cov-report=xml -n 4 --junitxml=test-results/junit.xml --durations=5 --timeout=120 --timeout_method=thread no_output_timeout: 15m - run: name: Rename the coverage files @@ -1301,20 +1399,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "respx==0.22.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" # Run pytest and generate JUnit XML report - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/audio_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/audio_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m - run: name: Rename the coverage files @@ -1341,24 +1444,28 @@ jobs: steps: - checkout - setup_google_dns + - restore_cache: + keys: + - v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-cov==5.0.0" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-xdist==3.6.1" - pip install "pytest-rerunfailures==14.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv sync --frozen --all-groups --all-extras --python "$(which python)" + - save_cache: + paths: + - ./.venv + key: v2-dependencies-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} # Run pytest and generate JUnit XML report - run: name: Run tests command: | - pwd - ls - python -m pytest -vv \ + uv run --no-sync python -m pytest -vv \ tests/local_testing/test_dual_cache.py \ tests/local_testing/test_redis_batch_optimizations.py \ tests/local_testing/test_router_utils.py \ @@ -1395,26 +1502,25 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - pip install python-dotenv - pip install pytest - pip install tiktoken - pip install aiohttp - pip install openai - pip install click - pip install "boto3==1.42.80" - pip install jinja2 - pip install "tokenizers==0.22.2" - pip install "uvloop==0.21.0" - pip install "fastuuid==0.14.0" - pip install jsonschema + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - setup_litellm_enterprise_pip - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/local_testing/test_basic_python_version.py + uv run --no-sync python -m pytest -vv tests/local_testing/test_basic_python_version.py installing_litellm_on_python_3_13: docker: @@ -1431,21 +1537,24 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip uv - pip install wheel setuptools - uv pip install --system -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "pytest-cov==5.0.0" - pip install "tomli==2.2.1" - pip install "mcp==1.26.0" + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Run tests command: | pwd ls - python -m pytest -v tests/local_testing/test_basic_python_version.py + uv run --no-sync python -m pytest -v tests/local_testing/test_basic_python_version.py helm_chart_testing: machine: image: ubuntu-2204:2023.10.1 # Use machine executor instead of docker @@ -1457,27 +1566,21 @@ jobs: - attach_workspace: at: ~/project - setup_google_dns - # Install Helm - - run: - name: Install Helm - command: | - curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + - install_helm + - install_kind - # Install kind + # Install kubectl (pinned version with official checksum verification) - run: - name: Install Kind + name: Install kubectl v1.31.4 command: | - curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - # Install kubectl - - run: - name: Install kubectl - command: | - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x kubectl - sudo mv kubectl /usr/local/bin/ + curl -sSLf -o /tmp/kubectl \ + https://dl.k8s.io/release/v1.31.4/bin/linux/amd64/kubectl + curl -sSLf -o /tmp/kubectl.sha256 \ + https://dl.k8s.io/release/v1.31.4/bin/linux/amd64/kubectl.sha256 + echo "$(cat /tmp/kubectl.sha256) /tmp/kubectl" | sha256sum -c - + chmod +x /tmp/kubectl + sudo mv /tmp/kubectl /usr/local/bin/ + rm -f /tmp/kubectl.sha256 # Create kind cluster - run: @@ -1546,42 +1649,47 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - pip install ruff - pip install pylint - pip install pyright - pip install beautifulsoup4 - pip install . - curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - - run: python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) - - run: ruff check ./litellm + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" + - run: uv run --no-sync python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) + - run: uv run --no-sync ruff check ./litellm # - run: python ./tests/documentation_tests/test_general_setting_keys.py - - run: python ./tests/code_coverage_tests/check_licenses.py - - run: python ./tests/code_coverage_tests/check_provider_folders_documented.py - - run: python ./tests/code_coverage_tests/router_code_coverage.py - - run: python ./tests/code_coverage_tests/test_chat_completion_imports.py - - run: python ./tests/code_coverage_tests/info_log_check.py - - run: python ./tests/code_coverage_tests/check_guardrail_apply_decorator.py - - run: python ./tests/code_coverage_tests/test_ban_set_verbose.py - - run: python ./tests/code_coverage_tests/code_qa_check_tests.py - - run: python ./tests/code_coverage_tests/check_get_model_cost_key_performance.py - - run: python ./tests/code_coverage_tests/test_proxy_types_import.py - - run: python ./tests/code_coverage_tests/callback_manager_test.py - - run: python ./tests/code_coverage_tests/recursive_detector.py - - run: python ./tests/code_coverage_tests/test_router_strategy_async.py - - run: python ./tests/code_coverage_tests/litellm_logging_code_coverage.py - - run: python ./tests/documentation_tests/test_env_keys.py - - run: python ./tests/documentation_tests/test_router_settings.py - - run: python ./tests/documentation_tests/test_api_docs.py - - run: python ./tests/code_coverage_tests/ensure_async_clients_test.py - - run: python ./tests/code_coverage_tests/enforce_llms_folder_style.py - - run: python ./tests/documentation_tests/test_circular_imports.py - - run: python ./tests/code_coverage_tests/prevent_key_leaks_in_exceptions.py - - run: python ./tests/code_coverage_tests/check_unsafe_enterprise_import.py - - run: python ./tests/code_coverage_tests/ban_copy_deepcopy_kwargs.py - - run: python ./tests/code_coverage_tests/check_fastuuid_usage.py - - run: python ./tests/code_coverage_tests/memory_test.py - - run: helm lint ./deploy/charts/litellm-helm + - run: uv run --no-sync python ./tests/code_coverage_tests/check_licenses.py + - run: uv run --no-sync python ./tests/code_coverage_tests/check_provider_folders_documented.py + - run: uv run --no-sync python ./tests/code_coverage_tests/router_code_coverage.py + - run: uv run --no-sync python ./tests/code_coverage_tests/test_chat_completion_imports.py + - run: uv run --no-sync python ./tests/code_coverage_tests/info_log_check.py + - run: uv run --no-sync python ./tests/code_coverage_tests/check_guardrail_apply_decorator.py + - run: uv run --no-sync python ./tests/code_coverage_tests/test_ban_set_verbose.py + - run: uv run --no-sync python ./tests/code_coverage_tests/code_qa_check_tests.py + - run: uv run --no-sync python ./tests/code_coverage_tests/check_get_model_cost_key_performance.py + - run: uv run --no-sync python ./tests/code_coverage_tests/test_proxy_types_import.py + - run: uv run --no-sync python ./tests/code_coverage_tests/callback_manager_test.py + - run: uv run --no-sync python ./tests/code_coverage_tests/recursive_detector.py + - run: uv run --no-sync python ./tests/code_coverage_tests/test_router_strategy_async.py + - run: uv run --no-sync python ./tests/code_coverage_tests/litellm_logging_code_coverage.py + - run: uv run --no-sync python ./tests/documentation_tests/test_env_keys.py + - run: uv run --no-sync python ./tests/documentation_tests/test_router_settings.py + - run: uv run --no-sync python ./tests/documentation_tests/test_api_docs.py + - run: uv run --no-sync python ./tests/code_coverage_tests/ensure_async_clients_test.py + - run: uv run --no-sync python ./tests/code_coverage_tests/enforce_llms_folder_style.py + - run: uv run --no-sync python ./tests/documentation_tests/test_circular_imports.py + - run: uv run --no-sync python ./tests/code_coverage_tests/prevent_key_leaks_in_exceptions.py + - run: uv run --no-sync python ./tests/code_coverage_tests/check_unsafe_enterprise_import.py + - run: uv run --no-sync python ./tests/code_coverage_tests/ban_copy_deepcopy_kwargs.py + - run: uv run --no-sync python ./tests/code_coverage_tests/check_fastuuid_usage.py + - run: uv run --no-sync python ./tests/code_coverage_tests/memory_test.py + # helm lint is handled by the dedicated helm_chart_testing job db_migration_disable_update_check: machine: @@ -1605,16 +1713,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - pip install apscheduler - - run: - name: Install dockerize - command: | - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -1625,9 +1735,9 @@ jobs: -e POSTGRES_DB=litellm_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -1649,9 +1759,9 @@ jobs: --config /app/config.yaml \ --port 4000 \ --use_prisma_db_push - - run: - name: Wait for schema seed to complete - command: dockerize -wait http://localhost:4001 -timeout 5m + - wait_for_service: + url: http://localhost:4001 + timeout: "300" - run: name: Stop schema seed container command: docker stop schema-seed && docker rm schema-seed @@ -1671,9 +1781,9 @@ jobs: litellm-docker-database:ci \ --config /app/config.yaml \ --port 4000 - - run: - name: Wait for container to be ready - command: dockerize -wait http://localhost:4000 -timeout 1m + - wait_for_service: + url: http://localhost:4000 + timeout: "60" - run: name: Check container logs for expected message command: | @@ -1691,7 +1801,7 @@ jobs: - run: name: Run Basic Proxy Startup Tests (Health Readiness and Chat Completion) command: | - python -m pytest -v tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 + uv run --no-sync python -m pytest -v tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 no_output_timeout: 15m build_and_test: @@ -1718,42 +1828,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - pip install "mypy==1.18.2" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install pyarrow - pip install "boto3==1.42.80" - pip install langchain - pip install "langfuse>=2.0.0" - pip install "logfire==0.29.0" - pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema - pip install "httpx==0.28.1" - pip install "gunicorn==23.0.0" - pip install "anyio==4.8.0" - pip install "aiodynamo==23.10.1" - pip install "asyncio==3.4.3" - pip install "PyGithub==1.59.1" - pip install "openai==1.100.1" - pip install "litellm[proxy]" - pip install "pytest-xdist==3.6.1" - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -1764,9 +1850,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - run: name: Load Docker Database Image command: | @@ -1818,15 +1904,15 @@ jobs: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -s -v tests/*.py -x --junitxml=test-results/junit.xml -n 4 --durations=5 --ignore=tests/otel_tests --ignore=tests/spend_tracking_tests --ignore=tests/pass_through_tests --ignore=tests/proxy_admin_ui_tests --ignore=tests/load_tests --ignore=tests/llm_translation --ignore=tests/llm_responses_api_testing --ignore=tests/mcp_tests --ignore=tests/guardrails_tests --ignore=tests/image_gen_tests --ignore=tests/pass_through_unit_tests + uv run --no-sync python -m pytest -s -v tests/*.py -x --junitxml=test-results/junit.xml -n 4 --durations=5 --ignore=tests/otel_tests --ignore=tests/spend_tracking_tests --ignore=tests/pass_through_tests --ignore=tests/proxy_admin_ui_tests --ignore=tests/load_tests --ignore=tests/llm_translation --ignore=tests/llm_responses_api_testing --ignore=tests/mcp_tests --ignore=tests/guardrails_tests --ignore=tests/image_gen_tests --ignore=tests/pass_through_unit_tests no_output_timeout: 15m # Store test results @@ -1841,10 +1927,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version - run: name: Install Python 3.10 @@ -1860,43 +1944,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - pip install "mypy==1.18.2" - pip install "jsonlines==4.0.0" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install pyarrow - pip install "boto3==1.42.80" - pip install langchain - pip install "langchain_mcp_adapters==0.0.5" - pip install "langfuse>=2.0.0" - pip install "logfire==0.29.0" - pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema - pip install "httpx==0.28.1" - pip install "gunicorn==23.0.0" - pip install "anyio==4.8.0" - pip install "aiodynamo==23.10.1" - pip install "asyncio==3.4.3" - pip install "PyGithub==1.59.1" - pip install "openai==1.100.1" - # Run pytest and generate JUnit XML report - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -1907,9 +1966,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -1964,15 +2023,15 @@ jobs: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -s -vv tests/openai_endpoints_tests --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -s -vv tests/openai_endpoints_tests --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Store test results @@ -1987,10 +2046,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version - run: name: Install Python 3.9 @@ -2006,40 +2063,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r .circleci/requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - pip install "mypy==1.18.2" - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.133.0" - pip install pyarrow - pip install "boto3==1.42.80" - pip install langchain - pip install "langfuse>=2.0.0" - pip install "logfire==0.29.0" - pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema - pip install "httpx==0.28.1" - pip install "gunicorn==23.0.0" - pip install "anyio==4.8.0" - pip install "aiodynamo==23.10.1" - pip install "asyncio==3.4.3" - pip install "PyGithub==1.59.1" - pip install "openai==1.100.1" - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2050,9 +2085,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2092,27 +2127,19 @@ jobs: --config /app/config.yaml \ --port 4000 \ --detailed_debug \ - - run: - name: Install curl and dockerize - command: | - sudo apt-get update - sudo apt-get install -y curl - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -v tests/otel_tests -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -v tests/otel_tests -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Clean up first container - run: @@ -2145,17 +2172,17 @@ jobs: - run: name: Start outputting logs for second container - command: docker logs -f my-app-2 + command: docker logs -f my-app-3 background: true - - run: - name: Wait for second app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run second round of tests command: | - python -m pytest -v tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 + uv run --no-sync python -m pytest -v tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 no_output_timeout: 15m # Store test results @@ -2170,10 +2197,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version - run: name: Install Python 3.9 @@ -2189,17 +2214,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r requirements.txt - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2210,9 +2236,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2256,15 +2282,15 @@ jobs: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/spend_tracking_tests -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/spend_tracking_tests -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Clean up first container - run: @@ -2282,10 +2308,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version - run: name: Install Python 3.9 @@ -2301,21 +2325,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2326,9 +2347,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2380,30 +2401,22 @@ jobs: --config /app/config.yaml \ --port 4001 \ --detailed_debug - - run: - name: Install curl and dockerize - command: | - sudo apt-get update - sudo apt-get install -y curl - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for instance 1 to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m - - run: - name: Wait for instance 2 to be ready - command: dockerize -wait http://localhost:4001 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" + - wait_for_service: + url: http://localhost:4001 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/multi_instance_e2e_tests -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/multi_instance_e2e_tests -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Clean up first container # Store test results @@ -2419,10 +2432,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version sudo systemctl restart docker - run: @@ -2439,22 +2450,18 @@ jobs: - run: name: Install Dependencies command: | - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install aiohttp - python -m pip install --upgrade pip - python -m pip install -r requirements.txt - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - pip install "assemblyai==0.37.0" - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2465,9 +2472,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2493,27 +2500,19 @@ jobs: --config /app/config.yaml \ --port 4000 \ --detailed_debug \ - - run: - name: Install curl and dockerize - command: | - sudo apt-get update - sudo apt-get install -y curl - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | pwd ls - python -m pytest -vv tests/store_model_in_db_tests -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/store_model_in_db_tests -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m - run: name: Stop and remove containers @@ -2550,9 +2549,18 @@ jobs: - run: name: Install Dependencies command: | - python -m pip install --upgrade pip - pip install "pytest==7.3.1" "pytest-asyncio==0.21.1" "pytest-retry==1.6.3" \ - "pytest-mock==3.12.0" "mypy==1.18.2" aiohttp apscheduler + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Build Docker image command: | @@ -2601,25 +2609,17 @@ jobs: --config /app/config.yaml \ --port 4000 \ --detailed_debug \ - - run: - name: Install curl and dockerize - command: | - sudo apt-get update - sudo apt-get install -y curl - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run tests command: | - python -m pytest -vv tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 no_output_timeout: 15m # Clean up first container - run: @@ -2652,45 +2652,18 @@ jobs: - run: name: Install Dependencies command: | - export PATH="$HOME/miniconda/bin:$PATH" - source $HOME/miniconda/etc/profile.d/conda.sh - conda activate myenv - pip install "pytest==7.3.1" - pip install "pytest-retry==1.6.3" - pip install "pytest-asyncio==0.21.1" - pip install "google-cloud-aiplatform==1.133.0" - pip install aiohttp - pip install "openai==1.100.1" - pip install "assemblyai==0.37.0" - python -m pip install --upgrade pip - pip install "pydantic==2.12.5" - pip install "pytest==7.3.1" - pip install "pytest-mock==3.12.0" - pip install "pytest-asyncio==0.21.1" - pip install "boto3==1.42.80" - pip install "mypy==1.18.2" - pip install pyarrow - pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema - pip install "httpx==0.27.0" - pip install "anyio==4.8.0" - pip install "asyncio==3.4.3" - pip install "PyGithub==1.59.1" - pip install "google-cloud-aiplatform==1.59.0" - pip install "anthropic==0.54.0" - pip install "langchain_mcp_adapters==0.0.5" - pip install "langchain_openai==0.2.1" - pip install "langgraph==0.3.18" - pip install "fastuuid==0.13.5" - pip install -r requirements.txt - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2701,9 +2674,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2741,9 +2714,9 @@ jobs: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" # Add Ruby installation and testing before the existing Node.js and Python tests - run: name: Install Ruby and Bundler @@ -2806,7 +2779,7 @@ jobs: conda activate myenv pwd ls - python -m pytest -v tests/pass_through_tests/ -x --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -v tests/pass_through_tests/ -x --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Store test results @@ -2822,10 +2795,8 @@ jobs: - checkout - setup_google_dns - run: - name: Install Docker CLI (In case it's not already installed) + name: Verify Docker is available command: | - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker $USER docker version - run: name: Install Python 3.10 @@ -2841,21 +2812,18 @@ jobs: - run: name: Install Dependencies command: | - export PATH="$HOME/miniconda/bin:$PATH" - source $HOME/miniconda/etc/profile.d/conda.sh - conda activate myenv - pip install "pytest==7.3.1" - pip install "pytest-asyncio==0.21.1" - pip install "boto3==1.42.80" - pip install "httpx==0.27.0" - pip install "claude-agent-sdk" - pip install -r requirements.txt - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + if [ -f "$HOME/miniconda/etc/profile.d/conda.sh" ]; then + export PATH="$HOME/miniconda/bin:$PATH" + source "$HOME/miniconda/etc/profile.d/conda.sh" + conda activate myenv + fi + uv sync --frozen --all-groups --all-extras --python "$(which python)" - run: name: Start PostgreSQL Database command: | @@ -2866,9 +2834,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - attach_workspace: at: ~/project - run: @@ -2899,9 +2867,9 @@ jobs: name: Start outputting logs command: docker logs -f my-app background: true - - run: - name: Wait for app to be ready - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Run Claude Agent SDK E2E Tests command: | @@ -2912,7 +2880,7 @@ jobs: export LITELLM_API_KEY="sk-1234" pwd ls - python -m pytest -vv tests/proxy_e2e_anthropic_messages_tests/ -x -s --junitxml=test-results/junit.xml --durations=5 + uv run --no-sync python -m pytest -vv tests/proxy_e2e_anthropic_messages_tests/ -x -s --junitxml=test-results/junit.xml --durations=5 no_output_timeout: 15m # Store test results @@ -2937,17 +2905,20 @@ jobs: - run: name: Combine Coverage command: | - python -m venv venv - . venv/bin/activate - pip install coverage - coverage combine realtime_translation_coverage ocr_coverage search_coverage mcp_coverage litellm_mcps_tests_coverage logging_coverage audio_coverage local_testing_part1_coverage local_testing_part2_coverage pass_through_unit_tests_coverage batches_coverage guardrails_coverage redis_caching_coverage - coverage xml + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv tool run --from 'coverage[toml]==7.10.6' coverage combine realtime_translation_coverage ocr_coverage search_coverage mcp_coverage litellm_mcps_tests_coverage logging_coverage audio_coverage local_testing_part1_coverage local_testing_part2_coverage pass_through_unit_tests_coverage batches_coverage guardrails_coverage redis_caching_coverage + uv tool run --from 'coverage[toml]==7.10.6' coverage xml - codecov/upload: file: ./coverage.xml publish_proxy_extras: docker: - - image: cimg/python:3.8 + - image: cimg/python:3.12 working_directory: ~/project/litellm-proxy-extras environment: TWINE_USERNAME: __token__ @@ -2959,10 +2930,15 @@ jobs: - run: name: Check if litellm-proxy-extras dir or pyproject.toml was modified command: | - echo "Install TOML package." - python -m pip install toml + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" # Get current version from pyproject.toml - CURRENT_VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['tool']['poetry']['version'])") + CURRENT_VERSION=$(python -c 'import tomllib; from pathlib import Path; data = tomllib.loads(Path("pyproject.toml").read_text()); print(data["project"]["version"])') # Get last published version from PyPI LAST_VERSION=$(curl -s https://pypi.org/pypi/litellm-proxy-extras/json | python -c "import json, sys; print(json.load(sys.stdin)['info']['version'])") @@ -2971,7 +2947,7 @@ jobs: echo "Last published version: $LAST_VERSION" # Compare versions using Python's packaging.version - VERSION_COMPARE=$(python -c "from packaging import version; print(1 if version.parse('$CURRENT_VERSION') < version.parse('$LAST_VERSION') else 0)") + VERSION_COMPARE=$(uv run --with 'packaging==25.0' python -c "from packaging import version; print(1 if version.parse('$CURRENT_VERSION') < version.parse('$LAST_VERSION') else 0)") echo "Version compare: $VERSION_COMPARE" if [ "$VERSION_COMPARE" = "1" ]; then @@ -2979,38 +2955,17 @@ jobs: exit 1 fi - # If versions are equal or current is greater, check contents - pip download --no-deps litellm-proxy-extras==$LAST_VERSION -d /tmp - - echo "Contents of /tmp directory:" - ls -la /tmp - - # Find the downloaded file (could be .whl or .tar.gz) - DOWNLOADED_FILE=$(ls /tmp/litellm_proxy_extras-*) - echo "Downloaded file: $DOWNLOADED_FILE" - - # Extract based on file extension - if [[ "$DOWNLOADED_FILE" == *.whl ]]; then - echo "Extracting wheel file..." - unzip -q "$DOWNLOADED_FILE" -d /tmp/extracted - EXTRACTED_DIR="/tmp/extracted" - else - echo "Extracting tar.gz file..." - tar -xzf "$DOWNLOADED_FILE" -C /tmp - EXTRACTED_DIR="/tmp/litellm_proxy_extras-$LAST_VERSION" - fi - - echo "Contents of extracted package:" - ls -R "$EXTRACTED_DIR" + # If versions are equal or current is greater, compare against the published package contents. + EXTRACTED_DIR=$(uv run --with "litellm-proxy-extras==$LAST_VERSION" python -c 'import importlib.util; from pathlib import Path; spec = importlib.util.find_spec("litellm_proxy_extras"); assert spec is not None and spec.origin is not None, "litellm_proxy_extras not found in uv-run environment"; print(Path(spec.origin).resolve().parent)') # Compare contents - if ! diff -r "$EXTRACTED_DIR/litellm_proxy_extras" ./litellm_proxy_extras; then + if ! diff -r "$EXTRACTED_DIR" ./litellm_proxy_extras; then if [ "$CURRENT_VERSION" = "$LAST_VERSION" ]; then echo "Error: Changes detected in litellm-proxy-extras but version was not bumped" echo "Current version: $CURRENT_VERSION" echo "Last published version: $LAST_VERSION" echo "Changes:" - diff -r "$EXTRACTED_DIR/litellm_proxy_extras" ./litellm_proxy_extras + diff -r "$EXTRACTED_DIR" ./litellm_proxy_extras exit 1 fi else @@ -3021,7 +2976,7 @@ jobs: - run: name: Get new version command: | - NEW_VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['tool']['poetry']['version'])") + NEW_VERSION=$(python -c 'import tomllib; from pathlib import Path; data = tomllib.loads(Path("pyproject.toml").read_text()); print(data["project"]["version"])') echo "export NEW_VERSION=$NEW_VERSION" >> $BASH_ENV - run: @@ -3029,27 +2984,21 @@ jobs: command: | cd ~/project # Check pyproject.toml - CURRENT_VERSION=$(python -c "import toml; dep = toml.load('pyproject.toml')['tool']['poetry']['dependencies']['litellm-proxy-extras']; print(dep['version'] if isinstance(dep, dict) else dep)") + CURRENT_VERSION=$(uv run --with 'packaging==25.0' python -c 'import tomllib; from packaging.requirements import Requirement; from pathlib import Path; data = tomllib.loads(Path("pyproject.toml").read_text()); matches = [spec.version for requirement in data["project"]["optional-dependencies"]["proxy"] for parsed in [Requirement(requirement)] if parsed.name == "litellm-proxy-extras" and parsed.specifier for spec in parsed.specifier if spec.operator == "=="]; print(matches[0] if matches else (_ for _ in ()).throw(SystemExit("Could not find exact litellm-proxy-extras pin in project.optional-dependencies.proxy")))') if [ "$CURRENT_VERSION" != "$NEW_VERSION" ]; then echo "Error: Version in pyproject.toml ($CURRENT_VERSION) doesn't match new version ($NEW_VERSION)" exit 1 fi - # Check requirements.txt - REQ_VERSION=$(grep -oP 'litellm-proxy-extras==\K[0-9.]+' requirements.txt) - if [ "$REQ_VERSION" != "$NEW_VERSION" ]; then - echo "Error: Version in requirements.txt ($REQ_VERSION) doesn't match new version ($NEW_VERSION)" - exit 1 - fi - - run: name: Publish to PyPI command: | echo -e "[pypi]\nusername = $PYPI_PUBLISH_USERNAME\npassword = $PYPI_PUBLISH_PASSWORD" > ~/.pypirc - python -m pip install --upgrade pip build twine setuptools wheel + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" rm -rf build dist - python -m build - twine upload --verbose dist/* + uv build + uv tool run --from 'twine==6.2.0' twine upload --verbose dist/* ui_build: docker: @@ -3146,19 +3095,22 @@ jobs: - setup_google_dns - restore_cache: keys: - - ui-e2e-py-deps-v1-{{ checksum "requirements.txt" }} + - ui-e2e-py-deps-v2-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} - run: name: Install Python dependencies command: | - python -m pip install --upgrade pip uv - uv pip install --system -r requirements.txt - pip install "prisma==0.11.0" - prisma generate --schema litellm/proxy/schema.prisma + curl -LsSf -o /tmp/uv-install.sh https://astral.sh/uv/0.10.9/install.sh + echo "7fc46e39cb97290b57169c0c813a17970585ac519139f19006453c99b5f2f45f /tmp/uv-install.sh" | sha256sum -c - + env UV_NO_MODIFY_PATH=1 sh /tmp/uv-install.sh + rm -f /tmp/uv-install.sh + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" + export PATH="$HOME/.local/bin:$PATH" + uv sync --frozen --all-groups --all-extras --python "$(which python)" + uv run --no-sync python -m prisma generate --schema litellm/proxy/schema.prisma - save_cache: - key: ui-e2e-py-deps-v1-{{ checksum "requirements.txt" }} + key: ui-e2e-py-deps-v2-{{ checksum "uv.lock" }}-{{ checksum ".circleci/config.yml" }} paths: - - ~/.local/lib - - ~/.local/bin + - ./.venv - restore_cache: keys: - ui-e2e-node-deps-v1-{{ checksum "ui/litellm-dashboard/package-lock.json" }} @@ -3182,12 +3134,12 @@ jobs: find ../../litellm/proxy/_experimental/out -name '*.html' ! -name 'index.html' | while read -r f; do d="${f%.html}"; mkdir -p "$d"; mv "$f" "$d/index.html" done - - run: - name: Wait for PostgreSQL - command: dockerize -wait tcp://localhost:5432 -timeout 30s + - wait_for_service: + url: tcp://localhost:5432 + timeout: "30" - run: name: Push Prisma schema - command: prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss + command: uv run --no-sync python -m prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss - run: name: Seed database command: | @@ -3195,7 +3147,7 @@ jobs: -f ui/litellm-dashboard/e2e_tests/fixtures/seed.sql - run: name: Start mock LLM server - command: python ui/litellm-dashboard/e2e_tests/fixtures/mock_llm_server/server.py + command: uv run --no-sync python ui/litellm-dashboard/e2e_tests/fixtures/mock_llm_server/server.py background: true - run: name: Start LiteLLM proxy @@ -3206,7 +3158,7 @@ jobs: SERVER_ROOT_PATH: "" PROXY_LOGOUT_URL: "" command: | - python -m litellm.proxy.proxy_cli \ + uv run --no-sync python -m litellm.proxy.proxy_cli \ --config ui/litellm-dashboard/e2e_tests/fixtures/config.yml \ --port 4000 background: true @@ -3272,12 +3224,6 @@ jobs: - setup_google_dns - attach_workspace: at: ~/project - - run: - name: Install dockerize - command: | - sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start PostgreSQL Database command: | @@ -3288,9 +3234,9 @@ jobs: -e POSTGRES_DB=litellm_schema_sync \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - run: name: Load Docker Database Image command: | @@ -3314,9 +3260,9 @@ jobs: name: Start outputting logs command: docker logs -f schema-sync background: true - - run: - name: Wait for proxy to be ready (schema sync complete) - command: dockerize -wait http://localhost:4000 -timeout 5m + - wait_for_service: + url: http://localhost:4000 + timeout: "300" - run: name: Stop schema sync container command: docker stop schema-sync @@ -3332,12 +3278,6 @@ jobs: - attach_workspace: at: ~/project - setup_google_dns - - run: - name: Install dockerize - command: | - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start PostgreSQL Database command: | @@ -3348,9 +3288,9 @@ jobs: -e POSTGRES_DB=circle_test \ -p 5432:5432 \ postgres:14 - - run: - name: Wait for PostgreSQL to be ready - command: dockerize -wait tcp://localhost:5432 -timeout 1m + - wait_for_service: + url: tcp://localhost:5432 + timeout: "60" - run: name: Load Docker Database Image command: | diff --git a/.circleci/requirements.txt b/.circleci/requirements.txt deleted file mode 100644 index be12ab2d0f..0000000000 --- a/.circleci/requirements.txt +++ /dev/null @@ -1,21 +0,0 @@ -# used by CI/CD testing -openai==1.100.1 -python-dotenv -tiktoken -importlib_metadata -cohere -redis==5.2.1 -redisvl==0.4.1 -anthropic -orjson==3.10.15 # fast /embedding responses -pydantic==2.12.5 -google-cloud-aiplatform==1.133.0 -google-cloud-iam==2.19.1 -fastapi-sso==0.16.0 -uvloop==0.21.0 -mcp==1.26.0 # for MCP server -semantic_router==0.1.10 # for auto-routing with litellm -fastuuid==0.14.0 -responses==0.25.7 # for proxy client tests -pytest-retry==1.6.3 # for automatic test retries -litellm-proxy-extras # for prisma migrations \ No newline at end of file diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 484baa9041..78f857d55d 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -1,17 +1,17 @@ #!/usr/bin/env bash set -e -echo "[post-create] Installing poetry via pip" -python -m pip install --upgrade pip -python -m pip install poetry +echo "[post-create] Installing uv" +curl -LsSf https://astral.sh/uv/0.10.9/install.sh | env UV_NO_MODIFY_PATH=1 sh +export PATH="$HOME/.local/bin:$PATH" -echo "[post-create] Installing Python dependencies (poetry)" -poetry install --with dev --extras proxy +echo "[post-create] Installing Python dependencies (uv)" +uv sync --frozen --group proxy-dev --extra proxy echo "[post-create] Generating Prisma client" -poetry run prisma generate +uv run --no-sync prisma generate echo "[post-create] Installing npm dependencies" cd ui/litellm-dashboard && npm ci -echo "[post-create] Done" \ No newline at end of file +echo "[post-create] Done" diff --git a/.gitguardian.yaml b/.gitguardian.yaml index 1eeec0677a..2a16ffe0c5 100644 --- a/.gitguardian.yaml +++ b/.gitguardian.yaml @@ -37,7 +37,7 @@ secret: - "docs/**" - "**/*.md" - "**/*.lock" - - "poetry.lock" + - "uv.lock" - "package-lock.json" # Ignore security incidents with the SHA256 of the occurrence (false positives) diff --git a/.github/workflows/_test-unit-base.yml b/.github/workflows/_test-unit-base.yml index d8dec73c42..9377cbeb0c 100644 --- a/.github/workflows/_test-unit-base.yml +++ b/.github/workflows/_test-unit-base.yml @@ -51,37 +51,30 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - - name: Cache Poetry dependencies + - name: Cache uv dependencies uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | - ~/.cache/pypoetry - ~/.cache/pip + ~/.cache/uv .venv - key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} restore-keys: | - ${{ runner.os }}-poetry- + ${{ runner.os }}-uv- - name: Install dependencies run: | - poetry config virtualenvs.in-project true - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install google-genai==1.22.0 \ - google-cloud-aiplatform==1.115.0 fastapi-offline==1.7.3 python-multipart==0.0.22 openapi-core==0.23.0 - - - name: Setup litellm-enterprise - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv sync --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | - poetry run pip install nodejs-wheel-binaries==24.13.1 - poetry run prisma generate --schema litellm/proxy/schema.prisma + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma - name: Run tests env: @@ -90,7 +83,7 @@ jobs: WORKERS: ${{ inputs.workers }} RERUNS: ${{ inputs.reruns }} run: | - poetry run pytest ${TEST_PATH:?} \ + uv run --no-sync pytest ${TEST_PATH:?} \ --tb=short -vv \ --maxfail="${MAX_FAILURES}" \ -n "${WORKERS}" \ diff --git a/.github/workflows/_test-unit-services-base.yml b/.github/workflows/_test-unit-services-base.yml index ce4c048c62..8e0b3568ae 100644 --- a/.github/workflows/_test-unit-services-base.yml +++ b/.github/workflows/_test-unit-services-base.yml @@ -86,44 +86,37 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - - name: Cache Poetry dependencies + - name: Cache uv dependencies uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | - ~/.cache/pypoetry - ~/.cache/pip + ~/.cache/uv .venv - key: ${{ runner.os }}-poetry-services-${{ hashFiles('poetry.lock') }} + key: ${{ runner.os }}-uv-services-${{ hashFiles('uv.lock') }} restore-keys: | - ${{ runner.os }}-poetry-services- + ${{ runner.os }}-uv-services- - name: Install dependencies run: | - poetry config virtualenvs.in-project true - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install google-genai==1.22.0 \ - google-cloud-aiplatform==1.115.0 fastapi-offline==1.7.3 python-multipart==0.0.22 openapi-core==0.23.0 - - - name: Setup litellm-enterprise - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv sync --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | - poetry run pip install nodejs-wheel-binaries==24.13.1 - poetry run prisma generate --schema litellm/proxy/schema.prisma + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma - name: Run Prisma migrations if: ${{ inputs.enable-postgres }} env: DATABASE_URL: ${{ secrets.DATABASE_URL }} run: | - poetry run prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss + uv run --no-sync prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss - name: Run tests env: @@ -134,7 +127,7 @@ jobs: DATABASE_URL: ${{ inputs.enable-postgres && secrets.DATABASE_URL || '' }} run: | if [ "${WORKERS}" = "0" ]; then - poetry run pytest ${TEST_PATH:?} \ + uv run --no-sync pytest ${TEST_PATH:?} \ --tb=short -vv \ --maxfail="${MAX_FAILURES}" \ --reruns "${RERUNS}" \ @@ -144,7 +137,7 @@ jobs: --cov-report=xml:coverage.xml \ --cov-config=pyproject.toml else - poetry run pytest ${TEST_PATH:?} \ + uv run --no-sync pytest ${TEST_PATH:?} \ --tb=short -vv \ --maxfail="${MAX_FAILURES}" \ -n "${WORKERS}" \ diff --git a/.github/workflows/auto_update_price_and_context_window.yml b/.github/workflows/auto_update_price_and_context_window.yml index 60e8993621..1c6c318c71 100644 --- a/.github/workflows/auto_update_price_and_context_window.yml +++ b/.github/workflows/auto_update_price_and_context_window.yml @@ -17,12 +17,13 @@ jobs: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - - name: Install Dependencies - run: | - pip install 'aiohttp==3.13.3' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - name: Update JSON Data run: | - python ".github/workflows/auto_update_price_and_context_window_file.py" + uv run --frozen --with 'aiohttp==3.13.3' python ".github/workflows/auto_update_price_and_context_window_file.py" - name: Create Pull Request run: | git add model_prices_and_context_window.json diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 52d64addea..17efbf9033 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -34,13 +34,21 @@ jobs: with: python-version: "3.12" - - name: Install dependencies - run: | - pip install -e "." - pip install pytest pytest-codspeed==4.3.0 + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - name: Run benchmarks uses: CodSpeedHQ/action@1c8ae4843586d3ba879736b7f6b7b0c990757fab # v4.12.1 with: mode: simulation - run: pytest tests/benchmarks/ --codspeed + run: > + env PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 + uv run --frozen --no-default-groups + --with pytest==8.3.5 + --with pytest-codspeed==4.3.0 + pytest + -p pytest_codspeed.plugin + tests/benchmarks/ + --codspeed diff --git a/.github/workflows/llm-translation-testing.yml b/.github/workflows/llm-translation-testing.yml index 922013c4b5..93b69e5c6a 100644 --- a/.github/workflows/llm-translation-testing.yml +++ b/.github/workflows/llm-translation-testing.yml @@ -31,26 +31,25 @@ jobs: with: python-version: "3.11" - - name: Install Poetry - run: | - pip install 'poetry==2.3.2' - poetry config virtualenvs.create true - poetry config virtualenvs.in-project true + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + enable-cache: false - - name: Restore Poetry dependencies cache - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.0.0 + - name: Restore uv dependencies cache + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | - ~/.cache/pypoetry + ~/.cache/uv .venv - key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} + key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} restore-keys: | - ${{ runner.os }}-poetry- + ${{ runner.os }}-uv- - name: Install dependencies run: | - poetry install --with dev - poetry run pip install 'pytest-xdist==3.8.0' 'pytest-timeout==2.4.0' + uv sync --frozen - name: Create test results directory run: mkdir -p test-results diff --git a/.github/workflows/publish_to_pypi.yml b/.github/workflows/publish_to_pypi.yml index 8f675bb307..d60254a0ac 100644 --- a/.github/workflows/publish_to_pypi.yml +++ b/.github/workflows/publish_to_pypi.yml @@ -24,10 +24,22 @@ jobs: with: python-version: "3.12" + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + enable-cache: false + - name: Check litellm version on PyPI id: check-litellm run: | - VERSION=$(grep -m1 '^version' pyproject.toml | sed 's/version = "\(.*\)"/\1/') + VERSION=$(python - <<'PY' + import tomllib + + with open("pyproject.toml", "rb") as f: + print(tomllib.load(f)["project"]["version"]) + PY + ) echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Checking if litellm $VERSION exists on PyPI..." @@ -42,43 +54,46 @@ jobs: - name: Sanity check proxy-extras version run: | - # Read pinned version from requirements.txt - REQ_VERSION=$(grep -oP 'litellm-proxy-extras==\K[0-9.]+' requirements.txt) - if [ -z "$REQ_VERSION" ]; then - echo "::error::Could not find litellm-proxy-extras version in requirements.txt" - exit 1 - fi - echo "requirements.txt pins litellm-proxy-extras==$REQ_VERSION" + # Read pinned version from project optional dependencies + PYPROJECT_VERSION=$(python3 - <<'PY' + import sys + import tomllib - # Read pinned version from pyproject.toml dependency - PYPROJECT_VERSION=$(python3 -c " - import re - with open('pyproject.toml') as f: - content = f.read() - match = re.search(r'litellm-proxy-extras\s*=\s*\{version\s*=\s*\"([^\"]+)\"', content) - if match: - print(match.group(1).lstrip('^~>=')) - else: - import sys - print('::error::Could not find litellm-proxy-extras dependency in pyproject.toml', file=sys.stderr) + with open("pyproject.toml", "rb") as f: + proxy_requirements = tomllib.load(f)["project"]["optional-dependencies"]["proxy"] + + version = None + for requirement in proxy_requirements: + normalized = requirement.split(";", 1)[0].strip() + if not normalized.startswith("litellm-proxy-extras"): + continue + parts = normalized.split("==", 1) + if len(parts) == 2 and parts[0].strip() == "litellm-proxy-extras": + candidate = parts[1].strip() + if candidate: + version = candidate + break + + if version is None: + print( + "::error::Could not find an exact litellm-proxy-extras pin in project.optional-dependencies.proxy", + file=sys.stderr, + ) sys.exit(1) - ") + + print(version) + PY + ) echo "pyproject.toml pins litellm-proxy-extras version: $PYPROJECT_VERSION" - # Check that both pinned versions match - if [ "$REQ_VERSION" != "$PYPROJECT_VERSION" ]; then - echo "::error::Version mismatch: requirements.txt has $REQ_VERSION but pyproject.toml has $PYPROJECT_VERSION" - exit 1 - fi - # Check that the pinned version exists on PyPI - echo "Checking if litellm-proxy-extras $REQ_VERSION exists on PyPI..." - HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/litellm-proxy-extras/$REQ_VERSION/json") + echo "Checking if litellm-proxy-extras $PYPROJECT_VERSION exists on PyPI..." + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/litellm-proxy-extras/$PYPROJECT_VERSION/json") if [ "$HTTP_STATUS" != "200" ]; then - echo "::error::litellm-proxy-extras $REQ_VERSION is not published on PyPI yet. Publish it before releasing litellm." + echo "::error::litellm-proxy-extras $PYPROJECT_VERSION is not published on PyPI yet. Publish it before releasing litellm." exit 1 fi - echo "litellm-proxy-extras $REQ_VERSION exists on PyPI. Sanity check passed." + echo "litellm-proxy-extras $PYPROJECT_VERSION exists on PyPI. Sanity check passed." publish-litellm: name: Publish litellm to PyPI @@ -100,16 +115,19 @@ jobs: with: python-version: "3.12" + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + enable-cache: false + - name: Copy model prices backup run: cp model_prices_and_context_window.json litellm/model_prices_and_context_window_backup.json - - name: Install build tools - run: python -m pip install --upgrade pip build==1.4.2 - - name: Build package run: | rm -rf build dist - python -m build + uv build - name: Verify build artifacts env: @@ -129,8 +147,7 @@ jobs: - name: Validate package metadata run: | - pip install twine==6.2.0 - twine check dist/* + uv tool run --from 'twine==6.2.0' twine check dist/* - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 diff --git a/.github/workflows/run_llm_translation_tests.py b/.github/workflows/run_llm_translation_tests.py index 5b3a4817ec..3f3a70efe9 100644 --- a/.github/workflows/run_llm_translation_tests.py +++ b/.github/workflows/run_llm_translation_tests.py @@ -325,7 +325,7 @@ def run_tests(test_path: str = "tests/llm_translation/", # Run pytest cmd = [ - "poetry", "run", "pytest", test_path, + "uv", "run", "--no-sync", "pytest", test_path, f"--junitxml={junit_xml}", "-v", "--tb=short", @@ -335,7 +335,7 @@ def run_tests(test_path: str = "tests/llm_translation/", # Add timeout if pytest-timeout is installed try: - subprocess.run(["poetry", "run", "python", "-c", "import pytest_timeout"], + subprocess.run(["uv", "run", "--no-sync", "python", "-c", "import pytest_timeout"], capture_output=True, check=True) cmd.extend(["--timeout=300"]) except: @@ -436,4 +436,4 @@ if __name__ == "__main__": commit=args.commit ) - sys.exit(exit_code) \ No newline at end of file + sys.exit(exit_code) diff --git a/.github/workflows/test-linting.yml b/.github/workflows/test-linting.yml index 5bb85716a1..eefa42e7fa 100644 --- a/.github/workflows/test-linting.yml +++ b/.github/workflows/test-linting.yml @@ -24,26 +24,28 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - name: Clean Python cache run: | find . -type d -name "__pycache__" -exec rm -rf {} + || true find . -name "*.pyc" -delete || true - - name: Check poetry.lock is up to date + - name: Check uv.lock is up to date run: | - poetry check --lock || (echo "❌ poetry.lock is out of sync with pyproject.toml. Run 'poetry lock' locally and commit the result." && exit 1) + uv lock --check || (echo "❌ uv.lock is out of sync with pyproject.toml. Run 'uv lock' locally and commit the result." && exit 1) - name: Install dependencies run: | - poetry install --with dev + uv sync --frozen - name: Check Black formatting run: | cd litellm - poetry run black --check --exclude '/enterprise/' . + uv run --no-sync black --check --exclude '/enterprise/' . cd .. - name: Debug - Check file state @@ -58,28 +60,28 @@ jobs: - name: Run Ruff linting run: | cd litellm - poetry run ruff check . + uv run --no-sync ruff check . cd .. - name: Print OpenAI version run: | - poetry run python -c "import openai; print(f'OpenAI version: {openai.__version__}')" + uv run --no-sync python -c "import openai; print(f'OpenAI version: {openai.__version__}')" - name: Run MyPy type checking run: | cd litellm - poetry run mypy . + uv run --no-sync mypy . cd .. - name: Check for circular imports run: | cd litellm - poetry run python ../tests/documentation_tests/test_circular_imports.py + uv run --no-sync python ../tests/documentation_tests/test_circular_imports.py cd .. - name: Check import safety run: | - poetry run python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) + uv run --no-sync python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) secret-scan: runs-on: ubuntu-latest @@ -98,18 +100,21 @@ jobs: with: python-version: "3.12" + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + - name: Run secret scan test run: | - pip install 'pytest==9.0.2' - pytest tests/litellm/test_no_hardcoded_secrets.py -v + uv run --frozen --with 'pytest==9.0.2' pytest tests/litellm/test_no_hardcoded_secrets.py -v - name: Run ggshield secret scan env: GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }} run: | if [ -n "$GITGUARDIAN_API_KEY" ]; then - pip install 'ggshield==1.48.0' - ggshield secret scan repo . + uv tool run --from 'ggshield==1.48.0' ggshield secret scan repo . else echo "GITGUARDIAN_API_KEY not set, skipping ggshield scan" fi diff --git a/.github/workflows/test-litellm.yml b/.github/workflows/test-litellm.yml index 0c040b3ebe..938647f5d0 100644 --- a/.github/workflows/test-litellm.yml +++ b/.github/workflows/test-litellm.yml @@ -31,23 +31,15 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - name: Install dependencies run: | - poetry lock - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install "pytest-retry==1.6.3" - poetry run pip install 'pytest-xdist==3.8.0' - poetry run pip install "google-genai==1.22.0" - poetry run pip install "google-cloud-aiplatform==1.115.0" - poetry run pip install "fastapi-offline==1.7.3" - poetry run pip install "python-multipart==0.0.22" - poetry run pip install "openapi-core==0.23.0" - - name: Setup litellm-enterprise as local package - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv lock --check + uv sync --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Run tests run: | - poetry run pytest tests/test_litellm --tb=short -vv --maxfail=10 -n 4 --durations=50 + uv run --no-sync pytest tests/test_litellm --tb=short -vv --maxfail=10 -n 4 --durations=50 diff --git a/.github/workflows/test-mcp.yml b/.github/workflows/test-mcp.yml index 1b228ab76b..11c5441bf9 100644 --- a/.github/workflows/test-mcp.yml +++ b/.github/workflows/test-mcp.yml @@ -27,26 +27,16 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - name: Install dependencies run: | - poetry lock - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install "pytest==7.3.1" - poetry run pip install "pytest-retry==1.6.3" - poetry run pip install "pytest-cov==5.0.0" - poetry run pip install "pytest-asyncio==0.21.1" - poetry run pip install "respx==0.22.0" - poetry run pip install "pydantic==2.11.0" - poetry run pip install "mcp==1.25.0" - poetry run pip install 'pytest-xdist==3.8.0' - - - name: Setup litellm-enterprise as local package - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv lock --check + uv sync --frozen --group proxy-dev --extra proxy --extra semantic-router - name: Run MCP tests run: | - poetry run pytest tests/mcp_tests -x -vv -n 4 --cov=litellm --cov-report=xml --durations=5 + uv run --no-sync pytest tests/mcp_tests -x -vv -n 4 --cov=litellm --cov-report=xml --durations=5 diff --git a/.github/workflows/test-unit-documentation.yml b/.github/workflows/test-unit-documentation.yml index d8b30de684..8440c53f9f 100644 --- a/.github/workflows/test-unit-documentation.yml +++ b/.github/workflows/test-unit-documentation.yml @@ -26,42 +26,35 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - - name: Cache Poetry dependencies + - name: Cache uv dependencies uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | - ~/.cache/pypoetry - ~/.cache/pip + ~/.cache/uv .venv - key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} restore-keys: | - ${{ runner.os }}-poetry- + ${{ runner.os }}-uv- - name: Install dependencies run: | - poetry config virtualenvs.in-project true - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install google-genai==1.22.0 \ - google-cloud-aiplatform==1.115.0 fastapi-offline==1.7.3 python-multipart==0.0.22 openapi-core==0.23.0 - - - name: Setup litellm-enterprise - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv sync --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | - poetry run pip install nodejs-wheel-binaries==24.13.1 - poetry run prisma generate --schema litellm/proxy/schema.prisma + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma # Run the same documentation tests that CircleCI ran (as direct Python scripts) - name: Run documentation validation tests run: | - poetry run python ./tests/documentation_tests/test_env_keys.py - poetry run python ./tests/documentation_tests/test_router_settings.py - poetry run python ./tests/documentation_tests/test_api_docs.py - poetry run python ./tests/documentation_tests/test_circular_imports.py + uv run --no-sync python ./tests/documentation_tests/test_env_keys.py + uv run --no-sync python ./tests/documentation_tests/test_router_settings.py + uv run --no-sync python ./tests/documentation_tests/test_api_docs.py + uv run --no-sync python ./tests/documentation_tests/test_circular_imports.py diff --git a/.github/workflows/test-unit-proxy-db.yml b/.github/workflows/test-unit-proxy-db.yml index 49d399e874..35d3c018a4 100644 --- a/.github/workflows/test-unit-proxy-db.yml +++ b/.github/workflows/test-unit-proxy-db.yml @@ -34,7 +34,7 @@ jobs: - test-group: remaining test-path: "tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py" workers: 8 - timeout: 20 + timeout: 30 uses: ./.github/workflows/_test-unit-services-base.yml with: test-path: ${{ matrix.test-path }} diff --git a/.github/workflows/test-unit-proxy-legacy.yml b/.github/workflows/test-unit-proxy-legacy.yml index a939113726..d4f5c38a61 100644 --- a/.github/workflows/test-unit-proxy-legacy.yml +++ b/.github/workflows/test-unit-proxy-legacy.yml @@ -50,43 +50,36 @@ jobs: with: python-version: "3.12" - - name: Install Poetry - run: pip install 'poetry==2.3.2' + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" - - name: Cache Poetry dependencies + - name: Cache uv dependencies uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | - ~/.cache/pypoetry - ~/.cache/pip + ~/.cache/uv .venv - key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} restore-keys: | - ${{ runner.os }}-poetry- + ${{ runner.os }}-uv- - name: Install dependencies run: | - poetry config virtualenvs.in-project true - poetry install --with dev,proxy-dev --extras "proxy semantic-router" - poetry run pip install google-genai==1.22.0 \ - google-cloud-aiplatform==1.115.0 fastapi-offline==1.7.3 python-multipart==0.0.22 openapi-core==0.23.0 - - - name: Setup litellm-enterprise - run: | - poetry run pip install --force-reinstall --no-deps -e enterprise/ + uv sync --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | - poetry run pip install nodejs-wheel-binaries==24.13.1 - poetry run prisma generate --schema litellm/proxy/schema.prisma + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma - name: Run tests - ${{ matrix.test-group.name }} env: TEST_PATH: ${{ matrix.test-group.path }} run: | - poetry run pytest ${TEST_PATH} \ + uv run --no-sync pytest ${TEST_PATH} \ --tb=short -vv \ --maxfail=10 \ -n 2 \ diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index 47636ce8e9..943efb392a 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -21,6 +21,12 @@ jobs: with: persist-credentials: false + - name: Free up disk space + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/share/boost + sudo apt-get clean + df -h / + - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12 diff --git a/AGENTS.md b/AGENTS.md index 9493a3404a..0d898fc6d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -123,7 +123,7 @@ LiteLLM supports MCP for agent workflows: ## RUNNING SCRIPTS -Use `poetry run python script.py` to run Python scripts in the project environment (for non-test files). +Use `uv run python script.py` to run Python scripts in the project environment (for non-test files). ## GITHUB TEMPLATES @@ -234,16 +234,16 @@ When opening issues or pull requests, follow these templates: ### Environment -- Poetry is installed in `~/.local/bin`; the update script ensures it is on `PATH`. +- uv is installed in `~/.local/bin`; the update script ensures it is on `PATH`. - Python 3.12, Node 22 are pre-installed. -- The virtual environment lives under `~/.cache/pypoetry/virtualenvs/`. +- The project virtual environment lives under `.venv/`. ### Running the proxy server Start the proxy with a config file: ```bash -poetry run litellm --config dev_config.yaml --port 4000 +uv run litellm --config dev_config.yaml --port 4000 ``` The proxy takes ~15-20 seconds to fully start (it runs Prisma migrations on boot). Wait for `/health` to return before sending requests. Without a PostgreSQL `DATABASE_URL`, the proxy connects to a default Neon dev database embedded in the `litellm-proxy-extras` package. @@ -252,17 +252,16 @@ The proxy takes ~15-20 seconds to fully start (it runs Prisma migrations on boot See `CLAUDE.md` and the `Makefile` for standard commands. Key notes: -- `psycopg-binary` must be installed (`poetry run pip install psycopg-binary`) because the pytest-postgresql plugin requires it and the lock file only includes `psycopg` (no binary). -- `openapi-core` must be installed (`poetry run pip install openapi-core`) for the OpenAPI compliance tests in `tests/test_litellm/interactions/`. +- `uv sync --group proxy-dev --extra proxy` installs the Prisma and proxy-side test dependencies used by the standard local workflow. - The `--timeout` pytest flag is NOT available; don't pass it. -- Unit tests: `poetry run pytest tests/test_litellm/ -x -vv -n 4` -- **Before committing, always run `poetry run black .` to format your code.** Black formatting is enforced in CI. -- If `poetry install` fails with "pyproject.toml changed significantly since poetry.lock was last generated", run `poetry lock` first to regenerate the lock file. +- Unit tests: `uv run pytest tests/test_litellm/ -x -vv -n 4` +- **Before committing, always run `uv run black .` to format your code.** Black formatting is enforced in CI. +- If `uv sync` fails because the lockfile is outdated, run `uv lock` and retry. ### Lint ```bash -cd litellm && poetry run ruff check . +cd litellm && uv run ruff check . ``` Ruff is the primary fast linter. For the full lint suite (including mypy, black, circular imports), run `make lint` per `CLAUDE.md`. @@ -273,4 +272,4 @@ Ruff is the primary fast linter. For the full lint suite (including mypy, black, - The proxy at port 4000 serves a **pre-built** static UI from `litellm/proxy/_experimental/out/`. After making UI code changes, you must run `npm run build` in the dashboard directory and copy the output: `cp -r ui/litellm-dashboard/out/* litellm/proxy/_experimental/out/` for the proxy to serve the updated UI. - SVGs used as provider logos (loaded via `` tags) must NOT use `fill="currentColor"` — replace with an explicit color like `#000000` or use the `-color` variant from lobehub icons, since CSS color inheritance does not work inside `` elements. - Provider logos live in `ui/litellm-dashboard/public/assets/logos/` (source) and `litellm/proxy/_experimental/out/assets/logos/` (pre-built). Both locations must have the file for it to work in dev and proxy-served modes. -- UI Vitest tests: `cd ui/litellm-dashboard && npx vitest run` \ No newline at end of file +- UI Vitest tests: `cd ui/litellm-dashboard && npx vitest run` diff --git a/CLAUDE.md b/CLAUDE.md index a4e723d5b1..043055408c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ### Installation - `make install-dev` - Install core development dependencies - `make install-proxy-dev` - Install proxy development dependencies with full feature set -- `make install-test-deps` - Install all test dependencies +- `make install-test-deps` - Install the full local test environment and generate the Prisma client ### Testing - `make test` - Run all tests @@ -20,14 +20,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `make format` - Apply Black code formatting - `make lint-ruff` - Run Ruff linting only - `make lint-mypy` - Run MyPy type checking only -- **Before committing, always run `poetry run black .` to format your code.** Black formatting is enforced in CI. +- **Before committing, always run `uv run black .` to format your code.** Black formatting is enforced in CI. ### Single Test Files -- `poetry run pytest tests/path/to/test_file.py -v` - Run specific test file -- `poetry run pytest tests/path/to/test_file.py::test_function -v` - Run specific test +- `uv run pytest tests/path/to/test_file.py -v` - Run specific test file +- `uv run pytest tests/path/to/test_file.py::test_function -v` - Run specific test ### Running Scripts -- `poetry run python script.py` - Run Python scripts (use for non-test files) +- `uv run python script.py` - Run Python scripts (use for non-test files) ### GitHub Issue & PR Templates When contributing to the project, use the appropriate templates: @@ -154,6 +154,14 @@ LiteLLM is a unified interface for 100+ LLM providers with two main components: - Optional features enabled via environment variables - Separate licensing and authentication for enterprise features +### CI Supply-Chain Safety +- **Never pipe a remote script into a shell** (`curl ... | bash`, `wget ... | sh`). Download the artifact to a file, verify its SHA-256 checksum, then install. +- **Pin every external tool to a specific version** with a full URL (not `latest` or `stable`). Unversioned downloads silently change under you. +- **Verify checksums for all downloaded binaries.** Use the provider's official `.sha256` / `.sha256sum` sidecar file when available; otherwise compute and hardcode the digest. +- **Prefer reusable CircleCI commands** (`commands:` section) so a tool is installed and verified in exactly one place, then referenced everywhere with `- install_` or `- wait_for_service`. +- **Don't add tools just because they were there before.** Audit whether an external dependency is still needed. If it can be replaced with a shell one-liner or a tool already in the image, remove it. +- These rules apply to every download in CI: binaries, install scripts, language version managers, package repos. No exceptions. + ### HTTP Client Cache Safety - **Never close HTTP/SDK clients on cache eviction.** `LLMClientCache._remove_key()` must not call `close()`/`aclose()` on evicted clients — they may still be used by in-flight requests. Doing so causes `RuntimeError: Cannot send a request, as the client has been closed.` after the 1-hour TTL expires. Cleanup happens at shutdown via `close_litellm_async_clients()`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c029ccce1a..8ac83341f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,9 +122,17 @@ Run all unit tests (uses parallel execution for speed): make test-unit ``` +If you're running broader test suites, proxy tests, or anything that touches PostgreSQL-backed fixtures/plugins, install the full local test environment first: + +```bash +make install-test-deps +``` + +This syncs the locked test environment used across the repo, including `psycopg` v3 plus `psycopg-binary` (used by `pytest-postgresql`), `psycopg2-binary` (used by some proxy E2E tests), and a generated Prisma client for DB-backed proxy tests, so pytest startup matches CI without manual package installs. + Run specific test files: ```bash -poetry run pytest tests/test_litellm/test_your_file.py -v +uv run pytest tests/test_litellm/test_your_file.py -v ``` ### Running Linting and Formatting Checks @@ -185,7 +193,7 @@ Run `make help` to see all available commands: make help # Show all available commands make install-dev # Install development dependencies make install-proxy-dev # Install proxy development dependencies -make install-test-deps # Install test dependencies (for running tests) +make install-test-deps # Install the full local test environment make format # Apply Black code formatting make format-check # Check Black formatting (matches CI) make lint # Run all linting checks @@ -247,7 +255,7 @@ To run the proxy server locally: make install-proxy-dev # Start the proxy server -poetry run litellm --config your_config.yaml +uv run litellm --config your_config.yaml ``` ### Docker Development @@ -332,4 +340,4 @@ Looking for ideas? Check out: - 🧪 Test coverage improvements - 🔌 New LLM provider integrations -Thank you for contributing to LiteLLM! 🚀 \ No newline at end of file +Thank you for contributing to LiteLLM! 🚀 diff --git a/Dockerfile b/Dockerfile index f4cb501ad8..a2cd1cb3ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,57 +3,75 @@ ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92 # Runtime image ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92f02178f37c94bb3d6001403716da59d6092dfe8d9b502 +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 + +FROM $UV_IMAGE AS uvbin # Builder stage FROM $LITELLM_BUILD_IMAGE AS builder -# Set the working directory to /app WORKDIR /app - USER root -# Install build dependencies -RUN apk add --no-cache bash gcc py3-pip python3 python3-dev openssl openssl-dev +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx -RUN python -m pip install build==1.4.2 +RUN apk add --no-cache \ + bash \ + gcc \ + python3 \ + python3-dev \ + openssl \ + openssl-dev \ + nodejs \ + npm \ + libsndfile -# Copy the current directory contents into the container at /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + 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 -# Convert Windows line endings to Unix and make executable +# 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 -# Build the package -RUN rm -rf dist/* && python -m build +# Install project and workspace packages (fast - deps already cached) +RUN uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 -# There should be only one wheel file now, assume the build only creates one -RUN ls -1 dist/*.whl | head -1 +RUN prisma generate --schema=./schema.prisma -# Install the package -RUN pip install dist/*.whl - -# install dependencies as wheels -RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt - -# ensure pyjwt is used, not jwt -RUN pip uninstall jwt -y -RUN pip uninstall PyJWT -y -RUN pip install PyJWT==2.12.0 --no-cache-dir +RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \ + sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh # Runtime stage FROM $LITELLM_RUNTIME_IMAGE AS runtime -# Ensure runtime stage runs as root USER root -# Install runtime dependencies (libsndfile needed for audio processing on ARM64) -RUN apk add --no-cache bash openssl tzdata nodejs npm python3 py3-pip libsndfile && \ +RUN apk add --no-cache bash openssl tzdata nodejs npm python3 libsndfile supervisor && \ npm install -g npm@11.12.1 tar@7.5.11 glob@11.1.0 @isaacs/brace-expansion@5.0.1 minimatch@10.2.4 diff@8.0.3 && \ - # SECURITY FIX: npm bundles tar, glob, and brace-expansion at multiple nested - # levels inside its dependency tree. `npm install -g ` only creates a - # SEPARATE global package, it does NOT replace npm's internal copies. - # We must find and replace EVERY copy inside npm's directory. GLOBAL="$(npm root -g)" && \ find "$GLOBAL/npm" -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ @@ -70,73 +88,24 @@ RUN apk add --no-cache bash openssl tzdata nodejs npm python3 py3-pip libsndfile find "$GLOBAL/npm" -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ done && \ - # SECURITY FIX: patch npm's own package.json metadata so scanners see the - # actual installed versions instead of the stale declared dependencies. find /usr/local/lib /usr/lib -path "*/node_modules/npm/package.json" -exec \ sed -i 's/"tar": "\^7\.5\.[0-9]*"/"tar": "^7.5.10"/g; s/"minimatch": "\^10\.[0-9.]*"/"minimatch": "^10.2.4"/g' {} + 2>/dev/null && \ npm cache clean --force && \ - # Remove the apk-tracked npm so its stale SBOM metadata (tar 7.5.9) is - # no longer visible to image scanners. The globally installed npm@latest - # at /usr/local/lib/node_modules/npm/ remains fully functional. { apk del --no-cache npm 2>/dev/null || true; } WORKDIR /app -# Copy the current directory contents into the container at /app -COPY . . -RUN ls -la /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Copy the built wheel from the builder stage to the runtime stage; assumes only one wheel file is present -COPY --from=builder /app/dist/*.whl . -COPY --from=builder /wheels/ /wheels/ +COPY --from=builder /app /app -# Install the built wheel using pip; again using a wildcard if it's the only file -RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ --no-deps && rm -f *.whl && rm -rf /wheels - -# Replace the nodejs-wheel-binaries bundled node with the system node (fixes CVE-2025-55130) -RUN NODEJS_WHEEL_NODE=$(find /usr/lib -path "*/nodejs_wheel/bin/node" 2>/dev/null) && \ - if [ -n "$NODEJS_WHEEL_NODE" ]; then cp /usr/bin/node "$NODEJS_WHEEL_NODE"; fi - -# Remove test files and keys from dependencies -RUN find /usr/lib -type f -path "*/tornado/test/*" -delete && \ - find /usr/lib -type d -path "*/tornado/test" -delete - -# SECURITY FIX: nodejs-wheel-binaries (pip package used by Prisma) bundles a complete -# npm with old vulnerable deps at /usr/lib/python3.*/site-packages/nodejs_wheel/. -# Patch every copy of tar, glob, and brace-expansion inside that tree. -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /usr/lib -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /usr/lib -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /usr/lib -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 /usr/lib -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /usr/lib -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - -# Install semantic_router and aurelio-sdk using script -# Convert Windows line endings to Unix and make executable -RUN sed -i 's/\r$//' docker/install_auto_router.sh && chmod +x docker/install_auto_router.sh && ./docker/install_auto_router.sh - -# Generate prisma client using the correct schema -RUN prisma generate --schema=./litellm/proxy/schema.prisma -# Convert Windows line endings to Unix for entrypoint scripts -RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh -RUN sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh +RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \ + find /app/.venv -type d -path "*/tornado/test" -delete EXPOSE 4000/tcp -RUN apk add --no-cache supervisor COPY docker/supervisord.conf /etc/supervisord.conf ENTRYPOINT ["docker/prod_entrypoint.sh"] - -# Append "--detailed_debug" to the end of CMD to view detailed debug logs CMD ["--port", "4000"] diff --git a/GEMINI.md b/GEMINI.md index a9d40c910b..9e950d89b3 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -22,11 +22,11 @@ This file provides guidance to Gemini when working with code in this repository. - `make lint-mypy` - Run MyPy type checking only ### Single Test Files -- `poetry run pytest tests/path/to/test_file.py -v` - Run specific test file -- `poetry run pytest tests/path/to/test_file.py::test_function -v` - Run specific test +- `uv run pytest tests/path/to/test_file.py -v` - Run specific test file +- `uv run pytest tests/path/to/test_file.py::test_function -v` - Run specific test ### Running Scripts -- `poetry run python script.py` - Run Python scripts (use for non-test files) +- `uv run python script.py` - Run Python scripts (use for non-test files) ### GitHub Issue & PR Templates When contributing to the project, use the appropriate templates: @@ -105,4 +105,4 @@ LiteLLM is a unified interface for 100+ LLM providers with two main components: ### Enterprise Features - Enterprise-specific code in `enterprise/` directory - Optional features enabled via environment variables -- Separate licensing and authentication for enterprise features \ No newline at end of file +- Separate licensing and authentication for enterprise features diff --git a/Makefile b/Makefile index 74031f418d..b6b674ff3b 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ help: @echo " make install-proxy-dev - Install proxy development dependencies" @echo " make install-dev-ci - Install dev dependencies (CI-compatible, pins OpenAI)" @echo " make install-proxy-dev-ci - Install proxy dev dependencies (CI-compatible)" - @echo " make install-test-deps - Install test dependencies" + @echo " make install-test-deps - Install the full local test environment" @echo " make install-helm-unittest - Install helm unittest plugin" @echo " make format - Apply Black code formatting" @echo " make format-check - Check Black code formatting (matches CI)" @@ -40,49 +40,44 @@ help: @echo " make test-integration - Run integration tests" @echo " make test-unit-helm - Run helm unit tests" -# Keep PIP simple for edge cases: -PIP := $(shell command -v pip > /dev/null 2>&1 && echo "pip" || echo "python3 -m pip") +UV := uv +UV_RUN := $(UV) run --no-sync # Show info info: - @echo "PIP: $(PIP)" + @echo "UV: $(UV)" # Installation targets install-dev: - poetry install --with dev + $(UV) sync --frozen install-proxy-dev: - poetry install --with dev,proxy-dev --extras proxy + $(UV) sync --frozen --group proxy-dev --extra proxy # CI-compatible installations (matches GitHub workflows exactly) install-dev-ci: - $(PIP) install openai==2.8.0 - poetry install --with dev - $(PIP) install openai==2.8.0 + $(UV) sync --frozen install-proxy-dev-ci: - poetry install --with dev,proxy-dev --extras proxy - $(PIP) install openai==2.8.0 + $(UV) sync --frozen --group proxy-dev --extra proxy install-test-deps: install-proxy-dev - poetry run $(PIP) install "pytest-retry==1.6.3" - poetry run $(PIP) install pytest-xdist - poetry run $(PIP) install openapi-core - cd enterprise && poetry run $(PIP) install -e . && cd .. + $(UV) sync --frozen --all-groups --all-extras + $(UV_RUN) prisma generate --schema litellm/proxy/schema.prisma install-helm-unittest: helm plugin install https://github.com/helm-unittest/helm-unittest --version v0.4.4 || echo "ignore error if plugin exists" # Formatting format: install-dev - cd litellm && poetry run black . && cd .. + cd litellm && $(UV_RUN) black . && cd .. format-check: install-dev - cd litellm && poetry run black --check . && cd .. + cd litellm && $(UV_RUN) black --check . && cd .. # Linting targets lint-ruff: install-dev - cd litellm && poetry run ruff check . && cd .. + cd litellm && $(UV_RUN) ruff check . && cd .. # faster linter for developing ... # inspiration from: @@ -96,37 +91,36 @@ lint-format-changed: install-dev $$start = $$1; $$count = $$2 || 1; $$end = $$start + $$count - 1; \ print "$$file:$$start:1-$$end:999\n"; \ }' | \ - while read range; do \ - file="$${range%%:*}"; \ - lines="$${range#*:}"; \ - echo "Formatting $$file (lines $$lines)"; \ - poetry run ruff format --range "$$lines" "$$file"; \ - done + while read range; do \ + file="$${range%%:*}"; \ + lines="$${range#*:}"; \ + echo "Formatting $$file (lines $$lines)"; \ + $(UV_RUN) ruff format --range "$$lines" "$$file"; \ + done lint-ruff-dev: install-dev @tmpfile=$$(mktemp /tmp/ruff-dev.XXXXXX) && \ cd litellm && \ - (poetry run ruff check . --output-format=pylint || true) > "$$tmpfile" && \ - poetry run diff-quality --violations=pylint "$$tmpfile" --compare-branch=origin/main && \ + ($(UV_RUN) ruff check . --output-format=pylint || true) > "$$tmpfile" && \ + $(UV_RUN) diff-quality --violations=pylint "$$tmpfile" --compare-branch=origin/main && \ cd .. ; \ rm -f "$$tmpfile" lint-ruff-FULL-dev: install-dev @files=$$(git diff --name-only origin/main -- '*.py'); \ - if [ -n "$$files" ]; then echo "$$files" | xargs poetry run ruff check; \ + if [ -n "$$files" ]; then echo "$$files" | xargs $(UV_RUN) ruff check; \ else echo "No changed .py files to check."; fi lint-mypy: install-dev - poetry run $(PIP) install types-requests types-setuptools types-redis types-PyYAML - cd litellm && poetry run mypy . --ignore-missing-imports && cd .. + cd litellm && $(UV_RUN) mypy . --ignore-missing-imports && cd .. lint-black: format-check check-circular-imports: install-dev - cd litellm && poetry run python ../tests/documentation_tests/test_circular_imports.py && cd .. + cd litellm && $(UV_RUN) python ../tests/documentation_tests/test_circular_imports.py && cd .. check-import-safety: install-dev - @poetry run python -c "from litellm import *; print('[from litellm import *] OK! no issues!');" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) + @$(UV_RUN) python -c "from litellm import *; print('[from litellm import *] OK! no issues!');" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) # Combined linting (matches test-linting.yml workflow) lint: format-check lint-ruff lint-mypy check-circular-imports check-import-safety @@ -135,46 +129,46 @@ lint: format-check lint-ruff lint-mypy check-circular-imports check-import-safet lint-dev: lint-format-changed lint-mypy check-circular-imports check-import-safety # Testing targets -test: - poetry run pytest tests/ +test: install-test-deps + $(UV_RUN) pytest tests/ test-unit: install-test-deps - poetry run pytest tests/test_litellm -x -vv -n 4 + $(UV_RUN) pytest tests/test_litellm -x -vv -n 4 # Matrix test targets (matching CI workflow groups) test-unit-llms: install-test-deps - poetry run pytest tests/test_litellm/llms --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/llms --tb=short -vv -n 4 --durations=20 test-unit-proxy-guardrails: install-test-deps - poetry run pytest tests/test_litellm/proxy/guardrails tests/test_litellm/proxy/management_endpoints tests/test_litellm/proxy/management_helpers --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/proxy/guardrails tests/test_litellm/proxy/management_endpoints tests/test_litellm/proxy/management_helpers --tb=short -vv -n 4 --durations=20 test-unit-proxy-core: install-test-deps - poetry run pytest tests/test_litellm/proxy/auth tests/test_litellm/proxy/client tests/test_litellm/proxy/db tests/test_litellm/proxy/hooks tests/test_litellm/proxy/policy_engine --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/proxy/auth tests/test_litellm/proxy/client tests/test_litellm/proxy/db tests/test_litellm/proxy/hooks tests/test_litellm/proxy/policy_engine --tb=short -vv -n 4 --durations=20 test-unit-proxy-misc: install-test-deps - poetry run pytest tests/test_litellm/proxy/_experimental tests/test_litellm/proxy/agent_endpoints tests/test_litellm/proxy/anthropic_endpoints tests/test_litellm/proxy/common_utils tests/test_litellm/proxy/discovery_endpoints tests/test_litellm/proxy/experimental tests/test_litellm/proxy/google_endpoints tests/test_litellm/proxy/health_endpoints tests/test_litellm/proxy/image_endpoints tests/test_litellm/proxy/middleware tests/test_litellm/proxy/openai_files_endpoint tests/test_litellm/proxy/pass_through_endpoints tests/test_litellm/proxy/prompts tests/test_litellm/proxy/public_endpoints tests/test_litellm/proxy/response_api_endpoints tests/test_litellm/proxy/spend_tracking tests/test_litellm/proxy/ui_crud_endpoints tests/test_litellm/proxy/vector_store_endpoints tests/test_litellm/proxy/test_*.py --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/proxy/_experimental tests/test_litellm/proxy/agent_endpoints tests/test_litellm/proxy/anthropic_endpoints tests/test_litellm/proxy/common_utils tests/test_litellm/proxy/discovery_endpoints tests/test_litellm/proxy/experimental tests/test_litellm/proxy/google_endpoints tests/test_litellm/proxy/health_endpoints tests/test_litellm/proxy/image_endpoints tests/test_litellm/proxy/middleware tests/test_litellm/proxy/openai_files_endpoint tests/test_litellm/proxy/pass_through_endpoints tests/test_litellm/proxy/prompts tests/test_litellm/proxy/public_endpoints tests/test_litellm/proxy/response_api_endpoints tests/test_litellm/proxy/spend_tracking tests/test_litellm/proxy/ui_crud_endpoints tests/test_litellm/proxy/vector_store_endpoints tests/test_litellm/proxy/test_*.py --tb=short -vv -n 4 --durations=20 test-unit-integrations: install-test-deps - poetry run pytest tests/test_litellm/integrations --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/integrations --tb=short -vv -n 4 --durations=20 test-unit-core-utils: install-test-deps - poetry run pytest tests/test_litellm/litellm_core_utils --tb=short -vv -n 2 --durations=20 + $(UV_RUN) pytest tests/test_litellm/litellm_core_utils --tb=short -vv -n 2 --durations=20 test-unit-other: install-test-deps - poetry run pytest tests/test_litellm/caching tests/test_litellm/responses tests/test_litellm/secret_managers tests/test_litellm/vector_stores tests/test_litellm/a2a_protocol tests/test_litellm/anthropic_interface tests/test_litellm/completion_extras tests/test_litellm/containers tests/test_litellm/enterprise tests/test_litellm/experimental_mcp_client tests/test_litellm/google_genai tests/test_litellm/images tests/test_litellm/interactions tests/test_litellm/passthrough tests/test_litellm/router_strategy tests/test_litellm/router_utils tests/test_litellm/types --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/caching tests/test_litellm/responses tests/test_litellm/secret_managers tests/test_litellm/vector_stores tests/test_litellm/a2a_protocol tests/test_litellm/anthropic_interface tests/test_litellm/completion_extras tests/test_litellm/containers tests/test_litellm/enterprise tests/test_litellm/experimental_mcp_client tests/test_litellm/google_genai tests/test_litellm/images tests/test_litellm/interactions tests/test_litellm/passthrough tests/test_litellm/router_strategy tests/test_litellm/router_utils tests/test_litellm/types --tb=short -vv -n 4 --durations=20 test-unit-root: install-test-deps - poetry run pytest tests/test_litellm/test_*.py --tb=short -vv -n 4 --durations=20 + $(UV_RUN) pytest tests/test_litellm/test_*.py --tb=short -vv -n 4 --durations=20 # Proxy unit tests (tests/proxy_unit_tests split alphabetically) test-proxy-unit-a: install-test-deps - poetry run pytest tests/proxy_unit_tests/test_[a-o]*.py --tb=short -vv -n 2 --durations=20 + $(UV_RUN) pytest tests/proxy_unit_tests/test_[a-o]*.py --tb=short -vv -n 2 --durations=20 test-proxy-unit-b: install-test-deps - poetry run pytest tests/proxy_unit_tests/test_[p-z]*.py --tb=short -vv -n 2 --durations=20 + $(UV_RUN) pytest tests/proxy_unit_tests/test_[p-z]*.py --tb=short -vv -n 2 --durations=20 -test-integration: - poetry run pytest tests/ -k "not test_litellm" +test-integration: install-test-deps + $(UV_RUN) pytest tests/ -k "not test_litellm" test-unit-helm: install-helm-unittest helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm @@ -188,6 +182,6 @@ test-llm-translation-single: install-test-deps @echo "Running single LLM translation test file..." @if [ -z "$(FILE)" ]; then echo "Usage: make test-llm-translation-single FILE=test_filename.py"; exit 1; fi @mkdir -p test-results - poetry run pytest tests/llm_translation/$(FILE) \ + $(UV_RUN) pytest tests/llm_translation/$(FILE) \ --junitxml=test-results/junit.xml \ -v --tb=short --maxfail=100 --timeout=300 diff --git a/README.md b/README.md index 4a790ce430..2c109dabf8 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Managing LLM calls across providers gets complicated fast — different SDKs, au ### Python SDK ```shell -pip install litellm +uv add litellm ``` ```python @@ -109,7 +109,7 @@ response = completion(model="anthropic/claude-sonnet-4-20250514", messages=[{"ro [**Getting Started - E2E Tutorial**](https://docs.litellm.ai/docs/proxy/docker_quick_start) - Setup virtual keys, make your first request ```shell -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' litellm --model gpt-4o ``` @@ -412,8 +412,8 @@ Support for more providers. Missing a provider or LLM Platform, raise a [feature #### Backend 1. (In root) create virtual environment `python -m venv .venv` 2. Activate virtual environment `source .venv/bin/activate` -3. Install dependencies `pip install -e ".[all]"` -4. `pip install prisma` +3. Install dependencies `uv sync --all-extras --group proxy-dev` +4. `uv run prisma generate` 5. `prisma generate` 6. Start proxy backend `python litellm/proxy/proxy_cli.py` @@ -470,7 +470,7 @@ We welcome contributions to LiteLLM! Whether you're fixing bugs, adding features ## Quick Start for Contributors -This requires poetry to be installed. +This requires uv to be installed. ```bash git clone https://github.com/BerriAI/litellm.git @@ -520,4 +520,3 @@ All these checks must pass before your PR can be merged. - diff --git a/cookbook/litellm-ollama-docker-image/requirements.txt b/cookbook/litellm-ollama-docker-image/requirements.txt index 7990d251cc..815a42a679 100644 --- a/cookbook/litellm-ollama-docker-image/requirements.txt +++ b/cookbook/litellm-ollama-docker-image/requirements.txt @@ -1 +1 @@ -litellm==1.61.15 \ No newline at end of file +litellm==1.83.5 \ No newline at end of file diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index bbc1ef4562..1a85ee5c02 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -3,55 +3,66 @@ ARG LITELLM_BUILD_IMAGE=python:3.11-alpine@sha256:f07e2ace46f560f09a6eeec7b4913b # Runtime image ARG LITELLM_RUNTIME_IMAGE=python:3.11-alpine@sha256:f07e2ace46f560f09a6eeec7b4913b80ee99546e749ef82342a419a326620856 +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 + +FROM $UV_IMAGE AS uvbin -# Builder stage FROM $LITELLM_BUILD_IMAGE AS builder -# Set the working directory to /app WORKDIR /app -# Install build dependencies -RUN apk add --no-cache gcc python3-dev musl-dev +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx -RUN pip install --upgrade pip==26.0.1 && \ - pip install build==1.4.2 +RUN apk add --no-cache gcc python3-dev musl-dev nodejs npm libsndfile -# Copy the current directory contents into the container at /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + 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 the package -RUN rm -rf dist/* && python -m build +# Install project and workspace packages (fast - deps already cached) +RUN uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 -# There should be only one wheel file now, assume the build only creates one -RUN ls -1 dist/*.whl | head -1 +RUN prisma generate --schema=./schema.prisma -# Install the package -RUN pip install dist/*.whl +RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \ + sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh -# install dependencies as wheels -RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt - -# Runtime stage FROM $LITELLM_RUNTIME_IMAGE AS runtime -# Update dependencies and clean up, install libsndfile for audio processing -RUN apk upgrade --no-cache && apk add --no-cache libsndfile +RUN apk upgrade --no-cache && apk add --no-cache libsndfile nodejs npm WORKDIR /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Copy the built wheel from the builder stage to the runtime stage; assumes only one wheel file is present -COPY --from=builder /app/dist/*.whl . -COPY --from=builder /wheels/ /wheels/ - -# Install the built wheel using pip; again using a wildcard if it's the only file -RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ --no-deps && rm -f *.whl && rm -rf /wheels - -# Convert Windows line endings to Unix for entrypoint scripts -RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh -RUN sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh +COPY --from=builder /app /app EXPOSE 4000/tcp -# Set your entrypoint and command ENTRYPOINT ["docker/prod_entrypoint.sh"] CMD ["--port", "4000"] diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index 36dd5a7874..57ecef81eb 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -3,53 +3,72 @@ ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92 # Runtime image ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92f02178f37c94bb3d6001403716da59d6092dfe8d9b502 -# Builder stage +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 + +FROM $UV_IMAGE AS uvbin + FROM $LITELLM_BUILD_IMAGE AS builder -# Set the working directory to /app WORKDIR /app - USER root -# Install build dependencies +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx + RUN apk add --no-cache \ bash \ gcc \ - py3-pip \ python3 \ python3-dev \ openssl \ - openssl-dev + openssl-dev \ + nodejs \ + npm \ + libsndfile -RUN python -m pip install build==1.4.2 +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Copy the current directory contents into the container at /app +# 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 -# Convert Windows line endings to Unix and make executable +# 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 -# Build the package -RUN rm -rf dist/* && python -m build +# Install project and workspace packages (fast - deps already cached) +RUN uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 -# There should be only one wheel file now, assume the build only creates one -RUN ls -1 dist/*.whl | head -1 +RUN prisma generate --schema=./schema.prisma -# Install the package -RUN pip install dist/*.whl +RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \ + sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh -# install dependencies as wheels -RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt - -# Runtime stage FROM $LITELLM_RUNTIME_IMAGE AS runtime -# Ensure runtime stage runs as root USER root -# Install runtime dependencies -RUN apk add --no-cache bash openssl tzdata nodejs npm python3 py3-pip libsndfile && \ +RUN apk add --no-cache bash openssl tzdata nodejs npm python3 libsndfile supervisor && \ npm install -g npm@11.12.1 tar@7.5.11 glob@11.1.0 @isaacs/brace-expansion@5.0.1 minimatch@10.2.4 diff@8.0.3 && \ GLOBAL="$(npm root -g)" && \ find "$GLOBAL/npm" -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ @@ -73,66 +92,18 @@ RUN apk add --no-cache bash openssl tzdata nodejs npm python3 py3-pip libsndfile { apk del --no-cache npm 2>/dev/null || true; } WORKDIR /app -# Copy the current directory contents into the container at /app -COPY . . -RUN ls -la /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Copy the built wheel from the builder stage to the runtime stage; assumes only one wheel file is present -COPY --from=builder /app/dist/*.whl . -COPY --from=builder /wheels/ /wheels/ +COPY --from=builder /app /app -# Install the built wheel using pip; again using a wildcard if it's the only file -RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ --no-deps && rm -f *.whl && rm -rf /wheels +RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \ + find /app/.venv -type d -path "*/tornado/test" -delete -# SECURITY FIX: nodejs-wheel-binaries (pip package used by Prisma) bundles a complete -# npm with old vulnerable deps at /usr/lib/python3.*/site-packages/nodejs_wheel/. -# Patch every copy of tar, glob, and brace-expansion inside that tree. -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /usr/lib -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /usr/lib -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /usr/lib -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 /usr/lib -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /usr/lib -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - -# Install semantic_router and aurelio-sdk using script -# Convert Windows line endings to Unix and make executable -RUN sed -i 's/\r$//' docker/install_auto_router.sh && chmod +x docker/install_auto_router.sh && ./docker/install_auto_router.sh - -# ensure pyjwt is used, not jwt -RUN pip uninstall jwt -y -RUN pip uninstall PyJWT -y -RUN pip install PyJWT==2.12.0 --no-cache-dir - -# Build Admin UI (runtime stage) -# Convert Windows line endings to Unix and make executable -RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh - -# Generate prisma client -RUN prisma generate -# Convert Windows line endings to Unix for entrypoint scripts -RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh -RUN sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh EXPOSE 4000/tcp -RUN apk add --no-cache supervisor COPY docker/supervisord.conf /etc/supervisord.conf -# # Set your entrypoint and command - - ENTRYPOINT ["docker/prod_entrypoint.sh"] - -# Append "--detailed_debug" to the end of CMD to view detailed debug logs -# CMD ["--port", "4000", "--detailed_debug"] CMD ["--port", "4000"] diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index fb84230adc..88be7a6980 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -3,60 +3,70 @@ ARG LITELLM_BUILD_IMAGE=python:3.13-slim@sha256:739e7213785e88c0f702dcdc12c0973a # Runtime image ARG LITELLM_RUNTIME_IMAGE=python:3.13-slim@sha256:739e7213785e88c0f702dcdc12c0973afcbd606dbf021a589cab77d6b00b579d +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 + +FROM $UV_IMAGE AS uvbin -# Builder stage FROM $LITELLM_BUILD_IMAGE AS builder -# Set the working directory to /app WORKDIR /app - USER root -# Install build dependencies in one layer +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx + RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ g++ \ python3-dev \ libssl-dev \ pkg-config \ - && rm -rf /var/lib/apt/lists/* \ - && pip install --upgrade pip==26.0.1 build==1.4.2 + nodejs \ + npm \ + && rm -rf /var/lib/apt/lists/* -# Copy requirements first for better layer caching -COPY requirements.txt . +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Install Python dependencies with cache mount for faster rebuilds -RUN --mount=type=cache,target=/root/.cache/pip \ - pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt +# 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/ -# Fix JWT dependency conflicts early -RUN pip uninstall jwt -y || true && \ - pip uninstall PyJWT -y || true && \ - pip install PyJWT==2.12.0 --no-cache-dir +# 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 only necessary files for build -COPY pyproject.toml README.md schema.prisma poetry.lock ./ -COPY litellm/ ./litellm/ -COPY enterprise/ ./enterprise/ -COPY docker/ ./docker/ +# Copy full source tree +COPY . . -# Build Admin UI once -# Convert Windows line endings to Unix and make executable +# 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 -# Build the package -RUN rm -rf dist/* && python -m build +# Install project and workspace packages (fast - deps already cached) +RUN uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python -# Install the built package -RUN pip install dist/*.whl +RUN prisma generate --schema=./schema.prisma + +RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \ + sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh -# Runtime stage FROM $LITELLM_RUNTIME_IMAGE AS runtime -# Ensure runtime stage runs as root USER root -# Install only runtime dependencies RUN apt-get update && apt-get upgrade -y \ libxml2 \ libexpat1 \ @@ -72,9 +82,9 @@ RUN apt-get update && apt-get upgrade -y \ libc6 \ && apt-get install -y --no-install-recommends \ libssl3 \ - libatomic1 \ - nodejs \ - npm \ + libatomic1 \ + nodejs \ + npm \ && rm -rf /var/lib/apt/lists/* \ && npm install -g npm@11.12.1 tar@7.5.11 glob@11.1.0 @isaacs/brace-expansion@5.0.1 minimatch@10.2.4 diff@8.0.3 \ && GLOBAL="$(npm root -g)" \ @@ -99,53 +109,13 @@ RUN apt-get update && apt-get upgrade -y \ && apt-get purge -y npm WORKDIR /app +ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + XDG_CACHE_HOME=/app/.cache \ + PATH="/app/.venv/bin:${PATH}" -# Copy only necessary runtime files -COPY docker/entrypoint.sh docker/prod_entrypoint.sh ./docker/ -COPY litellm/ ./litellm/ -COPY pyproject.toml README.md schema.prisma poetry.lock ./ - -# Copy pre-built wheels and install everything at once -COPY --from=builder /wheels/ /wheels/ -COPY --from=builder /app/dist/*.whl . - -# Install all dependencies in one step with no-cache for smaller image -RUN pip install --no-cache-dir *.whl /wheels/* --no-index --find-links=/wheels/ --no-deps && \ - rm -f *.whl && \ - rm -rf /wheels - -# SECURITY FIX: nodejs-wheel-binaries (pip package used by Prisma) bundles a complete -# npm with old vulnerable deps at /usr/lib/python3.*/site-packages/nodejs_wheel/. -# Patch every copy of tar, glob, and brace-expansion inside that tree. -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /usr/lib -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /usr/lib -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /usr/lib -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 /usr/lib -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /usr/lib -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - -# Generate prisma client and set permissions -# Convert Windows line endings to Unix for entrypoint scripts -RUN prisma generate && \ - sed -i 's/\r$//' docker/entrypoint.sh && \ - sed -i 's/\r$//' docker/prod_entrypoint.sh && \ - chmod +x docker/entrypoint.sh && \ - chmod +x docker/prod_entrypoint.sh +COPY --from=builder /app /app EXPOSE 4000/tcp ENTRYPOINT ["docker/prod_entrypoint.sh"] - -# Append "--detailed_debug" to the end of CMD to view detailed debug logs -CMD ["--port", "4000"] \ No newline at end of file +CMD ["--port", "4000"] diff --git a/docker/Dockerfile.health_check b/docker/Dockerfile.health_check index e968bec340..b2cbb467f4 100644 --- a/docker/Dockerfile.health_check +++ b/docker/Dockerfile.health_check @@ -1,16 +1,22 @@ +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 +FROM $UV_IMAGE AS uvbin + FROM python:3.13-slim@sha256:739e7213785e88c0f702dcdc12c0973afcbd606dbf021a589cab77d6b00b579d WORKDIR /app -# Copy health check script and requirements +# Copy the uv binary and the health check script. +COPY --from=uvbin /uv /usr/local/bin/uv +COPY pyproject.toml uv.lock /app/ COPY scripts/health_check/health_check_client.py /app/health_check_client.py -COPY scripts/health_check/health_check_requirements.txt /app/requirements.txt -# Install dependencies -RUN pip install --no-cache-dir -r requirements.txt - -# Make script executable -RUN chmod +x /app/health_check_client.py +# Resolve and install the health-check dependencies from the project lockfile +# so the runtime image stays self-contained and reproducible. +RUN uv export --frozen --no-default-groups --only-group healthcheck --no-emit-project --no-hashes --output-file /tmp/health-check-requirements.txt \ + && uv pip install --system -r /tmp/health-check-requirements.txt \ + && rm /tmp/health-check-requirements.txt \ + && rm /app/pyproject.toml /app/uv.lock \ + && chmod +x /app/health_check_client.py # Run as non-root user RUN groupadd --gid 1000 appuser && useradd --uid 1000 --gid 1000 --no-create-home appuser diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index f3c7728146..5451bff808 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -2,51 +2,84 @@ ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92f02178f37c94bb3d6001403716da59d6092dfe8d9b502 ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:a5a619c1793039dcf92f02178f37c94bb3d6001403716da59d6092dfe8d9b502 ARG PROXY_EXTRAS_SOURCE=published +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 + +FROM $UV_IMAGE AS uvbin -# ----------------- -# Builder Stage -# ----------------- FROM $LITELLM_BUILD_IMAGE AS builder ARG PROXY_EXTRAS_SOURCE WORKDIR /app USER root -# Install build dependencies with retry logic (includes node for UI build) +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx + RUN for i in 1 2 3; do \ apk add --no-cache \ - python3 \ - python3-dev \ - py3-pip \ - clang \ - llvm \ - lld \ - gcc \ - linux-headers \ - build-base \ - bash \ - nodejs \ - npm && break || sleep 5; \ - done \ - && pip install --no-cache-dir --upgrade pip==26.0.1 build==1.4.2 + python3 \ + python3-dev \ + clang \ + llvm \ + lld \ + gcc \ + linux-headers \ + build-base \ + bash \ + coreutils \ + curl \ + openssl \ + openssl-dev \ + nodejs \ + npm \ + libsndfile && break || sleep 5; \ + done -# Cache Python dependencies -COPY requirements.txt . -RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt \ - && pip wheel --no-cache-dir --wheel-dir=/wheels/ "semantic_router==0.1.11" "aurelio-sdk==0.0.19" "PyJWT==2.12.0" +ENV UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + NVM_DIR=/root/.nvm \ + PATH="/root/.nvm/versions/node/v20.20.2/bin:/app/.venv/bin:${PATH}" \ + LITELLM_NON_ROOT=true \ + PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ + PRISMA_CLI_BINARY_TARGETS="debian-openssl-3.0.x" \ + XDG_CACHE_HOME=/app/.cache -# Copy source after dependency layers +# 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 ENV LITELLM_NON_ROOT=true -# Build Admin UI using the upstream command order while keeping a single RUN layer +# Build Admin UI once and stage the static output for the runtime image. # NOTE: .npmrc files (which may set ignore-scripts=true and min-release-age=3d) # are temporarily renamed during npm install/ci so they don't block lifecycle # scripts needed by the build. This is safe because npm ci installs from # package-lock.json with pinned versions + integrity hashes. -RUN mkdir -p /var/lib/litellm/ui && \ +RUN mkdir -p /var/lib/litellm/ui /var/lib/litellm/assets && \ ([ -f /app/.npmrc ] && mv /app/.npmrc /app/.npmrc.bak || true) && \ + NVM_VERSION="v0.40.4" && \ + NVM_CHECKSUM="4b7412c49960c7d31e8df72da90c1fb5b8cccb419ac99537b737028d497aba4f" && \ + NODE_VERSION="v20.20.2" && \ + NVM_SCRIPT="/tmp/install-nvm.sh" && \ + curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" -o "$NVM_SCRIPT" && \ + echo "${NVM_CHECKSUM} ${NVM_SCRIPT}" | sha256sum -c - && \ + bash "$NVM_SCRIPT" && \ + export NVM_DIR="$HOME/.nvm" && \ + . "$NVM_DIR/nvm.sh" && \ + nvm install "${NODE_VERSION}" && \ + nvm use "${NODE_VERSION}" && \ npm install -g npm@11.12.1 && \ npm install -g node-gyp@12.2.0 && \ ln -sf "$(npm root -g)/node-gyp" "$(npm root -g)/npm/node_modules/node-gyp" && \ @@ -56,12 +89,11 @@ RUN mkdir -p /var/lib/litellm/ui && \ cp /app/enterprise/enterprise_ui/enterprise_colors.json ./ui_colors.json; \ fi && \ ([ -f .npmrc ] && mv .npmrc .npmrc.bak || true) && \ - npm ci && \ + npm ci --no-audit --no-fund && \ ([ -f .npmrc.bak ] && mv .npmrc.bak .npmrc || true) && \ ([ -f /app/.npmrc.bak ] && mv /app/.npmrc.bak /app/.npmrc || true) && \ npm run build && \ cp -r /app/ui/litellm-dashboard/out/* /var/lib/litellm/ui/ && \ - mkdir -p /var/lib/litellm/assets && \ cp /app/litellm/proxy/logo.jpg /var/lib/litellm/assets/logo.jpg && \ ( cd /var/lib/litellm/ui && \ for html_file in *.html; do \ @@ -74,175 +106,106 @@ RUN mkdir -p /var/lib/litellm/ui && \ touch .litellm_ui_ready ) && \ cd /app/ui/litellm-dashboard && rm -rf ./out -# Build litellm wheel and place it in wheels dir (replace any PyPI wheels) -RUN rm -rf dist/* && python -m build && \ - rm -f /wheels/litellm-*.whl && \ - cp dist/*.whl /wheels/ - -# Optionally build local litellm-proxy-extras wheel -RUN if [ "$PROXY_EXTRAS_SOURCE" = "local" ]; then \ - cd /app/litellm-proxy-extras && rm -rf dist && python -m build && \ - cp dist/*.whl /wheels/; \ +RUN if [ "$PROXY_EXTRAS_SOURCE" = "published" ]; then \ + uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3 \ + --no-sources-package litellm-proxy-extras; \ + else \ + uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python3; \ fi -# Pre-cache Prisma binaries in the builder stage -ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ - PRISMA_CLI_BINARY_TARGETS="debian-openssl-3.0.x" \ - XDG_CACHE_HOME=/app/.cache \ - PATH="/usr/lib/python3.13/site-packages/nodejs/bin:${PATH}" - -RUN pip install --no-cache-dir prisma==0.11.0 nodejs-wheel-binaries==24.13.1 \ - && mkdir -p /app/.cache/npm - -RUN NPM_CONFIG_CACHE=/app/.cache/npm \ - python -c "import prisma.cli.prisma as p; p.ensure_cached()" - -RUN prisma generate && \ +RUN mkdir -p /app/.cache/npm && \ + prisma generate --schema=./schema.prisma && \ prisma --version && \ prisma migrate diff --from-empty --to-schema-datamodel ./schema.prisma --script > /dev/null 2>&1 || true -# ----------------- -# Runtime Stage -# ----------------- +RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \ + sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh + FROM $LITELLM_RUNTIME_IMAGE AS runtime ARG PROXY_EXTRAS_SOURCE WORKDIR /app USER root -# Install runtime dependencies with retry RUN for i in 1 2 3; do \ apk upgrade --no-cache && break || sleep 5; \ - done \ - && for i in 1 2 3; do \ - apk add --no-cache python3 py3-pip bash openssl tzdata nodejs npm supervisor && break || sleep 5; \ - done \ - && apk upgrade --no-cache nodejs \ - && npm install -g npm@11.12.1 tar@7.5.11 glob@11.1.0 @isaacs/brace-expansion@5.0.1 minimatch@10.2.4 diff@8.0.3 \ - && GLOBAL="$(npm root -g)" \ - && find "$GLOBAL/npm" -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done \ - && find "$GLOBAL/npm" -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done \ - && find "$GLOBAL/npm" -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 "$GLOBAL/npm" -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done \ - && find "$GLOBAL/npm" -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done \ - && find /usr/local/lib /usr/lib -path "*/node_modules/npm/package.json" -exec \ - sed -i 's/"tar": "\^7\.5\.[0-9]*"/"tar": "^7.5.10"/g; s/"minimatch": "\^10\.[0-9.]*"/"minimatch": "^10.2.4"/g' {} + 2>/dev/null \ - && npm cache clean --force \ - && { apk del --no-cache npm 2>/dev/null || true; } + done && \ + for i in 1 2 3; do \ + apk add --no-cache python3 bash openssl tzdata nodejs npm supervisor libsndfile && break || sleep 5; \ + done && \ + apk upgrade --no-cache nodejs && \ + npm install -g npm@11.12.1 tar@7.5.11 glob@11.1.0 @isaacs/brace-expansion@5.0.1 minimatch@10.2.4 diff@8.0.3 && \ + GLOBAL="$(npm root -g)" && \ + find "$GLOBAL/npm" -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ + rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ + done && \ + find "$GLOBAL/npm" -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ + rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ + done && \ + find "$GLOBAL/npm" -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 "$GLOBAL/npm" -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ + rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ + done && \ + find "$GLOBAL/npm" -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ + rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ + done && \ + find /usr/local/lib /usr/lib -path "*/node_modules/npm/package.json" -exec \ + sed -i 's/"tar": "\^7\.5\.[0-9]*"/"tar": "^7.5.10"/g; s/"minimatch": "\^10\.[0-9.]*"/"minimatch": "^10.2.4"/g' {} + 2>/dev/null && \ + npm cache clean --force && \ + { apk del --no-cache npm 2>/dev/null || true; } -# Copy artifacts from builder -COPY --from=builder /app/requirements.txt /app/requirements.txt -COPY --from=builder /app/docker/entrypoint.sh /app/docker/prod_entrypoint.sh /app/docker/ -COPY --from=builder /app/docker/supervisord.conf /etc/supervisord.conf -COPY --from=builder /app/schema.prisma /app/ -# Keep enterprise bridge module in runtime so `enterprise.enterprise_hooks` -# can load and register managed enterprise hooks (e.g. managed_files). -COPY --from=builder /app/enterprise /app/enterprise -# Copy prisma_migration.py for Helm migrations job compatibility -COPY --from=builder /app/litellm/proxy/prisma_migration.py /app/litellm/proxy/prisma_migration.py -COPY --from=builder /wheels/ /wheels/ +COPY --from=builder /app /app COPY --from=builder /var/lib/litellm/ui /var/lib/litellm/ui COPY --from=builder /var/lib/litellm/assets /var/lib/litellm/assets -COPY --from=builder /app/.cache /app/.cache -COPY --from=builder /app/litellm-proxy-extras /app/litellm-proxy-extras -COPY --from=builder \ - /usr/lib/python3.13/site-packages/nodejs* \ - /usr/lib/python3.13/site-packages/prisma* \ - /usr/lib/python3.13/site-packages/tomlkit* \ - /usr/lib/python3.13/site-packages/nodeenv* \ - /usr/lib/python3.13/site-packages/ -COPY --from=builder /usr/bin/prisma /usr/bin/prisma +COPY --from=builder /app/docker/supervisord.conf /etc/supervisord.conf -# Final runtime environment configuration -ENV PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ +ENV PATH="/app/.venv/bin:${PATH}" \ + PRISMA_BINARY_CACHE_DIR=/app/.cache/prisma-python/binaries \ PRISMA_CLI_BINARY_TARGETS="debian-openssl-3.0.x" \ HOME=/app \ LITELLM_NON_ROOT=true \ - XDG_CACHE_HOME=/app/.cache - -# Install packages from wheels and optional extras without network -RUN pip install --no-index --find-links=/wheels/ -r requirements.txt && \ - pip install --no-index --find-links=/wheels/ /wheels/litellm-*-py3-none-any.whl && \ - pip install --no-index --find-links=/wheels/ --no-deps semantic_router==0.1.11 && \ - pip install --no-index --find-links=/wheels/ aurelio-sdk==0.0.19 && \ - if [ "$PROXY_EXTRAS_SOURCE" = "local" ]; then \ - if ls /wheels/litellm_proxy_extras-*.whl >/dev/null 2>&1; then \ - pip install --no-index --find-links=/wheels/ /wheels/litellm_proxy_extras-*.whl; \ - else \ - echo "litellm_proxy_extras wheel not found; skipping local install"; \ - fi; \ - fi - -# SECURITY FIX: nodejs-wheel-binaries (pip package used by Prisma) bundles a complete -# npm with old vulnerable deps at /usr/lib/python3.*/site-packages/nodejs_wheel/. -# Patch every copy of tar, glob, and brace-expansion inside that tree. -RUN GLOBAL="$(npm root -g)" && \ - [ -n "$GLOBAL" ] || { echo "ERROR: npm root -g returned empty; aborting"; exit 1; } && \ - find /usr/lib -type d -name "tar" -path "*/node_modules/tar" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/tar" "$d"; \ - done && \ - find /usr/lib -type d -name "glob" -path "*/node_modules/glob" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/glob" "$d"; \ - done && \ - find /usr/lib -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 /usr/lib -type d -name "minimatch" -path "*/node_modules/minimatch" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/minimatch" "$d"; \ - done && \ - find /usr/lib -type d -name "diff" -path "*/node_modules/diff" | while read d; do \ - rm -rf "$d" && cp -rL "$GLOBAL/diff" "$d"; \ - done - -# Permissions, cleanup, and Prisma prep -# Convert Windows line endings to Unix for entrypoint scripts -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 && \ - chown -R nobody:nogroup /app /var/lib/litellm/ui /var/lib/litellm/assets /nonexistent /.npm && \ - pip uninstall jwt -y || true && \ - pip uninstall PyJWT -y || true && \ - pip install --no-index --find-links=/wheels/ PyJWT==2.12.0 --no-cache-dir && \ - rm -rf /wheels && \ - PRISMA_PATH=$(python -c "import os, prisma; print(os.path.dirname(prisma.__file__))") && \ - chown -R nobody:nogroup $PRISMA_PATH && \ - LITELLM_PKG_MIGRATIONS_PATH="$(python -c 'import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))' 2>/dev/null || echo '')/migrations" && \ - [ -n "$LITELLM_PKG_MIGRATIONS_PATH" ] && chown -R nobody:nogroup $LITELLM_PKG_MIGRATIONS_PATH && \ - LITELLM_PROXY_EXTRAS_PATH=$(python -c "import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))" 2>/dev/null || echo "") && \ - chgrp -R 0 $PRISMA_PATH /var/lib/litellm/ui /var/lib/litellm/assets && \ - [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chgrp -R 0 $LITELLM_PROXY_EXTRAS_PATH || true && \ - chmod -R g=u $PRISMA_PATH /var/lib/litellm/ui /var/lib/litellm/assets && \ - [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g=u $LITELLM_PROXY_EXTRAS_PATH || true && \ - chmod -R g+w $PRISMA_PATH /var/lib/litellm/ui /var/lib/litellm/assets && \ - [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g+w $LITELLM_PROXY_EXTRAS_PATH || true && \ - chmod -R g+rX $PRISMA_PATH && \ - chmod -R g+rX /app/.cache && \ - mkdir -p /tmp/.npm /nonexistent /.npm - -# Switch to non-root user for runtime -USER nobody - -# Generate Prisma client as nobody user to ensure correct file ownership -RUN prisma generate - -# Prisma runtime knobs for offline containers -ENV PRISMA_SKIP_POSTINSTALL_GENERATE=1 \ + XDG_CACHE_HOME=/app/.cache \ + PRISMA_SKIP_POSTINSTALL_GENERATE=1 \ PRISMA_HIDE_UPDATE_MESSAGE=1 \ PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1 \ NPM_CONFIG_CACHE=/app/.cache/npm \ NPM_CONFIG_PREFER_OFFLINE=true \ PRISMA_OFFLINE_MODE=true +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 && \ + chown -R nobody:nogroup /app /var/lib/litellm/ui /var/lib/litellm/assets /nonexistent /.npm /tmp/.npm && \ + PRISMA_PATH=$(python -c "import os, prisma; print(os.path.dirname(prisma.__file__))") && \ + chown -R nobody:nogroup "$PRISMA_PATH" && \ + LITELLM_PKG_MIGRATIONS_PATH="$(python -c 'import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))' 2>/dev/null || echo '')/migrations" && \ + [ -n "$LITELLM_PKG_MIGRATIONS_PATH" ] && chown -R nobody:nogroup "$LITELLM_PKG_MIGRATIONS_PATH" || true && \ + LITELLM_PROXY_EXTRAS_PATH=$(python -c "import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))" 2>/dev/null || echo "") && \ + chgrp -R 0 "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \ + [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chgrp -R 0 "$LITELLM_PROXY_EXTRAS_PATH" || true && \ + chmod -R g=u "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \ + [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g=u "$LITELLM_PROXY_EXTRAS_PATH" || true && \ + chmod -R g+w "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \ + [ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g+w "$LITELLM_PROXY_EXTRAS_PATH" || true && \ + chmod -R g+rX "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets /app/.cache + +USER nobody + +RUN prisma generate --schema=./schema.prisma + EXPOSE 4000/tcp + ENTRYPOINT ["/app/docker/prod_entrypoint.sh"] CMD ["--port", "4000"] diff --git a/docker/build_from_pip/Dockerfile.build_from_pip b/docker/build_from_pip/Dockerfile.build_from_pip index f26b993cce..bda742c71a 100644 --- a/docker/build_from_pip/Dockerfile.build_from_pip +++ b/docker/build_from_pip/Dockerfile.build_from_pip @@ -1,27 +1,53 @@ +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 +FROM $UV_IMAGE AS uvbin + FROM python:3.13-slim@sha256:739e7213785e88c0f702dcdc12c0973afcbd606dbf021a589cab77d6b00b579d +ARG LITELLM_VERSION=1.83.0 + WORKDIR /app -ENV HOME=/home/litellm -ENV PATH="${HOME}/venv/bin:$PATH" +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx -# Install runtime dependencies needed for building native extensions RUN apt-get update && \ - apt-get install -y --no-install-recommends gcc libffi-dev && \ + apt-get install -y --no-install-recommends gcc libffi-dev nodejs npm && \ rm -rf /var/lib/apt/lists/* -RUN python -m venv ${HOME}/venv -RUN ${HOME}/venv/bin/pip install --no-cache-dir --upgrade pip==26.0.1 +ENV UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + PATH="/app/.venv/bin:${PATH}" -COPY docker/build_from_pip/requirements.txt . -RUN --mount=type=cache,target=${HOME}/.cache/pip \ - ${HOME}/venv/bin/pip install -r requirements.txt - -# Copy Prisma schema file COPY schema.prisma . -# Generate prisma client -RUN prisma generate +# This image is specifically for validating/installing the published PyPI +# artifact, not the checked-out source tree. +# Keep the moved proxy-runtime packages explicit until the published PyPI +# artifact includes that extra; newer releases will simply dedupe these. +RUN uv venv --python python && \ + uv pip install --python /app/.venv/bin/python \ + "litellm[proxy,proxy-runtime]==${LITELLM_VERSION}" \ + "google-cloud-aiplatform==1.133.0" \ + "google-genai==1.37.0" \ + "anthropic[vertex]==0.84.0" \ + "grpcio==1.78.0" \ + "prometheus-client==0.20.0" \ + "langfuse==2.59.7" \ + "opentelemetry-api==1.28.0" \ + "opentelemetry-sdk==1.28.0" \ + "opentelemetry-exporter-otlp==1.28.0" \ + "ddtrace==2.19.0" \ + "sentry-sdk==2.21.0" \ + "mangum==0.17.0" \ + "azure-ai-contentsafety==1.0.0" \ + "azure-storage-file-datalake==12.20.0" \ + "pypdf==6.7.5" \ + "llm-sandbox==0.3.31" \ + "detect-secrets==1.5.0" \ + "prisma==0.11.0" \ + "openai==2.24.0" + +RUN prisma generate --schema=./schema.prisma EXPOSE 4000/tcp diff --git a/docker/build_from_pip/requirements.txt b/docker/build_from_pip/requirements.txt deleted file mode 100644 index ec6cf2438d..0000000000 --- a/docker/build_from_pip/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -litellm[proxy]==1.83.0 -prometheus_client==0.20.0 -langfuse==2.59.7 -prisma==0.11.0 -openai==2.24.0 -ddtrace==2.19.0 # for advanced DD tracing / profiling diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index a028e54262..003d9b21db 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,13 +1,16 @@ #!/bin/bash -echo $(pwd) +set -euo pipefail -# Run the Python migration script -python3 litellm/proxy/prisma_migration.py +REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +VENV_PYTHON="$REPO_ROOT/.venv/bin/python" +MIGRATION_SCRIPT="$REPO_ROOT/litellm/proxy/prisma_migration.py" -# Check if the Python script executed successfully -if [ $? -eq 0 ]; then - echo "Migration script ran successfully!" +if [ -x "$VENV_PYTHON" ]; then + "$VENV_PYTHON" "$MIGRATION_SCRIPT" +elif command -v uv >/dev/null 2>&1; then + (cd "$REPO_ROOT" && uv run --no-sync python "$MIGRATION_SCRIPT") else - echo "Migration script failed!" - exit 1 + python3 "$MIGRATION_SCRIPT" fi + +echo "Migration script ran successfully!" diff --git a/docker/install_auto_router.sh b/docker/install_auto_router.sh index 057baa19f5..4fedf201b4 100755 --- a/docker/install_auto_router.sh +++ b/docker/install_auto_router.sh @@ -1,3 +1,4 @@ #!/bin/bash -pip install semantic_router==0.1.11 --no-deps -pip install aurelio-sdk==0.0.19 --no-deps \ No newline at end of file +set -euo pipefail + +# semantic-router dependencies are installed via `uv sync`. diff --git a/docs/my-website/Dockerfile b/docs/my-website/Dockerfile index 87d1537237..4693d3a657 100644 --- a/docs/my-website/Dockerfile +++ b/docs/my-website/Dockerfile @@ -1,9 +1,32 @@ +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9 + +FROM $UV_IMAGE AS uvbin + FROM python:3.14.0a3-slim +COPY --from=uvbin /uv /usr/local/bin/uv +COPY --from=uvbin /uvx /usr/local/bin/uvx COPY . /app WORKDIR /app -RUN pip install -r requirements.txt + +ENV UV_PROJECT_ENVIRONMENT=/app/.venv \ + UV_LINK_MODE=copy \ + PATH="/app/.venv/bin:${PATH}" + +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + python3-dev \ + libssl-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +RUN uv sync --frozen --no-default-groups --no-editable \ + --extra proxy \ + --extra proxy-runtime \ + --extra extra_proxy \ + --extra semantic-router \ + --python python EXPOSE $PORT -CMD litellm --host 0.0.0.0 --port $PORT --workers 10 --config config.yaml \ No newline at end of file +CMD ["sh", "-c", "litellm --host 0.0.0.0 --port $PORT --workers 10 --config config.yaml"] diff --git a/docs/my-website/docs/adding_provider/generic_prompt_management_api.md b/docs/my-website/docs/adding_provider/generic_prompt_management_api.md index d1b119d94c..21055de3a7 100644 --- a/docs/my-website/docs/adding_provider/generic_prompt_management_api.md +++ b/docs/my-website/docs/adding_provider/generic_prompt_management_api.md @@ -378,7 +378,7 @@ if __name__ == "__main__": 1. Install dependencies: ```bash -pip install fastapi uvicorn +uv add fastapi uvicorn ``` 2. Save the code above to `prompt_server.py` diff --git a/docs/my-website/docs/caching/all_caches.md b/docs/my-website/docs/caching/all_caches.md index 6f81da9105..7cc329c93e 100644 --- a/docs/my-website/docs/caching/all_caches.md +++ b/docs/my-website/docs/caching/all_caches.md @@ -23,7 +23,7 @@ import TabItem from '@theme/TabItem'; Install redis ```shell -pip install redis +uv add redis ``` For the hosted version you can setup your own Redis DB here: https://redis.io/try-free/ @@ -55,7 +55,7 @@ response2 = completion( For GCP Memorystore Redis with IAM authentication: ```shell -pip install google-cloud-iam +uv add google-cloud-iam ``` ```python @@ -150,7 +150,7 @@ response2 = completion( Install boto3 ```shell -pip install boto3 +uv add boto3 ``` Set AWS environment variables @@ -187,7 +187,7 @@ response2 = completion( Install azure-storage-blob and azure-identity ```shell -pip install azure-storage-blob azure-identity +uv add azure-storage-blob azure-identity ``` ```python @@ -219,7 +219,7 @@ response2 = completion( Install redisvl client ```shell -pip install redisvl==0.4.1 +uv add redisvl==0.4.1 ``` For the hosted version you can setup your own Redis DB here: https://redis.io/try-free/ @@ -366,7 +366,7 @@ response2 = completion( Install the disk caching extra: ```shell -pip install "litellm[caching]" +uv add "litellm[caching]" ``` Then you can use the disk cache as follows. diff --git a/docs/my-website/docs/completion/message_sanitization.md b/docs/my-website/docs/completion/message_sanitization.md index 17482c5933..6114b640f0 100644 --- a/docs/my-website/docs/completion/message_sanitization.md +++ b/docs/my-website/docs/completion/message_sanitization.md @@ -401,7 +401,7 @@ response = litellm.completion( 3. Ensure you're using a recent version of LiteLLM: ```bash - pip install --upgrade litellm + uv add --upgrade-package litellm litellm ``` ### Unexpected Dummy Tool Results diff --git a/docs/my-website/docs/contributing.md b/docs/my-website/docs/contributing.md index 168d092ddc..9e2799ddd6 100644 --- a/docs/my-website/docs/contributing.md +++ b/docs/my-website/docs/contributing.md @@ -29,7 +29,7 @@ general_settings: Start the proxy on port 4000: ```bash -poetry run litellm --config config.yaml --port 4000 +uv run litellm --config config.yaml --port 4000 ``` The UI comes pre-built in the repo. Access it at `http://localhost:4000/ui` diff --git a/docs/my-website/docs/default_code_snippet.md b/docs/my-website/docs/default_code_snippet.md index 0921c31668..34c842de7f 100644 --- a/docs/my-website/docs/default_code_snippet.md +++ b/docs/my-website/docs/default_code_snippet.md @@ -16,7 +16,7 @@ If you want to use the non-hosted version, [go here](https://docs.litellm.ai/doc ``` -pip install litellm +uv add litellm ``` \ No newline at end of file diff --git a/docs/my-website/docs/extras/contributing_code.md b/docs/my-website/docs/extras/contributing_code.md index 673a83aca0..95d82f2c9c 100644 --- a/docs/my-website/docs/extras/contributing_code.md +++ b/docs/my-website/docs/extras/contributing_code.md @@ -41,7 +41,7 @@ git clone https://github.com/BerriAI/litellm.git Step 2: Install dev dependencies ```shell -poetry install --with dev --extras proxy +uv sync --group dev --extra proxy ``` ### 2. Adding tests diff --git a/docs/my-website/docs/index.md b/docs/my-website/docs/index.md index 6410e052b0..2f9ed281b4 100644 --- a/docs/my-website/docs/index.md +++ b/docs/my-website/docs/index.md @@ -26,13 +26,13 @@ import Image from '@theme/IdealImage'; ## Installation ```shell -pip install litellm +uv add litellm ``` To run the full Proxy Server (LLM Gateway): ```shell -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' ``` --- @@ -336,7 +336,7 @@ The proxy is a self-hosted OpenAI-compatible gateway. Any client that works with #### Step 1 — Start the proxy - + ```shell litellm --model huggingface/bigcode/starcoder diff --git a/docs/my-website/docs/integrations/letta.md b/docs/my-website/docs/integrations/letta.md index 9711999df5..1be902065b 100644 --- a/docs/my-website/docs/integrations/letta.md +++ b/docs/my-website/docs/integrations/letta.md @@ -16,7 +16,7 @@ Letta allows you to build LLM agents that can: ## Prerequisites ```bash -pip install letta litellm +uv add letta litellm ``` ## Quick Start @@ -910,7 +910,7 @@ for model in models: ``` ### Common SDK Issues -- **Import errors**: Ensure `pip install litellm letta` is run +- **Import errors**: Ensure `uv add litellm letta` is run - **Model format**: Use `provider/model` format (e.g., `openai/gpt-4`) - **API key format**: Different providers have different key formats - **Rate limits**: Implement exponential backoff for retries diff --git a/docs/my-website/docs/langchain/langchain.md b/docs/my-website/docs/langchain/langchain.md index c67375ce1b..b692f1bfd7 100644 --- a/docs/my-website/docs/langchain/langchain.md +++ b/docs/my-website/docs/langchain/langchain.md @@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem'; ## Pre-Requisites ```shell -!pip install litellm langchain +!uv add litellm langchain ``` ## Quick Start diff --git a/docs/my-website/docs/learn/gateway_quickstart.md b/docs/my-website/docs/learn/gateway_quickstart.md index acec259758..eb7a15cfd4 100644 --- a/docs/my-website/docs/learn/gateway_quickstart.md +++ b/docs/my-website/docs/learn/gateway_quickstart.md @@ -13,7 +13,7 @@ If you need a Docker or database-first setup, use the [Docker + Database tutoria ## 1. Install The Gateway ```bash -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' ``` ## 2. Set One Provider Key diff --git a/docs/my-website/docs/learn/sdk_quickstart.md b/docs/my-website/docs/learn/sdk_quickstart.md index 0fb8c3f02a..522a7251e3 100644 --- a/docs/my-website/docs/learn/sdk_quickstart.md +++ b/docs/my-website/docs/learn/sdk_quickstart.md @@ -11,7 +11,7 @@ Use this path if you are integrating LiteLLM directly into application code. ## 1. Install LiteLLM ```bash -pip install litellm==1.82.6 +uv add 'litellm==1.82.6' ``` ## 2. Set Provider Credentials diff --git a/docs/my-website/docs/load_test.md b/docs/my-website/docs/load_test.md index 071b097904..52274024eb 100644 --- a/docs/my-website/docs/load_test.md +++ b/docs/my-website/docs/load_test.md @@ -17,7 +17,7 @@ model_list: api_base: https://exampleopenaiendpoint-production.up.railway.app/ ``` -2. `pip install locust` +2. `uv add locust` 3. Create a file called `locustfile.py` on your local machine. Copy the contents from the litellm load test located [here](https://github.com/BerriAI/litellm/blob/main/.github/workflows/locustfile.py) diff --git a/docs/my-website/docs/load_test_advanced.md b/docs/my-website/docs/load_test_advanced.md index d7bc35e74e..b23f0da35c 100644 --- a/docs/my-website/docs/load_test_advanced.md +++ b/docs/my-website/docs/load_test_advanced.md @@ -70,7 +70,7 @@ litellm_settings: callbacks: ["prometheus"] # Enterprise LiteLLM Only - use prometheus to get metrics on your load test ``` -2. `pip install locust` +2. `uv add locust` 3. Create a file called `locustfile.py` on your local machine. Copy the contents from the litellm load test located [here](https://github.com/BerriAI/litellm/blob/main/.github/workflows/locustfile.py) @@ -138,7 +138,7 @@ litellm_settings: callbacks: ["prometheus"] # Enterprise LiteLLM Only - use prometheus to get metrics on your load test ``` -2. `pip install locust` +2. `uv add locust` 3. Create a file called `locustfile.py` on your local machine. Copy the contents from the litellm load test located [here](https://github.com/BerriAI/litellm/blob/main/.github/workflows/locustfile.py) diff --git a/docs/my-website/docs/mcp_aws_sigv4.md b/docs/my-website/docs/mcp_aws_sigv4.md index e556ad244f..337bc83869 100644 --- a/docs/my-website/docs/mcp_aws_sigv4.md +++ b/docs/my-website/docs/mcp_aws_sigv4.md @@ -224,7 +224,7 @@ SigV4-authenticated MCP servers skip the standard health check on proxy startup. Install the `botocore` package: ```bash -pip install botocore +uv add botocore ``` `botocore` is used for SigV4 credential handling and is required when using `aws_sigv4` auth. diff --git a/docs/my-website/docs/mcp_oauth.md b/docs/my-website/docs/mcp_oauth.md index 5c4b70cc5b..3340533286 100644 --- a/docs/my-website/docs/mcp_oauth.md +++ b/docs/my-website/docs/mcp_oauth.md @@ -205,7 +205,7 @@ sequenceDiagram Use [BerriAI/mock-oauth2-mcp-server](https://github.com/BerriAI/mock-oauth2-mcp-server) to test locally: ```bash title="Terminal 1 - Start mock server" showLineNumbers -pip install fastapi uvicorn +uv add fastapi uvicorn python mock_oauth2_mcp_server.py # starts on :8765 ``` diff --git a/docs/my-website/docs/observability/braintrust.md b/docs/my-website/docs/observability/braintrust.md index 645ce074ca..84f54dc0fd 100644 --- a/docs/my-website/docs/observability/braintrust.md +++ b/docs/my-website/docs/observability/braintrust.md @@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem'; ## Quick Start ```python -# pip install braintrust +# uv add braintrust import litellm import os diff --git a/docs/my-website/docs/observability/lago.md b/docs/my-website/docs/observability/lago.md index 337a2b553e..a7663cb98c 100644 --- a/docs/my-website/docs/observability/lago.md +++ b/docs/my-website/docs/observability/lago.md @@ -22,7 +22,7 @@ litellm.callbacks = ["lago"] # logs cost + usage of successful calls to lago ```python -# pip install lago +# uv add lago import litellm import os diff --git a/docs/my-website/docs/observability/langfuse_integration.md b/docs/my-website/docs/observability/langfuse_integration.md index 32849ebdb9..f696f9be41 100644 --- a/docs/my-website/docs/observability/langfuse_integration.md +++ b/docs/my-website/docs/observability/langfuse_integration.md @@ -26,9 +26,9 @@ For Langfuse v3, we recommend using the [Langfuse OTEL](./langfuse_otel_integrat ## Usage with LiteLLM Python SDK ### Pre-Requisites -Ensure you have run `pip install langfuse` for this integration +Ensure you have run `uv add langfuse` for this integration ```shell -pip install langfuse==2.59.7 litellm +uv add langfuse==2.59.7 litellm ``` ### Quick Start @@ -44,7 +44,7 @@ litellm.success_callback = ["langfuse"] litellm.failure_callback = ["langfuse"] # logs errors to langfuse ``` ```python -# pip install langfuse +# uv add langfuse import litellm import os @@ -335,7 +335,7 @@ Be aware that if you are continuing an existing trace, and you set `update_trace ## Troubleshooting & Errors ### Data not getting logged to Langfuse ? -- Ensure you're on the latest version of langfuse `pip install langfuse -U`. The latest version allows litellm to log JSON input/outputs to langfuse +- Ensure you're on the latest version of langfuse `uv add langfuse -U`. The latest version allows litellm to log JSON input/outputs to langfuse - Follow [this checklist](https://langfuse.com/faq/all/missing-traces) if you don't see any traces in langfuse. ## Support & Talk to Founders diff --git a/docs/my-website/docs/observability/langfuse_otel_integration.md b/docs/my-website/docs/observability/langfuse_otel_integration.md index 79ad2f6f75..90f7f7becc 100644 --- a/docs/my-website/docs/observability/langfuse_otel_integration.md +++ b/docs/my-website/docs/observability/langfuse_otel_integration.md @@ -24,7 +24,7 @@ The Langfuse OpenTelemetry integration allows you to send LiteLLM traces and obs 2. **API Keys**: Get your public and secret keys from your Langfuse project settings 3. **Dependencies**: Install required packages: ```bash - pip install litellm opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp + uv add litellm opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp ``` ## Configuration diff --git a/docs/my-website/docs/observability/langsmith_integration.md b/docs/my-website/docs/observability/langsmith_integration.md index bf867319de..5eb36cd814 100644 --- a/docs/my-website/docs/observability/langsmith_integration.md +++ b/docs/my-website/docs/observability/langsmith_integration.md @@ -18,7 +18,7 @@ join our [discord](https://discord.gg/wuPM9dRgDw) ## Pre-Requisites ```shell -pip install litellm +uv add litellm ``` ## Quick Start diff --git a/docs/my-website/docs/observability/levo_integration.md b/docs/my-website/docs/observability/levo_integration.md index 3e46cf6b92..c11e720aeb 100644 --- a/docs/my-website/docs/observability/levo_integration.md +++ b/docs/my-website/docs/observability/levo_integration.md @@ -36,7 +36,7 @@ Send all your LLM requests and responses to Levo for monitoring and analysis usi **1. Install OpenTelemetry dependencies:** ```bash -pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc +uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc ``` **2. Enable Levo callback in your LiteLLM config:** @@ -133,7 +133,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ``` 4. **Check for initialization errors**: Look for errors in LiteLLM startup logs. Common issues: - - Missing OpenTelemetry packages: Install with `pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc` + - Missing OpenTelemetry packages: Install with `uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc` - Missing required environment variables: All four required variables must be set - Invalid collector URL: Ensure the URL is correct and reachable @@ -150,7 +150,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ - Solution: Set the `LEVOAI_COLLECTOR_URL` environment variable with your collector endpoint URL from Levo support. **Error: "No module named 'opentelemetry'"** -- Solution: Install OpenTelemetry packages: `pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc` +- Solution: Install OpenTelemetry packages: `uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc` ## Additional Resources diff --git a/docs/my-website/docs/observability/literalai_integration.md b/docs/my-website/docs/observability/literalai_integration.md index 128c86b2cc..88ae730921 100644 --- a/docs/my-website/docs/observability/literalai_integration.md +++ b/docs/my-website/docs/observability/literalai_integration.md @@ -11,7 +11,7 @@ import Image from '@theme/IdealImage'; Ensure you have the `literalai` package installed: ```shell -pip install literalai litellm +uv add literalai litellm ``` ## Quick Start diff --git a/docs/my-website/docs/observability/logfire_integration.md b/docs/my-website/docs/observability/logfire_integration.md index 00652c0f1a..bf6b03e205 100644 --- a/docs/my-website/docs/observability/logfire_integration.md +++ b/docs/my-website/docs/observability/logfire_integration.md @@ -17,11 +17,11 @@ join our [discord](https://discord.gg/wuPM9dRgDw) Ensure you have installed the following packages to use this integration ```shell -pip install litellm +uv add litellm -pip install opentelemetry-api==1.25.0 -pip install opentelemetry-sdk==1.25.0 -pip install opentelemetry-exporter-otlp==1.25.0 +uv add opentelemetry-api==1.25.0 +uv add opentelemetry-sdk==1.25.0 +uv add opentelemetry-exporter-otlp==1.25.0 ``` ## Quick Start @@ -33,7 +33,7 @@ litellm.callbacks = ["logfire"] ``` ```python -# pip install logfire +# uv add logfire import litellm import os diff --git a/docs/my-website/docs/observability/lunary_integration.md b/docs/my-website/docs/observability/lunary_integration.md index 4f1dca9d4a..fee07091cb 100644 --- a/docs/my-website/docs/observability/lunary_integration.md +++ b/docs/my-website/docs/observability/lunary_integration.md @@ -15,7 +15,7 @@ You can reach out to us anytime by [email](mailto:hello@lunary.ai) or directly [ ### Pre-Requisites ```shell -pip install litellm lunary +uv add litellm lunary ``` ### Quick Start @@ -124,7 +124,7 @@ my_chain("Chain input") ### Step1: Install dependencies and set your environment variables Install the dependencies ```shell -pip install litellm lunary +uv add litellm lunary ``` Get you Lunary public key from from https://app.lunary.ai/settings diff --git a/docs/my-website/docs/observability/mlflow.md b/docs/my-website/docs/observability/mlflow.md index 5fa46bdfda..4018c97048 100644 --- a/docs/my-website/docs/observability/mlflow.md +++ b/docs/my-website/docs/observability/mlflow.md @@ -17,7 +17,7 @@ MLflow’s integration with LiteLLM supports advanced observability compatible w Install MLflow: ```shell -pip install "litellm[mlflow]" +uv add "litellm[mlflow]" ``` To enable MLflow auto tracing for LiteLLM: @@ -167,7 +167,7 @@ This approach generates a unified trace, combining your custom Python code with For using `mlflow` on LiteLLM Proxy Server, you need to install the `mlflow` package on your docker container. ```shell -pip install "mlflow>=3.1.4" +uv add "mlflow>=3.1.4" ``` ### Configuration diff --git a/docs/my-website/docs/observability/openmeter.md b/docs/my-website/docs/observability/openmeter.md index 2f53568757..b3e07ef8ff 100644 --- a/docs/my-website/docs/observability/openmeter.md +++ b/docs/my-website/docs/observability/openmeter.md @@ -28,7 +28,7 @@ litellm.callbacks = ["openmeter"] # logs cost + usage of successful calls to ope ```python -# pip install openmeter +# uv add openmeter import litellm import os diff --git a/docs/my-website/docs/observability/opentelemetry_integration.md b/docs/my-website/docs/observability/opentelemetry_integration.md index 80ef1bcc98..f8fcebf7ab 100644 --- a/docs/my-website/docs/observability/opentelemetry_integration.md +++ b/docs/my-website/docs/observability/opentelemetry_integration.md @@ -27,7 +27,7 @@ USE_OTEL_LITELLM_REQUEST_SPAN=true Install the OpenTelemetry SDK: ``` -pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp +uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp ``` Set the environment variables (different providers may require different variables): @@ -63,7 +63,7 @@ OTEL_EXPORTER_OTLP_PROTOCOL=grpc OTEL_EXPORTER_OTLP_HEADERS="api-key=key,other-config-value=value" ``` -> Note: OTLP gRPC requires `grpcio`. Install via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: OTLP gRPC requires `grpcio`. Install via `uv add "litellm[grpc]"` (or `grpcio`). @@ -75,7 +75,7 @@ OTEL_ENDPOINT="https://api.lmnr.ai:8443" OTEL_HEADERS="authorization=Bearer " ``` -> Note: OTLP gRPC requires `grpcio`. Install via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: OTLP gRPC requires `grpcio`. Install via `uv add "litellm[grpc]"` (or `grpcio`). diff --git a/docs/my-website/docs/observability/phoenix_integration.md b/docs/my-website/docs/observability/phoenix_integration.md index 67ba815557..998e0fca6c 100644 --- a/docs/my-website/docs/observability/phoenix_integration.md +++ b/docs/my-website/docs/observability/phoenix_integration.md @@ -22,7 +22,7 @@ Use just 2 lines of code, to instantly log your responses **across all providers You can also use the instrumentor option instead of the callback, which you can find [here](https://docs.arize.com/phoenix/tracing/integrations-tracing/litellm). ```bash -pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp litellm[proxy] +uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp litellm[proxy] ``` ```python litellm.callbacks = ["arize_phoenix"] @@ -73,7 +73,7 @@ environment_variables: PHOENIX_COLLECTOR_HTTP_ENDPOINT: "https://app.phoenix.arize.com/s//v1/traces" # OPTIONAL - For setting the HTTP endpoint ``` -> Note: If you set the gRPC endpoint, install `grpcio` via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: If you set the gRPC endpoint, install `grpcio` via `uv add "litellm[grpc]"` (or `grpcio`). 2. Start the proxy diff --git a/docs/my-website/docs/observability/qualifire_integration.md b/docs/my-website/docs/observability/qualifire_integration.md index cf866f467b..cf376136e1 100644 --- a/docs/my-website/docs/observability/qualifire_integration.md +++ b/docs/my-website/docs/observability/qualifire_integration.md @@ -23,7 +23,7 @@ Looking for Qualifire Guardrails? Check out the [Qualifire Guardrails Integratio 2. Get your API key and webhook URL from the Qualifire dashboard ```bash -pip install litellm +uv add litellm ``` ## Quick Start diff --git a/docs/my-website/docs/observability/raw_request_response.md b/docs/my-website/docs/observability/raw_request_response.md index 71305dae69..011a3a74af 100644 --- a/docs/my-website/docs/observability/raw_request_response.md +++ b/docs/my-website/docs/observability/raw_request_response.md @@ -12,7 +12,7 @@ See the raw request/response sent by LiteLLM in your logging provider (OTEL/Lang ```python -# pip install langfuse +# uv add langfuse import litellm import os diff --git a/docs/my-website/docs/observability/scrub_data.md b/docs/my-website/docs/observability/scrub_data.md index f8bb4d556c..4e13d1b5a1 100644 --- a/docs/my-website/docs/observability/scrub_data.md +++ b/docs/my-website/docs/observability/scrub_data.md @@ -60,7 +60,7 @@ litellm.callbacks = [customHandler] 3. Test it! ```python -# pip install langfuse +# uv add langfuse import os import litellm diff --git a/docs/my-website/docs/observability/signoz.md b/docs/my-website/docs/observability/signoz.md index f306b143ef..7af0c29406 100644 --- a/docs/my-website/docs/observability/signoz.md +++ b/docs/my-website/docs/observability/signoz.md @@ -17,7 +17,7 @@ Instrumenting LiteLLM in your AI applications with telemetry ensures full observ - A [SigNoz Cloud account](https://signoz.io/teams/) with an active ingestion key - Internet access to send telemetry data to SigNoz Cloud - [LiteLLM](https://www.litellm.ai/) SDK or Proxy integration -- For Python: `pip` installed for managing Python packages and _(optional but recommended)_ a Python virtual environment to isolate dependencies +- For Python: `uv` installed for managing Python packages and _(optional but recommended)_ a Python virtual environment to isolate dependencies ## Monitoring LiteLLM @@ -37,7 +37,7 @@ No-code auto-instrumentation is recommended for quick setup with minimal code ch **Step 1:** Install the necessary packages in your Python environment. ```bash -pip install \ +uv add \ opentelemetry-api \ opentelemetry-distro \ opentelemetry-exporter-otlp \ @@ -99,7 +99,7 @@ OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai \ opentelemetry-instrument ``` -> Note: OTLP gRPC requires `grpcio`. Install via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: OTLP gRPC requires `grpcio`. Install via `uv add "litellm[grpc]"` (or `grpcio`). > 📌 Note: We're using `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai` in the run command to disable the OpenAI instrumentor for tracing. This avoids conflicts with LiteLLM's native telemetry/instrumentation, ensuring that telemetry is captured exclusively through LiteLLM's built-in instrumentation. @@ -120,7 +120,7 @@ Code-based instrumentation gives you fine-grained control over your telemetry co **Step 1:** Install the necessary packages in your Python environment. ```bash -pip install \ +uv add \ opentelemetry-api \ opentelemetry-sdk \ opentelemetry-exporter-otlp \ @@ -338,7 +338,7 @@ You can also check out our custom LiteLLM SDK dashboard [here](https://signoz.i **Step 1:** Install the necessary packages in your Python environment. ```bash -pip install opentelemetry-api \ +uv add opentelemetry-api \ opentelemetry-sdk \ opentelemetry-exporter-otlp \ 'litellm[proxy]' @@ -364,7 +364,7 @@ export OTEL_METRICS_EXPORTER="otlp" export OTEL_LOGS_EXPORTER="otlp" ``` -> Note: OTLP gRPC requires `grpcio`. Install via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: OTLP gRPC requires `grpcio`. Install via `uv add "litellm[grpc]"` (or `grpcio`). - Set the `` to match your SigNoz Cloud [region](https://signoz.io/docs/ingestion/signoz-cloud/overview/#endpoint) - Replace `` with your SigNoz [ingestion key](https://signoz.io/docs/ingestion/signoz-cloud/keys/) diff --git a/docs/my-website/docs/observability/slack_integration.md b/docs/my-website/docs/observability/slack_integration.md index 468d8b5945..2b7737a0cf 100644 --- a/docs/my-website/docs/observability/slack_integration.md +++ b/docs/my-website/docs/observability/slack_integration.md @@ -13,7 +13,7 @@ join our [discord](https://discord.gg/wuPM9dRgDw) ### Step 1 ```shell -pip install litellm +uv add litellm ``` ### Step 2 diff --git a/docs/my-website/docs/observability/sumologic_integration.md b/docs/my-website/docs/observability/sumologic_integration.md index 87e20ca57e..d7f057df52 100644 --- a/docs/my-website/docs/observability/sumologic_integration.md +++ b/docs/my-website/docs/observability/sumologic_integration.md @@ -25,7 +25,7 @@ join our [discord](https://discord.gg/wuPM9dRgDw) For more details, see the [HTTP Logs & Metrics Source](https://www.sumologic.com/help/docs/send-data/hosted-collectors/http-source/logs-metrics/) documentation. ```shell -pip install litellm +uv add litellm ``` ## Quick Start diff --git a/docs/my-website/docs/observability/wandb_integration.md b/docs/my-website/docs/observability/wandb_integration.md index 3c1a336395..1126998c99 100644 --- a/docs/my-website/docs/observability/wandb_integration.md +++ b/docs/my-website/docs/observability/wandb_integration.md @@ -21,9 +21,9 @@ join our [discord](https://discord.gg/wuPM9dRgDw) ::: ## Pre-Requisites -Ensure you have run `pip install wandb` for this integration +Ensure you have run `uv add wandb` for this integration ```shell -pip install wandb litellm +uv add wandb litellm ``` ## Quick Start @@ -33,7 +33,7 @@ Use just 2 lines of code, to instantly log your responses **across all providers litellm.success_callback = ["wandb"] ``` ```python -# pip install wandb +# uv add wandb import litellm import os diff --git a/docs/my-website/docs/pass_through/bedrock.md b/docs/my-website/docs/pass_through/bedrock.md index 65c5d8caad..19345c031f 100644 --- a/docs/my-website/docs/pass_through/bedrock.md +++ b/docs/my-website/docs/pass_through/bedrock.md @@ -566,7 +566,7 @@ You can use the [LangChain AWS SDK](https://python.langchain.com/docs/integratio **1. Install LangChain AWS**: ```bash showLineNumbers -pip install langchain-aws +uv add langchain-aws ``` **2. Setup LiteLLM Proxy**: diff --git a/docs/my-website/docs/projects/Harbor.md b/docs/my-website/docs/projects/Harbor.md index 684dfa9372..ee9d355dcb 100644 --- a/docs/my-website/docs/projects/Harbor.md +++ b/docs/my-website/docs/projects/Harbor.md @@ -5,7 +5,7 @@ ```bash # Install -pip install harbor +uv add harbor # Run a benchmark with any LiteLLM-supported model harbor run --dataset terminal-bench@2.0 \ diff --git a/docs/my-website/docs/projects/openai-agents.md b/docs/my-website/docs/projects/openai-agents.md index 86983e7e51..7d7ff0c0b0 100644 --- a/docs/my-website/docs/projects/openai-agents.md +++ b/docs/my-website/docs/projects/openai-agents.md @@ -12,7 +12,7 @@ The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) is a lig ### 1. Install Dependencies ```bash -pip install "openai-agents[litellm]" +uv add "openai-agents[litellm]" ``` ### 2. Add Model to Config diff --git a/docs/my-website/docs/providers/azure/azure.md b/docs/my-website/docs/providers/azure/azure.md index 682f263c10..de6ab6a07e 100644 --- a/docs/my-website/docs/providers/azure/azure.md +++ b/docs/my-website/docs/providers/azure/azure.md @@ -1143,7 +1143,7 @@ In production, [Router connects to a Redis Cache](#redis-queue) to track usage a #### Quick Start ```python -pip install litellm +uv add litellm ``` ```python diff --git a/docs/my-website/docs/providers/azure_ai.md b/docs/my-website/docs/providers/azure_ai.md index 68e2df676e..c39967dba3 100644 --- a/docs/my-website/docs/providers/azure_ai.md +++ b/docs/my-website/docs/providers/azure_ai.md @@ -121,7 +121,7 @@ response = completion( See all litellm.completion supported params [here](../completion/input.md#translated-openai-params) ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables diff --git a/docs/my-website/docs/providers/bedrock.md b/docs/my-website/docs/providers/bedrock.md index e5942cc111..750b91f8ca 100644 --- a/docs/my-website/docs/providers/bedrock.md +++ b/docs/my-website/docs/providers/bedrock.md @@ -16,7 +16,7 @@ ALL Bedrock models (Anthropic, Meta, Deepseek, Mistral, Amazon, etc.) are Suppor LiteLLM requires `boto3` to be installed on your system for Bedrock requests ```shell -pip install boto3>=1.28.57 +uv add boto3>=1.28.57 ``` :::info diff --git a/docs/my-website/docs/providers/bedrock_realtime_with_audio.md b/docs/my-website/docs/providers/bedrock_realtime_with_audio.md index a2d9813ffd..d725f6ecd1 100644 --- a/docs/my-website/docs/providers/bedrock_realtime_with_audio.md +++ b/docs/my-website/docs/providers/bedrock_realtime_with_audio.md @@ -319,7 +319,7 @@ Complete working examples are available in the LiteLLM repository: ## Requirements ```bash -pip install litellm websockets pyaudio +uv add litellm websockets pyaudio ``` ## AWS Configuration diff --git a/docs/my-website/docs/providers/bytez.md b/docs/my-website/docs/providers/bytez.md index fc7a684ee8..3e2222fe68 100644 --- a/docs/my-website/docs/providers/bytez.md +++ b/docs/my-website/docs/providers/bytez.md @@ -126,7 +126,7 @@ If you wish to use custom formatting, please let us know via either [help@bytez. See all litellm.completion supported params [here](https://docs.litellm.ai/docs/completion/input) ```py -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables @@ -160,7 +160,7 @@ Any kwarg supported by huggingface we also support! (Provided the model supports Example `repetition_penalty` ```py -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables diff --git a/docs/my-website/docs/providers/clarifai.md b/docs/my-website/docs/providers/clarifai.md index eb46901db2..d1f592fe39 100644 --- a/docs/my-website/docs/providers/clarifai.md +++ b/docs/my-website/docs/providers/clarifai.md @@ -14,7 +14,7 @@ Anthropic, OpenAI, Qwen, xAI, Gemini and most of Open soured LLMs are Supported ## Pre-Requisites ```bash -pip install litellm +uv add litellm ``` ## Required Environment Variables diff --git a/docs/my-website/docs/providers/databricks.md b/docs/my-website/docs/providers/databricks.md index 2791d55dff..aaccb93073 100644 --- a/docs/my-website/docs/providers/databricks.md +++ b/docs/my-website/docs/providers/databricks.md @@ -59,7 +59,7 @@ If no credentials are provided, LiteLLM will use the Databricks SDK for automati from litellm import completion # No environment variables needed - uses Databricks SDK unified auth -# Requires: pip install databricks-sdk +# Requires: uv add databricks-sdk response = completion( model="databricks/databricks-dbrx-instruct", messages=[{"role": "user", "content": "Hello!"}], @@ -220,7 +220,7 @@ response = completion( See all litellm.completion supported params [here](../completion/input.md#translated-openai-params) ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables @@ -457,7 +457,7 @@ For embedding models, databricks lets you pass in an additional param 'instructi ```python -# !pip install litellm +# !uv add litellm from litellm import embedding import os ## set ENV variables diff --git a/docs/my-website/docs/providers/huggingface.md b/docs/my-website/docs/providers/huggingface.md index 985351e9f6..46ea93bbe0 100644 --- a/docs/my-website/docs/providers/huggingface.md +++ b/docs/my-website/docs/providers/huggingface.md @@ -341,7 +341,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ```python -# pip install openai +# uv add openai from openai import OpenAI client = OpenAI( diff --git a/docs/my-website/docs/providers/langgraph.md b/docs/my-website/docs/providers/langgraph.md index 9b4b24cf8f..eea8459c72 100644 --- a/docs/my-website/docs/providers/langgraph.md +++ b/docs/my-website/docs/providers/langgraph.md @@ -187,7 +187,7 @@ Before using LiteLLM with LangGraph, you need a running LangGraph server. ### 1. Install the LangGraph CLI ```bash -pip install "langgraph-cli[inmem]" +uv add "langgraph-cli[inmem]" ``` ### 2. Create a new LangGraph project @@ -200,7 +200,7 @@ cd my-agent ### 3. Install dependencies ```bash -pip install -e . +uv add -e . ``` ### 4. Set your API key diff --git a/docs/my-website/docs/providers/oci.md b/docs/my-website/docs/providers/oci.md index 1d7a0a3d50..182bb4407a 100644 --- a/docs/my-website/docs/providers/oci.md +++ b/docs/my-website/docs/providers/oci.md @@ -80,7 +80,7 @@ Use an OCI SDK `Signer` object for authentication. This method: To use this method, install the OCI SDK: ```bash -pip install oci +uv add oci ``` This method is an alternative when using the LiteLLM SDK on Oracle Cloud Infrastructure (instances or Oracle Kubernetes Engine). diff --git a/docs/my-website/docs/providers/ollama.md b/docs/my-website/docs/providers/ollama.md index d59d9dd0ce..bf32993c1d 100644 --- a/docs/my-website/docs/providers/ollama.md +++ b/docs/my-website/docs/providers/ollama.md @@ -49,7 +49,7 @@ for chunk in response: ## Example usage - Streaming + Acompletion Ensure you have async_generator installed for using ollama acompletion with streaming ```shell -pip install async_generator +uv add async_generator ``` ```python diff --git a/docs/my-website/docs/providers/petals.md b/docs/my-website/docs/providers/petals.md index b5dd1705b4..c64b097c7e 100644 --- a/docs/my-website/docs/providers/petals.md +++ b/docs/my-website/docs/providers/petals.md @@ -8,7 +8,7 @@ Petals: https://github.com/bigscience-workshop/petals ## Pre-Requisites Ensure you have `petals` installed ```shell -pip install git+https://github.com/bigscience-workshop/petals +uv add git+https://github.com/bigscience-workshop/petals ``` ## Usage diff --git a/docs/my-website/docs/providers/predibase.md b/docs/my-website/docs/providers/predibase.md index 9f25309c19..978db3d14d 100644 --- a/docs/my-website/docs/providers/predibase.md +++ b/docs/my-website/docs/providers/predibase.md @@ -186,7 +186,7 @@ model_list: See all litellm.completion supported params [here](https://docs.litellm.ai/docs/completion/input) ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables @@ -219,7 +219,7 @@ Send params [not supported by `litellm.completion()`](https://docs.litellm.ai/do Example `adapter_id`, `adapter_source` are Predibase specific param - [See List](https://github.com/BerriAI/litellm/blob/8a35354dd6dbf4c2fcefcd6e877b980fcbd68c58/litellm/llms/predibase.py#L54) ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables diff --git a/docs/my-website/docs/providers/pydantic_ai_agent.md b/docs/my-website/docs/providers/pydantic_ai_agent.md index e96295faaf..4e24e6d4e4 100644 --- a/docs/my-website/docs/providers/pydantic_ai_agent.md +++ b/docs/my-website/docs/providers/pydantic_ai_agent.md @@ -23,7 +23,7 @@ LiteLLM requires Pydantic AI agents to follow the [A2A (Agent-to-Agent) protocol #### Install Dependencies ```bash -pip install pydantic-ai fasta2a uvicorn +uv add pydantic-ai fasta2a uvicorn ``` #### Create Agent diff --git a/docs/my-website/docs/providers/replicate.md b/docs/my-website/docs/providers/replicate.md index 8e71d3ac99..db24d21827 100644 --- a/docs/my-website/docs/providers/replicate.md +++ b/docs/my-website/docs/providers/replicate.md @@ -231,7 +231,7 @@ Model Name | Function Call See all litellm.completion supported params [here](https://docs.litellm.ai/docs/completion/input) ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables @@ -264,7 +264,7 @@ Send params [not supported by `litellm.completion()`](https://docs.litellm.ai/do Example `seed`, `min_tokens` are Replicate specific param ```python -# !pip install litellm +# !uv add litellm from litellm import completion import os ## set ENV variables diff --git a/docs/my-website/docs/providers/sap.md b/docs/my-website/docs/providers/sap.md index 3877cb6ef1..5d11dba5c0 100644 --- a/docs/my-website/docs/providers/sap.md +++ b/docs/my-website/docs/providers/sap.md @@ -51,7 +51,7 @@ The resource group is typically configured separately in your AI Core deployment ### Step 1: Install LiteLLM ```bash -pip install litellm +uv add litellm ``` ### Step 2: Set Your Credentials diff --git a/docs/my-website/docs/providers/vertex.md b/docs/my-website/docs/providers/vertex.md index a3eb673f03..0079bd2f57 100644 --- a/docs/my-website/docs/providers/vertex.md +++ b/docs/my-website/docs/providers/vertex.md @@ -1216,7 +1216,7 @@ curl http://0.0.0.0:4000/chat/completions \ ## Pre-requisites -* `pip install google-cloud-aiplatform` (pre-installed on proxy docker image) +* `uv add google-cloud-aiplatform` (pre-installed on proxy docker image) * Authentication: * run `gcloud auth application-default login` See [Google Cloud Docs](https://cloud.google.com/docs/authentication/external/set-up-adc) * Alternatively you can set `GOOGLE_APPLICATION_CREDENTIALS` diff --git a/docs/my-website/docs/providers/vllm.md b/docs/my-website/docs/providers/vllm.md index 1a37f2f10e..6fc3a9f328 100644 --- a/docs/my-website/docs/providers/vllm.md +++ b/docs/my-website/docs/providers/vllm.md @@ -517,11 +517,11 @@ curl -X POST http://0.0.0.0:4000/chat/completions \ -## (Deprecated) for `vllm pip package` +## (Deprecated) for packaged `vllm` installs ### Using - `litellm.completion` ``` -pip install litellm vllm +uv add litellm vllm ``` ```python import litellm @@ -616,4 +616,3 @@ test_vllm_custom_model() ``` [Implementation Code](https://github.com/BerriAI/litellm/blob/6b3cb1898382f2e4e80fd372308ea232868c78d1/litellm/utils.py#L1414) - diff --git a/docs/my-website/docs/proxy/caching.md b/docs/my-website/docs/proxy/caching.md index 3357dcb28b..39a9cfefc7 100644 --- a/docs/my-website/docs/proxy/caching.md +++ b/docs/my-website/docs/proxy/caching.md @@ -214,7 +214,7 @@ For GCP Memorystore Redis with IAM authentication, install the required dependen ::: ```shell -pip install google-cloud-iam +uv add google-cloud-iam ``` diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index f7833fc02c..c04c3e2cc1 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -32,10 +32,10 @@ docker pull docker.litellm.ai/berriai/litellm:main-latest - + ```shell -$ pip install 'litellm[proxy]' +$ uv tool install 'litellm[proxy]' ``` @@ -191,33 +191,32 @@ EXPOSE 4000/tcp CMD ["--port", "4000", "--config", "config.yaml", "--detailed_debug"] ``` -### Build from litellm `pip` package +### Build from published LiteLLM packages -Follow these instructions to build a docker container from the litellm pip package. If your company has a strict requirement around security / building images you can follow these steps. +Follow these instructions to build a Docker container from published LiteLLM packages. If your company has a strict requirement around security or image provenance, you can follow these steps. -**Note:** You'll need to copy the `schema.prisma` file from the [litellm repository](https://github.com/BerriAI/litellm/blob/main/schema.prisma) to your build directory alongside the Dockerfile and requirements.txt. +**Note:** Copy the `schema.prisma` file from the [LiteLLM repository](https://github.com/BerriAI/litellm/blob/main/schema.prisma) into your build directory alongside this Dockerfile. Dockerfile ```shell FROM cgr.dev/chainguard/python:latest-dev +ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.10.9 USER root WORKDIR /app -ENV HOME=/home/litellm -ENV PATH="${HOME}/venv/bin:$PATH" +ENV UV_TOOL_BIN_DIR=/usr/local/bin # Install runtime dependencies RUN apk update && \ apk add --no-cache gcc python3-dev openssl openssl-dev -RUN python -m venv ${HOME}/venv -RUN ${HOME}/venv/bin/pip install --no-cache-dir --upgrade pip +COPY --from=$UV_IMAGE /uv /usr/local/bin/uv +COPY --from=$UV_IMAGE /uvx /usr/local/bin/uvx -COPY requirements.txt . -RUN --mount=type=cache,target=${HOME}/.cache/pip \ - ${HOME}/venv/bin/pip install -r requirements.txt +RUN uv tool install 'litellm[proxy,proxy-runtime,extra_proxy]==1.57.3' \ + --python python # Copy Prisma schema file COPY schema.prisma . @@ -232,22 +231,12 @@ CMD ["--port", "4000"] ``` -Example `requirements.txt` - -```shell -litellm[proxy]==1.57.3 # Specify the litellm version you want to use -litellm-enterprise -prometheus_client -langfuse -prisma -``` - Build the docker image ```shell docker build \ - -f Dockerfile.build_from_pip \ - -t litellm-proxy-with-pip-5 . + -f Dockerfile \ + -t litellm-proxy-from-package-5 . ``` Run the docker image @@ -258,7 +247,7 @@ docker run \ -e OPENAI_API_KEY="sk-1222" \ -e DATABASE_URL="postgresql://xxxxxxxxx \ -p 4000:4000 \ - litellm-proxy-with-pip-5 \ + litellm-proxy-from-package-5 \ --config /app/config.yaml --detailed_debug ``` @@ -760,7 +749,7 @@ RUN chmod +x ./docker/entrypoint.sh EXPOSE 4000/tcp # 👉 Key Change: Install hypercorn -RUN pip install hypercorn +RUN uv add hypercorn # Override the CMD instruction with your desired command and arguments # WARNING: FOR PROD DO NOT USE `--detailed_debug` it slows down response times, instead use the following CMD diff --git a/docs/my-website/docs/proxy/docker_quick_start.md b/docs/my-website/docs/proxy/docker_quick_start.md index 58a5660475..391793773f 100644 --- a/docs/my-website/docs/proxy/docker_quick_start.md +++ b/docs/my-website/docs/proxy/docker_quick_start.md @@ -70,15 +70,15 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \ }' ``` -:::tip Already have pip installed? -You can skip the curl install and run `litellm --setup` directly after `pip install 'litellm[proxy]'`. +:::tip Already have uv installed? +You can skip the curl install and run `litellm --setup` directly after `uv tool install 'litellm[proxy]'`. ::: --- ## Pre-Requisites -Choose your install method. **Docker Compose** users complete their full setup inside the tab and are done. **Docker** and **pip** users continue with the steps below the tabs. +Choose your install method. **Docker Compose** users complete their full setup inside the tab and are done. **Docker** and **LiteLLM CLI** users continue with the steps below the tabs. @@ -92,10 +92,10 @@ docker pull docker.litellm.ai/berriai/litellm:main-latest - + ```shell -$ pip install 'litellm[proxy]' +$ uv tool install 'litellm[proxy]' ``` @@ -269,7 +269,7 @@ Virtual keys let you track spend, set rate limits, and control model access per :::note Docker Compose users -Your setup is complete — the steps below are for **Docker** and **pip** users only. +Your setup is complete — the steps below are for **Docker** and **LiteLLM CLI** users only. ::: --- @@ -336,7 +336,7 @@ docker run \ - + ```shell $ litellm --config /app/config.yaml --detailed_debug @@ -463,7 +463,7 @@ Track spend and control model access via virtual keys for the proxy. Your Postgres container is already running — skip ahead to [Create Key w/ RPM Limit](#create-key-w-rpm-limit) below. ::: -**Docker / pip users** — you need a Postgres database (e.g. [Supabase](https://supabase.com/), [Neon](https://neon.tech/), or self-hosted). Add `general_settings` to your `config.yaml`: +**Docker / LiteLLM CLI users** — you need a Postgres database (e.g. [Supabase](https://supabase.com/), [Neon](https://neon.tech/), or self-hosted). Add `general_settings` to your `config.yaml`: ```yaml model_list: diff --git a/docs/my-website/docs/proxy/guardrails/lasso_security.md b/docs/my-website/docs/proxy/guardrails/lasso_security.md index 363be894e4..c1d7ea4895 100644 --- a/docs/my-website/docs/proxy/guardrails/lasso_security.md +++ b/docs/my-website/docs/proxy/guardrails/lasso_security.md @@ -11,7 +11,7 @@ Use [Lasso Security](https://www.lasso.security/) to protect your LLM applicatio The Lasso guardrail requires the `ulid-py` package (version 1.1.0 or higher) for generating unique conversation identifiers: ```shell -pip install ulid-py>=1.1.0 +uv add ulid-py>=1.1.0 ``` This package is used to create lexicographically sortable identifiers for tracking conversations and sessions in the Lasso Security platform. diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 2f81498799..166269af47 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -351,7 +351,7 @@ We will use the `--config` to set `litellm.success_callback = ["langfuse"]` this **Step 1** Install langfuse ```shell -pip install langfuse>=2.0.0 +uv add langfuse>=2.0.0 ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` @@ -982,7 +982,7 @@ OTEL_ENDPOINT="http:/0.0.0.0:4317" OTEL_HEADERS="x-honeycomb-team=" # Optional ``` -> Note: OTLP gRPC requires `grpcio`. Install via `pip install "litellm[grpc]"` (or `grpcio`). +> Note: OTLP gRPC requires `grpcio`. Install via `uv add "litellm[grpc]"` (or `grpcio`). Add `otel` as a callback on your `litellm_config.yaml` @@ -1587,7 +1587,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ #### Step1: Install dependencies and set your environment variables Install the dependencies ```shell -pip install litellm lunary +uv add litellm lunary ``` Get you Lunary public key from from https://app.lunary.ai/settings @@ -2516,7 +2516,7 @@ If api calls fail (llm/database) you can log those to Sentry: **Step 1** Install Sentry ```shell -pip install --upgrade sentry-sdk +uv add --upgrade sentry-sdk ``` **Step 2**: Save your Sentry_DSN and add `litellm_settings`: `failure_callback` diff --git a/docs/my-website/docs/proxy/prometheus.md b/docs/my-website/docs/proxy/prometheus.md index d8f0d83b59..3345957247 100644 --- a/docs/my-website/docs/proxy/prometheus.md +++ b/docs/my-website/docs/proxy/prometheus.md @@ -9,7 +9,7 @@ LiteLLM Exposes a `/metrics` endpoint for Prometheus to Poll ## Quick Start -If you're using the LiteLLM CLI with `litellm --config proxy_config.yaml` then you need to `pip install prometheus_client==0.20.0`. **This is already pre-installed on the litellm Docker image** +If you're using the LiteLLM CLI with `litellm --config proxy_config.yaml` then you need to `uv add prometheus_client==0.20.0`. **This is already pre-installed on the litellm Docker image** Add this to your proxy config.yaml ```yaml diff --git a/docs/my-website/docs/proxy/pyroscope_profiling.md b/docs/my-website/docs/proxy/pyroscope_profiling.md index fa3db3a878..19d12ba24e 100644 --- a/docs/my-website/docs/proxy/pyroscope_profiling.md +++ b/docs/my-website/docs/proxy/pyroscope_profiling.md @@ -7,13 +7,13 @@ LiteLLM proxy can send continuous CPU profiles to [Grafana Pyroscope](https://gr 1. **Install the optional dependency** (required only when enabling Pyroscope): ```bash - pip install pyroscope-io + uv add pyroscope-io ``` Or install the proxy extra: ```bash - pip install "litellm[proxy]" + uv add "litellm[proxy]" ``` 2. **Set environment variables** before starting the proxy: diff --git a/docs/my-website/docs/proxy/quick_start.md b/docs/my-website/docs/proxy/quick_start.md index cf1ab78b35..dbc018e129 100644 --- a/docs/my-website/docs/proxy/quick_start.md +++ b/docs/my-website/docs/proxy/quick_start.md @@ -13,7 +13,7 @@ LiteLLM Server (LLM Gateway) manages: * **Load Balancing**: between [Multiple Models](#multiple-models---quick-start) + [Deployments of the same model](#multiple-instances-of-1-model) - LiteLLM proxy can handle 1.5k+ requests/second during load tests. ```shell -$ pip install 'litellm[proxy]' +$ uv tool install 'litellm[proxy]' ``` ## Quick Start - LiteLLM Proxy CLI diff --git a/docs/my-website/docs/proxy/user_keys.md b/docs/my-website/docs/proxy/user_keys.md index 72ec8ccd75..7bce152321 100644 --- a/docs/my-website/docs/proxy/user_keys.md +++ b/docs/my-website/docs/proxy/user_keys.md @@ -881,7 +881,7 @@ Credits [@vividfog](https://github.com/ollama/ollama/issues/305#issuecomment-175 ```shell -$ pip install aider +$ uv add aider $ aider --openai-api-base http://0.0.0.0:4000 --openai-api-key fake-key ``` @@ -889,7 +889,7 @@ $ aider --openai-api-base http://0.0.0.0:4000 --openai-api-key fake-key ```python -pip install pyautogen +uv add pyautogen ``` ```python diff --git a/docs/my-website/docs/proxy_api.md b/docs/my-website/docs/proxy_api.md index 7612645fb5..73c5a56587 100644 --- a/docs/my-website/docs/proxy_api.md +++ b/docs/my-website/docs/proxy_api.md @@ -66,16 +66,16 @@ git clone https://github.com/krrishdholakia/open-interpreter-litellm-fork ``` To run it do: ``` -poetry build +uv build # call gpt-4 - always add 'litellm_proxy/' in front of the model name -poetry run interpreter --model litellm_proxy/gpt-4 +uv run interpreter --model litellm_proxy/gpt-4 # call llama-70b - always add 'litellm_proxy/' in front of the model name -poetry run interpreter --model litellm_proxy/togethercomputer/llama-2-70b-chat +uv run interpreter --model litellm_proxy/togethercomputer/llama-2-70b-chat # call claude-2 - always add 'litellm_proxy/' in front of the model name -poetry run interpreter --model litellm_proxy/claude-2 +uv run interpreter --model litellm_proxy/claude-2 ``` And that's it! @@ -83,4 +83,4 @@ And that's it! Now you can call any model you like! -Want us to add more models? [Let us know!](https://github.com/BerriAI/litellm/issues/new/choose) \ No newline at end of file +Want us to add more models? [Let us know!](https://github.com/BerriAI/litellm/issues/new/choose) diff --git a/docs/my-website/docs/proxy_auth.md b/docs/my-website/docs/proxy_auth.md index 91084b34a3..bb5601cb85 100644 --- a/docs/my-website/docs/proxy_auth.md +++ b/docs/my-website/docs/proxy_auth.md @@ -72,7 +72,7 @@ response = litellm.completion( -**Required package:** `pip install azure-identity` +**Required package:** `uv add azure-identity` ### Generic OAuth2 (Okta, Auth0, Keycloak, etc.) diff --git a/docs/my-website/docs/proxy_server.md b/docs/my-website/docs/proxy_server.md index 7b6f15a604..1c05620753 100644 --- a/docs/my-website/docs/proxy_server.md +++ b/docs/my-website/docs/proxy_server.md @@ -13,7 +13,7 @@ Docs outdated. New docs 👉 [here](./simple_proxy) ## Usage ```shell -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' ``` ```shell $ litellm --model ollama/codellama @@ -213,7 +213,7 @@ docker compose up -d ```python -pip install pyautogen +uv add pyautogen ``` ```python @@ -329,7 +329,7 @@ git clone https://github.com/OpenBMB/ChatDev.git cd ChatDev conda create -n ChatDev_conda_env python=3.9 -y conda activate ChatDev_conda_env -pip install -r requirements.txt +uv add -r requirements.txt ``` ### Run ChatDev w/ Proxy ```shell @@ -346,7 +346,7 @@ python3 run.py --task "a script that says hello world" --name "hello world" ```python -pip install langroid +uv add langroid ``` ```python @@ -383,7 +383,7 @@ Credits [@pchalasani](https://github.com/pchalasani) and [Langroid](https://gith Here's how to use the local proxy to test codellama/mistral/etc. models for different github repos ```shell -pip install litellm +uv add litellm ``` ```shell @@ -440,7 +440,7 @@ Credits [@vividfog](https://github.com/ollama/ollama/issues/305#issuecomment-175 ```shell -$ pip install aider +$ uv add aider $ aider --openai-api-base http://0.0.0.0:8000 --openai-api-key fake-key ``` @@ -448,7 +448,7 @@ $ aider --openai-api-base http://0.0.0.0:8000 --openai-api-key fake-key ```python -pip install pyautogen +uv add pyautogen ``` ```python @@ -564,7 +564,7 @@ git clone https://github.com/OpenBMB/ChatDev.git cd ChatDev conda create -n ChatDev_conda_env python=3.9 -y conda activate ChatDev_conda_env -pip install -r requirements.txt +uv add -r requirements.txt ``` ### Run ChatDev w/ Proxy ```shell @@ -581,7 +581,7 @@ python3 run.py --task "a script that says hello world" --name "hello world" ```python -pip install langroid +uv add langroid ``` ```python diff --git a/docs/my-website/docs/rag_ingest.md b/docs/my-website/docs/rag_ingest.md index 7adc2d70b5..35b2cf4c32 100644 --- a/docs/my-website/docs/rag_ingest.md +++ b/docs/my-website/docs/rag_ingest.md @@ -287,7 +287,7 @@ When `vector_store_id` is omitted, LiteLLM automatically creates: 1. Create a RAG corpus in Vertex AI console or via API 2. Create a GCS bucket for file uploads 3. Authenticate via `gcloud auth application-default login` -4. Install: `pip install 'google-cloud-aiplatform>=1.60.0'` +4. Install: `uv add 'google-cloud-aiplatform>=1.60.0'` ::: ### vector_store (AWS S3 Vectors) diff --git a/docs/my-website/docs/response_api.md b/docs/my-website/docs/response_api.md index 0c428000c7..3ab61a97a4 100644 --- a/docs/my-website/docs/response_api.md +++ b/docs/my-website/docs/response_api.md @@ -831,7 +831,7 @@ The system automatically selects the appropriate mode based on provider capabili ```python showLineNumbers title="WebSocket with Python" import json -from websocket import create_connection # pip install websocket-client +from websocket import create_connection # uv add websocket-client # Connect to LiteLLM proxy WebSocket endpoint ws = create_connection( diff --git a/docs/my-website/docs/sdk_custom_pricing.md b/docs/my-website/docs/sdk_custom_pricing.md index c857711510..011229abe5 100644 --- a/docs/my-website/docs/sdk_custom_pricing.md +++ b/docs/my-website/docs/sdk_custom_pricing.md @@ -5,7 +5,7 @@ Register custom pricing for sagemaker completion model. For cost per second pricing, you **just** need to register `input_cost_per_second`. ```python -# !pip install boto3 +# !uv add boto3 from litellm import completion, completion_cost os.environ["AWS_ACCESS_KEY_ID"] = "" @@ -35,7 +35,7 @@ def test_completion_sagemaker(): ```python -# !pip install boto3 +# !uv add boto3 from litellm import completion, completion_cost ## set ENV variables diff --git a/docs/my-website/docs/secret_managers/azure_key_vault.md b/docs/my-website/docs/secret_managers/azure_key_vault.md index 4ea53d2ea9..3e697ebded 100644 --- a/docs/my-website/docs/secret_managers/azure_key_vault.md +++ b/docs/my-website/docs/secret_managers/azure_key_vault.md @@ -14,7 +14,7 @@ 1. Install Proxy dependencies ```bash -pip install 'litellm[proxy]' 'litellm[extra_proxy]' +uv tool install 'litellm[proxy]' 'litellm[extra_proxy]' ``` 2. Save Azure details in your environment diff --git a/docs/my-website/docs/troubleshoot/pip_venv_upgrade.md b/docs/my-website/docs/troubleshoot/pip_venv_upgrade.md index 6f5699e3fb..3bdaa6a05a 100644 --- a/docs/my-website/docs/troubleshoot/pip_venv_upgrade.md +++ b/docs/my-website/docs/troubleshoot/pip_venv_upgrade.md @@ -1,21 +1,21 @@ -# Upgrading LiteLLM Proxy (pip/venv) +# Upgrading LiteLLM Proxy (uv/venv) -Guide for upgrading LiteLLM Proxy when installed via pip in a virtual environment. +Guide for upgrading LiteLLM Proxy when installed via uv in a virtual environment. :::info Important Always activate your virtual environment before running any `litellm` or `prisma` commands. All commands in this guide assume you're working inside an activated venv. ::: -## How pip/venv Upgrades Work +## How uv/venv Upgrades Work There are two pieces that need to stay in sync: 1. **Prisma client** - Generated Python code that talks to the DB 2. **DB schema** - Tables/columns in PostgreSQL -When you upgrade via pip, the `litellm-proxy-extras` package ships with a new `schema.prisma` and a `migrations/` directory. But unlike the Docker image, pip install does NOT automatically regenerate the Prisma client or run migrations. You have to do both manually. +When you upgrade via uv, the `litellm-proxy-extras` package ships with a new `schema.prisma` and a `migrations/` directory. But unlike the Docker image, `uv add` does not automatically regenerate the Prisma client or run migrations. You have to do both manually. -## Upgrade Workflow (pip/venv) +## Upgrade Workflow (uv/venv) ### 1. Stop the proxy @@ -30,7 +30,7 @@ pg_dump -h -U -d -F c -f backup_$(date +%Y%m%d).dump ### 3. Upgrade the package ```bash -pip install 'litellm[proxy]==' +uv add 'litellm[proxy]==' ``` ### 4. Regenerate the Prisma client @@ -91,7 +91,7 @@ litellm --config your_config.yaml --port 4000 ### Before applying migrations: Preview what will change -Run `pip install 'litellm[proxy]=='` first (Step 3) so the new `schema.prisma` is available. +Run `uv add 'litellm[proxy]=='` first (Step 3) so the new `schema.prisma` is available. ```bash prisma migrate diff \ diff --git a/docs/my-website/docs/tutorials/TogetherAI_liteLLM.md b/docs/my-website/docs/tutorials/TogetherAI_liteLLM.md index dd9dd28867..97159dbba4 100644 --- a/docs/my-website/docs/tutorials/TogetherAI_liteLLM.md +++ b/docs/my-website/docs/tutorials/TogetherAI_liteLLM.md @@ -4,7 +4,7 @@ https://together.ai/ ```python -!pip install litellm +!uv add litellm ``` diff --git a/docs/my-website/docs/tutorials/claude_agent_sdk.md b/docs/my-website/docs/tutorials/claude_agent_sdk.md index c56784ba2d..f01fc778c4 100644 --- a/docs/my-website/docs/tutorials/claude_agent_sdk.md +++ b/docs/my-website/docs/tutorials/claude_agent_sdk.md @@ -12,7 +12,7 @@ The Claude Agent SDK provides a high-level interface for building AI agents. By ### 1. Install Dependencies ```bash -pip install claude-agent-sdk +uv add claude-agent-sdk ``` ### 2. Start LiteLLM Proxy @@ -104,7 +104,7 @@ See our [cookbook example](https://github.com/BerriAI/litellm/tree/main/cookbook # Clone and run the example git clone https://github.com/BerriAI/litellm.git cd litellm/cookbook/anthropic_agent_sdk -pip install -r requirements.txt +uv add -r requirements.txt python main.py ``` diff --git a/docs/my-website/docs/tutorials/claude_non_anthropic_models.md b/docs/my-website/docs/tutorials/claude_non_anthropic_models.md index 75ac08e309..0bba0f8ad0 100644 --- a/docs/my-website/docs/tutorials/claude_non_anthropic_models.md +++ b/docs/my-website/docs/tutorials/claude_non_anthropic_models.md @@ -22,7 +22,7 @@ LiteLLM automatically translates between different provider formats, allowing yo First, install LiteLLM with proxy support: ```bash -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' ``` ## Configuration diff --git a/docs/my-website/docs/tutorials/claude_responses_api.md b/docs/my-website/docs/tutorials/claude_responses_api.md index 2a6a1236ab..bf46036f22 100644 --- a/docs/my-website/docs/tutorials/claude_responses_api.md +++ b/docs/my-website/docs/tutorials/claude_responses_api.md @@ -28,7 +28,7 @@ This tutorial is based on [Anthropic's official LiteLLM configuration documentat First, install LiteLLM with proxy support: ```bash -pip install 'litellm[proxy]' +uv tool install 'litellm[proxy]' ``` ### 1. Setup config.yaml diff --git a/docs/my-website/docs/tutorials/compare_llms.md b/docs/my-website/docs/tutorials/compare_llms.md index 0252263a16..72c27aa2f1 100644 --- a/docs/my-website/docs/tutorials/compare_llms.md +++ b/docs/my-website/docs/tutorials/compare_llms.md @@ -23,7 +23,7 @@ cd litellm/cookbook/benchmark ### Install Dependencies ``` -pip install litellm click tqdm tabulate termcolor +uv add litellm click tqdm tabulate termcolor ``` ### Configuration - Set LLM API Keys + LLMs in benchmark.py @@ -88,7 +88,7 @@ Benchmark Results for 'When will BerriAI IPO?':