From f66cdfa32d790503e8c61050cd10bccafd39bc7f Mon Sep 17 00:00:00 2001 From: Wenyao Gao Date: Thu, 15 Oct 2026 16:45:40 -0700 Subject: [PATCH 1/2] feat: add Muse Spark Open B300 AgentX support --- .../sglang/b300-fp4-mtp/agentic.yaml | 68 +++++++++++++++++++ inferencex-e2e/configs/nvidia-master.yaml | 14 ++++ inferencex-e2e/configs/runners.yaml | 1 + inferencex-e2e/docs/MODELS.md | 1 + inferencex-e2e/infx/srt_slurm/single_node.py | 4 +- .../infx/srt_slurm/synthetic_acceptance.py | 10 +++ .../tests/srt_slurm/test_srt_single_node.py | 9 ++- .../srt_slurm/test_synthetic_acceptance.py | 33 +++++++++ inferencex-e2e/perf-changelog.yaml | 6 ++ 9 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 inferencex-e2e/benchmarks/single_node/srt-slurm-recipes/musespark/sglang/b300-fp4-mtp/agentic.yaml diff --git a/inferencex-e2e/benchmarks/single_node/srt-slurm-recipes/musespark/sglang/b300-fp4-mtp/agentic.yaml b/inferencex-e2e/benchmarks/single_node/srt-slurm-recipes/musespark/sglang/b300-fp4-mtp/agentic.yaml new file mode 100644 index 0000000000..65f8f30d93 --- /dev/null +++ b/inferencex-e2e/benchmarks/single_node/srt-slurm-recipes/musespark/sglang/b300-fp4-mtp/agentic.yaml @@ -0,0 +1,68 @@ +base: + schema: 2 + name: musespark-fp4-b300-sglang-agentic + resources: + gpu_type: b300 + gpus_per_node: 8 + slurm: + time_limit: '4:00:00' + frontend: + type: sglang + enable_multiple_frontends: false + observability: + enabled: false + tachometer: + enabled: false + engine: sglang + roles: + agg: + nodes: 1 + workers: 1 + gpus: 4 + args: + served-model-name: meta-models/Muse-Spark-Open-816B-A42B-NVFP4 + trust-remote-code: true + tensor-parallel-size: 4 + expert-parallel-size: 4 + quantization: modelopt_fp4 + moe-runner-backend: flashinfer_trtllm + kv-cache-dtype: fp8_e4m3 + mem-fraction-static: 0.8 + enable-layernorm-sp: true + enable-megamoe-for-layernorm-sp: true + # DFlash2 verifies 8 tokens per step: the current token plus 7 drafts. + speculative-algorithm: DFLASH + speculative-draft-model-path: RadixArk/Muse-Spark-Open-DFlash2 + speculative-draft-attention-backend: fa4 + speculative-num-draft-tokens: 8 + mm-feature-transport: cpu + reasoning-parser: muse + tool-call-parser: muse + watchdog-timeout: 3600 + enable-metrics: true + env: + PYTHONNOUSERSITE: '1' + PYTHONUNBUFFERED: '1' + # Outlast AIPerf's pooled connections past Uvicorn's keep-alive. + SGLANG_TIMEOUT_KEEP_ALIVE: '900' + # Cold 533 GB weight loads from shared storage plus graph capture outlast the default. + health_check: + max_attempts: 1440 + interval_seconds: 5 + benchmark: + env: + # The gated Hub repo is unreadable with the job token; tokenize from the staged checkpoint. + TOKENIZER: /model + # Warmup can leave bytes unacknowledged past AIPerf's 30 s default. + AIPERF_HTTP_TCP_USER_TIMEOUT: '900000' + AIPERF_REQUIRED_SERVER_METRIC_PREFIX: 'sglang:' + +zip_override_tp4: + roles: + agg: + args: + max-running-requests: [2, 4, 8, 16, 32, 48, 64, 96, 128, 256] + cuda-graph-max-bs-decode: [2, 4, 8, 16, 32, 48, 64, 96, 128, 256] + benchmark: + env: + CONC: ['1', '2', '4', '8', '16', '24', '32', '48', '64', '128'] diff --git a/inferencex-e2e/configs/nvidia-master.yaml b/inferencex-e2e/configs/nvidia-master.yaml index 2ff820f5b9..2488e9edb6 100644 --- a/inferencex-e2e/configs/nvidia-master.yaml +++ b/inferencex-e2e/configs/nvidia-master.yaml @@ -9661,3 +9661,17 @@ dsv4-fp4-gb200-dynamo-vllm-agentic-dspark-disagg: tp: 16 ep: 16 dp-attn: true + +musespark-fp4-b300-sglang-agentic-dflash: + image: lmsysorg/sglang:dev-muse-spark-open@sha256:8cfc5a72cb296771f5f8a4a4688cb6d05685610a830cbb616fa4ab0eb7bb2b0a + model: meta-models/Muse-Spark-Open-816B-A42B-NVFP4 + model-prefix: musespark + runner: cluster:b300-dsxe + precision: fp4 + framework: sglang + multinode: false + srt-recipe-dir: musespark/sglang/b300-fp4-mtp + scenarios: + agentic-coding: + - search-space: + - { tp: 4, ep: 4, kv-offloading: none, spec-decoding: draft_model, conc-list: [1, 2, 4, 8, 16, 24, 32, 48, 64, 128], srt-recipe: agentic.yaml } diff --git a/inferencex-e2e/configs/runners.yaml b/inferencex-e2e/configs/runners.yaml index 4d25914d7e..120edaf81e 100644 --- a/inferencex-e2e/configs/runners.yaml +++ b/inferencex-e2e/configs/runners.yaml @@ -523,6 +523,7 @@ clusters: MiniMax-M3: {root: scratch, dir: MiniMax-M3} MiniMax-M3-MXFP8: {root: scratch, dir: MiniMax-M3-MXFP8} MiniMax-M3-NVFP4: {root: scratch, dir: MiniMax-M3-NVFP4} + Muse-Spark-Open-816B-A42B-NVFP4: {root: data-models, dir: Muse-Spark-Open-816B-A42B-NVFP4} Qwen3.5-397B-A17B-FP8: {root: scratch, dir: Qwen3.5-397B-A17B-FP8} Qwen3.5-397B-A17B-NVFP4: {root: scratch, dir: Qwen3.5-397B-A17B-NVFP4} Qwen3.5-397B-A17B-NVFP4-V2: {root: scratch, dir: Qwen3.5-397B-A17B-NVFP4-V2} diff --git a/inferencex-e2e/docs/MODELS.md b/inferencex-e2e/docs/MODELS.md index 0733de44f4..bb04039310 100644 --- a/inferencex-e2e/docs/MODELS.md +++ b/inferencex-e2e/docs/MODELS.md @@ -110,6 +110,7 @@ Other offloading tiers, including NVMe KV cache offloading, are outside the init | Model architecture class | Prefix | Date added | Active scenarios | Deprecated scenarios | |---|---|---|---|---| +| Muse Spark Open 816B-A42B | `musespark` | 2026-10-15 | Agentic coding (SGLang with DFlash2 draft `RadixArk/Muse-Spark-Open-DFlash2`; golden AL curve `musespark_dflash.yaml`) | | | DeepSeek-V4.1-Flash | `dsv41flash` | 2026-09-10 | Agentic coding (vLLM: DSpark, Engram UVA offload; SGLang: DSpark arms added per SKU from 2026-09-17; ATOM: MI355X TP2/TP4 DSpark added 2026-09-23, removed in #3463, restored on srt-slurm 2026-09-30; GPU validation pending) | | | GLM-5.3 | `glm5.3` | 2026-09-22 ([#3366](https://github.com/SemiAnalysisAI/InferenceX/pull/3366)) | Agentic coding (MTP only, per the deprecation policy) | Single-turn 1k1k (deprecated for all models before this model was added; never run) | | Qwen3.8-Flash-Next | `qwen3.8next` | 2026-08-26 ([#2742](https://github.com/SemiAnalysisAI/InferenceX/pull/2742)) | Agentic coding | | diff --git a/inferencex-e2e/infx/srt_slurm/single_node.py b/inferencex-e2e/infx/srt_slurm/single_node.py index c9f8b6c8ac..3e7fc8c6c1 100644 --- a/inferencex-e2e/infx/srt_slurm/single_node.py +++ b/inferencex-e2e/infx/srt_slurm/single_node.py @@ -113,9 +113,9 @@ def validate_recipe(recipe: dict[str, Any], environment: Mapping[str, str]) -> N if environment["FRAMEWORK"] not in {"sglang", "trt", "atom", "vllm"}: raise ValueError(f"Unsupported single-node framework: {environment['FRAMEWORK']!r}") spec = spec_parameters(role, engine) - if spec and spec["method"] not in {"eagle", "eagle3", "nextn", "mtp", "dspark"}: + if spec and spec["method"] not in {"eagle", "eagle3", "nextn", "mtp", "dspark", "dflash"}: raise ValueError( - "Single-node SRT supports only native MTP, EAGLE3, DSpark or no speculation" + "Single-node SRT supports only native MTP, EAGLE3, DSpark, DFlash or no speculation" ) speculation = "mtp" if spec else workload.get("SPEC_DECODING", "none") agentic = environment["IS_AGENTIC"] == "1" diff --git a/inferencex-e2e/infx/srt_slurm/synthetic_acceptance.py b/inferencex-e2e/infx/srt_slurm/synthetic_acceptance.py index 2de8687c22..9884fe1a07 100644 --- a/inferencex-e2e/infx/srt_slurm/synthetic_acceptance.py +++ b/inferencex-e2e/infx/srt_slurm/synthetic_acceptance.py @@ -77,6 +77,16 @@ def spec_parameters(role: Mapping[str, Any], engine: str) -> dict[str, Any]: algorithm = next(iter(algorithms), "") if not algorithm: return {} + if algorithm == "dflash": + # DFlash verifies a block of the current token plus its drafts. + width = args.get("speculative-num-draft-tokens") + return { + "method": "dflash", + "num_speculative_tokens": width - 1 + if isinstance(width, int) and not isinstance(width, bool) + else None, + "model": args.get("speculative-draft-model-path", ""), + } return { "method": "dspark" if algorithm == "dspark" else algorithm, "num_speculative_tokens": args.get( diff --git a/inferencex-e2e/infx/tests/srt_slurm/test_srt_single_node.py b/inferencex-e2e/infx/tests/srt_slurm/test_srt_single_node.py index 4d88d9c86f..0e1b6bbdad 100644 --- a/inferencex-e2e/infx/tests/srt_slurm/test_srt_single_node.py +++ b/inferencex-e2e/infx/tests/srt_slurm/test_srt_single_node.py @@ -114,15 +114,18 @@ def test_ambiguous_native_variants_are_rejected(point): runtime_arguments(str(path), env) -def test_mtp_binding_uses_real_verification_and_the_chat_template(point, tmp_path): +@pytest.mark.parametrize("algorithm,spec_decoding", [("EAGLE", "mtp"), ("DFLASH", "draft_model")]) +def test_speculative_binding_uses_real_verification_and_the_chat_template( + point, tmp_path, algorithm, spec_decoding +): path, recipe, env = point recipe["roles"]["agg"]["args"].update({ - "expert-parallel-size": 4, "speculative-algorithm": "EAGLE", + "expert-parallel-size": 4, "speculative-algorithm": algorithm, "speculative-num-steps": 2, "speculative-num-draft-tokens": 3, }) recipe["roles"]["agg"]["env"] = {"SGLANG_SIMULATE_ACC_LEN": "2.5"} path.write_text(yaml.safe_dump({"base": recipe})) - env = {**env, "EP_SIZE": "4", "SPEC_DECODING": "mtp"} + env = {**env, "EP_SIZE": "4", "SPEC_DECODING": spec_decoding} _, bound = select_recipe(f"{path}:base", env) assert bound["benchmark"]["env"]["USE_CHAT_TEMPLATE"] == "true" concrete = tmp_path / "bound.yaml" diff --git a/inferencex-e2e/infx/tests/srt_slurm/test_synthetic_acceptance.py b/inferencex-e2e/infx/tests/srt_slurm/test_synthetic_acceptance.py index b51e80bfd1..be7be00ed8 100644 --- a/inferencex-e2e/infx/tests/srt_slurm/test_synthetic_acceptance.py +++ b/inferencex-e2e/infx/tests/srt_slurm/test_synthetic_acceptance.py @@ -51,6 +51,7 @@ def golden_dir(tmp_path: Path) -> Path: ("minimaxm3_eagle3.yaml", "minimax-m3", 2.5), ("minimaxm3_eagle3_gqa.yaml", "minimax-m3", 2.6), ("glm5.3_mtp.yaml", "glm-5.3", 3.2), + ("musespark_dflash.yaml", "muse-spark-open", 3.4), ]: (directory / filename).write_text( yaml.safe_dump( @@ -177,6 +178,20 @@ def test_kimi_curve_requires_an_explicit_supported_sampler( "SGLANG_SIMULATE_ACC_TOKEN_MODE": "real-draft-token", }, ), + ( + "sglang", + { + "speculative-algorithm": "DFLASH", + "speculative-draft-model-path": "RadixArk/Muse-Spark-Open-DFlash2", + "speculative-num-draft-tokens": 4, + }, + {"MODEL_PREFIX": "musespark"}, + { + "SGLANG_SIMULATE_ACC_LEN": "3.4", + "SGLANG_SIMULATE_ACC_METHOD": "match-expected", + "SGLANG_SIMULATE_ACC_TOKEN_MODE": "real-draft-token", + }, + ), ( "dynamo-sglang", {"speculative-algorithm": "EAGLE", "speculative-num-steps": 3}, @@ -226,6 +241,24 @@ def test_engine_token_selection_and_environment( assert result["environment"] == {"KEEP_GLOBAL": "yes"} +@pytest.mark.parametrize("width", [None, True, 1, 4.0]) +def test_dflash_rejects_invalid_verification_block(golden_dir: Path, width: Any) -> None: + recipe = { + "roles": { + "agg": { + "args": { + "speculative-algorithm": "DFLASH", + "speculative-num-draft-tokens": width, + } + } + } + } + with pytest.raises(ValueError, match="positive integer draft length"): + build_overrides( + recipe, "sglang", {**ENV, "MODEL_PREFIX": "musespark"}, golden_dir=golden_dir + ) + + @pytest.mark.parametrize( "environment", [{"EVAL_ONLY": "true"}, {"IS_AGENTIC": "0"}, {"SPEC_DECODING": "none"}], diff --git a/inferencex-e2e/perf-changelog.yaml b/inferencex-e2e/perf-changelog.yaml index f66d1177c7..fec849518b 100644 --- a/inferencex-e2e/perf-changelog.yaml +++ b/inferencex-e2e/perf-changelog.yaml @@ -9892,3 +9892,9 @@ - "The new image brings Kimi-K3 ROCm changes vllm-project/vllm#59591 (latent MoE up-proj stores only the local rank's shards), #59069 (AttnRes output fused with per-token FP8 input quantization), #60717 (vLLM's FP8 max in that fused quantization), #60446 (KDA autotune keys), #59523 (cuMem CUDA-graph pool offload on ROCm) and #59880 (speculative block reservations across KV cache groups with a KV connector)." - "No change to the Inferact/Kimi-K3-DSpark draft's precision: K3DSparkModel and get_draft_quant_config() are unchanged between the two images, so the draft keeps BF16 weights and an fp8 KV cache." pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/3951 + +- config-keys: [musespark-fp4-b300-sglang-agentic-dflash] + description: + - "Add Muse Spark Open NVFP4 SGLang DFlash2 AgentX on B300 at TP4/EP4 with GPU-resident KV and concurrency 1 to 128." + - "Use the committed golden curve for DFlash acceptance and the staged checkpoint for serving and tokenization." + pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/XXX From a23546bea6a5f1d34b75261728ac110403a86540 Mon Sep 17 00:00:00 2001 From: Wenyao Gao Date: Thu, 15 Oct 2026 16:46:28 -0700 Subject: [PATCH 2/2] chore: link B300 Muse Spark sweep to PR 4146 --- inferencex-e2e/perf-changelog.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inferencex-e2e/perf-changelog.yaml b/inferencex-e2e/perf-changelog.yaml index fec849518b..24c92172b4 100644 --- a/inferencex-e2e/perf-changelog.yaml +++ b/inferencex-e2e/perf-changelog.yaml @@ -9897,4 +9897,4 @@ description: - "Add Muse Spark Open NVFP4 SGLang DFlash2 AgentX on B300 at TP4/EP4 with GPU-resident KV and concurrency 1 to 128." - "Use the committed golden curve for DFlash acceptance and the staged checkpoint for serving and tokenization." - pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/XXX + pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/4146