Нема описа

Alexander Korolev 6e3b31bccd task: Render the sm121 health cluster report readably in plain format from the unified HealthReport (TASK_0003) пре 1 месец
.agents 17a91a9b19 cleanup: remove legacy r12/r18 build dirs (non-rebuildable) + blocked r27-dflash2 recipe пре 1 месец
docker-images bc8bb0d90a r27: b12x loader pinned_wc -> managed (discord patch, 1350->2000 tk/s prefill claim), hard-gated пре 1 месец
history c957beb710 eager A/B done (159 vs 158, rumor not confirmed); aiden 4x 163 = campaign record; night closed пре 1 месец
host-scripts 65198e60fc feat(recipe): add built-in script aliases and after_stop hook пре 3 месеци
locales 6e3b31bccd task: Render the sm121 health cluster report readably in plain format from the unified HealthReport (TASK_0003) пре 1 месец
recipes 17a91a9b19 cleanup: remove legacy r12/r18 build dirs (non-rebuildable) + blocked r27-dflash2 recipe пре 1 месец
runs 8ad35cba78 night part 2: aiden 2x benched (159, first canonical), r27 lane 156 (-2 vs r18), reports saved пре 1 месец
scripts 84f4cb456a feat(workflow): add land-branch.sh for solo-git integrate gate пре 2 месеци
specs 8936fde0cd queue rocenante research (b12x #295): zero-port, b12x pin bump + env toggle пре 1 месец
src 6e3b31bccd task: Render the sm121 health cluster report readably in plain format from the unified HealthReport (TASK_0003) пре 1 месец
.gitignore 5d587215dd separation complete: r18 dir carries source tarballs; state doc records r12/r18 split + r17 rebuild caveat пре 1 месец
AGENTS.md ccd615fcad fix hardmode for night shift + future: version-pin tool (2.6.1.dev45, --hardmode = 88 scenarios), re-baseline anchor lane before gating; reports commit to recipes/<lane>/reports/; new eugr-4x test recipe (2x stays production default until 4x dramatic) пре 1 месец
CLAUDE.md 5ab8a05080 restore recipes/ in-tree — recipe + docker-images must share one revision пре 1 месец
CODE_REVIEW.md 1ba1d99e05 fix: resolve review findings across modules пре 3 месеци
CONVENTIONS.md 3379f83f32 chore(bigpowers): adopt bigpowers lifecycle and conventions пре 2 месеци
Cargo.lock 51912523c5 feat(registry): publish --registry upload + sm121 registry list/show/install пре 1 месец
Cargo.toml 51912523c5 feat(registry): publish --registry upload + sm121 registry list/show/install пре 1 месец
DEVELOPMENT.md 30efdff974 docs: restructure for end users: DEVELOPMENT.md + user-friendly README пре 4 месеци
Makefile 65cbf60fce build: enforce quality gates via scripts/check.sh пре 4 месеци
README.md 5ab8a05080 restore recipes/ in-tree — recipe + docker-images must share one revision пре 1 месец

README.md

sm121-launcher

Launch vLLM / SGLang inference clusters across DGX Spark machines via SSH.

Quick Install

# From source
cargo install --path .

# Or download a pre-built binary (requires Rust toolchain to build)
cargo build --release
# Binary at target/release/sm121-launcher

Quick Start

# Create config.toml (see Configuration below)
# Launch a recipe
sm121-launcher serve --recipe deepseek-v4-flash

# Follow logs
sm121-launcher logs --recipe deepseek-v4-flash --follow

# Stop containers
sm121-launcher stop --recipe deepseek-v4-flash

Commands

Command Description
serve --recipe <name> Launch recipe on cluster
logs --recipe <name> [--follow] Show container logs
stop --recipe <name> Stop and remove containers
health [--cluster <name>] Check cluster health
doctor --recipe <name> [--repair] Check/repair node prerequisites
exec --cluster <name> -- <command> Run command on cluster nodes
exec --cluster <name> --alias <name> Run predefined alias
power off --cluster <name> [--node <name>] Shutdown + HA turn_off
power on --cluster <name> [--node <name>] HA turn_on (wake nodes)
power switch --from <src> --to <tgt> Switch clusters
tmux serve --recipe <name> Launch via tmux (no Docker)
tmux status --recipe <name> Check tmux session health
tmux logs --recipe <name> Capture tmux pane output
init-host-keys Auto-write SSH host keys
-d / --dry-run Preview commands without executing

Cluster selection

sm121-launcher serve --recipe deepseek-v4-flash              # auto (smallest match)
sm121-launcher serve --recipe glm51 --cluster 4nodes-switch  # exact name
sm121-launcher serve --recipe glm51 --cluster 4             # prefix match
sm121-launcher serve --recipe glm51 --cluster 2             # picks 2nodes-dac

Prefix must be unambiguous — error if multiple clusters match.

Recipes

Recipes are TOML files in recipes/<name>/recipe.toml, resolved relative to the current directory (then ~/.config/sm121-launcher/recipes/). This repo is the source of truth: a recipe and its image build context (docker-images/<dir>/ — Dockerfile, patches, version pins) are versioned together, since the served image must match the recipe's revision exactly. sm121 publish flattens both into a self-contained bundle; the registry data repo https://git.kilork.org/sm121/recipes stores those published bundles (discovery/sharing artifact, never the source).

Creating a recipe

engine = "vllm"                        # or "sglang" (default: "vllm")
name = "My Model"
docker_image = "my-image:tag"
model = "org/model-name"
required_nodes = 2                     # optional
tensor_parallel_size = 2               # TP across nodes
pipeline_parallel_size = 1             # vLLM only, 0 = auto (equals nnodes)
distributed_backend = "mp"             # "mp" (default) or "ray"
shm_size = "64gb"                      # optional, only added when set

entrypoint = ["vllm", "serve"]         # optional, appended after image

args = [
    "--kv-cache-dtype", "fp8",
    "--max-model-len", "32768",
]

before_serve = [                       # recipe-level pre-serve hooks
    { command = "echo ready", on_failure = "abort" },
]

[env]
VLLM_LOGGING_COLOR = "1"              # override any launcher default

Configuration

The launcher reads config.toml from the current directory, falling back to ~/.config/sm121-launcher/config.toml.

Minimal

ssh_user = "kilork"
ssh_key_path = "/path/to/ssh/key"

[[clusters]]
name = "4nodes-switch"
master_ib_ip = "192.168.200.15"
nodes = [
    { name = "dgx1", ssh_ip = "10.0.0.1", ib_ip = "192.168.200.15", rank = 0 },
    { name = "dgx2", ssh_ip = "10.0.0.2", ib_ip = "192.168.200.13", rank = 1 },
    { name = "dgx3", ssh_ip = "10.0.0.3", ib_ip = "192.168.200.17", rank = 2 },
    { name = "dgx4", ssh_ip = "10.0.0.4", ib_ip = "192.168.200.19", rank = 3 },
]

With Home Assistant (power management)

[[clusters]]
name = "4nodes-switch"
master_ib_ip = "192.168.200.15"
extra_entities = ["switch.network_switch"]   # cluster-level switches
nodes = [
    { name = "dgx1", ssh_ip = "10.0.0.1", ib_ip = "192.168.200.15", rank = 0, entity = "switch.dgx1" },
    { name = "dgx2", ssh_ip = "10.0.0.2", ib_ip = "192.168.200.13", rank = 1, entity = "switch.dgx2" },
    { name = "dgx3", ssh_ip = "10.0.0.2", ib_ip = "192.168.200.17", rank = 2, entity = "switch.dgx3" },
    { name = "dgx4", ssh_ip = "10.0.0.2", ib_ip = "192.168.200.19", rank = 3, entity = "switch.dgx4" },
]

[nccl."4nodes-switch"]
ib_hca = "rocep1s0f0,roceP2p1s0f0"
socket_ifname = "enp1s0f0np0"
ib_gid_index = 3
ib_disable = 0

[home_assistant]
url = "http://homeassistant.local:8123"
token = "long-lived-access-token"
shutdown_delay_seconds = 30

[exec_aliases]
gpu-lock = "sudo nvidia-smi -lgc 300,2300"
shutdown = "sudo shutdown now"

[before_serve."4nodes-switch"]
commands = [
    "sudo nvidia-smi -lgc 300,2300",
    { command = "sudo nvidia-smi -pm 1", on_failure = "warn" },
]

NCCL config

Required for each cluster. Controls InfiniBand/RoCE communication:

Field Example Purpose
ib_hca rocep1s0f0,roceP2p1s0f0 RDMA devices
socket_ifname enp1s0f0np0 Network interface
ib_gid_index 3 RoCE GID index
ib_disable 0 Keep IB enabled

Power Management

Requires [home_assistant] in config.toml.

# Graceful shutdown + wait + turn off smart plugs
sm121-launcher power off --cluster 4nodes-switch

# Wake nodes via smart plugs
sm121-launcher power on --cluster 4nodes-switch

# Switch clusters (disable 4 → enable 2)
sm121-launcher power switch --from 4 --to 2

# Single node
sm121-launcher power off --cluster 4nodes-switch --node dgx1

# Dry-run
sm121-launcher -d power off --cluster 4nodes-switch

Power-off flow: SSH sudo shutdown now → wait shutdown_delay_seconds → HA turn_off entity → HA turn_off cluster extras.

Power-on flow: HA turn_on (no SSH, assumes smart plug wakes the machine).

Switch flow: disable nodes/extras only in source → enable all in target.

Environment Variables

The launcher sets these automatically. Recipe [env] overrides any.

Auto-set (always)

Env var Source Description
NCCL_IB_DISABLE nccl.<name>.ib_disable InfiniBand on/off
NCCL_IB_HCA nccl.<name>.ib_hca RDMA devices
NCCL_SOCKET_IFNAME nccl.<name>.socket_ifname NCCL control interface
GLOO_SOCKET_IFNAME same Gloo (PyTorch) interface
TP_SOCKET_IFNAME same vLLM TP coordinator
MN_IF_NAME same Multi-node interface
UCX_NET_DEVICES same UCX devices
OMPI_MCA_btl_tcp_if_include same OpenMPI TCP interface
NCCL_IB_GID_INDEX nccl.<name>.ib_gid_index RoCE GID
VLLM_HOST_IP / SGLANG_WORKER_IP node.ib_ip Engine host IP
HF_HOME /root/.cache/huggingface HF cache (volume-mounted)
OMP_NUM_THREADS 4 OpenMP threads
NCCL_IGNORE_CPU_AFFINITY 1 Don't pin NCCL threads
NCCL_NET IB NCCL IB plugin
NCCL_DEBUG WARN NCCL log level
VLLM_LOGGING_COLOR 1 Colored logs (head only)
TERM xterm-256color Terminal type (head only)

Ray backend (when distributed_backend = "ray")

RAY_ADDRESS, RAY_PORT, RAY_NODE_IP_ADDRESS, RAY_OVERRIDE_NODE_IP_ADDRESS

Internationalization

Messages are localized. Auto-detects system locale.

# Override locale (works on both macOS and Linux)
LANG=ru_RU.UTF-8 sm121-launcher serve --recipe deepseek-v4-flash

Available: English (fallback), Russian, Spanish, Italian, French, German.

Project Links