mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-07 06:15:58 -10:00
setup: split build helpers (cmake, ninja) out of the runtime requirements
requirements.txt keeps the runtime packages only; requirements-build.txt pins cmake and ninja. setup.cmake_build installs only the helper that is missing, right before a local engine or image-encoder build, so the ready-made engine path (and serving) never runs pip for build tools. ram_gb() reads hw.memsize on macOS and no longer crashes without /proc/meminfo. docs/INSTALL.md notes the split; two tests in tools/test_setup_pins.py cover it. From the owner's rebuild branch strata/rebuild-2026-10. setup.py merged onto 0.1.45 without conflicts (0.1.45's MIN_ENGINE and pins are unchanged).
This commit is contained in:
1 parent
c968d37a48
commit
a242537838
5 files changed
+73
-12
No files matched your search
@@ -27,6 +27,11 @@ you the first time: Python 3.12 if you have none (for your user account, no admi
|
||||
the build tools (Visual Studio Build Tools + CUDA Toolkit on Windows, `build-essential` + CUDA on Ubuntu) and compiles
|
||||
the engine for your GPU (asks first; 20-40 minutes once). More: [details](DETAILS.md#before-you-start).
|
||||
|
||||
The normal Python install uses `requirements.txt` and does not install CMake or Ninja. If setup has to compile an
|
||||
engine or image encoder, it uses existing CMake/Ninja tools when available and installs missing pinned helpers from
|
||||
`requirements-build.txt` into `.venv`. An older `.venv` keeps any build helpers it already has; setup does not remove
|
||||
packages during an update.
|
||||
|
||||
## Windows
|
||||
|
||||
1. [Download this project](https://github.com/Niko1221/Strata/archive/refs/heads/main.zip) and unzip it (or
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Build helpers for a local engine or vision-encoder compile. Ready-made engines do not need these.
|
||||
cmake==4.4.3
|
||||
ninja==1.13.2
|
||||
+2
-3
@@ -1,4 +1,5 @@
|
||||
# The Python packages setup.py installs into .venv, pinned so a checkout installs the same ones on any day (#214).
|
||||
# Runtime Python packages setup.py installs into .venv, pinned so a checkout installs the same ones on any day (#214).
|
||||
# CMake and Ninja are in requirements-build.txt and are installed only if a local build needs them.
|
||||
# setup.py's pip step reads this file; an install from before it keeps the packages it has.
|
||||
# Python 3.10+ (numpy's newest releases need a newer Python: one line per range).
|
||||
numpy==2.5.3; python_version >= "3.12"
|
||||
@@ -9,8 +10,6 @@ regex==2026.9.10
|
||||
pyyaml==6.0.3
|
||||
tqdm==4.70.1
|
||||
requests==2.34.2
|
||||
cmake==4.4.3
|
||||
ninja==1.13.2
|
||||
pillow==12.3.0
|
||||
psutil==7.2.2
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ What the first run does (each step is skipped when it is already done):
|
||||
2. asks the questions
|
||||
3. installs the Python packages it needs into .venv (numpy, jinja2, ..., and NVIDIA's CUDA libraries)
|
||||
4. gets the Strata engine: a ready-made build for RTX 20/30/40/50 cards (no compiler needed); if none fits your PC,
|
||||
it installs the build tools (asks first) and compiles the engine for your GPU. AMD (--backend hip, chosen by
|
||||
it installs the missing build tools (asks first) and compiles the engine for your GPU. AMD (--backend hip, chosen by
|
||||
itself on a PC with no usable NVIDIA card): the ready-made HIP engine on Windows, compiled here on Linux
|
||||
5. downloads the model from Hugging Face (resumable), and the vision encoder if you want images
|
||||
6. prepares the model for Strata and fetches the MTP draft layer (~5 GB, from the original Qwen checkpoint)
|
||||
@@ -196,8 +196,10 @@ ENGINE12_DIR = "engine-cuda12"
|
||||
MIN_ENGINE = (0, 1, 45) # versions compare all numbers; v0.1.45: Qwen3.6 parallel requests and pictures, fixes #2346 #2372 #2396; v0.1.44.1: CPUs without AVX2 start again (#2324: an illegal instruction before main in the NVFP4 CPU rows), the CUDA 12.0/12.1 toolkits build (#2322), a crashed engine start is named instead of retried as a GPU runtime start, Mac M2 (#2323); v0.1.44: UD-Q4/Q5/Q6_K_XL and Q8_0 Unsloth files in setup (K-quant experts, MMQ prompt library strata_kq), NVFP4 packs, Qwen3.6-35B-A3B, Intel Arc A-series kernels, the pipelined-decode and latency work; v0.1.43.1: the adaptive overlap no longer collapses decode on pinned-host desktops (#1964 #1975), Windows PLE mmap stall (#1984), pinned prompt uploads (#1996), the I/O watchdog allowance; v0.1.43: adaptive-tier copies overlap on CUDA, P100 (sm_60) kernel tables, RDNA4 (gfx1200/gfx1201) defaults (route tail skip, measured PCIe share, 16K prompt chunks, fused IQ3 prompt experts); v0.1.42.1: the GGUF-in-place file tier on Windows unmaps every shard, dual-socket starts judge each NUMA node and STRATA_DROP_CACHE_ON_EXIT, tools/strata_bench.py; v0.1.42: STRATA_ROUTE_TAIL_SKIP=7 default on CUDA (+13-16% decode), the AMD 2-GPU checkpoint fix (H13), Phase 2 fixes and opt-ins; v0.1.41.1 (folded into 0.1.42): the #1576 card order, #1616 stage size, #1595 CPU share, #1630/#1607 Windows HIP chunk cap and commit-limit warning, #1603 parallel-request bookkeeping; v0.1.41: CPU prefill share on by default (NVIDIA, chunks < 1024), --batch-groups auto, the file tier read in batches on Windows, Pascal decode fix, per-arch tables; v0.1.40.3: the #1357 MTP router guard, the #1376 Windows HIP cache floor, #461 runtime DLLs, Intel A750 first-request fix; v0.1.40.2: F4 verify windows, the Linux file tier (#1194), the stager wait (#1057), #1264/#1201/#1139 fixes, opt-in CPU share (#1282), Intel Arc; v0.1.40: --resident-experts on a layer split with the split+resident variant (#848), --kv k8v4 with KV streaming (#711); v0.1.39: the #577 file-tier regression fixed, the OpenAI Responses API (#451, Codex), a reply stuck on one token ended (#606), the head before the arena (#620), effort_position (#458), --vram-reserve hot resize opt-in (#533), PR batch; v0.1.38: prompts faster (one gather per expert group #372, the first chunk's PLE rows beside layer 0 #374, DeltaNet three heads per thread #413), --kv q4_0 prompts on tensor cores (#452), Q5_0 experts on the GPU (#473), IQ4_XS on AVX-2 (#415), unbuffered expert loading on Windows (#357 #362), --peer-device (#531), a 6 GB card starts (#496), PR batch; v0.1.37: a silent engine is restarted (#481), Windows AMD counts the desktop's VRAM (#380 #377 #497), a steadier PCIe probe (#485), fixes #496 #495 #498 #505 #493; v0.1.36: a cancelled prompt logged as read so far (#471), the draft-head hint (#474), UPDATE.bat (#475), --expert-profile-save (#477); v0.1.35: Windows AMD uses its bundled HIP runtime (#468 #461), the low-RAM resident mode on Windows 32 GB (#467), fixes #460 #459 #446 #447 #457 #448 #444; v0.1.34: AMD on Windows (a ready-made HIP engine), an MCP server for AI assistants (tools/strata_mcp.py), a shorter README; v0.1.33: a portable image encoder again (#411 #412), setup recommends instead of forcing (#406 #403 #364 #384), fixes #352 #365 #369 #371 #375 #393 #408 #414; v0.1.32: split prompts faster (#340), AMD router +12%, Unsloth Q4 in setup, faster Q4 prompts, #326/#327/#342/#344 fixes, PR batch; v0.1.31: Unsloth UD-Q4_K_XL (experimental), GGUF-in-place low-RAM mode, Windows GGUF load 2x, server race + tokenizer fixes, AMD intrinsics; v0.1.30: short prompts faster (streaming from 1024 tokens), resident low-RAM variant, multi-GPU session carve, RDNA4; v0.1.29: sampled answers faster (split top-k), #154 correctness fixes; v0.1.28: the expert cache reserves the draft head, a cancelled request no longer fails the next; v0.1.27: RTX 20 (sm_75) in the ready-made engine, the HIP build without CUDA headers; v0.1.26: the draft layer's prompt pass in batches; v0.1.25: faster prompts (grouping off the copy engine, fused hyper-connection kernels), AMD HIP backend, --kv k8v4; v0.1.24: long prompts faster (QSA select on tensor cores); v0.1.23: image requests honor sampling, 8 GB cards start, batched verify window; v0.1.22: faster prompts (tensor-core attention), multi-GPU across images/steering/KV streaming; v0.1.21: multi-GPU layer split (--gpus); v0.1.20: system-prompt checkpoint, PCIe probe, hit rate; v0.1.19: penalties
|
||||
# KV bytes per context token and attention layer: 8-bit 1056, rotated 4-bit 576, hybrid K8V4 (8-bit K, 4-bit V) 816
|
||||
KV_CELL_BYTES = {"q4_0": 576, "k8v4": 816}
|
||||
PY_PACKAGES = ["numpy", "jinja2", "regex", "pyyaml", "tqdm", "requests", "cmake", "ninja", "pillow", "psutil"]
|
||||
PY_PACKAGES = ["numpy", "jinja2", "regex", "pyyaml", "tqdm", "requests", "pillow", "psutil"]
|
||||
REQUIREMENTS = ROOT / "requirements.txt" # the same packages and their dependencies, pinned (#214)
|
||||
BUILD_PACKAGES = ["cmake", "ninja"]
|
||||
BUILD_REQUIREMENTS = ROOT / "requirements-build.txt"
|
||||
|
||||
# NVFP4 experts (MODEL-UPDATE Phase 4; the engine path is ported from sergqwer/strata-nvfp4, MIT). Ready-made Strata
|
||||
# packs on Hugging Face by the fork's author: nothing is converted here. Every file is pinned by its upload's commit,
|
||||
@@ -585,9 +587,20 @@ def _memory_status():
|
||||
def ram_gb():
|
||||
if WIN:
|
||||
return _memory_status().ullTotalPhys / 2**30
|
||||
for line in open("/proc/meminfo"):
|
||||
if line.startswith("MemTotal"):
|
||||
return int(line.split()[1]) * 1024 / 2**30
|
||||
if sys.platform == "darwin":
|
||||
try:
|
||||
value = subprocess.run(["sysctl", "-n", "hw.memsize"], capture_output=True,
|
||||
text=True, timeout=3, check=True).stdout.strip()
|
||||
return int(value) / 2**30
|
||||
except (OSError, ValueError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
return 0.0
|
||||
try:
|
||||
with open("/proc/meminfo") as stream:
|
||||
for line in stream:
|
||||
if line.startswith("MemTotal"):
|
||||
return int(line.split()[1]) * 1024 / 2**30
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
return 0.0
|
||||
|
||||
|
||||
@@ -2038,7 +2051,7 @@ def req_name(line: str) -> str:
|
||||
|
||||
|
||||
def requirement_lines(path: Path | None = None) -> list[str]:
|
||||
"""requirements.txt's requirements, comments and blank lines left out."""
|
||||
"""A requirements file's requirements, comments and blank lines left out."""
|
||||
lines = []
|
||||
for raw in (path or REQUIREMENTS).read_text(encoding="utf-8").splitlines():
|
||||
line = raw.split("#", 1)[0].strip()
|
||||
@@ -3873,9 +3886,15 @@ def install_build_tools(gpu, yes):
|
||||
|
||||
|
||||
def cmake_build(src, bdir, target, defs, vcvars, bat_name):
|
||||
# The ready-made engine path never calls this function. Keep build helpers out of
|
||||
# the normal serving environment, and use already installed tools offline.
|
||||
build_packages = requirement_lines(BUILD_REQUIREMENTS) if BUILD_REQUIREMENTS.exists() else BUILD_PACKAGES
|
||||
missing = [package for package in build_packages if find_tool(req_name(package)) is None]
|
||||
if missing:
|
||||
pip_install(missing, "CMake and Ninja build tools")
|
||||
cmake, ninja = find_tool("cmake"), find_tool("ninja")
|
||||
if cmake is None or ninja is None:
|
||||
fail("cmake / ninja not found after installing them", "run: .venv python -m pip install cmake ninja")
|
||||
fail("cmake / ninja not found after installing them", "run: .venv python -m pip install -r requirements-build.txt")
|
||||
conf = [cmake, "-G", "Ninja", f"-DCMAKE_MAKE_PROGRAM={ninja}", "-S", str(src), "-B", str(bdir),
|
||||
"-DCMAKE_BUILD_TYPE=Release", *defs]
|
||||
build = [cmake, "--build", str(bdir), "--target", target, "-j", str(max(2, (os.cpu_count() or 4) // 2))]
|
||||
@@ -4882,7 +4901,7 @@ def update_install(have: list, a) -> int:
|
||||
say(" it finds an earlier install's model files next to it and reuses them.")
|
||||
return 0
|
||||
pip_install(requirement_lines() if REQUIREMENTS.exists() else PY_PACKAGES,
|
||||
"numpy, jinja2, regex, pyyaml, tqdm, requests, cmake, ninja, pillow, psutil")
|
||||
"numpy, jinja2, regex, pyyaml, tqdm, requests, pillow, psutil")
|
||||
if not a.build:
|
||||
update_installed_engine(a.prebuilt)
|
||||
mtp_done: set = set()
|
||||
@@ -6647,7 +6666,7 @@ def main() -> int:
|
||||
# ---- 3. python packages
|
||||
step(3, "Python packages")
|
||||
pip_install(requirement_lines() if REQUIREMENTS.exists() else PY_PACKAGES,
|
||||
"numpy, jinja2, regex, pyyaml, tqdm, requests, cmake, ninja, pillow, psutil")
|
||||
"numpy, jinja2, regex, pyyaml, tqdm, requests, pillow, psutil")
|
||||
|
||||
# ---- 4. the engine
|
||||
step(4, "the Strata engine")
|
||||
|
||||
@@ -319,6 +319,41 @@ class Requirements(unittest.TestCase):
|
||||
self.assertEqual(setup.req_name('numpy==2.5.3; python_version >= "3.12"'), "numpy")
|
||||
self.assertEqual(setup.req_name("charset_normalizer==3"), "charset-normalizer")
|
||||
|
||||
def test_build_helpers_are_pinned_but_absent_from_runtime_install(self):
|
||||
runtime = {setup.req_name(x) for x in setup.requirement_lines()}
|
||||
build = setup.requirement_lines(setup.BUILD_REQUIREMENTS)
|
||||
self.assertEqual({setup.req_name(x) for x in build}, {"cmake", "ninja"})
|
||||
self.assertTrue(all("==" in x for x in build))
|
||||
self.assertTrue(runtime.isdisjoint({"cmake", "ninja"}))
|
||||
|
||||
def test_local_build_installs_only_missing_helper(self):
|
||||
installed = False
|
||||
requests = []
|
||||
commands = []
|
||||
|
||||
def find_tool(name):
|
||||
if name == "cmake":
|
||||
return "/usr/bin/cmake"
|
||||
return "/venv/bin/ninja" if installed else None
|
||||
|
||||
def pip_install(packages, what):
|
||||
nonlocal installed
|
||||
requests.append((packages, what))
|
||||
installed = True
|
||||
|
||||
def run(command, **kwargs):
|
||||
commands.append(command)
|
||||
return mock.Mock(returncode=0)
|
||||
|
||||
with mock.patch.object(setup, "WIN", False), \
|
||||
mock.patch.object(setup, "find_tool", side_effect=find_tool), \
|
||||
mock.patch.object(setup, "pip_install", side_effect=pip_install), \
|
||||
mock.patch.object(setup, "run", side_effect=run):
|
||||
setup.cmake_build(ROOT, ROOT / "build-test", "strata", [], None, "unused.bat")
|
||||
self.assertEqual(requests, [(["ninja==1.13.2"], "CMake and Ninja build tools")])
|
||||
self.assertEqual(commands[0][:4], ["/usr/bin/cmake", "-G", "Ninja", "-DCMAKE_MAKE_PROGRAM=/venv/bin/ninja"])
|
||||
self.assertEqual(commands[1][:3], ["/usr/bin/cmake", "--build", str(ROOT / "build-test")])
|
||||
|
||||
def pip(self, stamp, packages, installed=()):
|
||||
ran = []
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
|
||||
Reference in new issue
Block a user