Files
python-inference/packaging/build.spec
T

93 lines
3.1 KiB
RPMSpec

# PyInstaller spec for Dev Assist Chatbot.
#
# Build (from the repo root, in an environment with the CUDA 13.x toolkit
# installed - the toolkit is only needed at BUILD time, to copy its runtime
# DLLs into the bundle; end users only need an NVIDIA driver):
#
# uv run pyinstaller packaging/build.spec --noconfirm
#
# Output lands in dist/DevAssistChatbot/. That whole folder is the app -
# onedir rather than onefile, since unpacking a multi-GB bundle to a temp
# dir on every launch would make startup unusable.
import os
from pathlib import Path
from PyInstaller.utils.hooks import collect_submodules
PROJECT_ROOT = Path(SPECPATH).parent # noqa: F821 - injected by PyInstaller
VENV_SITE_PACKAGES = PROJECT_ROOT / ".venv" / "Lib" / "site-packages"
LLAMA_CPP_LIB = VENV_SITE_PACKAGES / "llama_cpp" / "lib"
if not LLAMA_CPP_LIB.is_dir():
raise SystemExit(
f"Expected llama_cpp's compiled libs at {LLAMA_CPP_LIB} - run this via "
"'uv run pyinstaller ...' from the repo root so the project venv is used."
)
CUDA_PATH = os.environ.get("CUDA_PATH")
if not CUDA_PATH:
raise SystemExit(
"CUDA_PATH is not set. Building requires the CUDA toolkit that "
"llama-cpp-python was compiled against, so its runtime DLLs can be "
"copied into the bundle (end users won't need the toolkit installed)."
)
CUDA_BIN = Path(CUDA_PATH) / "bin" / "x64"
# llama.cpp's ggml backend (ggml-cuda.dll etc.) is loaded dynamically at
# runtime, not linked at import time, so PyInstaller's static analysis can't
# discover it - every DLL in llama_cpp/lib/ has to be added explicitly. They
# all go into the same output folder so Windows' DLL search (which checks a
# loaded DLL's own directory for its dependencies) can resolve cublas from
# next to ggml-cuda.dll.
CUDA_RUNTIME_DLLS = [
"cudart64_13.dll",
"cublas64_13.dll",
"cublasLt64_13.dll",
"nvrtc64_130_0.dll",
"nvrtc-builtins64_134.dll",
]
binaries = [(str(dll), "llama_cpp/lib") for dll in LLAMA_CPP_LIB.glob("*.dll")]
for name in CUDA_RUNTIME_DLLS:
dll_path = CUDA_BIN / name
if not dll_path.is_file():
raise SystemExit(f"Expected CUDA runtime DLL not found: {dll_path}")
binaries.append((str(dll_path), "llama_cpp/lib"))
# ddgs picks its search backend (duckduckgo, bing, ...) via importlib at
# runtime rather than a static import, so PyInstaller won't find those
# engine modules on its own either.
hidden_imports = collect_submodules("ddgs") + ["pynvml"]
a = Analysis( # noqa: F821
[str(PROJECT_ROOT / "main.py")],
pathex=[str(PROJECT_ROOT)],
binaries=binaries,
datas=[],
hiddenimports=hidden_imports,
hookspath=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure) # noqa: F821
exe = EXE( # noqa: F821
pyz,
a.scripts,
[],
exclude_binaries=True,
name="DevAssistChatbot",
console=False,
)
coll = COLLECT( # noqa: F821
exe,
a.binaries,
a.zipfiles,
a.datas,
# UPX would try (slowly, and sometimes badly) to compress the ~650MB of
# CUDA runtime DLLs; skip it.
upx=False,
name="DevAssistChatbot",
)