From b70ddc2fd8cef1c86807974fb2cbef06f92bc85f Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 19:14:32 +0000 Subject: [PATCH 1/5] ci(rust): split rust jobs, use nextest and Swatinem/rust-cache Split the Rust workflow into fmt, clippy, nextest and wheel jobs so they run in parallel, replace manual actions/cache with Swatinem/rust-cache, and install a pinned checksum-verified cargo-nextest. Make two python-bridge tests self-contained so they pass when nextest runs each test in its own process. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/test-rust.yml | 81 +++++++++++++------ .../crates/python-bridge/src/lifecycle/mod.rs | 52 +++++++----- .../crates/python-bridge/src/marshal.rs | 1 + 3 files changed, 89 insertions(+), 45 deletions(-) diff --git a/.github/workflows/test-rust.yml b/.github/workflows/test-rust.yml index c4847aca20d..00c6fed8ae3 100644 --- a/.github/workflows/test-rust.yml +++ b/.github/workflows/test-rust.yml @@ -68,9 +68,9 @@ env: CARGO_TERM_COLOR: always jobs: - rust-lint: + rust-fmt: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 5 defaults: run: working-directory: litellm-rust @@ -81,24 +81,67 @@ jobs: - run: rustup toolchain install --no-self-update - - run: cargo fmt --check + - run: cargo fmt --all --check - - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + rust-clippy: + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + working-directory: litellm-rust + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: - path: | - ~/.cargo/registry - ~/.cargo/git - litellm-rust/target - key: ${{ runner.os }}-cargo-${{ github.job }}-${{ hashFiles('rust-toolchain.toml', '.cargo/**', 'litellm-rust/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ github.job }}- + persist-credentials: false + + - run: rustup toolchain install --no-self-update + + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: litellm-rust + cache-on-failure: true - run: cargo clippy --workspace --all-targets --locked -- -D warnings rust-test: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 20 + defaults: + run: + working-directory: litellm-rust + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - run: rustup toolchain install --no-self-update + + - name: Install cargo-nextest 0.9.143 + working-directory: ${{ runner.temp }} + run: | + curl -fsSL --retry 3 -o cargo-nextest.tar.gz \ + https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-0.9.143/cargo-nextest-0.9.143-x86_64-unknown-linux-gnu.tar.gz + echo "66786b9abe23920d022a182d1416b1bbc8130dd4872a9553d76985a1708dcd1e cargo-nextest.tar.gz" | sha256sum -c - + mkdir -p bin + tar xzf cargo-nextest.tar.gz -C bin cargo-nextest + echo "$PWD/bin" >> "$GITHUB_PATH" + + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: litellm-rust + cache-on-failure: true + + - run: cargo nextest run --workspace --locked + + - run: cargo test --workspace --doc --locked + + rust-wheel: + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: @@ -114,18 +157,10 @@ jobs: - run: rustup toolchain install --no-self-update - - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - litellm-rust/target - key: ${{ runner.os }}-cargo-${{ github.job }}-${{ hashFiles('rust-toolchain.toml', '.cargo/**', 'litellm-rust/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ github.job }}- - - - run: cargo test --workspace --locked - working-directory: litellm-rust + workspaces: litellm-rust + cache-on-failure: true - run: uv build --wheel --out-dir dist diff --git a/litellm-rust/crates/python-bridge/src/lifecycle/mod.rs b/litellm-rust/crates/python-bridge/src/lifecycle/mod.rs index cf9f31c3c13..c4b8d8eaae0 100644 --- a/litellm-rust/crates/python-bridge/src/lifecycle/mod.rs +++ b/litellm-rust/crates/python-bridge/src/lifecycle/mod.rs @@ -599,6 +599,34 @@ mod tests { static PYTHON_GLOBALS: Mutex<()> = Mutex::new(()); + fn install_lifecycle_module(py: Python<'_>) -> Bound<'_, PyModule> { + py.run( + pyo3::ffi::c_str!( + r#" +import sys +import types + +sys.modules.setdefault('litellm', types.ModuleType('litellm')) +sys.modules.setdefault('litellm.rust_bridge', types.ModuleType('litellm.rust_bridge')) +"# + ), + None, + None, + ) + .unwrap(); + let source = std::ffi::CString::new(include_str!( + "../../../../../litellm/rust_bridge/lifecycle.py" + )) + .unwrap(); + PyModule::from_code( + py, + &source, + pyo3::ffi::c_str!("lifecycle.py"), + pyo3::ffi::c_str!("litellm.rust_bridge.lifecycle"), + ) + .unwrap() + } + fn install_logging_worker(py: Python<'_>, worker: &Bound<'_, PyAny>) -> PyResult<()> { py.import("litellm.litellm_core_utils.logging_worker")? .setattr("GLOBAL_LOGGING_WORKER", worker) @@ -773,17 +801,7 @@ mod tests { .unwrap_or_else(|error| error.into_inner()); Python::initialize(); Python::attach(|py| { - let source = std::ffi::CString::new(include_str!( - "../../../../../litellm/rust_bridge/lifecycle.py" - )) - .unwrap(); - PyModule::from_code( - py, - &source, - pyo3::ffi::c_str!("lifecycle.py"), - pyo3::ffi::c_str!("litellm.rust_bridge.lifecycle"), - ) - .unwrap(); + install_lifecycle_module(py); let route = SyntheticRoute( PythonCallState::new( py, @@ -819,17 +837,7 @@ mod tests { Python::initialize(); Python::attach(|py| { py.import("asyncio").unwrap(); - let source = std::ffi::CString::new(include_str!( - "../../../../../litellm/rust_bridge/lifecycle.py" - )) - .unwrap(); - let module = PyModule::from_code( - py, - &source, - pyo3::ffi::c_str!("lifecycle.py"), - pyo3::ffi::c_str!("litellm.rust_bridge.lifecycle"), - ) - .unwrap(); + let module = install_lifecycle_module(py); let locals = PyDict::new(py); locals .set_item("drive", module.getattr("drive").unwrap()) diff --git a/litellm-rust/crates/python-bridge/src/marshal.rs b/litellm-rust/crates/python-bridge/src/marshal.rs index 9038eb971b3..7f00298905f 100644 --- a/litellm-rust/crates/python-bridge/src/marshal.rs +++ b/litellm-rust/crates/python-bridge/src/marshal.rs @@ -190,6 +190,7 @@ mod tests { #[test] fn required_shapes_preserve_nested_values_and_existing_errors() { + Python::initialize(); let nested = json!([{"role": "user", "content": [{"type": "text", "text": "hi"}]}]); assert_eq!( Value::Array(required_array("messages", nested.clone()).unwrap()), From e8b5632c20676b1e5ec74de5cd21ac38875d5fa6 Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 19:22:02 +0000 Subject: [PATCH 2/5] ci(rust): run the token counter timing test alone under nextest Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm-rust/.config/nextest.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 litellm-rust/.config/nextest.toml diff --git a/litellm-rust/.config/nextest.toml b/litellm-rust/.config/nextest.toml new file mode 100644 index 00000000000..1762a151573 --- /dev/null +++ b/litellm-rust/.config/nextest.toml @@ -0,0 +1,3 @@ +[[profile.default.overrides]] +filter = "test(long_repeated_runs_stay_cheap)" +threads-required = "num-cpus" From e850232f0208fb81c1187143bf9694b79d69149f Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 19:43:42 +0000 Subject: [PATCH 3/5] test(rust): assert merge cost scales linearly instead of a wall-clock bound Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm-rust/.config/nextest.toml | 3 --- .../crates/token-counter/src/tiktoken.rs | 19 +++++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) delete mode 100644 litellm-rust/.config/nextest.toml diff --git a/litellm-rust/.config/nextest.toml b/litellm-rust/.config/nextest.toml deleted file mode 100644 index 1762a151573..00000000000 --- a/litellm-rust/.config/nextest.toml +++ /dev/null @@ -1,3 +0,0 @@ -[[profile.default.overrides]] -filter = "test(long_repeated_runs_stay_cheap)" -threads-required = "num-cpus" diff --git a/litellm-rust/crates/token-counter/src/tiktoken.rs b/litellm-rust/crates/token-counter/src/tiktoken.rs index c479ae01be9..7a9e71ed587 100644 --- a/litellm-rust/crates/token-counter/src/tiktoken.rs +++ b/litellm-rust/crates/token-counter/src/tiktoken.rs @@ -195,14 +195,21 @@ mod tests { } #[test] - fn long_repeated_runs_stay_cheap() { + fn long_repeated_runs_cost_close_to_linear() { let ranks = ranks(); let mut scratch = MergeScratch::default(); - let piece = vec![b' '; 1 << 20]; - let started = std::time::Instant::now(); - let count = ranks.count_piece(&piece, &mut scratch); - assert!(count > 0); - assert!(started.elapsed().as_secs() < 5, "{:?}", started.elapsed()); + let mut time = |len: usize| { + let piece = vec![b' '; len]; + let started = std::time::Instant::now(); + assert!(ranks.count_piece(&piece, &mut scratch) > 0); + started.elapsed() + }; + let small = (0..3).map(|_| time(1 << 14)).min().unwrap(); + let large = time(1 << 18); + assert!( + large < small * 64, + "{small:?} for 2^14 bytes, {large:?} for 2^18" + ); } #[test] From 3f15dcd96b10206becb807a9d8bc57f2f9323096 Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 19:47:48 +0000 Subject: [PATCH 4/5] ci(rust): fold fmt into the clippy job Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/test-rust.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test-rust.yml b/.github/workflows/test-rust.yml index 00c6fed8ae3..e56726deb53 100644 --- a/.github/workflows/test-rust.yml +++ b/.github/workflows/test-rust.yml @@ -68,22 +68,7 @@ env: CARGO_TERM_COLOR: always jobs: - rust-fmt: - runs-on: ubuntu-latest - timeout-minutes: 5 - defaults: - run: - working-directory: litellm-rust - steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - persist-credentials: false - - - run: rustup toolchain install --no-self-update - - - run: cargo fmt --all --check - - rust-clippy: + rust-lint: runs-on: ubuntu-latest timeout-minutes: 15 defaults: @@ -96,6 +81,8 @@ jobs: - run: rustup toolchain install --no-self-update + - run: cargo fmt --all --check + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: litellm-rust From 48df3d5a48d87e45538f02625b5444e4ec867d6a Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 19:54:46 +0000 Subject: [PATCH 5/5] ci(rust): install nextest via pinned taiki-e/install-action Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/test-rust.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-rust.yml b/.github/workflows/test-rust.yml index e56726deb53..551f783d4f9 100644 --- a/.github/workflows/test-rust.yml +++ b/.github/workflows/test-rust.yml @@ -107,15 +107,9 @@ jobs: - run: rustup toolchain install --no-self-update - - name: Install cargo-nextest 0.9.143 - working-directory: ${{ runner.temp }} - run: | - curl -fsSL --retry 3 -o cargo-nextest.tar.gz \ - https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-0.9.143/cargo-nextest-0.9.143-x86_64-unknown-linux-gnu.tar.gz - echo "66786b9abe23920d022a182d1416b1bbc8130dd4872a9553d76985a1708dcd1e cargo-nextest.tar.gz" | sha256sum -c - - mkdir -p bin - tar xzf cargo-nextest.tar.gz -C bin cargo-nextest - echo "$PWD/bin" >> "$GITHUB_PATH" + - uses: taiki-e/install-action@d438492cf8a250514fa2d34b30bc3c0dc37c65ff # v2.87.8 + with: + tool: cargo-nextest@0.9.143 - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: