Repository navigation
Ship mstar_rust in the Linux wheels on PyPI - #883
Conversation
The release now builds Linux wheels per CPython version (3.12, 3.13) and architecture (x86_64, aarch64) with cibuildwheel, and setup.py compiles rust/ into them, so pip install mstar-ai on Linux gets the extension and MSTAR_RUST_ZMQ/MSTAR_RUST_GRAPH=AUTO pick the Rust paths. The sdist and the pure-Python wheel are unchanged and remain the fallback elsewhere. Only cibuildwheel builds compile rust/: setuptools' editable finder cannot import a top-level extension built in place, and compiling it would slow every pip install -e . and move CPU Core onto the Rust runtime. Checkouts keep building it with maturin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Yes we need to not forget about this for the next Pypi release, rust needs to be built into the wheel, was mentioning this to @merceod |
|
I tried it and it works, I know it is in draft but looks mostly right.. We want python 3.14 as well, right? Also probably need tests, packaging.md chagnes, check on the third party licenses + pinning rust compiler. Also rust HTTP frontend binary |
|
Oh and also since it can come from the mstar-ai wheel and rust/'s mstar-rust, if both get installed in one environment I believe uninstalling either deletes the other copy |
|
Taking out of draft mode. It was draft because I haven't had the chance to look at it but perhaps it's ready to receive comments already |
Automated review🟡 No findings on Description
Questions
What the review checked
Advisory, not a merge gate. Rules: |
pyo3 0.23 stops at 3.13; 0.26 is the first release tested against 3.14's release candidates without 0.27's FromPyObject signature change. Its only breakage here is the allow_threads -> detach rename. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI took whatever stable was that day and the release wheels whatever rustup installed. One file now sets the version for CI, the wheels and local builds; 1.99.1 is what CI resolved stable to on main today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setuptools-rust's RustBin installs it into the environment's scripts directory, which find_server_binary now checks after $PATH, so --rust-frontend finds it without an activated venv. mstar-server --help prints usage instead of starting a mock server on port 8000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cargo-about writes the notices for every crate, and the C/C++ that zeromq-src, onig_sys and esaxx-rs vendor, into LICENSES-THIRD-PARTY.txt, which setuptools ships in dist-info/licenses. A license not in rust/about.toml's accepted list fails the build. libzmq's LGPL-3.0 (with its static-linking exception) is not accepted yet and needs a decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release had no check that the Linux wheels carry what they claim: each one is now installed without dependencies, outside the build container, and must import mstar_rust, run mstar-server --help from the scripts directory, and list the third-party licenses, or the release publishes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gaurav-Shah05
left a comment
There was a problem hiding this comment.
LGTM. One thought on the wheels job.
CI builds and tests mstar_rust with maturin, but the release wheels build it with setuptools-rust and upload it without importing it. If that build can't load, users get no error, because M* falls back to the Python graph runtime and pyzmq.
libzmq 4.3.4's exception is its own Classpath-style text, not SPDX's LGPL-3.0-linking-exception, so it cannot be accepted as that id. With LGPL-3.0-or-later unaccepted, the license step fails every release; pypi needing wheels meant the sdist, pure wheel and aliases never published either. pypi now runs on its own (and checks the tag before the wheel matrix finishes), and pypi-wheels uploads the Linux wheels once both pass. Docs and the frontend error no longer say PyPI ships the extension. Findings: R1.1, R1.16, R1.13 (PACKAGING step 3, README line break) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An unactivated venv's python found an older mstar-server elsewhere on $PATH first and launched it against its own bridge code, which has no version handshake with the frontend. Findings: R1.7 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Linux wheel's top-level mstar_rust module and a maturin-built mstar-rust package can both sit in one environment; the package directory wins, and both report the same crate version. The startup line now names the loaded file, the version warning gives the fix for a checkout and for a pip install, and PACKAGING.md / installation.rst state the precedence and that uninstalling one never deletes the other's files (Kirill's question). Findings: R1.4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rust/server built mstar-rust without extension-module, so pyo3-ffi linked libpython into the frontend, which uses only the transport. In manylinux that means no shared libpython to link against, and where it links, a binary tied to one interpreter; the wheel check could not see it because setup-python puts its libdir on LD_LIBRARY_PATH. Now the bindings, graph and tensors build only with `python` (extension-module implies it, the default stays extension-module), the server takes no features, and the wheel check asserts no libpython in the binary's NEEDED and runs it with LD_LIBRARY_PATH unset. cargo test takes --features python where it used to take only --no-default-features. Findings: R1.2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on a release The shipped build ran only at release time, so a crate bump that changes a license, a toolchain bump or a setuptools-rust/maturin divergence first surfaced as a failed release. The workflow now also runs on workflow_dispatch and on PRs touching rust/, the build files or itself; both pypi jobs run only for a release. Off a release, the license step's failure does not stop the build, so those runs still check the wheels while libzmq's license awaits sign-off. Findings: R1.3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installing a Linux wheel turns the Rust transport and graph runtime on by default (AUTO), including on CPython 3.13/3.14 and aarch64, where rust-transport never runs; the check only imported the extension. Each wheel now also runs test/rust with its dependencies (CPU torch) from a copy of test/, so mstar and mstar_rust both import from the wheel. Findings: R1.5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wheel whose extension disagreed with EXPECTED_MSTAR_RUST_VERSION would ship and then warn at every startup; the clean-venv check now asserts the version it imports against the constant in the checkout. Findings: R1.12 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setuptools-rust passed no --locked, so a wheel could compile versions other than the Cargo.lock the license notices were collected from, and both targets shipped unstripped into six wheels (PyPI's size quota). Done in setup.py rather than [profile.release] so local release builds keep their symbols for profiling. Findings: R1.8, R1.9 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… images A minimal profile left `cargo fmt` and `cargo clippy` failing on the pinned toolchain. scripts/ci/Dockerfile and docker/Dockerfile.xpu installed stable, so every fresh container downloaded 1.99.1 on first use (and failed offline); they now install no default and then the toolchain rust-toolchain.toml names (mac.sh passes the repo as a named build context so the CI image can copy it). Findings: R1.10 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release builds wheels for both. Findings: R1.11 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cargo-about's `private = { ignore = true }` skips only crates that are not
publishable, so both were listed as third-party Apache-2.0 code in the
notices, contrary to about.toml's comment.
Findings: R1.15
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrap the wheel check's comment, and say which step fails in about.toml's libzmq note. Findings: R1.12, R1.1 (comment follow-ups) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntend Since pyo3 moved behind the `python` feature, rust/server's tree no longer contains pyo3 and its helpers, so notices collected from it alone left out crates only the mstar_rust .so links. cargo-about now runs on each manifest (it takes one at a time, and the two lockfiles are separate) into LICENSES-THIRD-PARTY-mstar_rust.txt and -mstar-server.txt; the wheel check requires both. Findings: R2.1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
continue-on-error on the license step hid every license failure off a release, so a crate bump to an unaccepted license or a changed checksum on a clarified file still showed green. Those runs now generate from a copy of about.toml with LGPL-3.0-or-later accepted and are otherwise strict, so the step needs no continue-on-error and the check requires both notices unconditionally. A release still uses about.toml as is. Findings: R2.2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ded runs PR and manual runs built the whole release matrix (three CPythons per arch, each LTO-linked, then a CPU-torch venv per wheel). They now build cp312 only, with CIBW_BUILD at job level so the check counts wheels from the same value, and a newer push cancels a PR or manual run in progress. Findings: R2.3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With --default-toolchain none and the install done inside ./mstar, cargo failed anywhere outside the clone, including the image's WORKDIR. The pinned channel, read from rust-toolchain.toml, is now the default too. Findings: R2.4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setup.py still said only release jobs compile rust/, and PACKAGING.md's Checks bullet left out the version check, the no-libpython check and the test/rust run. Findings: R2.5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib.rs carried 16 separate #[cfg(feature = "python")] attributes, one per use, class, impl and the #[pymodule]. The bindings now live, unchanged apart from their crate:: imports, in a `bindings` module gated once, and lib.rs re-exports PyZmqCommunicator for the graph runtime. Findings: R2.6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sdist had only configs/, so `CIBUILDWHEEL=1 pip wheel <sdist>` failed on the missing rust/Cargo.toml and a packager building from the sdist could not produce the Rust build. A plain sdist install still builds pure Python; the tracked rust/ sources are about 0.7 MB. Findings: R2.7 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…outs The workflow had no top-level permissions, and now that pull_request runs build PR code, the wheels job's checkout kept its token in .git, which cibuildwheel copies into the build container. The default is now contents: read, only the two publish jobs add id-token: write, and both checkouts set persist-credentials: false (nothing later needs git auth). Findings: R2.8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the group keyed only on the ref, a manual run on a release tag joined the release run's group and could cancel it. The group now includes the event name. The concurrency comment no longer says a release run is never superseded (a newer one still replaces a pending one), and the license step's garbled sentence is rewritten. Findings: R3.1, R3.3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t build needs The gate is CIBUILDWHEEL=1, not the release workflow, now that the sdist carries rust/. The notices are generated only by the wheels job, so a Rust build from the sdist has to run the two cargo about commands first. Findings: R3.2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zmq-sys compiles libzmq from zeromq-src, which never defines HAVE_POSIX_MEMALIGN. libzmq then allocates its command queues with malloc (16-byte aligned) although command_t is aligned(64). GCC on GitHub's ubuntu-26.04 runners comes from Ubuntu's amd64v3 builds and targets x86-64-v3 by default, so it copies command_t with vmovdqa ymm stores; the first one into a malloc'd chunk faults, in zmq::mailbox_t::send on the first communicator test. main passes only because its rust-transport cache still holds a libzmq built on ubuntu-24.04; this PR's lockfile change missed that cache. Define what libzmq's own build detects on Linux, through a root .cargo/config.toml so that every cargo run in the repo (rust/, rust/server, setup.py's wheel builds) sees it, and ship that file in the sdist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rust/about.toml now accepts libzmq 4.3.4's license, declared as LGPL-3.0-or-later WITH AdditionRef-libzmq-static-linking-exception: no SPDX exception id matches the static-linking exception at the end of its COPYING.LESSER, and the notices carry that whole file, exception included. With it accepted, PR and release runs use the same config, so the temporary copy that tolerated LGPL on PR runs goes, and a release's pypi-wheels job publishes the Linux wheels; it stays a separate job so a failing wheels build still holds back only them. README, installation.rst and PACKAGING.md no longer say the wheels are off PyPI, and say where libzmq's source is: the upstream v4.3.4 release that zeromq-src vendors unmodified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main since 73c6c17: #883 mstar_rust in the Linux wheels, #901 output spans (BatchedModelOutput.output_spans, NodeSubmodule.spanned_outputs, the worker's crop), #904 DiT seed noise drawn at ingest (on_request_added, NoiseStager deleted, to_device_nonblocking as one memcpy into pinned memory), #903 API-server torch pool from the CPU quota and pooled image encodes, #924 robotics harness, #819 Inductor cache under ~/.cache, #881 CommGroup.agree / JointGroups.agree (agree_across_ranks and flagged_on_any_rank deleted), #724 CUDA IPC KV in one 2D memcpy a page, #387 GLM-5.2 (MLA, fp8 MoE with NoAuxTcRouter, the DSA resource), #921 vjepa2 AC crop, #922 Cosmos3 contiguous positions, #923 dense attention falls back when fa3-fwd has no kernel for the GPU. Conflicts: - mstar/utils/h2d.py, to_device_nonblocking: the stack's signature (data: Tensor | Sequence, device, dtype=None) and DeviceCapability pin_memory gate, with main's tensor path (#904): a CPU tensor is written into a fresh pinned buffer with one memcpy rather than pin_memory()'s copy_ over the intra-op pool. Every CPU tensor now goes through a fresh buffer, so the stack's "an already-pinned source is read in place" caveat (R3.3) no longer applies and is dropped from the docstring. test_accelerator_graph_backend's pinning test checks the pinned torch.empty and its non-blocking copy instead of pin_memory(). - mstar/model/components/diffusion/denoise_loop.py: imports, both (main's copy, the stack's functools). Main code on a superseded path, ported here: - GLM-5.2 (glm52/submodules.py): its own check_stop / check_stop_batched (four arguments, step_walk, its own row-map loop) / _stops -> token_stop = TokenStop(decode_loop, new_token, eos_token_ids, sampler) with a _token_stops override for its two differences (a stop token at prefill ends the reply at the first decode step; max_tokens counts the prefill's token), and a check_stop override for that closing step, whose token postprocess drops. test_glm52_model's helper and test_model_kv_layouts' GLM cap test build the real submodule (over a stub language model) instead of object.__new__, which would leave token_stop unset. - PinnedStager.to_device (#387), a second "host values -> new device tensor" helper beside to_device_nonblocking: deleted with its test (test_pinned_stager_to_device.py); its callers, GLM-5.2's prefill seq_lens and the DSA resource's page tables, call to_device_nonblocking(list, device, dtype=torch.long). test_dsa_resource spies on that instead. The other files both sides touched (engine.py, accelerator_graph_runner.py, kv/manager.py, attn/base.py, ragged/base.py, communication.py, model/base.py, submodule_base.py, entrypoint.py, conductor.py, moe.py, image_io.py, vjepa2_model.py, docs, tests) auto-merged into disjoint hunks: per file the merged delta equals main's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pip install mstar-aiinstalls a pure-Python wheel (py3-none-any), so PyPI users never get M*'s compiled Rust parts: themstar_rustextension module (Rust transport, graph runtime, SHM arena) and themstar-serverHTTP frontend. Today only people who buildrust/from a checkout with maturin and cargo have them. This PR adds platform wheels: manylinux_2_28 wheels for CPython 3.12, 3.13 and 3.14 on x86_64 and aarch64, built with cibuildwheel, each carryingmstar_rust,mstar-serverand the license notices for everything compiled into them. pip picks a matching platform wheel and falls back to the pure wheel everywhere else (macOS, other architectures, other Pythons), which runs the Python transport and graph runtime as it does today.The wheels statically link libzmq 4.3.4, which they ship under LGPL-3.0-or-later with libzmq's static-linking exception.
How it works:
setup.pyadds setuptools-rust targets (RustExtensionforrust/,RustBinforrust/server/) only whenCIBUILDWHEEL=1. Every other build (pip install -e .,python -m build, an install from the sdist) stays pure Python. The wheel builds pass--lockedand strip both binaries. The sdist now carriesrust/andrust-toolchain.toml, soCIBUILDWHEEL=1 pip wheel <sdist>builds the Rust wheel too.bindings.rs, plusgraphandtensors) sit behind a newpythoncargo feature.extension-moduleimplies it and stays the default.rust/servertakes no features, somstar-serverlinks no libpython: manylinux has no shared libpython to link, and a linked one would tie the binary to one interpreter.cargo testforrust/is nowcargo test --release --no-default-features --features python.cargo-about(rust/about.toml,rust/about.hbs) writesLICENSES-THIRD-PARTY-mstar_rust.txtandLICENSES-THIRD-PARTY-mstar-server.txtinto each platform wheel'sdist-info/licenses/. The C/C++ that crates vendor (libzmq via zeromq-src, Oniguruma via onig_sys, esaxx via esaxx-rs) is declared by hand. A license not inacceptedfails the job.mstar_rustat the versionEXPECTED_MSTAR_RUST_VERSIONnames, list both license notices, have no libpython inmstar-server'sNEEDED, and runmstar-server --helpwithLD_LIBRARY_PATHunset. Then it's installed with its dependencies (CPU torch) and runstest/rustfrom a copy oftest/, somstarandmstar_rustboth import from the wheel. That covers CPython 3.13, 3.14 and aarch64, whichrust-transportnever runs.release.ymlruns on a published release, onworkflow_dispatch, and on PRs that touchrust/**,rust-toolchain.toml,.cargo/config.toml,setup.py,pyproject.toml,MANIFEST.inorrelease.yml. PR and manual runs build cp312 per arch, publish nothing and cancel when superseded; they use the same license config as a release. A release builds all three CPythons.pypipublishes the sdist, the pure wheel and the alias packages as before.pypi-wheelsuploads the platform wheels only afterwheelsandpypiboth pass, so a failingwheelsholds back only the platform wheels. The workflow token is read-only exceptid-token: writeon the two publish jobs.HAVE_POSIX_MEMALIGN, so libzmqmallocs its command queues (16-byte aligned) thoughcommand_tisaligned(64). GCC on GitHub's ubuntu-26.04 runners comes from Ubuntu's amd64v3 builds and targets x86-64-v3, so it copiescommand_twith aligned 32-byte AVX stores, and the first one segfaults (zmq::mailbox_t::send, on the first communicator test). main passes only because itsrust-transportcache still holds a libzmq built on ubuntu-24.04. A root.cargo/config.tomlpasses the defines libzmq's own build uses on Linux (HAVE_POSIX_MEMALIGN=1,ZMQ_CACHELINE_SIZE=64) to every cargo run in the repo, including setup.py's wheel builds, and the sdist ships it.rust-toolchain.tomlpins Rust 1.99.1 (with rustfmt and clippy) for CI, the release wheels, the CI and XPU Docker images and local builds. pyo3 goes from 0.23 to 0.26 because CPython 3.14 needs 0.25 or later; 0.26 renamesallow_threadstodetach, which is the only change ingraph/runtime.rsandtensors.rs.Behaviour change:
MSTAR_RUST_ZMQandMSTAR_RUST_GRAPHdefault toAUTO, which picks the Rust runtime wherevermstar_rustimports. So once the platform wheels publish, everypip install mstar-aion Linux x86_64/aarch64 with CPython 3.12–3.14 runs the control mesh and the graph runtime on Rust with no config change.MSTAR_SHM_ARENAdefaults to0and doesn't change. Each process logscontrol mesh transport: rust <version> from <path> (MSTAR_RUST_ZMQ=AUTO), andMSTAR_RUST_ZMQ=0pins pyzmq.A maturin build of
rust/installs a separatemstar-rustdistribution as amstar_rust/package directory, while the wheel ships a top-levelmstar_rust.cpython-3XX-*.so. They're different files, so uninstalling one never removes the other, but the package directory takes precedence on import and shadows the wheel's copy. The log line above names the loaded file, the version-mismatch warning gives the fix for a checkout (maturin develop) and for a pip install (upgrade, andpip uninstall mstar-rustif a leftover build shadows the wheel), and PACKAGING.md and installation.rst say so.find_server_binaryalso checks the running environment's scripts directory before$PATH, so an unactivated venv doesn't launch an oldermstar-serverfrom$PATH.New dependencies:
setuptools-rust>=1.11in[build-system] requires, because it lets the existing setuptools build (which also copiesconfigs/into the package) compile the extension and the binary into the same wheel, which maturin can't do.cargo-about0.9.2, in the release workflow only, to generate the license notices.How it was tested
test/rustagainst the installed wheel: 534 passed, 5 skipped (CUDA, sudo) on each of 3.12, 3.13 and 3.14. aarch64 wheels: import andmstar-server --helpunder qemu.-n 4,OMP_NUM_THREADS=2): 8131 passed, with the same 2 failures as main (test_dump_stacks_retries_with_sudo_only_when_asked[False/True], from py-spy in the box's venv).cargo test --release --no-default-features --features pythoninrust/: 77 passed.cargo test --releaseinrust/server: 56 passed.-march=x86-64-v3), building the same test binary CI built (mstar_rust-fc38c2648720017d). It crashed 3 of 3 runs onvmovdqa %ymm0,(%rax)inzmq::mailbox_t::send, with%rax16-byte aligned. With.cargo/config.tomlthe lib tests andrust/server's 56 tests pass there. A plain 26.04 amd64 userland, whose GCC targets baseline x86-64, doesn't crash.src/,include/,external/) match the upstream v4.3.4 tarball.rust-transportbuilt libzmq fresh on ubuntu-26.04 (no cache) and passed: 77 + 56 cargo tests, interop 522 passed, 3 skipped.release.ymlbuilt both cp312 wheels; the clean-venv check passed andtest/rustagainst each gave 518 passed, 3 skipped on x86_64 and on aarch64.pypiandpypi-wheelswere skipped (PR run). CPU Core 8083 passed, the TP lockstep sim 11 passed, build and dynamo-smoke passed.test/rust/,test/modular/test_row_output_store.pyandtest/cion the Eras box with--rust: 584 passed, 5 skipped.Not in this PR / follow-ups
--no-isolationwith the build deps preinstalled.🤖 Generated with Claude Code