Skip to main content

ModelExpress Python Client

Python client for ModelExpress -- high-performance GPU-to-GPU model weight transfers using NVIDIA NIXL over RDMA/InfiniBand.

Instead of each inference engine instance loading model weights from storage, one instance loads the model and transfers weights directly to later instances via GPUDirect RDMA, bypassing the CPU entirely.

Installation

# From PyPI (coming soon)
pip install modelexpress

# Editable install from source
pip install -e .

# With test dependencies
pip install -e ".[dev]"

# Additionally install the pinned protobuf code generator when changing protobuf APIs
pip install -e ".[codegen]"

NIXL is expected to be supplied by the runtime environment (TRT-LLM, SGLang, Dynamo, and NemoRL runtime images all ship nixl-cu12 or nixl-cu13). For a bare-environment install, run pip install nixl-cu12 or pip install nixl-cu13 separately, matching your host CUDA toolkit.

Requirements

  • Python >= 3.10
  • protobuf >= 5.27.2 and < 7
  • NVIDIA GPUs with RDMA/InfiniBand support
  • NIXL (NVIDIA Interconnect eXchange Library)
  • A running ModelExpress server (Rust gRPC service backed by Redis)

Quick Start with vLLM

vLLM 0.23.0 and newer recognize --load-format modelexpress natively. Install the ModelExpress Python package in the vLLM image; no VLLM_PLUGINS setting or manual loader registration is required. For older vLLM versions, set VLLM_PLUGINS=modelexpress or call register_modelexpress_loaders() manually.

export MX_SERVER_ADDRESS="modelexpress-server:8001"

vllm serve deepseek-ai/DeepSeek-V4-Pro \
    --load-format modelexpress \
    --tensor-parallel-size 8 \
    --trust-remote-code

Starting the vLLM engine with the modelexpress load format on the source worker will load the weights from disk and register/publish the NIXL and tensor metadata to the MX server. The mx load format is kept as a backward-compatible alias. On the target worker, it retrieves metadata from the MX server and streams weights over RDMA from GPU to GPU. Set MX_ARTIFACT_TRANSFER=1 to also reuse compatible vLLM JIT caches from a ready source.

Quick Start with SGLang

SGLang integrates through its remote_instance loader with the modelexpress backend. Use an SGLang image that includes upstream sgl-project/sglang#24723, such as the known-good release image lmsysorg/sglang:v0.5.13.post1, and install the ModelExpress package into that image.

export MX_SERVER_ADDRESS="modelexpress-server:8001"

python -m sglang.launch_server \
    --model-path deepseek-ai/DeepSeek-V3 \
    --tp 8 \
    --load-format remote_instance \
    --remote-instance-weight-loader-backend modelexpress \
    --modelexpress-config '{"transport": "nixl"}'

Quick Start with TensorRT-LLM

TensorRT-LLM integrates through its native checkpoint_format="MX" interface. Install ModelExpress in a qualified TensorRT-LLM image, then construct the PyTorch backend with the ModelExpress server configuration:

from tensorrt_llm.llmapi import LLM

llm = LLM(
    model="/model",
    checkpoint_format="MX",
    mx_config={
        "server_url": "modelexpress-server:8001",
    },
    tensor_parallel_size=4,
    backend="pytorch",
)

The first replica falls back to the Hugging Face checkpoint and publishes its post-transform weights; later compatible replicas receive them through ModelExpress. The current qualified scope is the LlamaForCausalLM family. See the TensorRT-LLM P2P example for the qualified-image requirement and production-style Kubernetes deployment.

Programmatic Usage

RL trainer publication

An RL framework creates a weight version through the external Refit API. Each trainer actor then invokes its rank-local client to stage and publish one shard. Worker registration, manifest serving, and internal shard CRUD remain hidden behind the client.

from modelexpress_rl import (
    ModelExpressTrainerClient,
    ModelExpressTrainerConfig,
    WeightVersionRef,
)

trainer = ModelExpressTrainerClient.initialize(ModelExpressTrainerConfig())
trainer.bind_tensors(megatron_tensor_specs)
trainer.publish_version(version=WeightVersionRef(version.uid))

The deployment supplies MODEL_NAME, MX_TRAINER_ENGINE, MX_TRAINER_STAGING_MODE, MX_WEIGHT_PAYLOAD_FORMAT, MX_WORKER_HOST, and the normal ModelExpress server configuration. The Megatron adapter derives its source slot from logical tensor names and shard geometry. DP replicas of the same partition therefore publish redundant workers for one slot, while distinct TP partitions remain separate required slots. The NIXL metadata endpoint is derived from MX_WORKER_HOST and the client-owned NIXL manager's listen port. LOCAL_RANK selects the device unless device_id is passed to initialize().

