diff --git a/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-dep16-throughput-agentic.yaml b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-dep16-throughput-agentic.yaml new file mode 100644 index 0000000000..04b082bcf9 --- /dev/null +++ b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-dep16-throughput-agentic.yaml @@ -0,0 +1,132 @@ +name: "kimi-k3-vllm-agg-gb200-dep16-throughput-agentic" + +# Day-0 GB200 translation of the official throughput-oriented multi_node_dep +# profile. TP4 x DP4 gives EP16 across four four-GPU GB200 nodes. +# https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_dep + +model: + path: "kimi-k3" + container: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + precision: "fp4" + +identity: + model: + repo: "moonshotai/Kimi-K3" + container: + image: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + frameworks: + dynamo: "1.3.0" + +dynamo: + install: false + +environment: + ETCD_LEASE_TTL: "7200" + +slurm: + time_limit: "8:00:00" + +health_check: + max_attempts: 2160 + interval_seconds: 10 + +resources: + gpu_type: "gb200" + gpus_per_node: 4 + agg_nodes: 4 + agg_workers: 1 + gpus_per_agg: 16 + +infra: + etcd_nats_dedicated_node: false + nats_max_payload_mb: 32 + +frontend: + type: dynamo + enable_multiple_frontends: false + args: + dyn-chat-processor: "dynamo" + router-mode: "kv" + router-kv-events: true + router-temperature: "0" + router-min-initial-workers: 1 + kv-cache-block-size: 64 + +backend: + type: vllm + connector: null + aggregated_environment: + HF_HUB_CACHE: "/hf_hub_cache" + HUGGINGFACE_HUB_CACHE: "/hf_hub_cache" + TRANSFORMERS_CACHE: "/hf_hub_cache" + VLLM_ENGINE_READY_TIMEOUT_S: "7200" + VLLM_RPC_TIMEOUT: "600000" + VLLM_LOG_STATS_INTERVAL: "1" + VLLM_USE_V2_MODEL_RUNNER: "0" + VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION: "1" + VLLM_ALLREDUCE_USE_FLASHINFER: "1" + VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl" + NCCL_CUMEM_ENABLE: "1" + NCCL_MNNVL_ENABLE: "1" + NCCL_NVLS_ENABLE: "1" + UCX_MEMTYPE_CACHE: "n" + UCX_NET_DEVICES: "mlx5_0:1,mlx5_1:1,mlx5_2:1,mlx5_3:1" + UCX_TLS: "rc,cuda_copy" + NCCL_IB_HCA: "mlx5_0,mlx5_1,mlx5_2,mlx5_3" + NCCL_P2P_LEVEL: "NVL" + NVIDIA_GDRCOPY: "1" + PYTORCH_ALLOC_CONF: "expandable_segments:True" + DG_JIT_CACHE_DIR: "/tmp/dg-cache-kimi-k3-gb200-dep16-{job_id}" + vllm_config: + aggregated: + served-model-name: "moonshotai/Kimi-K3" + tensor-parallel-size: 4 + pipeline-parallel-size: 1 + data-parallel-size: 4 + data-parallel-rpc-port: 13345 + enable-expert-parallel: true + trust-remote-code: true + load-format: "fastsafetensors" + safetensors-load-strategy: "lazy" + kv-cache-dtype: "fp8" + attention-backend: "FLASHINFER_MLA" + attention-config: '{"mla_prefill_backend":"TRTLLM_RAGGED","use_prefill_query_quantization":true}' + moe-backend: "deep_gemm_mega_moe" + kda-prefill-backend: "flashkda" + kernel-config: '{"enable_cutedsl_warmup":true}' + all2all-backend: "flashinfer_nvlink_one_sided" + gpu-memory-utilization: 0.92 + max-num-seqs: 128 + max-num-batched-tokens: 16384 + block-size: 64 + language-model-only: true + disable-custom-all-reduce: true + enable-prefix-caching: true + scheduler-cls: "vllm.v1.core.sched.async_scheduler.AsyncScheduler" + dyn-tool-call-parser: "kimi_k3" + reasoning-parser: "kimi_k3" + dyn-reasoning-parser: "kimi_k3" + no-enable-flashinfer-autotune: true + +sbatch_directives: + cpus-per-task: "144" + mem: "0" + +srun_options: + container-remap-root: "" + +benchmark: + type: custom + aiperf_server_metrics: true + command: bash /infmax-workspace/benchmarks/multi_node/agentic_srt.sh + env: + INFMAX_CONTAINER_WORKSPACE: "/infmax-workspace" + RESULT_DIR: "/logs/agentic" + PORT: "8000" + IS_MULTINODE: "true" + AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING: "0" + AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" + AGENTIC_WARMUP_GRACE_PERIOD: "3600" + AIPERF_DATASET_MMAP_CACHE_DIR: "/aiperf_mmap_cache" + HF_HUB_CACHE: "/hf_hub_cache" + WEKA_LOADER_OVERRIDE: "semianalysis_cc_traces_weka_062126" diff --git a/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tep16-balanced-agentic.yaml b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tep16-balanced-agentic.yaml new file mode 100644 index 0000000000..6db5821a1a --- /dev/null +++ b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tep16-balanced-agentic.yaml @@ -0,0 +1,131 @@ +name: "kimi-k3-vllm-agg-gb200-tep16-balanced-agentic" + +# Day-0 GB200 translation of the official balanced multi_node_tep profile. +# Dense layers and MoE experts are sharded across 16 GPUs on four GB200 nodes +# with the official FP8 KV cache. +# https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_tep + +model: + path: "kimi-k3" + container: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + precision: "fp4" + +identity: + model: + repo: "moonshotai/Kimi-K3" + container: + image: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + frameworks: + dynamo: "1.3.0" + +dynamo: + install: false + +environment: + ETCD_LEASE_TTL: "7200" + +slurm: + time_limit: "8:00:00" + +health_check: + max_attempts: 2160 + interval_seconds: 10 + +resources: + gpu_type: "gb200" + gpus_per_node: 4 + agg_nodes: 4 + agg_workers: 1 + gpus_per_agg: 16 + +infra: + etcd_nats_dedicated_node: false + nats_max_payload_mb: 32 + +frontend: + type: dynamo + enable_multiple_frontends: false + args: + dyn-chat-processor: "dynamo" + router-mode: "kv" + router-kv-events: true + router-temperature: "0" + router-min-initial-workers: 1 + kv-cache-block-size: 64 + +backend: + type: vllm + connector: null + aggregated_environment: + HF_HUB_CACHE: "/hf_hub_cache" + HUGGINGFACE_HUB_CACHE: "/hf_hub_cache" + TRANSFORMERS_CACHE: "/hf_hub_cache" + VLLM_ENGINE_READY_TIMEOUT_S: "7200" + VLLM_RPC_TIMEOUT: "600000" + VLLM_LOG_STATS_INTERVAL: "1" + VLLM_USE_V2_MODEL_RUNNER: "0" + VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION: "1" + VLLM_ALLREDUCE_USE_FLASHINFER: "1" + VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl" + NCCL_CUMEM_ENABLE: "1" + NCCL_MNNVL_ENABLE: "1" + NCCL_NVLS_ENABLE: "1" + UCX_MEMTYPE_CACHE: "n" + UCX_NET_DEVICES: "mlx5_0:1,mlx5_1:1,mlx5_2:1,mlx5_3:1" + UCX_TLS: "rc,cuda_copy" + NCCL_IB_HCA: "mlx5_0,mlx5_1,mlx5_2,mlx5_3" + NCCL_P2P_LEVEL: "NVL" + NVIDIA_GDRCOPY: "1" + PYTORCH_ALLOC_CONF: "expandable_segments:True" + DG_JIT_CACHE_DIR: "/tmp/dg-cache-kimi-k3-gb200-tep16-{job_id}" + vllm_config: + aggregated: + served-model-name: "moonshotai/Kimi-K3" + tensor-parallel-size: 16 + pipeline-parallel-size: 1 + enable-expert-parallel: true + trust-remote-code: true + load-format: "fastsafetensors" + safetensors-load-strategy: "lazy" + kv-cache-dtype: "fp8" + attention-backend: "FLASHINFER_MLA" + attention-config: '{"mla_prefill_backend":"TRTLLM_RAGGED","use_prefill_query_quantization":true}' + moe-backend: "flashinfer_trtllm" + kda-prefill-backend: "flashkda" + kernel-config: '{"enable_cutedsl_warmup":true}' + all2all-backend: "flashinfer_nvlink_one_sided" + gpu-memory-utilization: 0.92 + max-num-seqs: 32 + max-num-batched-tokens: 8192 + block-size: 64 + language-model-only: true + disable-custom-all-reduce: true + enable-prefix-caching: true + scheduler-cls: "vllm.v1.core.sched.async_scheduler.AsyncScheduler" + dyn-tool-call-parser: "kimi_k3" + reasoning-parser: "kimi_k3" + dyn-reasoning-parser: "kimi_k3" + no-enable-flashinfer-autotune: true + compilation-config: '{"pass_config":{"fuse_allreduce_rms":false}}' + +sbatch_directives: + cpus-per-task: "144" + mem: "0" + +srun_options: + container-remap-root: "" + +benchmark: + type: custom + aiperf_server_metrics: true + command: bash /infmax-workspace/benchmarks/multi_node/agentic_srt.sh + env: + INFMAX_CONTAINER_WORKSPACE: "/infmax-workspace" + RESULT_DIR: "/logs/agentic" + PORT: "8000" + IS_MULTINODE: "true" + AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING: "0" + AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" + AIPERF_DATASET_MMAP_CACHE_DIR: "/aiperf_mmap_cache" + HF_HUB_CACHE: "/hf_hub_cache" + WEKA_LOADER_OVERRIDE: "semianalysis_cc_traces_weka_062126" diff --git a/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tp16-latency-agentic.yaml b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tp16-latency-agentic.yaml new file mode 100644 index 0000000000..8c5389505b --- /dev/null +++ b/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic/agg-gb200-tp16-latency-agentic.yaml @@ -0,0 +1,128 @@ +name: "kimi-k3-vllm-agg-gb200-tp16-latency-agentic" + +# Day-0 GB200 translation of the official latency-oriented multi_node_tp +# profile. TP16 spans four GB200 nodes and uses the official FP8 KV cache. +# https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_tp + +model: + path: "kimi-k3" + container: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + precision: "fp4" + +identity: + model: + repo: "moonshotai/Kimi-K3" + container: + image: "nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1" + frameworks: + dynamo: "1.3.0" + +dynamo: + install: false + +environment: + ETCD_LEASE_TTL: "7200" + +slurm: + time_limit: "8:00:00" + +health_check: + max_attempts: 2160 + interval_seconds: 10 + +resources: + gpu_type: "gb200" + gpus_per_node: 4 + agg_nodes: 4 + agg_workers: 1 + gpus_per_agg: 16 + +infra: + etcd_nats_dedicated_node: false + nats_max_payload_mb: 32 + +frontend: + type: dynamo + enable_multiple_frontends: false + args: + dyn-chat-processor: "dynamo" + router-mode: "kv" + router-kv-events: true + router-temperature: "0" + router-min-initial-workers: 1 + kv-cache-block-size: 64 + +backend: + type: vllm + connector: null + aggregated_environment: + HF_HUB_CACHE: "/hf_hub_cache" + HUGGINGFACE_HUB_CACHE: "/hf_hub_cache" + TRANSFORMERS_CACHE: "/hf_hub_cache" + VLLM_ENGINE_READY_TIMEOUT_S: "7200" + VLLM_RPC_TIMEOUT: "600000" + VLLM_LOG_STATS_INTERVAL: "1" + VLLM_USE_V2_MODEL_RUNNER: "0" + VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION: "1" + VLLM_ALLREDUCE_USE_FLASHINFER: "1" + VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl" + NCCL_CUMEM_ENABLE: "1" + NCCL_MNNVL_ENABLE: "1" + NCCL_NVLS_ENABLE: "1" + UCX_MEMTYPE_CACHE: "n" + UCX_NET_DEVICES: "mlx5_0:1,mlx5_1:1,mlx5_2:1,mlx5_3:1" + UCX_TLS: "rc,cuda_copy" + NCCL_IB_HCA: "mlx5_0,mlx5_1,mlx5_2,mlx5_3" + NCCL_P2P_LEVEL: "NVL" + NVIDIA_GDRCOPY: "1" + PYTORCH_ALLOC_CONF: "expandable_segments:True" + DG_JIT_CACHE_DIR: "/tmp/dg-cache-kimi-k3-gb200-tp16-{job_id}" + vllm_config: + aggregated: + served-model-name: "moonshotai/Kimi-K3" + tensor-parallel-size: 16 + pipeline-parallel-size: 1 + trust-remote-code: true + load-format: "fastsafetensors" + safetensors-load-strategy: "lazy" + kv-cache-dtype: "fp8" + attention-backend: "FLASHINFER_MLA" + attention-config: '{"mla_prefill_backend":"TRTLLM_RAGGED","use_prefill_query_quantization":true}' + moe-backend: "flashinfer_trtllm" + kda-prefill-backend: "flashkda" + kernel-config: '{"enable_cutedsl_warmup":true}' + gpu-memory-utilization: 0.92 + max-num-seqs: 8 + max-num-batched-tokens: 8192 + block-size: 64 + language-model-only: true + disable-custom-all-reduce: true + enable-prefix-caching: true + scheduler-cls: "vllm.v1.core.sched.async_scheduler.AsyncScheduler" + dyn-tool-call-parser: "kimi_k3" + reasoning-parser: "kimi_k3" + dyn-reasoning-parser: "kimi_k3" + no-enable-flashinfer-autotune: true + compilation-config: '{"pass_config":{"fuse_allreduce_rms":false}}' + +sbatch_directives: + cpus-per-task: "144" + mem: "0" + +srun_options: + container-remap-root: "" + +benchmark: + type: custom + aiperf_server_metrics: true + command: bash /infmax-workspace/benchmarks/multi_node/agentic_srt.sh + env: + INFMAX_CONTAINER_WORKSPACE: "/infmax-workspace" + RESULT_DIR: "/logs/agentic" + PORT: "8000" + IS_MULTINODE: "true" + AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING: "0" + AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" + AIPERF_DATASET_MMAP_CACHE_DIR: "/aiperf_mmap_cache" + HF_HUB_CACHE: "/hf_hub_cache" + WEKA_LOADER_OVERRIDE: "semianalysis_cc_traces_weka_062126" diff --git a/configs/nvidia-master.yaml b/configs/nvidia-master.yaml index 8cc99a8a0b..9ad4926354 100644 --- a/configs/nvidia-master.yaml +++ b/configs/nvidia-master.yaml @@ -8049,6 +8049,78 @@ dsv4-fp4-gb300-dynamo-vllm-agentic-mtp-disagg: tp: 8 ep: 8 dp-attn: true + +# Day-0 Kimi K3 AgentX coverage on GB200. The three aggregate srt-slurm +# profiles mirror the serving strategies synthesized by the official recipe: +# latency: multi-node TP16 (4 GB200 nodes / 16 GPUs) +# balanced: multi-node TEP16 (4 GB200 nodes / 16 GPUs) +# throughput: multi-node TP4 x DP4 (4 GB200 nodes / EP16) +# https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200 +kimik3-fp4-gb200-dynamo-vllm-agentic: + image: nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0-kimi-k3-dev.1 + model: moonshotai/Kimi-K3 + model-prefix: kimik3 + runner: cluster:gb200-nv + precision: fp4 + framework: dynamo-vllm + router: { name: dynamo-router, version: "1.3.0" } + multinode: true + disagg: false + scenarios: + agentic-coding: + - search-space: + # Latency oriented: multi_node_tp strategy, TP16 across four GB200 + # nodes. + # https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_tp + - spec-decoding: none + conc-list: [1, 2, 4, 8] + prefill: + num-worker: 1 + tp: 16 + ep: 16 + dp-attn: false + additional-settings: + - "CONFIG_FILE=recipes/vllm/kimi-k3/agentic/agg-gb200-tp16-latency-agentic.yaml" + decode: + num-worker: 0 + tp: 16 + ep: 16 + dp-attn: false + # Balanced: multi_node_tep strategy, TEP16 across four GB200 nodes. + # https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_tep + - spec-decoding: none + conc-list: [8, 16, 24, 32] + prefill: + num-worker: 1 + tp: 16 + ep: 16 + dp-attn: false + additional-settings: + - "CONFIG_FILE=recipes/vllm/kimi-k3/agentic/agg-gb200-tep16-balanced-agentic.yaml" + decode: + num-worker: 0 + tp: 16 + ep: 16 + dp-attn: false + # Throughput oriented: official multi_node_dep strategy, DEP16 across + # four GB200 nodes (TP4 x DP4 = EP16, one local DP rank per node). + # The recipe allows up to 3600s for full-context saturation warmup to drain. + # https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200&nodes=4&strategy=multi_node_dep + - spec-decoding: none + conc-list: [32, 64, 96, 128, 192, 256] + prefill: + num-worker: 1 + tp: 4 + ep: 16 + dp-attn: true + additional-settings: + - "CONFIG_FILE=recipes/vllm/kimi-k3/agentic/agg-gb200-dep16-throughput-agentic.yaml" + decode: + num-worker: 0 + tp: 4 + ep: 16 + dp-attn: true + dsv4-fp4-gb300-dynamo-sglang-agentic-agg: image: lmsysorg/sglang:nightly-dev-cu13-20260711-7de33ce8 model: deepseek-ai/DeepSeek-V4-Pro diff --git a/configs/runners.yaml b/configs/runners.yaml index 851b821ba2..bb0156ab9a 100644 --- a/configs/runners.yaml +++ b/configs/runners.yaml @@ -247,6 +247,7 @@ labels: - gb200-nv_0 - gb200-nv_1 - gb200-nv_2 + - gb200-nv_3 cluster:gb300-nv: - gb300-nv_0 - gb300-nv_1 diff --git a/perf-changelog.yaml b/perf-changelog.yaml index 09108c33ea..7315dd0060 100644 --- a/perf-changelog.yaml +++ b/perf-changelog.yaml @@ -5159,3 +5159,10 @@ - "Bring-up validated in run 30326393603: all 12 configs green, zero ServerDisconnectedError after the keep-alive fix. GPU KV resolves to 42.23 GiB / 3,249,215 tokens, i.e. ~3.1 max-length requests, against MAX_NUM_SEQS = 2*CONC." - "Measured behaviour: below conc 8 the GPU-resident and DRAM arms are within run-to-run noise (1-5%). At conc 16 and 24 the GPU-resident arm thrashes -- prefix cache hit rate 2.7%, TTFT p50 86s and 191s, 49.6 and 54.9 output tok/s -- because the working set exceeds GPU KV and prefixes are recomputed. The DRAM arm holds TTFT p50 0.85s and 6.2s for 245.0 and 260.6 output tok/s (4-5x), with the CPU tier serving a 62% external prefix cache hit rate at conc 24. The high-conc GPU-resident points are retained deliberately as the honest baseline that makes the offload gain legible." pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2371 + + +- config-keys: + - kimik3-fp4-gb200-dynamo-vllm-agentic + description: + - "Add Day-0 Kimi K3 MXFP4 GB200 AgentX performance points following the official vLLM recipe: TP16 (conc 1-8), TEP16 (conc 8-32), and TP4 x DP4 = EP16 (conc 32-256). https://recipes.vllm.ai/moonshotai/Kimi-K3?hardware=gb200" + pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2354 diff --git a/runners/launch_gb200-nv.sh b/runners/launch_gb200-nv.sh index 426cd2dd87..2f913e692c 100755 --- a/runners/launch_gb200-nv.sh +++ b/runners/launch_gb200-nv.sh @@ -155,6 +155,12 @@ elif [[ $FRAMEWORK == "dynamo-vllm" ]]; then if [[ $MODEL_PREFIX == "kimik2.5" && $PRECISION == "fp4" ]]; then export MODEL_PATH="/mnt/lustre01/models/kimi-k2.5-nvfp4" export SRT_SLURM_MODEL_PREFIX="kimi-k2.5-nvfp4" + elif [[ $MODEL_PREFIX == "kimik3" && $PRECISION == "fp4" ]]; then + # Load Kimi K3 from node-local NVMe for faster startup. The checkpoint + # must be pre-staged at this exact path on every allocated GB200 node. + # This alias matches model.path in the checked-in AgentX recipes. + export MODEL_PATH="/mnt/numa1/models/Kimi-K3" + export SRT_SLURM_MODEL_PREFIX="kimi-k3" elif [[ $MODEL_PREFIX == "dsv4" && $PRECISION == "fp4" ]]; then # The FP4 checkpoint is staged on compute-visible Lustre. The former # /mnt/numa1 path is no longer present on watchtower compute nodes; @@ -172,7 +178,7 @@ elif [[ $FRAMEWORK == "dynamo-vllm" ]]; then export MODEL_PATH="/mnt/lustre01/models/MiniMax-M3-MXFP8" export SRT_SLURM_MODEL_PREFIX="minimax-m3-mxfp8" else - echo "Unsupported model prefix/precision combination: $MODEL_PREFIX/$PRECISION. Supported combinations for dynamo-vllm: kimik2.5/fp4, dsv4/fp4, minimaxm2.5/fp4, minimaxm2.5/fp8, minimaxm3/fp8" + echo "Unsupported model prefix/precision combination: $MODEL_PREFIX/$PRECISION. Supported combinations for dynamo-vllm: kimik2.5/fp4, kimik3/fp4, dsv4/fp4, minimaxm2.5/fp4, minimaxm2.5/fp8, minimaxm3/fp8" exit 1 fi else @@ -183,7 +189,7 @@ NGINX_IMAGE="nginx:1.27.4" uses_watchtower_shared_fs() { case "$MODEL_PREFIX" in - minimaxm2.5|minimaxm3|kimik2.5|qwen3.5) return 0 ;; + minimaxm2.5|minimaxm3|kimik2.5|kimik3|qwen3.5) return 0 ;; *) return 1 ;; esac } @@ -352,8 +358,7 @@ fi # TODO(CJQ): make first class upon srt-slurm upstream refactor if [[ "$IS_AGENTIC" == "1" ]]; then - # Agentic multi-node uses the same pinned cquil11/srt-slurm-nv commit as - # launch_gb300-nv.sh — everything the agentic recipes need is there: + # Agentic multi-node pins cquil11/srt-slurm-nv revisions that provide: # - BenchmarkType.CUSTOM + benchmark.command + benchmark.env # (the hook that hands off to benchmarks/multi_node/agentic_srt.sh) # - DynamoConfig.wheel (recipes pin the ai-dynamo wheel) @@ -363,10 +368,19 @@ if [[ "$IS_AGENTIC" == "1" ]]; then # must reach the agentic_srt.sh srun) git clone https://github.com/cquil11/srt-slurm-nv.git "$SRT_REPO_DIR" cd "$SRT_REPO_DIR" - git checkout de59739b172e507e15ebf145bfe305f606e82fbf - mkdir -p recipes/vllm/deepseek-v4/agentic - cp -rT "$GITHUB_WORKSPACE/benchmarks/multi_node/srt-slurm-recipes/vllm/deepseek-v4/agentic" \ - recipes/vllm/deepseek-v4/agentic + if [[ "$MODEL_PREFIX" == "kimik3" ]]; then + # Kimi K3 additionally needs vLLM TP groups within data parallel and + # every DP engine metrics endpoint exposed to AIPerf. + git checkout b1fb626fbdbfe3306dcb51cb181ab35861ec3b1c + mkdir -p recipes/vllm/kimi-k3/agentic + cp -rT "$GITHUB_WORKSPACE/benchmarks/multi_node/srt-slurm-recipes/vllm/kimi-k3/agentic" \ + recipes/vllm/kimi-k3/agentic + else + git checkout de59739b172e507e15ebf145bfe305f606e82fbf + mkdir -p recipes/vllm/deepseek-v4/agentic + cp -rT "$GITHUB_WORKSPACE/benchmarks/multi_node/srt-slurm-recipes/vllm/deepseek-v4/agentic" \ + recipes/vllm/deepseek-v4/agentic + fi elif [[ $FRAMEWORK == "dynamo-vllm" && $MODEL_PREFIX == "dsv4" ]]; then git clone https://github.com/NVIDIA/srt-slurm.git "$SRT_REPO_DIR" cd "$SRT_REPO_DIR" @@ -630,43 +644,54 @@ if [[ "${EVAL_ONLY:-false}" != "true" ]]; then exit 1 fi - # Find all result subdirectories - RESULT_SUBDIRS=$(find "$LOGS_DIR" -maxdepth 1 -type d -name "*isl*osl*" 2>/dev/null) - - if [ -z "$RESULT_SUBDIRS" ]; then - echo "Warning: No result subdirectories found in $LOGS_DIR" + if [[ "$IS_AGENTIC" == "1" ]]; then + # The custom benchmark runs inside the compute-visible + # INFMAX_WORKSPACE mount. Its aggregation step writes one + # ${RESULT_FILENAME}_conc.json there per point; stage those files + # back to GITHUB_WORKSPACE for the workflow guard and artifact upload. + copy_agentic_results \ + "$INFMAX_WORKSPACE" \ + "$GITHUB_WORKSPACE" \ + "$RESULT_FILENAME" || exit 1 else - # Process results from all configurations - for result_subdir in $RESULT_SUBDIRS; do - echo "Processing result subdirectory: $result_subdir" - - # Extract configuration info from directory name - CONFIG_NAME=$(basename "$result_subdir") - - # Find all result JSON files - RESULT_FILES=$(find "$result_subdir" -name "results_concurrency_*.json" 2>/dev/null) - - for result_file in $RESULT_FILES; do - if [ -f "$result_file" ]; then - # Extract metadata from filename - # Files may be "results_concurrency_N_gpus_G_ctx_C_gen_D.json" (disagg) or "results_concurrency_N_gpus_G.json" (non-disagg) - filename=$(basename "$result_file") - concurrency=$(echo "$filename" | sed -n 's/results_concurrency_\([0-9]*\)_gpus_.*/\1/p') - gpus=$(echo "$filename" | sed -n 's/results_concurrency_[0-9]*_gpus_\([0-9][0-9]*\).*/\1/p') - ctx=$(echo "$filename" | sed -n 's/.*_ctx_\([0-9]*\)_gen_.*/\1/p') - gen=$(echo "$filename" | sed -n 's/.*_gen_\([0-9]*\)\.json/\1/p') - - echo "Processing concurrency $concurrency with $gpus GPUs (ctx: $ctx, gen: $gen): $result_file" - - if [ -n "$ctx" ] && [ -n "$gen" ]; then - WORKSPACE_RESULT_FILE="$GITHUB_WORKSPACE/${RESULT_FILENAME}_${CONFIG_NAME}_conc${concurrency}_gpus_${gpus}_ctx_${ctx}_gen_${gen}.json" - else - WORKSPACE_RESULT_FILE="$GITHUB_WORKSPACE/${RESULT_FILENAME}_${CONFIG_NAME}_conc${concurrency}_gpus_${gpus}.json" + # Find all fixed-sequence result subdirectories. + RESULT_SUBDIRS=$(find "$LOGS_DIR" -maxdepth 1 -type d -name "*isl*osl*" 2>/dev/null) + + if [ -z "$RESULT_SUBDIRS" ]; then + echo "Warning: No result subdirectories found in $LOGS_DIR" + else + # Process results from all configurations + for result_subdir in $RESULT_SUBDIRS; do + echo "Processing result subdirectory: $result_subdir" + + # Extract configuration info from directory name + CONFIG_NAME=$(basename "$result_subdir") + + # Find all result JSON files + RESULT_FILES=$(find "$result_subdir" -name "results_concurrency_*.json" 2>/dev/null) + + for result_file in $RESULT_FILES; do + if [ -f "$result_file" ]; then + # Extract metadata from filename + # Files may be "results_concurrency_N_gpus_G_ctx_C_gen_D.json" (disagg) or "results_concurrency_N_gpus_G.json" (non-disagg) + filename=$(basename "$result_file") + concurrency=$(echo "$filename" | sed -n 's/results_concurrency_\([0-9]*\)_gpus_.*/\1/p') + gpus=$(echo "$filename" | sed -n 's/results_concurrency_[0-9]*_gpus_\([0-9][0-9]*\).*/\1/p') + ctx=$(echo "$filename" | sed -n 's/.*_ctx_\([0-9]*\)_gen_.*/\1/p') + gen=$(echo "$filename" | sed -n 's/.*_gen_\([0-9]*\)\.json/\1/p') + + echo "Processing concurrency $concurrency with $gpus GPUs (ctx: $ctx, gen: $gen): $result_file" + + if [ -n "$ctx" ] && [ -n "$gen" ]; then + WORKSPACE_RESULT_FILE="$GITHUB_WORKSPACE/${RESULT_FILENAME}_${CONFIG_NAME}_conc${concurrency}_gpus_${gpus}_ctx_${ctx}_gen_${gen}.json" + else + WORKSPACE_RESULT_FILE="$GITHUB_WORKSPACE/${RESULT_FILENAME}_${CONFIG_NAME}_conc${concurrency}_gpus_${gpus}.json" + fi + copy_to_workspace "$result_file" "$WORKSPACE_RESULT_FILE" || exit 1 fi - copy_to_workspace "$result_file" "$WORKSPACE_RESULT_FILE" || exit 1 - fi + done done - done + fi fi echo "All result files processed" diff --git a/runners/slurm_utils.sh b/runners/slurm_utils.sh index c26b4f1690..55c8652b84 100644 --- a/runners/slurm_utils.sh +++ b/runners/slurm_utils.sh @@ -41,6 +41,36 @@ copy_to_workspace() { echo "Copied $(basename "$source_file") to $destination_file" } +copy_agentic_results() { + local source_dir="$1" + local workspace="$2" + local result_filename="$3" + local result_file + local copied=0 + + if [[ ! -d "$source_dir" ]]; then + echo "ERROR: agentic result directory not found at $source_dir" >&2 + return 1 + fi + + while IFS= read -r -d '' result_file; do + copy_to_workspace \ + "$result_file" \ + "$workspace/$(basename "$result_file")" || return 1 + copied=$((copied + 1)) + done < <( + find "$source_dir" -maxdepth 1 -type f \ + -name "${result_filename}_conc*.json" -print0 + ) + + if [[ "$copied" -eq 0 ]]; then + echo "ERROR: no ${result_filename}_conc*.json results found in $source_dir" >&2 + return 1 + fi + + echo "Copied $copied agentic result file(s)" +} + copy_eval_artifacts() { local eval_dir="$1" local workspace="$2" diff --git a/runners/test_slurm_utils.py b/runners/test_slurm_utils.py new file mode 100644 index 0000000000..c027cc9b2f --- /dev/null +++ b/runners/test_slurm_utils.py @@ -0,0 +1,57 @@ +import subprocess +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +SLURM_UTILS = REPO_ROOT / "runners" / "slurm_utils.sh" + + +def run_bash(command: str, *args: Path | str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["bash", "-c", command, "bash", *(str(arg) for arg in args)], + check=False, + capture_output=True, + text=True, + ) + + +def test_copy_agentic_results_stages_only_matching_points(tmp_path: Path) -> None: + source = tmp_path / "source" + workspace = tmp_path / "workspace" + source.mkdir() + workspace.mkdir() + (source / "run_conc1.json").write_text('{"conc": 1}\n') + (source / "run_conc16.json").write_text('{"conc": 16}\n') + (source / "other_conc1.json").write_text('{"conc": 1}\n') + + result = run_bash( + 'source "$1"; copy_agentic_results "$2" "$3" run', + SLURM_UTILS, + source, + workspace, + ) + + assert result.returncode == 0, result.stderr + assert sorted(path.name for path in workspace.iterdir()) == [ + "run_conc1.json", + "run_conc16.json", + ] + + +def test_copy_agentic_results_fails_when_aggregate_is_missing( + tmp_path: Path, +) -> None: + source = tmp_path / "source" + workspace = tmp_path / "workspace" + source.mkdir() + workspace.mkdir() + + result = run_bash( + 'source "$1"; copy_agentic_results "$2" "$3" run', + SLURM_UTILS, + source, + workspace, + ) + + assert result.returncode != 0 + assert "no run_conc*.json results found" in result.stderr