npx skills add ...
npx skills add nvidia/tensorrt-llm --skill exec-local-slurm
Submit and monitor a Slurm job on a local cluster. Supports two modes: (1) Persistent allocation (default) — allocates nodes once via nohup salloc, imports the container once, installs once, and reuses across runs by setting SLURM env vars and running the sbatch script via bash. (2) One-shot sbatch — submits a fully-generated Slurm script via sbatch, polls job status, reads logs on completion, and reports results. Workflow-agnostic — handles pytest, eval, benchmark, and custom scripts identically.
npx skills add nvidia/tensorrt-llm --skill exec-local-slurm
Submit a Slurm job locally, monitor it, and report results. Uses persistent allocation by default to eliminate queue wait, container import, and install overhead on repeated runs.
The orchestrator passes these fields in the skill prompt:
| Field | Description |
|---|---|
script_path | Full local path to the generated .slurm script |
work_dir | Local work directory for logs and artifacts |
model_name | Short model name for reporting |
workflow_type | pytest, eval, custom, or benchmark — for output parsing hints |
success_patterns | Comma-separated patterns indicating success |
failure_patterns | Comma-separated patterns indicating failure |
log_file_pattern | Log filename pattern with %j placeholder (e.g., llama_auto_test_%j.out) |
persistent_mode | Default: true for all local slurm workflows. Set to false to force one-shot sbatch (opt-out). |
release_allocation | true to release the current allocation and stop. Only set when the user explicitly says no more jobs are needed. Default: false. Never auto-release. |
alloc_time_limit | Walltime for the persistent allocation. Default: 04:00:00. |
docker_image | Container image for persistent container import. From job_spec.json. |
container_name | Container name used in the .slurm script (e.g., llama_auto_test). Must match exactly. From job_spec.json. |
container_mounts | Comma-separated mount mappings. From job_spec.json. |
repo_root | Repo root path (for locating state file and project path). |
slurm_params | Slurm parameters object: partition, account, nodes, ntasks, ntasks_per_node, gpus_per_node. From job_spec.json. |
build_project=true)This executor owns image selection and the build for the local SLURM cluster. Skip the entire pre-step when build_project=false.
device_type in job_spec.json (resolved upstream by env-check / internal-env-info). If absent, probe from any compute node via srun -p <partition> --ntasks-per-node=1 nvidia-smi --query-gpu=name --format=csv,noheader | head -1. If skills/internal-env-info/ is not installed, skip the upstream lookup silently and rely on the srun probe — do not report the missing skill as an error.job_spec.slurm_env.partitions[] for the entry where name == partition and read its arch (x86_64 / aarch64). Case-executor's Step 2.5 already detected this — do not run scontrol show node here. If slurm_env is absent or the entry's arch is null (case-executor ran without SLURM tools), fall back to inferring from device_type: Grace-based parts (GB* / GH*) → aarch64; everything else → x86_64.docker_image is already set in job_spec.json, use it directly. Otherwise read <repo_root>/jenkins/current_image_tags.properties and pick the tag matching the partition's CPU arch.-a flag): H100/H200 → 90-real; B200/GB200/B300/GB300 → 100-real; A100 → 80-real; L40S → 89-real. Default 100-real.exec-slurm-compile skill with repo_dir=<repo_root>, partition, account, container_image=<resolved tag>, user_root_dir, arch. Wait for completion.BUILD_FAILED with the last 100 lines of the build log.build_project, device_type, repo_root, partition, account, user_root_dir, and (optionally) docker_image come from job_spec.json.
This step runs before any execution. It determines whether to reuse an existing persistent allocation, create a new one, or fall through to one-shot sbatch.
If release_allocation=true:
<repo_root>/work_dirs/.slurm_alloc.jsonjob_id is present:
.salloc.logIf persistent_mode=false, skip entirely to Step 1 (One-Shot Path).
Read <repo_root>/work_dirs/.slurm_alloc.json.
scancel <job_id>, delete state file, go to Step 0Dscancel <job_id>, delete state file, go to Step 0DParams compatibility check:
partition must matchnodes in state must be >= requested nodes (a 2-node allocation can serve 1-node jobs)docker_image must matchBefore allocating, ensure no orphaned allocations exist and log the reason:
Convert alloc_time_limit from HH:MM:SS to seconds (e.g., 04:00:00 → 14400).
Retrieve job ID:
If no job appears after 10 seconds, read <repo_root>/work_dirs/.salloc.log
for errors (bad partition, invalid account, etc.) and report to user.
Otherwise, poll until RUNNING (every 10s, max 60 polls). Once RUNNING, get the nodelist:
Import container on all nodes (using the same container name as the
.slurm script — critical for pyxis reuse):
Warm up filesystem mounts on all nodes — ls each mounted path so
Lustre/NFS metadata is cached for later use:
Parse mount targets from container_mounts — the right-hand side of each
host:container pair.
Check GPU status on all nodes (no container needed — nvidia-smi is on
the host):
Run install on all nodes with --container-writable (so packages persist
in the named container across srun calls). Install all common requirements
upfront so that subsequent jobs of any workflow type can skip install:
For custom workflow with skip_install=true, skip this step entirely.
Write state file <repo_root>/work_dirs/.slurm_alloc.json:
Proceed to Step 1 (Persistent Path).
Allocation is valid. Check if container name matches current request:
container_name in state file matches container_name from
job_spec.json → proceed to Step 1 (Persistent Path). Container and install
are already set up.container_name in state file. Proceed to Step 1 (Persistent Path).Two execution paths depending on persistent_mode.
Pre-flight time check — verify the allocation has enough remaining time:
If remaining time < job's time_limit from slurm_params, warn user:
"Allocation has left but job expects <time_limit>. The job may
be killed early."
Pre-flight GPU check — verify GPUs are not occupied by leftover processes:
If unexpected processes found, warn user before proceeding.
Run the sbatch script with SLURM env vars — the #SBATCH directives are
comments when run via bash; the inner srun inherits the env vars and uses
the persistent allocation:
Run with run_in_background. Container reuse is automatic (pyxis skips
re-import when --container-name already exists on the node). The install
step (Step 1 srun) always runs to keep the container up to date.
Step cancellation: If a job hangs or the user wants to abort, cancel just the srun step without killing the allocation:
The allocation stays RUNNING — new jobs can run immediately. The job name
<account>-trtllm.persistent uniquely identifies allocations created by this
skill via squeue --name=<account>-trtllm.persistent.
Parse the job ID from output: Submitted batch job <JOB_ID>.
If sbatch fails, report the error immediately and stop.
One-shot mode:
Persistent mode:
Both polling modes must also monitor the log file for hang indicators. Every
60 seconds, check the log file for the pattern hang detected
(case-insensitive). If found, terminate the job and skip to Step 5 with
HANG_DETECTED status.
One-shot mode: Poll every 30 seconds, max 60 polls (30 minutes):
PENDING → RUNNING → COMPLETING → donePENDING for > 5 minutes, check the reason:
squeue -j <JOB_ID> returns empty (no rows)<work_dir>/<log_file_pattern> with %j replaced
by <JOB_ID>) and check:
HANG_DETECTED is printed, immediately cancel the job:
Persistent mode: While waiting for run_in_background completion
notification, launch a hang monitoring loop using run_in_background:
When the main process completes normally, kill the monitoring loop. If the monitor fires first, it cancels the active Slurm step (not the allocation itself — the allocation stays for future runs).
On completion:
Get exit code:
sacct -j <JOB_ID> --format=JobID,State,ExitCode,Elapsed,MaxRSS -nrun_in_background result exit codeResolve log file paths:
%j in log_file_pattern with <JOB_ID>:
<work_dir>/<MODEL_NAME>_..._<JOB_ID>.out<work_dir>/<MODEL_NAME>_..._<JOB_ID>.err<work_dir>/<model_name>_<workflow_type>.logRead log files:
.err fileSearch for patterns:
success_patterns entryfailure_patterns entryReturn a structured result:
X passed, Y failed in Zs summary line. Status maps:
COMPLETED + 0 exit → PASSED; non-zero → FAILEDaccuracy: lines. If --check_accuracy was used,
assertion error means threshold not met8_done_*.txt completion marker| Symptom | Likely Cause | Action |
|---|---|---|
TIMEOUT | Job exceeded time limit | Report and suggest increasing time |
OUT_OF_MEMORY | OOM on node | Report and suggest reducing batch size |
Exit 1:0 + FAILED | Non-zero exit from srun | Read .err file for details |
CANCELLED | Preempted or user cancelled | Check sacct --format=JobID,State,Reason |
| Empty output log | Container failed to start | Check .err / .salloc.log for mount or image errors |
HANG_DETECTED in log | Process/GPU hang (deadlock, NCCL timeout) | Job auto-cancelled; check last 200 log lines for root cause |
srun: error: Unable to create step | Allocation expired | Delete state file, re-run (will re-allocate) |
srun: error: ... does not exist | Named container missing | Delete state file, re-run (will re-import) |
squeue with the -uall flagsacct (not squeue) for final job status in one-shot moderun_in_background + sleep 30 && to avoid blocking (one-shot)trtllm-agent-toolkit:internal-env-info skill
when it is installed. If the skill is absent, skip the lookup silently and
use the caller-supplied fields or the srun probe in Step 1 — do not report
the missing skill as an error.-N <nodes> to every direct srun --jobid call — without it,
srun defaults to 1 node even in a multi-node allocation--container-name in the .slurm script, otherwise pyxis will re-import
the image and lose --container-writable state