From 41b509d2cad7a56465694d4667079e29683b8694 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Thu, 22 Oct 2026 11:39:24 -0700 Subject: [PATCH 1/2] Pin the TensorRT-RTX EP 0.5.0 wheel (TensorRT-RTX 1.7.1) - Bump the stock trt-rtx pin (x86_64 and aarch64) from onnxruntime-ep-nv-tensorrt-rtx-cu13 0.4.0 to 0.5.0, which bundles TensorRT-RTX 1.7.1 instead of 1.6.1. Same ABI and minimum ORT. - SONAME links (libtensorrt_rtx.so.1, libtensorrt_onnxparser_rtx.so.1) follow whichever runtime version is vendored instead of a hardcoded 1.6.1, which would have left the 0.5.0 EP unable to load. - The wheel records no version, so a vendored 0.4.0 counted as installed. PluginPackageSpec.VersionMarkerFile names a file only the pinned build ships (libtensorrt_rtx.so.1.7.1); an install without it is replaced, in the installer and in scripts/fetch-ep-plugins.sh. - The patched WSL2 build stays at 0.4.0-wsl2p2 (fix expected upstream in NVIDIA/TensorRT-RTX-EP-ABI#12). It links the stock runtime by SONAME and passes TensorrtRtxWsl2EndToEndTests on 1.7.1 (WSL2, RTX 5070). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 6 ++ README.md | 20 +++--- docs/compatibility.md | 10 +-- scripts/fetch-ep-plugins.sh | 49 +++++++++----- src/LinML.Catalog/PluginInstaller.cs | 26 ++++---- src/LinML.Catalog/PluginPackageSpec.cs | 45 ++++++++----- .../PluginExecutionProviderTests.cs | 2 +- .../PluginInstallerTests.cs | 65 ++++++++++++++++--- 8 files changed, 152 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9abf12..4e33607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to LinML are documented here. This project follows [Semantic ## [Unreleased] +### Changed +- **TensorRT-RTX 1.7.** The stock `trt-rtx` pin is `onnxruntime-ep-nv-tensorrt-rtx-cu13` **0.5.0** (x86_64 `manylinux_2_28` and aarch64 `manylinux_2_35`, as before), which bundles TensorRT-RTX **1.7.1** instead of 1.6.1. Same ABI and minimum ORT (1.24). TensorRT-RTX engines are not forward-compatible, so an engine cache built under 1.6.1 (`trt_engine_cache_enable`) is rebuilt. +- **A vendored 0.4.0 wheel is replaced.** The wheel records no version, so the installer and `scripts/fetch-ep-plugins.sh` now treat the versioned runtime it bundles (`libtensorrt_rtx.so.1.7.1`) as the marker of the pinned build: a 0.4.0 install in the same directory is replaced, verified before the old directory is touched. `PluginPackageSpec.VersionMarkerFile` is the internal opt-in. +- The SONAME links (`libtensorrt_rtx.so.1`, `libtensorrt_onnxparser_rtx.so.1`) now follow whichever runtime version the directory holds instead of a hardcoded 1.6.1, which would have left the 0.5.0 EP unable to load. +- The patched WSL2 build stays at `0.4.0-wsl2p2` and is not rebuilt: the fix is expected upstream ([NVIDIA/TensorRT-RTX-EP-ABI#12](https://github.com/NVIDIA/TensorRT-RTX-EP-ABI/pull/12)). It links the stock wheel's runtime by SONAME and is verified on TensorRT-RTX 1.7.1 in WSL2. + ### Added - **QNN in the catalog.** `linml catalog install qnn` installs the Qualcomm QNN plugin (`onnxruntime-qnn` 2.6.0, aarch64) on Snapdragon aarch64 hosts. On other architectures the catalog reports it as not applicable, `catalog install` refuses without downloading it, and `setup --all` and `doctor` skip it with the reason. Not verified on hardware. diff --git a/README.md b/README.md index 07dbc27..72b6c91 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ OpenVINO, TensorRT-RTX, MIGraphX and QNN are plugin execution providers. LinML d | Plugin | Provider name | Platforms | Pinned artifact | Installed to (under `~/.local/linml-deps`) | |--------|---------------|-----------|-----------------|--------------------------------------------| | OpenVINO | `OpenVINOExecutionProvider` | Linux x86_64 with an Intel CPU, GPU or NPU | Intel `onnxruntime-ep-openvino` 1.6.1 wheel from PyPI, with the OpenVINO 2026.2.1 runtime bundled | `ep-plugins/openvino/onnxruntime_ep_openvino/` | -| TensorRT-RTX | `NvTensorRTRTXExecutionProvider` | Linux x86_64; Linux aarch64 (SBSA) | NVIDIA `onnxruntime-ep-nv-tensorrt-rtx-cu13` 0.4.0 wheel from PyPI, with TensorRT-RTX 1.6.1 bundled | `ep-plugins/trt-rtx/onnxruntime_ep_nv_tensorrt_rtx/` (x86_64); `ep-plugins/trt-rtx-arm64/onnxruntime_ep_nv_tensorrt_rtx/` (aarch64) | +| TensorRT-RTX | `NvTensorRTRTXExecutionProvider` | Linux x86_64; Linux aarch64 (SBSA) | NVIDIA `onnxruntime-ep-nv-tensorrt-rtx-cu13` 0.5.0 wheel from PyPI, with TensorRT-RTX 1.7.1 bundled | `ep-plugins/trt-rtx/onnxruntime_ep_nv_tensorrt_rtx/` (x86_64); `ep-plugins/trt-rtx-arm64/onnxruntime_ep_nv_tensorrt_rtx/` (aarch64) | | TensorRT-RTX on WSL2 | same | WSL2 with GPU passthrough (`/dev/dxg`) | LinML patched build `0.4.0-wsl2p2` (GitHub release; glibc 2.35+, the same floor as the wheel), used alongside the wheel above | `ep-plugins/trt-rtx-wsl2/linml_trt_rtx_ep/` | | MIGraphX | `MIGraphXExecutionProvider` | Linux x86_64 with an AMD GPU and ROCm | LinML build `2611.1` (GitHub release), built from AMD's `onnxruntime-ep-amdgpu` | `ep-plugins/migraphx/linml_migraphx_ep/` | | QNN | `QNNExecutionProvider` | Linux aarch64 with a Qualcomm Snapdragon NPU | Qualcomm `onnxruntime-qnn` 2.6.0 tarball (GitHub release), with the QAIRT 2.50.40 runtime bundled | `ep-plugins/qnn/onnxruntime_qnn/` | @@ -246,14 +246,14 @@ These commands use only `curl`, `sha256sum`, `unzip`, and `mktemp`. Each downloa TMP="$(mktemp -d)" case "$(uname -m)" in x86_64) - WHEEL=onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_28_x86_64.whl - URL=https://files.pythonhosted.org/packages/9c/68/093626a054300fa7f4f1a6d18b775d185034c6bb7fb056ac4f9d1e9b50d4/$WHEEL - SHA256=68b8f7306b50cf76daf997b8be24ec3813498c15d48e0ddd211db14f2a9daa19 + WHEEL=onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_28_x86_64.whl + URL=https://files.pythonhosted.org/packages/6b/ea/033a6d73b74a443cbf67247e3700b2a2f5076b37af4b00e433fbbd46fa6d/$WHEEL + SHA256=32f575c633d171b4edf4b53d3bb22b72a6c9c0a850d2982ce9a788a5d40d3298 DIR=trt-rtx ;; aarch64) - WHEEL=onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_35_aarch64.whl - URL=https://files.pythonhosted.org/packages/41/63/ef110d73730969957740d770b29c158ca79a9df32c65ee081b01f8b64124/$WHEEL - SHA256=0ccf6a636f9f49d74e3e7e3b1b90287aa1c818db770b47fad6e8aa344f4290a5 + WHEEL=onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_35_aarch64.whl + URL=https://files.pythonhosted.org/packages/9c/5e/9b63610651a0f5d2c8712b84f64c8cee3b3b83564cc20be207d00272d982/$WHEEL + SHA256=1f0bb2b89bb3dfd43096fccb8ec1f581c266c5852c4e6ba672b7bf790c8c96c2 DIR=trt-rtx-arm64 # The aarch64 wheel is manylinux_2_35: refuse an older glibc before downloading it. GLIBC="$(getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}')" || GLIBC="" @@ -269,8 +269,8 @@ These commands use only `curl`, `sha256sum`, `unzip`, and `mktemp`. Each downloa unzip -q -j -o "$TMP/$WHEEL" 'onnxruntime_ep_nv_tensorrt_rtx/*' -d "$OUT" # The plugin links SONAMEs, but the wheel ships only versioned files: add the links. (cd "$OUT" && - ln -sf libtensorrt_rtx.so.1.6.1 libtensorrt_rtx.so.1 && - ln -sf libtensorrt_onnxparser_rtx.so.1.6.1 libtensorrt_onnxparser_rtx.so.1 && + ln -sf libtensorrt_rtx.so.1.7.1 libtensorrt_rtx.so.1 && + ln -sf libtensorrt_onnxparser_rtx.so.1.7.1 libtensorrt_onnxparser_rtx.so.1 && ln -sf "$(ls libcudart.so.13.* | head -n 1)" libcudart.so.13) rm -rf "$TMP" echo "TensorRT-RTX installed to $OUT" @@ -790,7 +790,7 @@ for warning in report.compatibility_warnings: It covers: - **Built-in EPs**: `CPUExecutionProvider`, `CUDAExecutionProvider`, and `TensorrtExecutionProvider` -- **Plugin EPs**: `OpenVINOExecutionProvider` (1.6.1), `NvTensorRTRTXExecutionProvider` (0.4.0), and `MIGraphXExecutionProvider` (2611.1) +- **Plugin EPs**: `OpenVINOExecutionProvider` (1.6.1), `NvTensorRTRTXExecutionProvider` (0.5.0), and `MIGraphXExecutionProvider` (2611.1) ```csharp using LinML.Catalog; diff --git a/docs/compatibility.md b/docs/compatibility.md index 48169dd..5faf2bd 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -18,9 +18,9 @@ **TensorRT-RTX on aarch64.** NVIDIA's TensorRT-RTX 1.6 supports Ubuntu 24.04 SBSA (Linux aarch64) with CUDA 13.4, gcc 11.4, and compute capabilities 7.5, 8.6, 8.9, -12.0 and 12.13. LinML pins the same EP version (0.4.0) for `manylinux_2_35_aarch64` +12.0 and 12.13. LinML pins the same EP version as on x64 (0.5.0, TensorRT-RTX 1.7.1) for `manylinux_2_35_aarch64` (glibc 2.35 or newer), selected automatically on aarch64 hosts. The aarch64 build is -not yet verified on aarch64 hardware. The x64 pin is unchanged. +not yet verified on aarch64 hardware. ## Execution Providers @@ -135,9 +135,11 @@ not yet verified on aarch64 hardware. The x64 pin is unchanged. detects WSL2 and an RTX Ampere+ GPU. On WSL2 the CLI prefers the patched build for `trt-rtx` (as does `env-gpu-deps.sh`); `TRT_RTX_CUSTOM_EP` overrides it (also in the catalog since 0.4.2). Notes: - The archive contains only the Apache-2.0 EP plugin `.so` (~5 MB, RUNPATH - `$ORIGIN`); it dynamically links the TensorRT-RTX 1.6.1 runtime + `$ORIGIN`); it dynamically links the TensorRT-RTX runtime by SONAME (`libtensorrt_rtx.so.1`, `libtensorrt_onnxparser_rtx.so.1`) and CUDA 13, - which the stock `trt-rtx` wheel provides. + which the stock `trt-rtx` wheel provides. It is built from EP 0.4.0 against the + 1.6.1 SDK; since the stock pin moved to 0.5.0 it runs on that wheel's TensorRT-RTX + 1.7.1 runtime (verified in WSL2 on an RTX 5070: `TensorrtRtxWsl2EndToEndTests`). - Built by `scripts/build-trt-rtx-ep.sh`, which pins the PR head commit and produces a byte-stable archive. It is **not** built in CI: the TensorRT-RTX SDK (headers + import libs) is under NVIDIA's license and cannot be diff --git a/scripts/fetch-ep-plugins.sh b/scripts/fetch-ep-plugins.sh index bf21dc1..2d5dccc 100644 --- a/scripts/fetch-ep-plugins.sh +++ b/scripts/fetch-ep-plugins.sh @@ -4,7 +4,7 @@ # ~/.local/linml-deps/ep-plugins/: # # trt-rtx NvTensorRTRTXExecutionProvider (NVIDIA). The wheel bundles the EP -# plugin .so AND the TensorRT-RTX 1.6.1 runtime libraries, so no +# plugin .so AND the TensorRT-RTX 1.7.1 runtime libraries, so no # separate TensorRT-RTX SDK install is needed. x86_64 hosts get the # x86_64 wheel; Linux aarch64 (SBSA) hosts get the aarch64 wheel. # openvino OpenVINOExecutionProvider (Intel onnxruntime-ep-openvino). The @@ -42,18 +42,27 @@ DEPS="${HOME}/.local/linml-deps" TMP_ROOT="$(mktemp -d)" trap 'rm -rf "$TMP_ROOT"' EXIT +# The TensorRT-RTX runtime the trt-rtx wheels bundle (same on both architectures). +TRT_RTX_RUNTIME_VERSION="1.7.1" +TRT_RTX_RUNTIME="libtensorrt_rtx.so.${TRT_RTX_RUNTIME_VERSION}" + fetch_trt_rtx() { local OUT="$DEPS/ep-plugins/trt-rtx/onnxruntime_ep_nv_tensorrt_rtx" - local WHEEL_URL="https://files.pythonhosted.org/packages/9c/68/093626a054300fa7f4f1a6d18b775d185034c6bb7fb056ac4f9d1e9b50d4/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_28_x86_64.whl" - local WHEEL_SHA256="68b8f7306b50cf76daf997b8be24ec3813498c15d48e0ddd211db14f2a9daa19" - local WHEEL_NAME="onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_28_x86_64.whl" + local WHEEL_URL="https://files.pythonhosted.org/packages/6b/ea/033a6d73b74a443cbf67247e3700b2a2f5076b37af4b00e433fbbd46fa6d/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_28_x86_64.whl" + local WHEEL_SHA256="32f575c633d171b4edf4b53d3bb22b72a6c9c0a850d2982ce9a788a5d40d3298" + local WHEEL_NAME="onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_28_x86_64.whl" local TMP="$TMP_ROOT/trt-rtx" echo "=== LinML EP plugin fetch (TensorRT-RTX) ===" + # The wheel records no version; the versioned TensorRT-RTX runtime it bundles + # marks the build, so an earlier wheel in the same directory is replaced. if [ -f "$OUT/libonnxruntime_providers_nv_tensorrt_rtx.so" ]; then - echo "Already vendored: $OUT" - return 0 + if [ -f "$OUT/$TRT_RTX_RUNTIME" ]; then + echo "Already vendored: $OUT" + return 0 + fi + echo "Vendored build at $OUT does not bundle $TRT_RTX_RUNTIME; replacing it." fi # Staged like the OpenVINO fetch: a failed extract never leaves a @@ -89,8 +98,8 @@ PYEOF # versioned files only. Create the SONAME symlinks next to the libraries. local lib for lib in libtensorrt_rtx libtensorrt_onnxparser_rtx; do - if [ -f "$STAGE/${lib}.so.1.6.1" ] && [ ! -e "$STAGE/${lib}.so.1" ]; then - ln -s "${lib}.so.1.6.1" "$STAGE/${lib}.so.1" + if [ -f "$STAGE/${lib}.so.${TRT_RTX_RUNTIME_VERSION}" ] && [ ! -e "$STAGE/${lib}.so.1" ]; then + ln -s "${lib}.so.${TRT_RTX_RUNTIME_VERSION}" "$STAGE/${lib}.so.1" fi done if [ -f "$STAGE/libcudart.so.13.4.36" ] && [ ! -e "$STAGE/libcudart.so.13" ]; then @@ -115,9 +124,9 @@ PYEOF fetch_trt_rtx_arm64() { local OUT="$DEPS/ep-plugins/trt-rtx-arm64/onnxruntime_ep_nv_tensorrt_rtx" - local WHEEL_URL="https://files.pythonhosted.org/packages/41/63/ef110d73730969957740d770b29c158ca79a9df32c65ee081b01f8b64124/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_35_aarch64.whl" - local WHEEL_SHA256="0ccf6a636f9f49d74e3e7e3b1b90287aa1c818db770b47fad6e8aa344f4290a5" - local WHEEL_NAME="onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_35_aarch64.whl" + local WHEEL_URL="https://files.pythonhosted.org/packages/9c/5e/9b63610651a0f5d2c8712b84f64c8cee3b3b83564cc20be207d00272d982/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_35_aarch64.whl" + local WHEEL_SHA256="1f0bb2b89bb3dfd43096fccb8ec1f581c266c5852c4e6ba672b7bf790c8c96c2" + local WHEEL_NAME="onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_35_aarch64.whl" local TMP="$TMP_ROOT/trt-rtx" echo "=== LinML EP plugin fetch (TensorRT-RTX) ===" @@ -130,9 +139,14 @@ fetch_trt_rtx_arm64() { return 1 fi + # The wheel records no version; the versioned TensorRT-RTX runtime it bundles + # marks the build, so an earlier wheel in the same directory is replaced. if [ -f "$OUT/libonnxruntime_providers_nv_tensorrt_rtx.so" ]; then - echo "Already vendored: $OUT" - return 0 + if [ -f "$OUT/$TRT_RTX_RUNTIME" ]; then + echo "Already vendored: $OUT" + return 0 + fi + echo "Vendored build at $OUT does not bundle $TRT_RTX_RUNTIME; replacing it." fi # Staged like the OpenVINO fetch: a failed extract never leaves a @@ -168,8 +182,8 @@ PYEOF # versioned files only. Create the SONAME symlinks next to the libraries. local lib for lib in libtensorrt_rtx libtensorrt_onnxparser_rtx; do - if [ -f "$STAGE/${lib}.so.1.6.1" ] && [ ! -e "$STAGE/${lib}.so.1" ]; then - ln -s "${lib}.so.1.6.1" "$STAGE/${lib}.so.1" + if [ -f "$STAGE/${lib}.so.${TRT_RTX_RUNTIME_VERSION}" ] && [ ! -e "$STAGE/${lib}.so.1" ]; then + ln -s "${lib}.so.${TRT_RTX_RUNTIME_VERSION}" "$STAGE/${lib}.so.1" fi done # The aarch64 wheel ships libcudart.so.13.4.46; link whatever patch version it has. @@ -368,8 +382,9 @@ fetch_trt_rtx_wsl2() { echo "=== LinML EP plugin fetch (TensorRT-RTX, WSL2 patch) ===" - # The patched EP dynamically links the TensorRT-RTX 1.6.1 runtime libraries, - # which only the stock wheel bundles; vendor the wheel first. + # The patched EP dynamically links the TensorRT-RTX runtime libraries (by + # SONAME, libtensorrt_rtx.so.1), which only the stock wheel bundles; vendor + # the wheel first. fetch_trt_rtx # The library alone does not say which build it is: an earlier one sits in the diff --git a/src/LinML.Catalog/PluginInstaller.cs b/src/LinML.Catalog/PluginInstaller.cs index 97e156f..98807db 100644 --- a/src/LinML.Catalog/PluginInstaller.cs +++ b/src/LinML.Catalog/PluginInstaller.cs @@ -268,22 +268,22 @@ internal static void CreateRequiredSymlinks(string directory) return; } - string[] sonamePairs = - [ - "libtensorrt_rtx.so.1.6.1", "libtensorrt_rtx.so.1", - "libtensorrt_onnxparser_rtx.so.1.6.1", "libtensorrt_onnxparser_rtx.so.1" - ]; - - for (int i = 0; i < sonamePairs.Length; i += 2) + // The TensorRT-RTX runtime is versioned (libtensorrt_rtx.so.1.7.1 in the 0.5.0 wheel, 1.6.1 in 0.4.0), + // so link whichever version the directory holds to the SONAME the EP needs. + foreach (string library in (string[])["libtensorrt_rtx", "libtensorrt_onnxparser_rtx"]) { - string targetName = sonamePairs[i]; - string linkName = sonamePairs[i + 1]; - string targetPath = Path.Combine(directory, targetName); - string linkPath = Path.Combine(directory, linkName); + string linkPath = Path.Combine(directory, library + ".so.1"); + if (File.Exists(linkPath)) + { + continue; + } - if (File.Exists(targetPath) && !File.Exists(linkPath)) + string? target = Directory.EnumerateFiles(directory, library + ".so.1.*") + .OrderBy(path => path, StringComparer.Ordinal) + .LastOrDefault(); + if (target is not null) { - File.CreateSymbolicLink(linkPath, targetName); + File.CreateSymbolicLink(linkPath, Path.GetFileName(target)); } } diff --git a/src/LinML.Catalog/PluginPackageSpec.cs b/src/LinML.Catalog/PluginPackageSpec.cs index 3b91dc4..6a32fcb 100644 --- a/src/LinML.Catalog/PluginPackageSpec.cs +++ b/src/LinML.Catalog/PluginPackageSpec.cs @@ -11,6 +11,8 @@ namespace LinML.Catalog; /// MinimumGlibc, when set, is the oldest glibc the wheel loads on. /// VersionManifestFile, when set, names a JSON file in the package whose epVersion records the /// version it was built as: an install only counts as vendored when that equals Version. +/// VersionMarkerFile, when set, names a file in the package that only the pinned build ships (for a +/// wheel with no manifest, its versioned runtime library): an install without it is an earlier build. /// internal sealed record PluginPackageSpec( ProviderId Id, @@ -24,7 +26,8 @@ internal sealed record PluginPackageSpec( Version MinimumOrtVersion, string Architecture = "x64", Version? MinimumGlibc = null, - string? VersionManifestFile = null) + string? VersionManifestFile = null, + string? VersionMarkerFile = null) { public string InstallDirectory(string installRoot) => Path.Combine(installRoot, InstallSubdirectory); @@ -59,13 +62,16 @@ internal sealed record PluginPackageSpec( /// /// Whether the library at exists and is the pinned build. A package without - /// a has nothing to compare, so any library counts. With one, the vendored - /// version must equal : successive builds share one install directory, and a library - /// that is merely present may be an earlier build the pin replaced. + /// a or has nothing to compare, so any + /// library counts. With a manifest, the vendored version must equal ; with a marker, the + /// marker must sit beside the library: successive builds share one install directory, and a library that + /// is merely present may be an earlier build the pin replaced. /// public bool IsCurrent(string libraryPath) => File.Exists(libraryPath) - && (VersionManifestFile is null || string.Equals(VendoredVersion(libraryPath), Version, StringComparison.Ordinal)); + && (VersionManifestFile is null || string.Equals(VendoredVersion(libraryPath), Version, StringComparison.Ordinal)) + && (VersionMarkerFile is null + || File.Exists(Path.Combine(Path.GetDirectoryName(libraryPath) ?? string.Empty, VersionMarkerFile))); /// Whether the pinned build is vendored under . public bool IsVendored(string installRoot) => IsCurrent(LibraryPath(installRoot)); @@ -152,37 +158,44 @@ internal static class KnownPlugins // NVIDIA TensorRT-RTX standalone plugin EP. Works on RTX Ampere+ GPUs. The // wheel is per architecture: x86_64 for desktop and workstation hosts, and - // aarch64 for Linux SBSA hosts (TensorRT-RTX 1.6 supports Ubuntu 24.04 SBSA). + // aarch64 for Linux SBSA hosts (TensorRT-RTX supports Ubuntu 24.04 SBSA). // Under WSL2, stock PyPI wheels register but expose zero devices due to - // virtual PCI bus mapping; see docs/compatibility.md. + // virtual PCI bus mapping; see docs/compatibility.md. The wheel has no version + // manifest, so its versioned TensorRT-RTX runtime marks the build: a 0.4.0 + // install (TensorRT-RTX 1.6.1) in the same directory is replaced. + // The TensorRT-RTX runtime the 0.5.0 wheels bundle (both architectures). + private const string TensorrtRtxRuntimeMarker = "libtensorrt_rtx.so.1.7.1"; + internal static readonly PluginPackageSpec TensorrtRtxLinuxX64 = new( Id: ProviderId.TensorrtRtx, RegistrationName: "NvTensorRTRTXExecutionProvider", - Version: "0.4.0", - WheelUrl: new Uri("https://files.pythonhosted.org/packages/9c/68/093626a054300fa7f4f1a6d18b775d185034c6bb7fb056ac4f9d1e9b50d4/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_28_x86_64.whl"), - WheelSha256: "68b8f7306b50cf76daf997b8be24ec3813498c15d48e0ddd211db14f2a9daa19", + Version: "0.5.0", + WheelUrl: new Uri("https://files.pythonhosted.org/packages/6b/ea/033a6d73b74a443cbf67247e3700b2a2f5076b37af4b00e433fbbd46fa6d/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_28_x86_64.whl"), + WheelSha256: "32f575c633d171b4edf4b53d3bb22b72a6c9c0a850d2982ce9a788a5d40d3298", PackagePrefix: "onnxruntime_ep_nv_tensorrt_rtx/", InstallSubdirectory: Path.Combine("ep-plugins", "trt-rtx", "onnxruntime_ep_nv_tensorrt_rtx"), LibraryFileName: "libonnxruntime_providers_nv_tensorrt_rtx.so", MinimumOrtVersion: new Version(1, 24, 0), - Architecture: "x64"); + Architecture: "x64", + VersionMarkerFile: TensorrtRtxRuntimeMarker); // The same EP for Linux aarch64 (SBSA). Same version and ABI, and the same - // bundled TensorRT-RTX 1.6.1 runtime, built for aarch64 (manylinux_2_35, so + // bundled TensorRT-RTX 1.7.1 runtime, built for aarch64 (manylinux_2_35, so // glibc 2.35 or newer). Installs beside the x64 build, not over it. Not yet // verified on aarch64 hardware. internal static readonly PluginPackageSpec TensorrtRtxLinuxArm64 = new( Id: ProviderId.TensorrtRtx, RegistrationName: "NvTensorRTRTXExecutionProvider", - Version: "0.4.0", - WheelUrl: new Uri("https://files.pythonhosted.org/packages/41/63/ef110d73730969957740d770b29c158ca79a9df32c65ee081b01f8b64124/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.4.0-py3-none-manylinux_2_35_aarch64.whl"), - WheelSha256: "0ccf6a636f9f49d74e3e7e3b1b90287aa1c818db770b47fad6e8aa344f4290a5", + Version: "0.5.0", + WheelUrl: new Uri("https://files.pythonhosted.org/packages/9c/5e/9b63610651a0f5d2c8712b84f64c8cee3b3b83564cc20be207d00272d982/onnxruntime_ep_nv_tensorrt_rtx_cu13-0.5.0-py3-none-manylinux_2_35_aarch64.whl"), + WheelSha256: "1f0bb2b89bb3dfd43096fccb8ec1f581c266c5852c4e6ba672b7bf790c8c96c2", PackagePrefix: "onnxruntime_ep_nv_tensorrt_rtx/", InstallSubdirectory: Path.Combine("ep-plugins", "trt-rtx-arm64", "onnxruntime_ep_nv_tensorrt_rtx"), LibraryFileName: "libonnxruntime_providers_nv_tensorrt_rtx.so", MinimumOrtVersion: new Version(1, 24, 0), Architecture: "arm64", - MinimumGlibc: new Version(2, 35)); + MinimumGlibc: new Version(2, 35), + VersionMarkerFile: TensorrtRtxRuntimeMarker); /// The TensorRT-RTX pin for this host: the wheel built for its architecture. public static PluginPackageSpec TensorrtRtx => diff --git a/tests/LinML.Catalog.Tests/PluginExecutionProviderTests.cs b/tests/LinML.Catalog.Tests/PluginExecutionProviderTests.cs index 04142d0..081cda4 100644 --- a/tests/LinML.Catalog.Tests/PluginExecutionProviderTests.cs +++ b/tests/LinML.Catalog.Tests/PluginExecutionProviderTests.cs @@ -178,7 +178,7 @@ public void Catalog_ListsAllProviders_AndFindsThemById() IExecutionProvider? trtRtx = catalog.Find(Contracts.ProviderId.TensorrtRtx); Assert.NotNull(trtRtx); Assert.Equal(ProviderSource.Plugin, trtRtx.Source); - Assert.Equal("0.4.0", trtRtx.PinnedVersion); + Assert.Equal("0.5.0", trtRtx.PinnedVersion); IExecutionProvider? migraphx = catalog.Find(Contracts.ProviderId.MigraphX); Assert.NotNull(migraphx); diff --git a/tests/LinML.Catalog.Tests/PluginInstallerTests.cs b/tests/LinML.Catalog.Tests/PluginInstallerTests.cs index 426e099..773d07b 100644 --- a/tests/LinML.Catalog.Tests/PluginInstallerTests.cs +++ b/tests/LinML.Catalog.Tests/PluginInstallerTests.cs @@ -317,8 +317,11 @@ public async Task Install_ReplacesLeftoverDirectoryWithoutLibrary() Assert.Empty(Directory.GetDirectories(dir)); } - [Fact] - public void CreateRequiredSymlinks_CreatesRelativeSonameLinksOnLinux() + // Whichever TensorRT-RTX runtime the wheel bundles (1.7.1 in 0.5.0, 1.6.1 in 0.4.0) gets its SONAME link. + [Theory] + [InlineData("1.7.1")] + [InlineData("1.6.1")] + public void CreateRequiredSymlinks_CreatesRelativeSonameLinksOnLinux(string runtimeVersion) { if (!OperatingSystem.IsLinux()) { @@ -326,16 +329,58 @@ public void CreateRequiredSymlinks_CreatesRelativeSonameLinksOnLinux() } using var root = new TempDir(); - string targetFile = Path.Combine(root.Path, "libtensorrt_rtx.so.1.6.1"); - File.WriteAllText(targetFile, "dummy"); + foreach (string library in (string[])["libtensorrt_rtx", "libtensorrt_onnxparser_rtx"]) + { + File.WriteAllText(Path.Combine(root.Path, $"{library}.so.{runtimeVersion}"), "dummy"); + } PluginInstaller.CreateRequiredSymlinks(root.Path); - string linkFile = Path.Combine(root.Path, "libtensorrt_rtx.so.1"); - Assert.True(File.Exists(linkFile)); - var linkTarget = File.ResolveLinkTarget(linkFile, returnFinalTarget: false); - Assert.NotNull(linkTarget); - Assert.Equal("libtensorrt_rtx.so.1.6.1", linkTarget.Name); + foreach (string library in (string[])["libtensorrt_rtx", "libtensorrt_onnxparser_rtx"]) + { + string linkFile = Path.Combine(root.Path, library + ".so.1"); + Assert.True(File.Exists(linkFile)); + var linkTarget = File.ResolveLinkTarget(linkFile, returnFinalTarget: false); + Assert.NotNull(linkTarget); + Assert.Equal($"{library}.so.{runtimeVersion}", linkTarget.Name); + } + } + + // A wheel with no manifest (the stock TensorRT-RTX wheel) is told apart by a file only the pinned build + // ships. 0.4.0 and 0.5.0 share one install directory, so a 0.4.0 install must be replaced, not kept. + private static PluginPackageSpec Marked(byte[] wheel) => + FakeWheel.Spec(wheel) with { VersionMarkerFile = "libruntime.so.2621" }; + + [Fact] + public async Task Install_MarkedPackage_ReplacesAnInstallWithoutTheMarker() + { + using var root = new TempDir(); + byte[] wheel = FakeWheel.Build(); + PluginPackageSpec pinned = Marked(wheel); + VendorByHand(pinned, root.Path, buildInfoVersion: null); + Assert.False(pinned.IsVendored(root.Path)); + + bool installed = await new PluginInstaller(new FakeWheelHandler(wheel)).InstallAsync(pinned, root.Path, CancellationToken.None); + + Assert.True(installed); + Assert.Equal("plugin", File.ReadAllText(pinned.LibraryPath(root.Path))); + Assert.True(pinned.IsVendored(root.Path)); + } + + [Fact] + public async Task Install_MarkedPackage_KeepsAnInstallWithTheMarker() + { + using var root = new TempDir(); + byte[] wheel = FakeWheel.Build(); + PluginPackageSpec pinned = Marked(wheel); + var handler = new FakeWheelHandler(wheel); + var installer = new PluginInstaller(handler); + await installer.InstallAsync(pinned, root.Path, CancellationToken.None); + + bool second = await installer.InstallAsync(pinned, root.Path, CancellationToken.None); + + Assert.False(second); + Assert.Equal(1, handler.Requests); } [Fact] @@ -351,7 +396,7 @@ public async Task Install_WhenAlreadyPresent_RepairsMissingSymlinks() string installDir = spec.InstallDirectory(root.Path); Directory.CreateDirectory(installDir); File.WriteAllText(spec.LibraryPath(root.Path), "dummy lib"); - File.WriteAllText(Path.Combine(installDir, "libtensorrt_rtx.so.1.6.1"), "dummy soname"); + File.WriteAllText(Path.Combine(installDir, spec.VersionMarkerFile!), "dummy soname"); var installer = new PluginInstaller(); bool installed = await installer.InstallAsync(spec, root.Path, CancellationToken.None); From 899b98e9d60e8ee19d03fd9cb013aaac087c27ee Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Thu, 22 Oct 2026 12:05:02 -0700 Subject: [PATCH 2/2] Link whichever libcudart patch version the x86_64 wheel ships fetch_trt_rtx hardcoded libcudart.so.13.4.36, so a wheel with another CUDA patch version would silently skip the libcudart.so.13 link and the EP would fail to load. Discover it as fetch_trt_rtx_arm64 and the installer do. Co-Authored-By: Claude Opus 5.5 --- scripts/fetch-ep-plugins.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/fetch-ep-plugins.sh b/scripts/fetch-ep-plugins.sh index 2d5dccc..0bc9fae 100644 --- a/scripts/fetch-ep-plugins.sh +++ b/scripts/fetch-ep-plugins.sh @@ -102,8 +102,13 @@ PYEOF ln -s "${lib}.so.${TRT_RTX_RUNTIME_VERSION}" "$STAGE/${lib}.so.1" fi done - if [ -f "$STAGE/libcudart.so.13.4.36" ] && [ ! -e "$STAGE/libcudart.so.13" ]; then - ln -s "libcudart.so.13.4.36" "$STAGE/libcudart.so.13" + # The CUDA runtime is patch-versioned (libcudart.so.13.4.36 in 0.5.0); link + # whatever patch version the wheel ships, as fetch_trt_rtx_arm64 does. + if [ ! -e "$STAGE/libcudart.so.13" ]; then + cudart="$(compgen -G "$STAGE/libcudart.so.13.*" | head -n 1 || true)" + if [ -n "$cudart" ]; then + ln -s "$(basename "$cudart")" "$STAGE/libcudart.so.13" + fi fi rm -rf "$OUT"