The client owns the NIXL manager and trainer-side manifest service. server_url selects the central ModelExpress control-plane service and defaults to the normal ModelExpress server configuration. A Megatron worker may initialize the client after selecting its CUDA device but before creating NCCL resources; the engine adapter is created lazily when the worker first requests its source slot or stages a shard after distributed setup.

Initialization fixes the staging mode and payload format. publish() hides manifest publication and the internal CreateWeightVersionShard RPC. The current Megatron adapter registers and exposes its live buffers through IN_PLACE, so callers must keep those tensors immutable while the version is published. The required lifecycle is synchronous: create and publish the version, update every generator, retire and release the version, and only then resume training or begin the next optimizer step.

Version creation and expected-source-slot declaration remain framework-orchestrator responsibilities. Each trainer adapter derives its own source slot from the engine's native topology; the orchestrator declares the expected slots using the same adapter-defined convention. initialize() selects the configured trainer engine and constructs its adapter internally. Megatron and FSDP implementations are available. Megatron-specific APIs live under modelexpress_rl; modelexpress.refit.reshard remains the shared, engine-neutral transfer core.

MxClient

MxClient is a lightweight gRPC client for communicating with the ModelExpress server:

from modelexpress import MxClient

client = MxClient(server_url="modelexpress-server:8001")

# Query for a source model
response = client.get_metadata("deepseek-ai/DeepSeek-V4-Pro")
if response.found:
    for worker in response.workers:
        print(f"Worker rank {worker.worker_rank}: {len(worker.tensors)} tensors")

# Wait for source readiness (blocks until ready or timeout)
success, session_id, metadata_hash = client.wait_for_ready(
    model_name="deepseek-ai/DeepSeek-V4-Pro",
    worker_id=0,
    timeout_seconds=7200,
)

client.close()

Registering Loaders Manually

Manual registration is only needed for integrations that construct vLLM loaders outside vLLM 0.23.0's native load-format path.

from modelexpress import register_modelexpress_loaders

register_modelexpress_loaders()
# Now vLLM recognizes --load-format modelexpress and mx

Environment Variables

