mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
ci: split slow unit shards and build the Rust bridge once per run (#44622)
* ci: split slow unit shards and build the Rust bridge once per run * ci: key the Rust bridge cache on source files only * ci: keep the unit setup ceiling unchanged with the shared Rust bridge * ci: fall back to the Cargo cache when the Rust bridge artifact is missing * ci: keep reruns on enterprise-routing for the prompt caching flake --------- Co-authored-by: yuneng <yuneng@berri.ai>
This commit is contained in:
parent
c29b42a32b
commit
169af2f883
3 changed files with 213 additions and 40 deletions
19
.github/actions/rust-bridge/action.yml
vendored
Normal file
19
.github/actions/rust-bridge/action.yml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
name: Set up the Rust bridge
|
||||
description: Select the shared Rust bridge artifact or the Cargo cache
|
||||
inputs:
|
||||
artifact:
|
||||
description: Rust bridge artifact name
|
||||
required: false
|
||||
default: ""
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Restore the Cargo build cache
|
||||
if: inputs.artifact == ''
|
||||
uses: ./.github/actions/cache-cargo-build
|
||||
- name: Download the Rust bridge artifact
|
||||
if: inputs.artifact != ''
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: rust-bridge-dist
|
||||
22
.github/workflows/_test-unit-base.yml
vendored
22
.github/workflows/_test-unit-base.yml
vendored
|
|
@ -69,6 +69,11 @@ on:
|
|||
description: "Unique name for the coverage artifact (must be unique per run)"
|
||||
required: true
|
||||
type: string
|
||||
rust-bridge-artifact:
|
||||
description: "Prebuilt editable Rust bridge artifact"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
|
@ -118,17 +123,28 @@ jobs:
|
|||
timeout-minutes: 5
|
||||
uses: ./.github/actions/cache-uv-downloads
|
||||
|
||||
- name: Cache the Rust build
|
||||
- name: Set up the Rust build
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 5
|
||||
uses: ./.github/actions/cache-cargo-build
|
||||
uses: ./.github/actions/rust-bridge
|
||||
with:
|
||||
artifact: ${{ inputs.rust-bridge-artifact }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 8
|
||||
env:
|
||||
RUST_BRIDGE_ARTIFACT: ${{ inputs.rust-bridge-artifact }}
|
||||
run: |
|
||||
diff -u model_prices_and_context_window.json litellm/model_prices_and_context_window_backup.json
|
||||
.github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml --extra caching --extra extra_proxy --extra proxy-runtime
|
||||
if [ -z "$RUST_BRIDGE_ARTIFACT" ]; then
|
||||
.github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml --extra caching --extra extra_proxy --extra proxy-runtime
|
||||
else
|
||||
.github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml --extra caching --extra extra_proxy --extra proxy-runtime --no-install-project
|
||||
uv pip install --no-deps --python .venv/bin/python rust-bridge-dist/*.whl
|
||||
cp rust-bridge-dist/litellm/rust_bridge/_native.abi3.so litellm/rust_bridge/_native.abi3.so
|
||||
uv run --no-sync python -c "import importlib.metadata; import litellm.rust_bridge._native; print(importlib.metadata.version('litellm'))"
|
||||
fi
|
||||
uv run --no-sync python -c 'import os, sys; print(sys.version); assert f"{sys.version_info.major}.{sys.version_info.minor}" == os.environ["UV_PYTHON"]'
|
||||
|
||||
- name: Cache Prisma binaries
|
||||
|
|
|
|||
212
.github/workflows/test-unit.yml
vendored
212
.github/workflows/test-unit.yml
vendored
|
|
@ -21,10 +21,8 @@ concurrency:
|
|||
# files that each wrapped a single call to _test-unit-base.yml. Adding a shard is
|
||||
# now one matrix entry rather than a new file.
|
||||
#
|
||||
# `name` is the shard id and nothing else, so each check reports as
|
||||
# "<shard> / Run tests" exactly as it did when the shard had its own file. Those
|
||||
# strings are the branch ruleset's required contexts, so they are load-bearing:
|
||||
# renaming an entry renames a required check and the ruleset stops matching it.
|
||||
# `name` is the shard id, and each check reports as "<shard> / Run tests".
|
||||
# Unit shard names are not required ruleset contexts, so matrix entries can be split freely.
|
||||
#
|
||||
# Every entry states its timeouts even when they equal the base workflow's
|
||||
# defaults. An absent matrix key renders as an empty string, which is not a
|
||||
|
|
@ -36,8 +34,101 @@ concurrency:
|
|||
# Folding it in here is a follow-up, together with generalising that guard into
|
||||
# assert_ci_coverage.py.
|
||||
jobs:
|
||||
rust-bridge:
|
||||
name: Build the Rust bridge
|
||||
outputs:
|
||||
artifact: ${{ steps.rust-bridge-artifact.outputs.name }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
env:
|
||||
UV_PYTHON: "3.12"
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Detect relevant changes
|
||||
id: changes
|
||||
timeout-minutes: 2
|
||||
uses: ./.github/actions/detect-changes
|
||||
|
||||
- name: Define editable Rust bridge cache key
|
||||
id: rust-bridge-key
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
env:
|
||||
RUST_BRIDGE_CACHE_KEY: ${{ runner.os }}-rust-bridge-editable-${{ hashFiles('litellm-rust/Cargo.lock', 'litellm-rust/Cargo.toml', 'litellm-rust/crates/**', 'pyproject.toml') }}
|
||||
run: echo "key=$RUST_BRIDGE_CACHE_KEY" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Python
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 3
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: ${{ env.UV_PYTHON }}
|
||||
|
||||
- name: Set up uv
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/setup-uv-with-retries
|
||||
with:
|
||||
version: "0.10.9"
|
||||
|
||||
- name: Restore editable Rust bridge
|
||||
id: rust-bridge-cache
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 5
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||
with:
|
||||
path: rust-bridge-dist
|
||||
key: ${{ steps.rust-bridge-key.outputs.key }}
|
||||
|
||||
- name: Cache the Rust build
|
||||
if: steps.changes.outputs.decision != 'skip' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
|
||||
timeout-minutes: 5
|
||||
uses: ./.github/actions/cache-cargo-build
|
||||
|
||||
- name: Build editable Rust bridge
|
||||
if: steps.changes.outputs.decision != 'skip' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
mkdir -p rust-bridge-dist
|
||||
uv run --no-project --with maturin==1.15.0 python -c 'import maturin; maturin.build_editable("rust-bridge-dist")'
|
||||
strip --strip-debug litellm/rust_bridge/_native.abi3.so
|
||||
mkdir -p rust-bridge-dist/litellm/rust_bridge
|
||||
cp litellm/rust_bridge/_native.abi3.so rust-bridge-dist/litellm/rust_bridge/_native.abi3.so
|
||||
|
||||
- name: Upload Rust bridge artifact
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 10
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
|
||||
with:
|
||||
name: rust-bridge-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: rust-bridge-dist/
|
||||
retention-days: 1
|
||||
|
||||
- name: Save editable Rust bridge
|
||||
if: steps.changes.outputs.decision != 'skip' && github.ref == 'refs/heads/main' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
|
||||
timeout-minutes: 10
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||
with:
|
||||
path: rust-bridge-dist
|
||||
key: ${{ steps.rust-bridge-key.outputs.key }}
|
||||
|
||||
- name: Expose the Rust bridge artifact
|
||||
id: rust-bridge-artifact
|
||||
if: steps.changes.outputs.decision != 'skip'
|
||||
timeout-minutes: 1
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
RUN_ATTEMPT: ${{ github.run_attempt }}
|
||||
run: echo "name=rust-bridge-${RUN_ID}-${RUN_ATTEMPT}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
unit:
|
||||
name: ${{ matrix.shard }}
|
||||
needs: rust-bridge
|
||||
if: ${{ !cancelled() }}
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
|
@ -51,8 +142,8 @@ jobs:
|
|||
test-path: >-
|
||||
tests/unit/decisions
|
||||
tests/unit/litellm_core_utils
|
||||
workers: 2
|
||||
reruns: 1
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -74,7 +165,7 @@ jobs:
|
|||
tests/unit/enterprise/proxy/test_file_deletion_blocking.py
|
||||
tests/unit/enterprise/proxy/test_managed_files_access_check.py
|
||||
tests/unit/enterprise/proxy/test_managed_files_hook.py
|
||||
workers: 2
|
||||
workers: 4
|
||||
reruns: 2
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
|
@ -85,8 +176,8 @@ jobs:
|
|||
tests/test_litellm/integrations
|
||||
tests/test_litellm/tracing
|
||||
tests/unit/integrations
|
||||
workers: 2
|
||||
reruns: 3
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -94,8 +185,8 @@ jobs:
|
|||
artifact-name: llm-vertex-ai
|
||||
test-path: >-
|
||||
tests/unit/llms/vertex_ai
|
||||
workers: 1
|
||||
reruns: 2
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -104,9 +195,21 @@ jobs:
|
|||
test-path: >-
|
||||
tests/unit/llms
|
||||
--ignore=tests/unit/llms/vertex_ai
|
||||
--ignore=tests/unit/llms/openai
|
||||
--ignore=tests/unit/llms/meta
|
||||
--ignore=tests/unit/llms/base_llm/batches/base_batches_config_test.py
|
||||
workers: 2
|
||||
reruns: 2
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: OpenAI and Meta Providers
|
||||
artifact-name: llm-openai-meta
|
||||
test-path: >-
|
||||
tests/unit/llms/openai
|
||||
tests/unit/llms/meta
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -115,6 +218,14 @@ jobs:
|
|||
test-path: >-
|
||||
tests/test_litellm/test_*.py
|
||||
tests/unit/test_*.py
|
||||
workers: 4
|
||||
reruns: 2
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: misc-dirs
|
||||
artifact-name: misc-dirs
|
||||
test-path: >-
|
||||
tests/unit/test_router
|
||||
tests/unit/a2a_protocol
|
||||
tests/unit/batches
|
||||
|
|
@ -135,8 +246,8 @@ jobs:
|
|||
tests/unit/vector_stores
|
||||
tests/unit/videos
|
||||
--ignore=tests/unit/rust_bridge/native_route_wheel_test.py
|
||||
workers: 2
|
||||
reruns: 2
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -144,9 +255,6 @@ jobs:
|
|||
artifact-name: proxy-auth
|
||||
test-path: >-
|
||||
tests/unit/proxy/auth
|
||||
tests/unit/proxy/hooks
|
||||
tests/unit/proxy/policy_engine
|
||||
tests/unit/proxy/client
|
||||
--ignore=tests/unit/proxy/auth/test_auth_checks.py
|
||||
--ignore=tests/unit/proxy/auth/test_user_api_key_auth.py
|
||||
--ignore=tests/unit/proxy/auth/test_default_end_user_budget_simple.py
|
||||
|
|
@ -154,35 +262,56 @@ jobs:
|
|||
--ignore=tests/unit/proxy/auth/test_models_fallback_endpoint.py
|
||||
--ignore=tests/unit/proxy/auth/test_multipart_bypass_repro.py
|
||||
--ignore=tests/unit/proxy/auth/test_proxy_routes.py
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: proxy-hooks-client
|
||||
artifact-name: proxy-hooks-client
|
||||
test-path: >-
|
||||
tests/unit/proxy/hooks
|
||||
tests/unit/proxy/policy_engine
|
||||
tests/unit/proxy/client
|
||||
--ignore=tests/unit/proxy/hooks/test_banned_keyword_list.py
|
||||
--ignore=tests/unit/proxy/hooks/test_unit_test_max_model_budget_limiter.py
|
||||
workers: 2
|
||||
reruns: 2
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: proxy-endpoints
|
||||
artifact-name: proxy-endpoints
|
||||
test-path: >-
|
||||
tests/unit/proxy/management_endpoints
|
||||
tests/unit/proxy/management_helpers
|
||||
tests/unit/proxy/list_api
|
||||
tests/unit/proxy/analytics_endpoints
|
||||
tests/unit/proxy/decisions_endpoints
|
||||
tests/unit/proxy/management_endpoints
|
||||
tests/unit/proxy/list_api
|
||||
tests/unit/proxy/memory
|
||||
tests/unit/proxy/guardrails
|
||||
tests/unit/proxy/management_helpers
|
||||
tests/unit/proxy/agent_endpoints
|
||||
tests/unit/proxy/openai_files_endpoint
|
||||
tests/unit/proxy/health_endpoints
|
||||
tests/unit/proxy/batches_endpoints
|
||||
--ignore=tests/unit/proxy/management_endpoints/test_jwt_key_mapping.py
|
||||
--ignore=tests/unit/proxy/management_endpoints/test_key_generate_prisma.py
|
||||
--ignore=tests/unit/proxy/management_endpoints/test_roi_calculator_endpoints.py
|
||||
--ignore=tests/unit/proxy/management_helpers/test_audit_logs_proxy.py
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: proxy-feature-endpoints
|
||||
artifact-name: proxy-feature-endpoints
|
||||
test-path: >-
|
||||
tests/unit/proxy/guardrails
|
||||
--ignore=tests/unit/proxy/google_endpoints/test_gemini_agents_endpoints.py
|
||||
--ignore=tests/unit/proxy/google_endpoints/test_google_endpoint_routing.py
|
||||
--ignore=tests/unit/proxy/google_endpoints/test_google_gemini_proxy_request.py
|
||||
--ignore=tests/unit/proxy/public_endpoints/test_blog_posts_endpoint.py
|
||||
tests/unit/proxy/anthropic_endpoints
|
||||
tests/unit/proxy/google_endpoints
|
||||
tests/unit/proxy/openai_files_endpoint
|
||||
tests/unit/proxy/batches_endpoints
|
||||
tests/unit/proxy/container_endpoints
|
||||
tests/unit/proxy/fine_tuning_endpoints
|
||||
tests/unit/proxy/vector_store_files_endpoints
|
||||
|
|
@ -192,11 +321,9 @@ jobs:
|
|||
tests/unit/proxy/ocr_endpoints
|
||||
tests/unit/proxy/search_endpoints
|
||||
tests/unit/proxy/vector_store_endpoints
|
||||
tests/unit/proxy/agent_endpoints
|
||||
tests/unit/proxy/a2a
|
||||
tests/unit/proxy/credential_endpoints
|
||||
tests/unit/proxy/discovery_endpoints
|
||||
tests/unit/proxy/health_endpoints
|
||||
tests/unit/proxy/shutdown
|
||||
tests/unit/proxy/public_endpoints
|
||||
tests/unit/proxy/prompts
|
||||
|
|
@ -207,7 +334,7 @@ jobs:
|
|||
tests/unit/proxy/config_resolvers
|
||||
tests/unit/proxy/utils
|
||||
workers: 4
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -215,7 +342,7 @@ jobs:
|
|||
artifact-name: proxy-server
|
||||
test-path: "tests/unit/proxy/proxy_server"
|
||||
workers: 4
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 60
|
||||
job-timeout-minutes: 100
|
||||
|
||||
|
|
@ -247,7 +374,7 @@ jobs:
|
|||
tests/unit/proxy/management_endpoints/test_roi_calculator_endpoints.py
|
||||
tests/unit/proxy/roi_calculator
|
||||
workers: 4
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -281,7 +408,7 @@ jobs:
|
|||
--ignore=tests/unit/proxy/test_update_spend.py
|
||||
--ignore=tests/unit/proxy/test_zero_cost_model_budget_bypass.py
|
||||
workers: 4
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -290,7 +417,7 @@ jobs:
|
|||
test-path: >-
|
||||
tests/unit/caching
|
||||
workers: 2
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -299,7 +426,7 @@ jobs:
|
|||
test-path: >-
|
||||
tests/unit/litellm_proxy_extras
|
||||
workers: 2
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -309,16 +436,25 @@ jobs:
|
|||
tests/unit/enterprise/integrations
|
||||
tests/unit/enterprise/proxy/auth
|
||||
tests/unit/enterprise/proxy/guardrails
|
||||
tests/unit/enterprise/proxy/hooks
|
||||
tests/unit/enterprise/proxy/management_endpoints
|
||||
tests/unit/enterprise/proxy/test_audit_logging_endpoints.py
|
||||
tests/unit/enterprise/proxy/test_liteadmin.py
|
||||
tests/unit/enterprise/enterprise_callbacks/test_prometheus_logging_callbacks.py
|
||||
workers: 4
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
- shard: enterprise-managed-files
|
||||
artifact-name: enterprise-managed-files
|
||||
test-path: >-
|
||||
tests/unit/enterprise/proxy/hooks
|
||||
workers: 4
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
dist: load
|
||||
|
||||
- shard: responses-caching-types
|
||||
artifact-name: responses-caching-types
|
||||
test-path: >-
|
||||
|
|
@ -326,7 +462,7 @@ jobs:
|
|||
tests/unit/types
|
||||
--ignore=tests/unit/responses/mcp
|
||||
workers: 2
|
||||
reruns: 2
|
||||
reruns: 0
|
||||
timeout-minutes: 20
|
||||
job-timeout-minutes: 60
|
||||
|
||||
|
|
@ -353,9 +489,11 @@ jobs:
|
|||
job-timeout-minutes: 60
|
||||
uses: ./.github/workflows/_test-unit-base.yml
|
||||
with:
|
||||
rust-bridge-artifact: ${{ needs.rust-bridge.outputs.artifact }}
|
||||
test-path: ${{ matrix.test-path }}
|
||||
workers: ${{ matrix.workers }}
|
||||
reruns: ${{ matrix.reruns }}
|
||||
timeout-minutes: ${{ matrix.timeout-minutes }}
|
||||
job-timeout-minutes: ${{ matrix.job-timeout-minutes }}
|
||||
dist: ${{ matrix.dist || 'loadscope' }}
|
||||
artifact-name: ${{ matrix.artifact-name }}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue