mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-12 22:33:54 -04:00
fix: prefer the Intel diffusers venv SYCL runtime at startup (#11971)
Fixes #11895 Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
1 parent
a8ff0c3a6e
commit
8a0548714d
3 files changed
+242
No files matched your search
+205
@@ -0,0 +1,205 @@
|
||||
#!/bin/bash
|
||||
# Exercise the real diffusers launcher with versioned ELF libraries, no torch,
|
||||
# downloads, venv creation or GPU required.
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
echo "SKIP: Intel Python runtime regression requires Linux ELF loading"
|
||||
exit 0
|
||||
fi
|
||||
for tool in gcc ldd python3; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "SKIP: $tool not available"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
if ! ldd --version 2>&1 | grep -qi 'glibc\|GNU libc'; then
|
||||
echo "SKIP: versioned ELF regression requires glibc"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
export TEST_SYSTEM_PYTHON
|
||||
TEST_SYSTEM_PYTHON=$(command -v python3)
|
||||
unset ENV_DIR BACKEND_FILE VIRTUAL_ENV PYTHONHOME LD_LIBRARY_PATH
|
||||
export PORTABLE_PYTHON=false BUILD_TYPE=cpu
|
||||
|
||||
# Fail immediately if startup attempts to provision anything.
|
||||
mkdir -p "$WORK/bin" "$WORK/good" "$WORK/bad" "$WORK/host" "$WORK/cwd"
|
||||
for tool in uv pip pip3 curl wget; do
|
||||
printf '#!/bin/bash\necho "Unexpected provisioning command" >&2\nexit 99\n' > "$WORK/bin/$tool"
|
||||
chmod +x "$WORK/bin/$tool"
|
||||
done
|
||||
export PATH="$WORK/bin:$PATH"
|
||||
|
||||
cat > "$WORK/loader.c" <<'C'
|
||||
int urFixtureOrigin(void) { return ORIGIN; }
|
||||
#ifdef MATCHING
|
||||
int urDeviceWaitExp(void) { return 42; }
|
||||
#else
|
||||
int urOldFunction(void) { return 0; }
|
||||
#endif
|
||||
C
|
||||
cat > "$WORK/loader.map" <<'MAP'
|
||||
LIBUR_LOADER_0.12 { global: ur*; local: *; };
|
||||
MAP
|
||||
cat > "$WORK/sycl.c" <<'C'
|
||||
int urDeviceWaitExp(void);
|
||||
int syclFixture(void) { return urDeviceWaitExp(); }
|
||||
C
|
||||
echo 'int adapterFixture(void) { return ORIGIN; }' > "$WORK/adapter.c"
|
||||
|
||||
# All loaders have the same SONAME and version namespace, but only the venv
|
||||
# copy exports the required versioned symbol (the reported failure class).
|
||||
for kind in good bad host; do
|
||||
flags=()
|
||||
case "$kind" in
|
||||
good) flags=(-DMATCHING -DORIGIN=1) ;;
|
||||
bad) flags=(-DORIGIN=2) ;;
|
||||
host) flags=(-DORIGIN=3) ;;
|
||||
esac
|
||||
gcc -shared -fPIC "${flags[@]}" "$WORK/loader.c" \
|
||||
-Wl,-soname,libur_loader.so.0 -Wl,--version-script,"$WORK/loader.map" \
|
||||
-o "$WORK/$kind/libur_loader.so.0"
|
||||
gcc -shared -fPIC "${flags[@]}" "$WORK/adapter.c" \
|
||||
-o "$WORK/$kind/libur_adapter_level_zero.so.0"
|
||||
done
|
||||
# The ELF loader expands $ORIGIN relative to the loaded library.
|
||||
# shellcheck disable=SC2016
|
||||
gcc -shared -fPIC "$WORK/sycl.c" -L"$WORK/good" -l:libur_loader.so.0 \
|
||||
-Wl,-soname,libsycl.so.9 -Wl,-rpath,'$ORIGIN' \
|
||||
-o "$WORK/good/libsycl.so.9"
|
||||
|
||||
cat > "$WORK/backend.py" <<'PY'
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
|
||||
assert sys.argv[1:] == ["--fixture"], sys.argv
|
||||
assert os.environ.get("LD_LIBRARY_PATH") == os.environ.get("EXPECTED_PATH"), (
|
||||
os.environ.get("LD_LIBRARY_PATH"), os.environ.get("EXPECTED_PATH")
|
||||
)
|
||||
if os.environ.get("LOAD_SYCL") == "1":
|
||||
sycl = ctypes.CDLL(os.path.join(os.environ["VIRTUAL_ENV"], "lib", "libsycl.so.9"))
|
||||
assert sycl.syclFixture() == 42
|
||||
if "EXPECTED_ORIGIN" in os.environ:
|
||||
expected = int(os.environ["EXPECTED_ORIGIN"])
|
||||
loader = ctypes.CDLL("libur_loader.so.0")
|
||||
assert loader.urFixtureOrigin() == expected
|
||||
adapter = ctypes.CDLL("libur_adapter_level_zero.so.0")
|
||||
assert adapter.adapterFixture() == expected
|
||||
print("fixture passed")
|
||||
PY
|
||||
|
||||
make_backend() {
|
||||
local dir="$1" env_dir="${2:-$1}"
|
||||
mkdir -p "$dir/common" "$env_dir/venv/bin" "$env_dir/venv/lib" "$env_dir/lib"
|
||||
cp "$REPO_ROOT/backend/python/diffusers/run.sh" "$dir/run.sh"
|
||||
cp "$REPO_ROOT/backend/python/common/libbackend.sh" "$dir/common/libbackend.sh"
|
||||
cp "$WORK/backend.py" "$dir/backend.py"
|
||||
cat > "$env_dir/venv/bin/python" <<'SH'
|
||||
#!/bin/bash
|
||||
exec "$TEST_SYSTEM_PYTHON" "$@"
|
||||
SH
|
||||
chmod +x "$env_dir/venv/bin/python"
|
||||
cp "$WORK/good/"* "$env_dir/venv/lib/"
|
||||
cp "$WORK/bad/"* "$env_dir/lib/"
|
||||
}
|
||||
|
||||
launch() {
|
||||
(cd "$WORK/cwd"; bash "$1/run.sh" --fixture)
|
||||
}
|
||||
|
||||
expect_failure() {
|
||||
if "$@" > "$WORK/failure.log" 2>&1; then
|
||||
echo "FAIL: incompatible loader unexpectedly succeeded"
|
||||
exit 1
|
||||
fi
|
||||
# A path assertion or launcher error must not count as the expected failure.
|
||||
if ! grep -q 'undefined symbol: urDeviceWaitExp, version LIBUR_LOADER_0.12' "$WORK/failure.log"; then
|
||||
cat "$WORK/failure.log"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
backend="$WORK/built/diffusers"
|
||||
make_backend "$backend"
|
||||
mv "$WORK/built" "$WORK/relocated"
|
||||
backend="$WORK/relocated/diffusers"
|
||||
export LOAD_SYCL=1 EXPECTED_ORIGIN=1
|
||||
|
||||
# Reproduce the old ordering using the same venv Python shim and backend file.
|
||||
export LD_LIBRARY_PATH="$backend/lib:$WORK/host"
|
||||
export EXPECTED_PATH="$LD_LIBRARY_PATH" VIRTUAL_ENV="$backend/venv"
|
||||
expect_failure "$backend/venv/bin/python" "$backend/backend.py" --fixture
|
||||
unset VIRTUAL_ENV
|
||||
|
||||
# Both inherited and packaged conflicts lose to the venv runtime.
|
||||
export LD_LIBRARY_PATH="$WORK/host" EXPECTED_PATH="$backend/venv/lib:$backend/lib:$WORK/host"
|
||||
launch "$backend"
|
||||
|
||||
# Every startBackend exec branch must receive the same search path.
|
||||
export BACKEND_FILE="$backend/backend.py"
|
||||
launch "$backend"
|
||||
unset BACKEND_FILE
|
||||
mv "$backend/backend.py" "$backend/server.py"
|
||||
launch "$backend"
|
||||
# BACKEND_NAME currently derives from the caller's working directory.
|
||||
mv "$backend/server.py" "$backend/cwd.py"
|
||||
launch "$backend"
|
||||
mv "$backend/cwd.py" "$backend/backend.py"
|
||||
|
||||
unset LD_LIBRARY_PATH
|
||||
export EXPECTED_PATH="$backend/venv/lib:$backend/lib:"
|
||||
launch "$backend"
|
||||
|
||||
# No packaged directory: the venv still takes precedence, with no empty entry
|
||||
# introduced when the inherited path is unset.
|
||||
mv "$backend/lib" "$backend/packaged"
|
||||
export EXPECTED_PATH="$backend/venv/lib"
|
||||
launch "$backend"
|
||||
export LD_LIBRARY_PATH="$WORK/host" EXPECTED_PATH="$backend/venv/lib:$WORK/host"
|
||||
launch "$backend"
|
||||
mv "$backend/packaged" "$backend/lib"
|
||||
|
||||
# The launcher accepts a relative path when its parent directory has spaces;
|
||||
# its existing unquoted $0 handling does not accept absolute paths with spaces.
|
||||
spaced="$WORK/relocated with spaces"
|
||||
make_backend "$spaced/diffusers"
|
||||
mkdir -p "$spaced/cwd"
|
||||
export EXPECTED_PATH="$spaced/diffusers/venv/lib:$spaced/diffusers/lib:$WORK/host"
|
||||
(cd "$spaced/cwd"; bash ../diffusers/run.sh --fixture)
|
||||
|
||||
separate="$WORK/separate environment"
|
||||
make_backend "$WORK/external" "$separate"
|
||||
export ENV_DIR="$separate" EXPECTED_PATH="$separate/venv/lib:$separate/lib:$WORK/host"
|
||||
launch "$WORK/external"
|
||||
unset ENV_DIR
|
||||
|
||||
# Missing and incompatible venv loaders must still fail, even though the venv
|
||||
# directory and SYCL library exist. Presence is not an ABI compatibility check.
|
||||
export EXPECTED_PATH="$backend/venv/lib:$backend/lib:$WORK/host"
|
||||
rm "$backend/venv/lib/libur_loader.so.0"
|
||||
expect_failure launch "$backend"
|
||||
cp "$WORK/bad/libur_loader.so.0" "$backend/venv/lib/"
|
||||
expect_failure launch "$backend"
|
||||
|
||||
# Without a SYCL library, unrelated CPU/CUDA files (or even a directory named
|
||||
# libsycl.so) must not change the existing library search order.
|
||||
rm "$backend/venv/lib/libsycl.so.9"
|
||||
mkdir "$backend/venv/lib/libsycl.so"
|
||||
touch "$backend/venv/lib/libcublas.so.12" "$backend/venv/lib/libcpu.so"
|
||||
export LOAD_SYCL=0 EXPECTED_ORIGIN=2 EXPECTED_PATH="$backend/lib:$WORK/host"
|
||||
launch "$backend"
|
||||
unset LD_LIBRARY_PATH
|
||||
export EXPECTED_PATH="$backend/lib:"
|
||||
launch "$backend"
|
||||
rm -rf "${backend:?}/lib"
|
||||
export LD_LIBRARY_PATH="$WORK/host" EXPECTED_PATH="$WORK/host" EXPECTED_ORIGIN=3
|
||||
launch "$backend"
|
||||
unset LD_LIBRARY_PATH EXPECTED_PATH EXPECTED_ORIGIN
|
||||
launch "$backend"
|
||||
|
||||
echo "PASS: Intel Python runtime precedence, relocation, fallback and import errors"
|
||||
Reference in new issue
Block a user