From e7de2ccf44d0e59b7657882e6a1dc642e348746e Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Tue, 13 Oct 2026 20:18:19 +0000 Subject: [PATCH] ai-dynamo-runtime: Add version 1.5.1 --- .github/workflows/build-ai-dynamo-runtime.yml | 198 ++++++++++++++++++ docs/packages/ai-dynamo-runtime.yaml | 6 + ...ngs-use-nixl-sys-stub-api-on-riscv64.patch | 50 +++++ 3 files changed, 254 insertions(+) create mode 100644 .github/workflows/build-ai-dynamo-runtime.yml create mode 100644 docs/packages/ai-dynamo-runtime.yaml create mode 100644 patches/ai-dynamo-runtime/1.5.1/0001-bindings-use-nixl-sys-stub-api-on-riscv64.patch diff --git a/.github/workflows/build-ai-dynamo-runtime.yml b/.github/workflows/build-ai-dynamo-runtime.yml new file mode 100644 index 000000000000..e5f45a359240 --- /dev/null +++ b/.github/workflows/build-ai-dynamo-runtime.yml @@ -0,0 +1,198 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the runtime_wheel_builder stage of +# https://github.com/ai-dynamo/dynamo/blob/v1.5.1/container/templates/wheel_builder.Dockerfile +# and the CPU pre-merge pytest selection of +# https://github.com/ai-dynamo/dynamo/blob/v1.5.1/.github/workflows/pr.yaml +name: Build ai-dynamo-runtime wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/ai-dynamo-runtime.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-ai-dynamo-runtime.yml' + - 'docs/packages/ai-dynamo-runtime.yaml' + - 'patches/ai-dynamo-runtime/**' + push: + branches: [main] + paths: + - '.github/workflows/build-ai-dynamo-runtime.yml' + - 'docs/packages/ai-dynamo-runtime.yaml' + - 'patches/ai-dynamo-runtime/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: ai-dynamo-runtime + version: ${{ inputs.version }} + + build_wheel: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + name: Build ai-dynamo-runtime ${{ matrix.version }} cp310-abi3-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 720 + container: + image: quay.io/pypa/manylinux_2_39_riscv64 + defaults: + run: + shell: bash + env: + AI_DYNAMO_RUNTIME_VERSION: ${{ matrix.version }} + PROTOC: /usr/bin/protoc + PROTOC_INCLUDE: /usr/include + + steps: + - name: Checkout dynamo v${{ env.AI_DYNAMO_RUNTIME_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: ai-dynamo/dynamo + ref: v${{ env.AI_DYNAMO_RUNTIME_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + sparse-checkout: patches/ai-dynamo-runtime + persist-credentials: false + + - name: Apply patches + run: git apply -v python-wheels/patches/ai-dynamo-runtime/${{ env.AI_DYNAMO_RUNTIME_VERSION }}/*.patch + + # Upstream downloads a protoc release zip, which has no riscv64 build. + - name: Install system dependencies + run: | + dnf -y --enablerepo=crb install protobuf-compiler protobuf-devel clang-devel + protoc --version + echo "/opt/python/cp312-cp312/bin" >> "$GITHUB_PATH" + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain none --profile minimal + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + channel="$(sed -n 's/^channel = "\(.*\)"$/\1/p' rust-toolchain.toml)" + "$HOME/.cargo/bin/rustup" toolchain install "$channel" --profile minimal + + - name: Build wheel + working-directory: lib/bindings/python + run: | + python -m pip install maturin + maturin build --release \ + --features "kv-indexer,slot-tracker,select-service,mm-routing,aic-forward-pass,request-trace-s3" \ + --out dist + + - name: Bundle third-party Rust licences + run: | + find "$HOME/.cargo/registry/src" -mindepth 2 -maxdepth 2 -type d | while IFS= read -r crate; do + dest="rust-licenses/$(basename "$crate")" + for lf in "$crate"/LICENSE* "$crate"/LICENCE* "$crate"/COPYING* "$crate"/NOTICE* "$crate"/UNLICENSE*; do + [ -e "$lf" ] || continue + mkdir -p "$dest" && cp "$lf" "$dest/" 2>/dev/null || true + done + done + python -m pip install pyyaml + for whl in lib/bindings/python/dist/*.whl; do + PYTHONPATH=container python -m compliance.bundle_wheel_notices --wheel "$whl" --licenses-dir rust-licenses -v + python -c 'import sys, zipfile; assert any(n.endswith(".dist-info/licenses/THIRD-PARTY-RUST-LICENSES.txt") for n in zipfile.ZipFile(sys.argv[1]).namelist())' "$whl" + done + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ai-dynamo-runtime-${{ env.AI_DYNAMO_RUNTIME_VERSION }}-cp310-abi3-manylinux_riscv64 + path: lib/bindings/python/dist/*.whl + if-no-files-found: error + + test_wheel: + needs: [setup, build_wheel] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + python: ["3.12", "3.13", "3.14"] + name: Test ai-dynamo-runtime ${{ matrix.version }} cp${{ matrix.python }} + runs-on: ubuntu-24.04-riscv + timeout-minutes: 90 + env: + AI_DYNAMO_RUNTIME_VERSION: ${{ matrix.version }} + UV_EXTRA_INDEX_URL: https://pypi.riseproject.dev/simple/ + UV_INDEX_STRATEGY: unsafe-best-match + UV_ONLY_BINARY: pydantic-core,uvloop,numpy,aiohttp,frozenlist,multidict,propcache,yarl + UV_PYTHON_PREFERENCE: only-managed + # etcd 3.4 refuses to start on architectures it does not list as supported. + ETCD_UNSUPPORTED_ARCH: riscv64 + + steps: + - name: Checkout dynamo v${{ env.AI_DYNAMO_RUNTIME_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: ai-dynamo/dynamo + ref: v${{ env.AI_DYNAMO_RUNTIME_VERSION }} + persist-credentials: false + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ai-dynamo-runtime-${{ env.AI_DYNAMO_RUNTIME_VERSION }}-cp310-abi3-manylinux_riscv64 + path: dist + + - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: ${{ matrix.python }} + activate-environment: true + enable-cache: false + + # Upstream's test image provides nats-server and etcd for the bindings' autouse fixture. + - name: Install NATS and etcd + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq --no-install-recommends nats-server etcd-server + + - name: Install wheel + run: | + uv pip install dist/*.whl + uv pip install pytest pytest-asyncio pytest-timeout pytest-forked pytest-benchmark aiohttp numpy + + # test_kserve_grpc.py needs tritonclient[grpc], which upstream gets from its framework images. + - name: Test + run: | + python -m pytest lib/bindings/python/tests \ + -m "pre_merge and gpu_0" \ + --ignore=lib/bindings/python/tests/test_kserve_grpc.py + + publish: + name: Publish ai-dynamo-runtime ${{ matrix.version }} + needs: [setup, build_wheel, test_wheel] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: ai-dynamo-runtime-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/ai-dynamo-runtime.yaml b/docs/packages/ai-dynamo-runtime.yaml new file mode 100644 index 000000000000..06aa1f212d25 --- /dev/null +++ b/docs/packages/ai-dynamo-runtime.yaml @@ -0,0 +1,6 @@ +package-name: ai-dynamo-runtime +source-code: https://github.com/ai-dynamo/dynamo +license: Apache-2.0 +versions: +- version: 1.5.1 + patched: true diff --git a/patches/ai-dynamo-runtime/1.5.1/0001-bindings-use-nixl-sys-stub-api-on-riscv64.patch b/patches/ai-dynamo-runtime/1.5.1/0001-bindings-use-nixl-sys-stub-api-on-riscv64.patch new file mode 100644 index 000000000000..dded244f2dc5 --- /dev/null +++ b/patches/ai-dynamo-runtime/1.5.1/0001-bindings-use-nixl-sys-stub-api-on-riscv64.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 13 Oct 2026 00:00:00 +0000 +Subject: [PATCH] bindings: use nixl-sys stub API on riscv64 + +dynamo-llm (block-manager, on by default) and dynamo-memory pull in +nixl-sys 1.3.2. Its build.rs is meant to fall back to a dlopen stub API +when NIXL is not installed - which is how upstream's own wheel is built +("sans nixl") - but build_nixl() calls get_arch() before it looks for +libnixl.so, and get_arch() panics on anything but x86_64/aarch64: + + panicked at build.rs: Unsupported architecture: riscv64 + +A panic is not the Err that triggers the stub fallback, so the build +dies on riscv64. Requesting nixl-sys's `stub-api` feature skips +build_nixl() entirely and compiles the same stubs upstream's wheel +ships. It is scoped to riscv64 so other targets are unchanged; the +proper fix is for nixl-sys's get_arch() to stop panicking. + +Upstream-Status: Inappropriate [workaround for nixl-sys get_arch() panicking on riscv64; the real fix belongs in ai-dynamo/nixl] + +Signed-off-by: Ludovic Henry +--- +diff --git a/lib/bindings/python/Cargo.lock b/lib/bindings/python/Cargo.lock +index a87d665..8054f83 100644 +--- a/lib/bindings/python/Cargo.lock ++++ b/lib/bindings/python/Cargo.lock +@@ -2404,6 +2404,7 @@ dependencies = [ + "dynamo-vllm-sidecar", + "dynamo-worker-selection-policy-catalog", + "futures", ++ "nixl-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", +diff --git a/lib/bindings/python/Cargo.toml b/lib/bindings/python/Cargo.toml +index 414c748..0055d28 100644 +--- a/lib/bindings/python/Cargo.toml ++++ b/lib/bindings/python/Cargo.toml +@@ -127,6 +127,10 @@ pythonize = "0.23" + [target.'cfg(target_os = "linux")'.dependencies] + dynamo-llm = { path = "../../llm" } + ++# nixl-sys's build.rs only knows x86_64/aarch64 and panics before its stub fallback. ++[target.'cfg(target_arch = "riscv64")'.dependencies] ++nixl-sys = { version = "=1.3.2", features = ["stub-api"] } ++ + [target.'cfg(not(target_os = "linux"))'.dependencies] + dynamo-llm = { path = "../../llm", default-features = false } +