Variable Default Description
MX_SERVER_ADDRESS localhost:8001 ModelExpress gRPC server address (recommended)
MODEL_EXPRESS_URL localhost:8001 Deprecated in favor of MX_SERVER_ADDRESS. Still read by all client paths and still takes precedence when both are set, because the TRT-LLM live-transfer integration reads only this name. It is removed once that path reads MX_SERVER_ADDRESS; until then set both to the same value.
MX_DISABLE_PATCHES 0 Emergency escape hatch that skips all runtime compatibility patches. Set to 1, true, yes, or on if a patch is incompatible with the installed engine.
MX_EXPECTED_WORKERS Auto-detected from TP size Number of GPU workers to coordinate
MX_SYNC_PUBLISH 0 Source: wait for all workers before publishing metadata
MX_SYNC_START 1 Target: wait for all source workers before transferring
MX_POOL_REG 0 Allocation-level NIXL registration (registers cudaMalloc blocks instead of individual tensors)
MX_P2P_METADATA 1 Serve tensor and artifact manifests directly from source workers; set to 0 to route full tensor metadata through the central server
MX_REFIT_METADATA_PORT 7555 Base NIXL metadata-listener port for RL generator refit; each rank adds its local device ID. Kept separate from MX_METADATA_PORT, which may remain owned by the boot-time loader
MX_ARTIFACT_TRANSFER 0 Transfer compatible vLLM TorchInductor, Triton, DeepGEMM, TileLang, CuTe DSL, and FlashInfer JIT caches, including persistent autotune files when supported by vLLM
MX_ARTIFACT_BUNDLE_ROOT $TMPDIR/modelexpress-artifacts Staging root for tarred cache artifact bundles
MX_ARTIFACT_COMPILE_CONFIG_DIGEST empty Optional compile-configuration compatibility digest for cache discovery
MX_ARTIFACT_READY_URL Framework default Readiness endpoint checked before a source publishes weights or JIT cache artifacts (http://127.0.0.1:8000/health for vLLM; http://127.0.0.1:30000/health for SGLang). On the non-head nodes of a multi-node engine, a loopback host is rewritten onto the head's address (the engine's own distributed-init address, else LWS_LEADER_ADDRESS), preserving the configured port and path. A non-loopback host is used verbatim
MX_ARTIFACT_READY_TIMEOUT_SECS 1800 Maximum time to wait for readiness and successful artifact publication
MX_HEARTBEAT_INTERVAL_SECS 30 Seconds between READY status heartbeats for published sources, including reshard rendezvous sources; keep below the server heartbeat timeout
MX_RESHARD_MAX_SEGMENTS_PER_COPY 64 Maximum exact descriptors for one no-gather refit copy before a compatible dim-0-sharded source is pulled once into contiguous staging and sliced locally
MX_RESHARD_FUSED_WIRE 1 Issue a refit's exact-segment, full-pull, and convert reads as one transport batch instead of draining each phase in turn. Set to 0 to restore the phased reads for an A/B comparison
MX_RESHARD_BATCH_INSTALL 1 Re-slice a refit's full-pulled sources with one batched torch._foreach_copy_ instead of one copy_() per captured view. Issues the same copies; a per-view loop costs thousands of kernel launches whose overhead can rival the RDMA. Set to 0 to restore the per-view loop for an A/B comparison
MX_RESHARD_CACHE_DESCRIPTORS 1 Build NIXL read descriptors once per stable transfer plan and reuse them across refits. Set to 0 to rebuild the descriptor lists on every step for an A/B comparison
MX_RESHARD_REQUIRE_FULL_COVERAGE 0 Fail a refit that installs less than MX_RESHARD_COVERAGE_FLOOR of the engine's parameter bytes. Off by default because partial and subset refit are intended; set to 1 for benchmark runs, where an incomplete refit produces timings that are the wrong magnitude
MX_RESHARD_COVERAGE_FLOOR 0.995 Fraction of engine parameter bytes a gated refit must install. Not 1.0: a few engine parameters, such as rotary inv_freq, are legitimately not refit material. Values outside [0.0, 1.0] are rejected
MX_RESHARD_HANDSHAKE_TIMEOUT_S 900 Budget for the whole P2P metadata handshake, across every trainer peer and every retry. Bounds the handshake independently of the refit timeout, so one unreachable publisher cannot consume the entire refit
MX_RESHARD_HANDSHAKE_ATTEMPT_S 20 Ceiling on a single peer dial. A reachable peer answers in well under a second, so a short attempt frees the budget to try a different peer rather than block on one
MX_RESHARD_HANDSHAKE_BACKOFF_S 2 Pause after a full pass over the pending peers makes no progress, so a transient stall is waited out rather than hammered
MX_REFIT_STAGE_RECORD 1 Emit one refit-stage-v2 JSON record per refit, giving a benchmark harness the per-stage timings without parsing logs. Set to 0 to silence it
MX_RESHARD_MAX_GBPS 0 Per-rank fabric ceiling in Gbps. A measured wire rate above it means the timing is wrong rather than the transfer being fast, so the refit is rejected. 0 disables the check, since only the operator knows the real per-rank limit
MX_RESHARD_PUBLISH_DIGEST 0 Have each trainer publish a position-sensitive digest of every shard it advertises, so a receiver can later confirm it installed the bytes the publisher held. Off by default: the reduction costs a pass over every published tensor, which is large next to a ~1.5 s wire, so turn it on when qualifying a build rather than when measuring throughput

UCX/NIXL Tuning

Variable Recommended Description
UCX_RNDV_SCHEME get_zcopy Zero-copy RDMA reads
UCX_RNDV_THRESH 0 Force rendezvous for all transfers
NIXL_LOG_LEVEL INFO NIXL logging level

Package Structure

Module Description
modelexpress.client MxClient -- gRPC client for the ModelExpress server
modelexpress.metadata Metadata clients, source identity, publishing, and worker manifest serving
modelexpress.refit Experimental RL weight-refit timing, receiver-driven resharding, and engine adapter contracts
modelexpress.engines.vllm.loader MxModelLoader -- vLLM integration
modelexpress.refit.reshard Engine-agnostic loader-geometry capture and bounded no-gather transfer planning
modelexpress.engines.sglang.loader MxModelLoader -- SGLang remote_instance integration
modelexpress.engines.trtllm.loader MxModelLoader -- TensorRT-LLM shared-strategy integration
modelexpress.vllm_loader Compatibility shim for the vLLM loader
modelexpress.nixl_transfer NixlTransferManager -- NIXL agent lifecycle and RDMA transfers
modelexpress.types TensorDescriptor, WorkerMetadata -- core data types
modelexpress.vllm_worker Compatibility worker extension for older manual-registration workflows

How It Works

  1. Source loads weights from disk, registers raw tensors with NIXL before FP8 processing, and publishes metadata to the ModelExpress server.
  2. Target creates dummy weights, waits for the source ready flag, then pulls raw tensors via RDMA read.
  3. Both source and target run process_weights_after_loading() independently, producing identical FP8-transformed weights.
  4. When artifact transfer is enabled, a healthy source publishes its pod-scoped JIT caches and later pods install compatible caches before model initialization.

This pre-processing transfer strategy is critical for FP8 models (e.g., DeepSeek-V4-Pro) where tensors are renamed and transformed during processing.

License

Apache-2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

modelexpress-0.6.0.tar.gz (521.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

modelexpress-0.6.0-py3-none-any.whl (373.0 kB view details)

Uploaded Python 3

modelexpress-0.6.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (459.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

modelexpress-0.6.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (459.4 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

modelexpress-0.6.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (458.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

modelexpress-0.6.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (459.3 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

modelexpress-0.6.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (458.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

modelexpress-0.6.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (459.5 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

modelexpress-0.6.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (457.9 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

modelexpress-0.6.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (458.6 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

File details

Details for the file modelexpress-0.6.0.tar.gz.

File metadata

  • Download URL: modelexpress-0.6.0.tar.gz
  • Upload date:
  • Size: 521.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.7

File hashes

Hashes for modelexpress-0.6.0.tar.gz
Algorithm Hash digest
SHA256 0fddd58403d42a2ed52bb9daba94318a78f2fde70a187cb5dec946b656fd01e4
MD5 384649e25ceceaa0deae1a359bbe47d1
BLAKE2b-256 7b93d57f0b43e8c030ef50829c1e016659e65e808069a8b397c49c78dde88d92

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: modelexpress-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 373.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.7

File hashes

Hashes for modelexpress-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9ca10d901e06628ad6059cfb6e84fb971782cae90f0ae6ff03d258bb3bce98fb
MD5 61244578a99503995b6bbe792736416a
BLAKE2b-256 336e9139beac4ae21b83110b020651feb15abcb4b915ff6f0c63f4e3d4a28489

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8ff3042393ecb8bdeca498b0cc6c4435adc44999de289eb4ca0f1ba6b68a133a
MD5 3c350f298b57613af01dd89022134960
BLAKE2b-256 51db985c2a54cd2ed6fe80e66ab890199b193bf7b1c5627a11d74ab2cb4d5feb

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 84bcab87a2a1a4e1e63133f9e819ab67d7ed3484c869309142f8e473a0932ca3
MD5 b253ae037ae13083e38b9a5a7d0417ed
BLAKE2b-256 50af7baa1780a18dd03de761fb937659c86dadd86713b5714738d60d38a16a6d

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8a4545bdb10f91d142d822ebca6562c26b0919520110692c4349b27a55f4e86a
MD5 c21c4ab64876daf815d47dee1118e1b4
BLAKE2b-256 730ee67549911364c6bc63a648df6d7f9c90a9b9f58a4ba76fd99977469af583

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 f22e742d53d2657396c01565c4355a0f6561daced33768b452ebbd398dde26ba
MD5 fd50b48707e2e1176758bd2d30ed2c33
BLAKE2b-256 da7ce7dc175a95b9b2418ca1391a0789e80c4e0fbf8983767d8bd838aea79f72

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 82abb407d5f8c4f20ac9ac8a9d18939f334e44192fff020edcbf9a96e28d9c2d
MD5 b5c648f655add8f92df7520e38496cc3
BLAKE2b-256 b3d8f7bea243ff98298214dd5a9aaae48eed8df22edcf51da34410efc51a5262

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 95334daf4691aa2e695943d746f119784c8bb37fd466aa1c164da2aca1b8b7ba
MD5 9c724551c28e84abcff11378959fed4e
BLAKE2b-256 f684b2410655516331dab86ab4b523259e8ff90cc8549c74687a4a5c255089ba

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7484dc548c04b25d7c1e764548754656a2ab34bd681ae7a2d6df6edf048ac2b7
MD5 00b65e66366fcca7ee5cf5b07a664b51
BLAKE2b-256 848a18175054439e717d902804d79f67eb02b70f319c3c5d16fb25e293dc48fc

See more details on using hashes here.

File details

Details for the file modelexpress-0.6.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for modelexpress-0.6.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 61b51a160ae54d15c21d1a4a89242ec65b7090e87b537465f35686ccbce73faa
MD5 275d7e74c179ad5c3d0945e36658f80a
BLAKE2b-256 5d8038eef649ee1b9464780b9a3f5ff9160f2032937c7043d859f3ddfb05060b

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.6.0 This release

10 files

0.5.1

10 files

0.5.0

10 files

0.4.1

10 files

0.4.0

4 files

0.3.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page