From dfb3eedf77d1e04ce68a5b2de0aa9f7a3c81acc0 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 23 May 2026 21:48:12 -0700 Subject: [PATCH] ci: broaden Linux + narrow Windows llama.cpp runtime patterns + trim #5741 comments (#5746) * ci: broaden Linux llama.cpp runtime pattern to lib*.so* #5741 patched the explicit Linux pattern list to add ``libllama-*-impl.so*`` after ggml-org/llama.cpp#23462 (between b9279 and b9283) split each binary's entry code into a paired ``lib-impl.so`` shared library. Same class of upstream repackaging will hit us again whenever a new shared lib is added. Mirror what macOS already does and replace the per-lib list with a single ``lib*.so*`` glob. ``copy_globs`` (line 3614) unions patterns, so the per-variant ``libggml-cuda.so*`` / ``libggml-hip.so*`` entries were never filtering anything; the spec lives in ``runtime_payload_health_groups`` (line 5209) which keeps the explicit minimum-required list per variant. Dry-run against b9296-bin-ubuntu-x64.tar.gz: 40 files copied (all ggml, llama, mtmd, impl variants + the two binaries we ship), 22 skipped (other CLIs, rpc-server, LICENSE). Functionally equal to the post-#5741 set. * cleanup: trim #5741 comments on the pydantic split Comments added in #5741 explained the original bug in full each time. They are mostly redundant with the commit message and the PR. Trim them to one short paragraph per site. No behavior change. * ci: narrow Windows runtime pattern to llama-server.exe + llama-quantize.exe Studio only invokes llama-server and llama-quantize. Mac and Linux already filter to those two binaries; Windows was the odd one out with ``*.exe`` copying every CLI upstream ships (llama-cli, llama-bench, llama-mtmd-cli, ...). Dry-run on b9296 (win cpu-x64, cpu-arm64, cuda-13.1, hip-radeon): 20 unused EXEs skipped per variant, all DLLs (incl. the new llama-*-impl.dll family) still copied via ``*.dll``. ``existing_install_matches_choice`` already checks llama-server.exe exists explicitly (line 5297), so the health gate is unchanged. --- install.ps1 | 13 +++---- install.sh | 13 +++---- .../backend/requirements/no-torch-runtime.txt | 18 +++------- studio/install_llama_prebuilt.py | 35 +++++-------------- studio/install_python_stack.py | 21 +++-------- tests/studio/install/test_rocm_support.py | 23 ++++++------ 6 files changed, 38 insertions(+), 85 deletions(-) diff --git a/install.ps1 b/install.ps1 index b26566cc3d..07512de720 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1302,13 +1302,9 @@ shell.Run cmd, 0, False # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.5.6" unsloth-zoo } if ($baseInstallExit -eq 0) { - # Install pydantic WITH deps so pip pins pydantic-core to - # the exact version pydantic's metadata requires. The - # --no-deps install of no-torch-runtime.txt below would - # otherwise pick the latest of each independently and - # trip pydantic's _ensure_pydantic_core_version check. - # pydantic's deps (annotated-types, pydantic-core, - # typing-extensions, typing-inspection) are torch-free. + # Resolve pydantic WITH deps so pip pins pydantic-core + # to the matching version (no-torch-runtime.txt below + # is --no-deps). All transitive deps are torch-free. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython pydantic } } if ($baseInstallExit -eq 0) { @@ -1358,8 +1354,7 @@ shell.Run cmd, 0, False # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --upgrade-package unsloth --upgrade-package unsloth-zoo "unsloth>=2026.5.6" unsloth-zoo } if ($baseInstallExit -eq 0) { - # Install pydantic WITH deps so pip pins pydantic-core to - # the matching version (see migrated branch above). + # Same pydantic-with-deps trick as the migrated branch. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython pydantic } } if ($baseInstallExit -eq 0) { diff --git a/install.sh b/install.sh index 9bdd935171..1e19e951de 100755 --- a/install.sh +++ b/install.sh @@ -1866,13 +1866,9 @@ if [ "$_MIGRATED" = true ]; then run_install_cmd "install unsloth (migrated no-torch)" uv pip install --python "$_VENV_PY" --no-deps \ --reinstall-package unsloth --reinstall-package unsloth-zoo \ "unsloth>=2026.5.6" unsloth-zoo - # Install pydantic WITH deps so pip pins pydantic-core to the - # exact version pydantic's own metadata requires. The --no-deps - # install below would otherwise pick the latest of each - # independently and trip pydantic's _ensure_pydantic_core_version - # check on the next import. pydantic's deps (annotated-types, - # pydantic-core, typing-extensions, typing-inspection) are - # torch-free, so this is safe on the no-torch path. + # Resolve pydantic WITH deps so pip pins pydantic-core to the + # matching version (no-torch-runtime.txt below is --no-deps). + # All transitive deps are torch-free. run_install_cmd "install pydantic (with deps for compatible core)" \ uv pip install --python "$_VENV_PY" pydantic _NO_TORCH_RT="$(_find_no_torch_runtime)" @@ -2051,8 +2047,7 @@ elif [ -n "$TORCH_INDEX_URL" ]; then run_install_cmd "install unsloth (no-torch)" uv pip install --python "$_VENV_PY" --no-deps \ --upgrade-package unsloth --upgrade-package unsloth-zoo \ "unsloth>=2026.5.6" unsloth-zoo - # Install pydantic WITH deps so pip pins pydantic-core to the - # exact version pydantic requires (see migrated branch above). + # Same pydantic-with-deps trick as the migrated branch. run_install_cmd "install pydantic (with deps for compatible core)" \ uv pip install --python "$_VENV_PY" pydantic _NO_TORCH_RT="$(_find_no_torch_runtime)" diff --git a/studio/backend/requirements/no-torch-runtime.txt b/studio/backend/requirements/no-torch-runtime.txt index c33ebf4d94..85294114b1 100644 --- a/studio/backend/requirements/no-torch-runtime.txt +++ b/studio/backend/requirements/no-torch-runtime.txt @@ -22,19 +22,11 @@ rich>=13.0 markdown-it-py>=3.0 mdurl>=0.1 pygments>=2.0 -# pydantic is intentionally NOT installed via this --no-deps file. -# install.sh / install.ps1 / install_python_stack.py run a separate -# `pip install pydantic` (with deps) just before this file is -# applied, so pip resolves `pydantic-core` to the exact version -# pydantic's `_ensure_pydantic_core_version` check expects. Listing -# pydantic + pydantic-core unpinned here and resolving them under -# --no-deps used to pick the latest of each independently and trip -# `SystemError: pydantic-core 2.X.Y is incompatible with the current -# pydantic version` on the first import (Windows fresh-venv repro -# was the canonical case). pydantic's transitive deps -# (annotated-types, pydantic-core, typing-extensions, -# typing-inspection) are torch-free, so installing it WITH deps -# does not pull torch. +# pydantic is intentionally NOT pinned here. install.sh / install.ps1 +# / install_python_stack.py run `pip install pydantic` WITH deps just +# before this --no-deps file is applied, so pip resolves pydantic-core +# to the exact version pydantic's _ensure_pydantic_core_version check +# expects. Pinning both under --no-deps used to drift them apart. pyyaml nest-asyncio diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index 91076a4743..7672af6630 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -3827,37 +3827,18 @@ def paired_runtime_dll_patterns(choice: AssetChoice) -> list[str]: def runtime_patterns_for_choice(choice: AssetChoice) -> list[str]: + # Broad shared-library glob + explicit binary names. Lets upstream + # repackage the SO/DLL set (e.g. ggml-org/llama.cpp#23462 split the + # per-binary entry code into paired ``lib-impl.so`` shared + # libraries between b9279 and b9283) without us re-enumerating + # every new file. Studio only invokes llama-server and llama-quantize; + # other CLIs upstream ships (llama-cli, llama-bench, ...) are skipped. if choice.install_kind in {"linux-cpu", "linux-cuda", "linux-rocm"}: - return [ - "llama-server", - "llama-quantize", - "libllama-common.so*", - "libllama.so*", - # Upstream llama.cpp split the per-binary entry code into - # paired ``libllama--impl.so`` shared libraries - # around release b9261. ``llama-server`` and - # ``llama-quantize`` are NEEDED-linked against - # ``libllama-server-impl.so`` / ``libllama-quantize-impl.so`` - # respectively, with RUNPATH ``$ORIGIN``. Without copying - # the impl ``.so`` files alongside the binaries, ldd - # reports them missing, preflight rejects the install, and - # the installer falls back to a source build on a fresh - # Linux install. Glob the whole family so future bundles - # that split additional binaries (e.g. ``llama-cli``, - # ``llama-bench``) keep working. - "libllama-*-impl.so*", - "libggml.so*", - "libggml-base.so*", - "libmtmd.so*", - "libggml-cpu-*.so*", - "libggml-cuda.so*", - "libggml-hip.so*", - "libggml-rpc.so*", - ] + return ["llama-server", "llama-quantize", "lib*.so*"] if choice.install_kind in {"macos-arm64", "macos-x64"}: return ["llama-server", "llama-quantize", "lib*.dylib"] if choice.install_kind in {"windows-cpu", "windows-cuda", "windows-hip"}: - return ["*.exe", "*.dll"] + return ["llama-server.exe", "llama-quantize.exe", "*.dll"] raise PrebuiltFallback( f"unsupported install kind for runtime overlay: {choice.install_kind}" ) diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py index 4dfa20032b..9166d35ce3 100644 --- a/studio/install_python_stack.py +++ b/studio/install_python_stack.py @@ -979,22 +979,11 @@ def install_python_stack() -> int: package_name, "unsloth-zoo", ) - # Pydantic ships its core as a separate compiled wheel - # (pydantic-core), and pydantic's ``_ensure_pydantic_core_version`` - # checks the installed core matches the exact version pinned in - # its own metadata. With ``--no-deps`` plus an unpinned - # ``pydantic`` / ``pydantic-core`` pair in no-torch-runtime.txt, - # pip resolved each to the newest available version and the two - # drifted (pydantic 2.13.4 pins pydantic-core==2.46.4 today, but - # pydantic-core 2.47.0 was the latest). On a fresh Windows venv - # the next ``import pydantic`` raised ``SystemError: ... - # incompatible with the current pydantic version``. - # - # Resolve them WITH deps in a focused pip call so pip picks a - # compatible pair. pydantic's own deps are - # ``annotated-types``, ``pydantic-core``, ``typing-extensions``, - # ``typing-inspection`` -- none of which transitively pull - # torch, so this is safe for the no-torch path. + # Resolve pydantic WITH deps so pip pins pydantic-core to the + # exact version pydantic's metadata declares. Under --no-deps + # alone pip picks the latest of each and trips pydantic's + # _ensure_pydantic_core_version check. Transitive deps are + # torch-free. pip_install( "Installing pydantic (with deps for compatible core)", "--no-cache-dir", diff --git a/tests/studio/install/test_rocm_support.py b/tests/studio/install/test_rocm_support.py index 698625e59b..e6f1ae1c65 100644 --- a/tests/studio/install/test_rocm_support.py +++ b/tests/studio/install/test_rocm_support.py @@ -346,28 +346,26 @@ class TestRuntimePatterns: patterns = runtime_patterns_for_choice(choice) assert "llama-server" in patterns assert "llama-quantize" in patterns - # Upstream split entry code into ``libllama--impl.so`` - # shared libraries (b9261+). llama-server and llama-quantize - # are NEEDED-linked against ``libllama-server-impl.so`` and - # ``libllama-quantize-impl.so`` respectively with RUNPATH - # ``$ORIGIN``, so the prebuilt overlay MUST copy them - # alongside the binaries or ldd reports them missing and - # preflight forces a source-build fallback. - assert "libllama-*-impl.so*" in patterns + # Broad lib*.so* covers libllama, libggml, libmtmd, libggml-cpu-*, + # plus the libllama--impl.so split that ggml-org/llama.cpp + # #23462 introduced between b9279 and b9283. + assert "lib*.so*" in patterns def test_linux_cuda_patterns(self): choice = AssetChoice( repo = "", tag = "", name = "", url = "", source_label = "", install_kind = "linux-cuda" ) patterns = runtime_patterns_for_choice(choice) - assert "libggml-cuda.so*" in patterns + # libggml-cuda.so is matched by lib*.so* now. + assert "lib*.so*" in patterns def test_linux_rocm_patterns(self): choice = AssetChoice( repo = "", tag = "", name = "", url = "", source_label = "", install_kind = "linux-rocm" ) patterns = runtime_patterns_for_choice(choice) - assert "libggml-hip.so*" in patterns + # libggml-hip.so is matched by lib*.so* now. + assert "lib*.so*" in patterns assert "llama-server" in patterns def test_windows_hip_patterns(self): @@ -380,7 +378,10 @@ class TestRuntimePatterns: install_kind = "windows-hip", ) patterns = runtime_patterns_for_choice(choice) - assert "*.exe" in patterns + # Narrowed from "*.exe" to the two binaries Studio actually + # invokes, mirroring the Linux/macOS pattern style. + assert "llama-server.exe" in patterns + assert "llama-quantize.exe" in patterns assert "*.dll" in patterns def test_macos_patterns(self):