Repository navigation
infra/delta: multinode config, DeltaAI catalog, launch-test --nodes (#174) - #178
Open
dadamsncsa wants to merge 17 commits into
Open
dadamsncsa wants to merge 17 commits into
dadamsncsa wants to merge 17 commits into
Conversation
vec-inf 0.9.0 launches num_nodes > 1 vLLM as a Ray cluster in N+1 srun steps. On DeltaAI each step gets its own Slingshot VNI, so the ranks cannot reach each other over CXI. For multinode vLLM only, LLMHub now builds MultinodeLauncher, a vec-inf ModelLauncher whose generator replaces the Ray branch. The job runs one srun step with one task per node and --kill-on-bad-exit=1. A rank script, written into the job directory, derives the node's hsn<digits> address, waits on a barrier, and execs the same container vec-inf would. Rank 0 runs vllm serve with the API; the other ranks run it --headless. They join through vLLM's --nnodes/--node-rank/--master-addr/--master-port with the mp executor. vLLM runs under `env -u FI_LOG_LEVEL`, so the image's libfabric warnings cannot make vec-inf report a serving job as FAILED. Single-node launches, and anything that is not vLLM, go to vec-inf unchanged. The route is chosen on vec-inf's resolved params, so the catalog's node count counts. The adapter refuses a multinode launch on any vec-inf other than 0.9.0. Contract tests T1-T5 cover the version guard, single-node byte identity over the catalog, the job's shape, the rank script run with stubbed tools, and the vec-inf names it depends on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
Drop the heredoc-delimiter guard (no generated line can equal it), inline the job-directory helper, and leave the multinode check to the generator, which refuses before sbatch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
- Clear the rendezvous directory before srun: a requeued job keeps its id, and stale rank files would satisfy the barrier at once. - Decide the NCCL debug file on the rank, so NCCL_DEBUG reaching the container through APPTAINERENV_* is caught too, not only the job env. - Time the barrier by the wall clock, not by loop iterations. - Document that a non-container multinode launch is refused, and that the rank's --env flags rely on llm_inference's one-flag-per-variable patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
… fixes Behaviour change: every multinode launch now records NCCL INFO for INIT and NET, written to one file per process in the job directory's nccl-debug/ and never to stderr. That is how a multinode job shows which transport each rank chose (provider, NIC, GDR). It applies unless the job env or the host sets a level, and a NCCL_DEBUG_FILE given by either is kept. From the fresh reader: - A rank whose srun step has no CUDA_VISIBLE_DEVICES fails with a reason, not a bash "unbound variable". - A job env that sets VLLM_HOST_IP, NCCL_SOCKET_IFNAME or GLOO_SOCKET_IFNAME is refused: each rank derives them, and --env would silently win. - The master-port probe's stderr stays out of the job's .err. - T5 also names ModelLauncher.slurm_script_path, which the override sets and launch() reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…check - The job env's VLLM_HOST_IP, NCCL_SOCKET_IFNAME, GLOO_SOCKET_IFNAME and VLLM_PORT are left out of a multinode job's container env and noted in the job log, not refused: each rank sets its own, and a deployment may set them for single-node jobs. - With no NCCL level anywhere, the level is INFO even when a debug file is named. - A rank without CUDA_VISIBLE_DEVICES fails before the barrier, so no other rank waits on it; a rank that cannot write its barrier marker says so. - The master-port probe's stderr is visible again: a failing ss should leave a trace. - T4 runs the rank script for each NCCL case, job env and host alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
- The job JSON records the env the job was given, without the per-rank keys left out of it. - A missing ss is reported on stdout; ss's own stderr stays out of the .err that vec-inf scans for fatal patterns. - A container module other than apptainer is refused: the rank script passes its values as APPTAINERENV_* and binds through APPTAINER_BINDPATH. - T3 and T4 also cover a job env that sets both the NCCL level and file; T5 names CONTAINER_MODULE_NAME. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
dadamsncsa
added a commit
that referenced
this pull request
Nov 6, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
dadamsncsa
force-pushed
the
multinode-kit
branch
from
November 6, 2026 20:56
9226934 to
4084499
Compare
dadamsncsa
marked this pull request as draft
November 6, 2026 20:58
…ted in full Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…odes (#174) - LLMHUB_VEC_INF_ENV carries FI_CXI_RDZV_THRESHOLD=8192 and FI_CXI_RDZV_GET_MIN=0 on both clusters until the images bake them (GAP-098: cross-node all_reduce corruption on DeltaAI, job 3533388). - DeltaAI's job pre-command exports SLURM_NETWORK=disable_rdzv_get, the site value; a multi-node step gets one VNI under either value. - DeltaAI renders its catalog through config/models-deltaai.yaml (render-models.py, LLMHUB_MODELS_OVERRIDE): five 2-node A100 entries at 1 node TP=4, no Mllama, no 405B, and Qwen3-235B at 2 nodes TP=4 PP=2 with 400G per node. Delta renders backend/config/models.yaml as is. - launch-test --nodes N. At N > 1 it checks one rank per node, every worker and stage loaded, NCCL on CXI, the pair in every rank's container, no libfabric line before ready, and that DELETE leaves the job CANCELLED with no node held. - T2 now compares each cluster's kit-rendered catalog, job env and pre-command. New tests check the kit's values and load DeltaAI's catalog with vec-inf's load_config(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…cct retry
The multinode checks read an impersonated job's files directly, as the
backend's GET /deployments/{id}/logs does, through the workspace's default
ACL for the service account, instead of an untried `sudo -u`. The
post-DELETE state check waits up to 60 s for sacct to leave
RUNNING/PENDING.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…rge rule Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…aunch-test checks From /code-review and the fresh reader on #178: - vec-inf merges a cluster's models.yaml over a base catalog, its packaged one by default, so the entries DeltaAI's override removes (405B, Mllama) came back at launch. With an override set, the rendered environment.yaml now names the rendered models.yaml as that base (cached_model_config_path). The environment render moves from inline Python to render-vec-inf-env.py, which the tests now call, so T2 and the kit tests use the kit's own render. - render-models.py: a `models:` name the base lacks is an error; new entries go under `add:`. - The five 2-node entries moved onto one GH200 node get 400G per node. None of them has been run on GH200 yet. - launch-test --nodes N: - --nodes is validated before use; - Slurm reads after "ready" cannot abort the script before DELETE (an impersonated job is the backend's to cancel); - a kit-side scancel fails the DELETE check; - "nodes freed" now checks the nodes' states in sinfo; - stages are counted by Worker_PP prefix; - CXI is counted per worker file; - GDRDMA is required on DeltaAI; - the pair is matched as whole fields; - the libfabric count needs the ready line. - Tests: the launch-path catalog (load_config() with no path, as vec-inf calls it) and the override typo errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
dadamsncsa
force-pushed
the
multinode-kit
branch
from
November 6, 2026 21:24
4084499 to
2197b34
Compare
dadamsncsa
marked this pull request as ready for review
November 6, 2026 21:24
…against the live server The client runs after launch-test's own inference checks and before its DELETE. It gets the endpoint, the deployment key, the served name and the job id in its environment, never argv, and its exit status counts toward launch-test's. This is how the Qwen3-235B acceptance run goes through LLMHub's launch and shutdown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
…DELETE timestamps; no Mllama on DeltaAI - launch-test's ready budget (the job's walltime) is counted only while Slurm runs the job, so a long queue cannot use it up before a large model has loaded. Pending has its own 4 h cap. - The shutdown records the DELETE time, sacct's Start and End, and the node states with a timestamp: the acceptance check is "free within 60 s". - DeltaAI leaves out all four Mllama entries. The two 11B entries join the 90B ones: neither image registers Mllama, and 11B-Vision-Instruct had resolved to SGLang through vec-inf's packaged catalog, whose image is not on DeltaAI. DeltaAI renders 90 entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #174. Part of #60. Stacked on #177 (the multinode adapter): review and merge that first.
What (
infra/delta, plus tests)The GAP-098 pair, on both clusters.
FI_CXI_RDZV_THRESHOLD=8192 FI_CXI_RDZV_GET_MIN=0are added toLLMHUB_VEC_INF_ENV(delta.env) until the images bake them.all_reducereturned wrong data on 146-181 of 3,000 calls at 896 KiB-1 MiB. With the pair, 0 (job 3533388).DeltaAI
SLURM_NETWORK=disable_rdzv_get, the site value since 2026-10-22, replacessingle_node_vni,disable_rdzv_get.single_node_vni, a 1-node step gets no VNI and creates no CXI service. That is safe because 1-node jobs already run withNCCL_NET_PLUGIN=noneand never drive the NIC. The 1-nodelaunch-testafter the deploy checks it.A DeltaAI catalog override.
config/models-deltaai.yamlis applied byrender-models.pythroughLLMHUB_MODELS_OVERRIDE:mem_per_node: 400G. None of the five has been run on GH200 yet;Llama-3.2-11B-Vision-Instructhad resolved to SGLang through vec-inf's packaged catalog, whose image is not on DeltaAI;Qwen3-235B-A22B-Instruct-2507is added at 2 nodes, TP=4 PP=2, 400G. So far it has served on 2 nodes only from a test harness, with all node memory (3536666,--mem=0, vLLM-step MaxRSS 41 GiB per node). This entry first runs through LLMHub in the acceptance run.vec-inf merges a catalog over a base one, its packaged catalog by default, so removed entries would come back at launch. With an override set, the rendered
environment.yamlalso names the renderedmodels.yamlas that base (cached_model_config_path, viarender-vec-inf-env.py). vec-inf's launch-pathload_config()then resolves exactly DeltaAI's 90 entries. Of the entries whose resolution this changes, all but the removed 11B one differ only insglang_args, which vLLM does not use.Override names are checked against the base:
removeandmodelsmust name an existing entry, andadda new one. Delta rendersbackend/config/models.yamlas is, with vec-inf's base catalog unchanged.launch-test --nodes N. At N > 1 it also reads the job's own files, as the backend's logs endpoint does:Worker_PP<k>stage;GDRDMArequired on DeltaAI;After
DELETE:sacctsays CANCELLED (a kit-sidescancelcounts as a failure);Slurm reads after "ready" can't abort it before the
DELETE: an impersonated job is the backend's to cancel.The ready budget (the job's walltime) counts only while Slurm runs the job, so a long queue can't use up a big model's load time; pending has its own 4 h cap. The shutdown is timestamped:
DELETE,sacctStart and End, and the node states.--client SCRIPTruns an acceptance client against the live server before that shutdown. It gets the endpoint and deployment key in its environment, never argv, and its exit status counts.Tests (at 631f143): 98 passed (T1-T5 +
test_kit_multinode_config.py); full suite 285 passed, 1 skipped; lint cleantest_kit_multinode_config.pychecks:SLURM_NETWORK;load_config()loads DeltaAI's 90 entries, on the launch path too, where none of the removed names comes back;LLMHub CI runs only on PRs into
main, so it runs here once #177 merges and this PR is retargeted.On hardware (DeltaAI, 2 × GH200, Qwen2.5-7B-Instruct, TP=4 PP=2)
Job 3541508 passed 14/14. It was launched from a dev session with this branch's rendered DeltaAI config and env, shaped as
launch-test --nodes 2requests it.RUNNING; all 8 workers.GDRDMA.scancel, CANCELLED, with both nodes freed.The base-catalog rule leaves that job's generated script byte-identical; it changes only which catalog vec-inf can resolve.
launch-test's multinode checks, run on that job's files: all pass. On a copy with the NCCL files removed and a libfabric line planted, 3 fail, as they should.
launch-test --nodes 2and a 1-nodelaunch-testthrough the DeltaAI dev stack follow once both branches are deployed there.🤖 Generated with Claude Code
https://claude.ai/code/session_01HnSD7f83LXZjJVqJ3RLAAD