Merge branch 'main' into dsv41-pd

This commit is contained in:
2026-09-22 14:56:35 +08:00
795 changed files with 55262 additions and 8438 deletions
+1 -1
View File
@@ -193,7 +193,7 @@ Spot: a command any commenter can trigger; a command that reruns far more than t
needs; an unrecognized command that is skipped silently; a reply that names the wrong needs; an unrecognized command that is skipped silently; a reply that names the wrong
backend or run. Examples: #35750, #31980, #34057, #37618, #38734, #38736, #36778. backend or run. Examples: #35750, #31980, #34057, #37618, #38734, #38736, #36778.
**E5. Fast-fail cascades link only jobs whose failures are correlated.** **E5. Fail-fast cascades link only jobs whose failures are correlated.**
Spot: a scheduled or manually dispatched run whose later jobs are cancelled by an earlier Spot: a scheduled or manually dispatched run whose later jobs are cancelled by an earlier
unrelated failure; another platform's lane cancelled by a CUDA failure. Examples: unrelated failure; another platform's lane cancelled by a CUDA failure. Examples:
#35392, #35238, #36146. #35392, #35238, #36146.
+22 -16
View File
@@ -1,11 +1,11 @@
--- ---
name: ci-workflow-guide name: ci-workflow-guide
description: Guide to SGLang CI workflow orchestration — stage ordering, fast-fail, gating, partitioning, execution modes, and debugging CI failures. Use when modifying CI workflows, adding stages, debugging CI pipeline issues, or understanding how tests are dispatched and gated across stages. description: Guide to SGLang CI workflow orchestration — stage ordering, fail-fast, gating, partitioning, execution modes, and debugging CI failures. Use when modifying CI workflows, adding stages, debugging CI pipeline issues, or understanding how tests are dispatched and gated across stages.
--- ---
# SGLang CI Workflow Orchestration Guide # SGLang CI Workflow Orchestration Guide
This skill covers the CI **infrastructure** layer — how tests are dispatched, gated, and fast-failed across stages. For test authoring (templates, fixtures, registration, model selection), see the [write-sglang-test skill](../write-sglang-test/SKILL.md). This skill covers the CI **infrastructure** layer — how tests are dispatched, gated, and aborted on failure across stages. For test authoring (templates, fixtures, registration, model selection), see the [write-sglang-test skill](../write-sglang-test/SKILL.md).
--- ---
@@ -24,9 +24,10 @@ This skill covers the CI **infrastructure** layer — how tests are dispatched,
| `.github/workflows/pr-test.yml` | Main workflow — all stages, jobs, conditions, matrix definitions | | `.github/workflows/pr-test.yml` | Main workflow — all stages, jobs, conditions, matrix definitions |
| `.github/workflows/pr-test-extra.yml` | Extra workflow — gated by BOTH `run-ci` and `run-ci-extra` labels | | `.github/workflows/pr-test-extra.yml` | Extra workflow — gated by BOTH `run-ci` and `run-ci-extra` labels |
| `.github/workflows/pr-gate.yml` | PR gating: draft check, `run-ci` label, per-user rate limiting | | `.github/workflows/pr-gate.yml` | PR gating: draft check, `run-ci` label, per-user rate limiting |
| `.github/actions/check-pr-test-health/action.yml` | Cross-job fast-fail: queries API for any failed job | | `.github/actions/check-pr-test-health/action.yml` | Cross-job fail-fast: queries API for any failed job |
| `.github/actions/wait-for-jobs/action.yml` | Stage gating: polls API until stage jobs complete | | `.github/actions/wait-for-jobs/action.yml` | Stage gating: polls API until stage jobs complete |
| `.github/actions/check-maintenance/action.yml` | Maintenance mode check | | `.github/actions/check-maintenance/action.yml` | Maintenance mode check |
| `.github/scripts/ci-labels.cjs` | Resolves the four CI control labels into dispatch axes |
| `test/run_suite.py` | Suite runner: collects, filters, partitions, executes tests | | `test/run_suite.py` | Suite runner: collects, filters, partitions, executes tests |
| `python/sglang/test/ci/ci_register.py` | Test registration (AST-parsed markers), LPT auto-partition | | `python/sglang/test/ci/ci_register.py` | Test registration (AST-parsed markers), LPT auto-partition |
| `python/sglang/test/ci/ci_utils.py` | `run_unittest_files()`: execution, retry, continue-on-error | | `python/sglang/test/ci/ci_utils.py` | `run_unittest_files()`: execution, retry, continue-on-error |
@@ -113,21 +114,21 @@ This skill covers the CI **infrastructure** layer — how tests are dispatched,
└─────────────────────────────────────┘ └─────────────────────────────────────┘
``` ```
**Every stage test job** includes a `check-pr-test-health` step after checkout — if any job in the run has already failed, the job fast-fails (red X) with a root cause annotation. **Every stage test job** includes a `check-pr-test-health` step after checkout — if any job in the run has already failed, the job fails fast (red X) with a root cause annotation.
**Scheduled runs** skip `wait-for-base-*` jobs, running all stages in parallel. Fast-fail is also disabled. **Scheduled runs** skip `wait-for-base-*` jobs, running all stages in parallel. Fail-fast is also disabled.
--- ---
## Fast-Fail Layers ## Fail-Fast Layers
4 layers of fast-fail, from fine to coarse: 4 layers of fail-fast, from fine to coarse:
| Layer | Mechanism | Granularity | Disabled on schedule? | | Layer | Mechanism | Granularity | Disabled on schedule? |
|-------|-----------|-------------|----------------------| |-------|-----------|-------------|----------------------|
| **1. Test method → file** | `unittest -f` (failfast) | One test method fails → entire test file stops immediately | Yes | | **1. Test method → file** | `unittest -f` (failfast) | One test method fails → entire test file stops immediately | Yes |
| **2. File → suite** | `run_unittest_files()` default | One test file fails → entire suite stops (`--continue-on-error` off) | Yes | | **2. File → suite** | `run_unittest_files()` default | One test file fails → entire suite stops (`--continue-on-error` off) | Yes |
| **3. Job → job (same stage)** | `check-pr-test-health` action | One job fails → other waiting jobs in same stage fast-fail (red X) | Yes | | **3. Job → job (same stage)** | `check-pr-test-health` action | One job fails → other waiting jobs in same stage fail-fast (red X) | Yes |
| **4. Stage → stage (cross-stage)** | `wait-for-base-*` + `needs` | Base A fails → base B/C jobs skip entirely (never get a runner) | Yes (wait jobs skipped) | | **4. Stage → stage (cross-stage)** | `wait-for-base-*` + `needs` | Base A fails → base B/C jobs skip entirely (never get a runner) | Yes (wait jobs skipped) |
- **Layer 1**: `-f` flag appended to all `python3 -m pytest` / `unittest` invocations in `ci_utils.py` - **Layer 1**: `-f` flag appended to all `python3 -m pytest` / `unittest` invocations in `ci_utils.py`
@@ -142,12 +143,17 @@ This skill covers the CI **infrastructure** layer — how tests are dispatched,
| Aspect | PR (`pull_request`) | Scheduled (`cron`, every 6h) | Manual dispatch (`workflow_dispatch`) | | Aspect | PR (`pull_request`) | Scheduled (`cron`, every 6h) | Manual dispatch (`workflow_dispatch`) |
|--------|---------------------|------------------------------|--------------------------------------| |--------|---------------------|------------------------------|--------------------------------------|
| **Stage ordering** | Sequential: A → B → C via `wait-for-base-*` | Parallel (all at once) | Single target stage only | | **Stage ordering** | Sequential: A → B → C via `wait-for-base-*` | Parallel (all at once) | Single target stage only |
| **Cross-job fast-fail** | Yes (`check-pr-test-health`) | Yes | Yes | | **Cross-job fail-fast** | Yes (`check-pr-test-health`) | Yes | Yes |
| **continue-on-error** | No (stop at first failure within suite) | Yes (run all tests) | No | | **continue-on-error** | No (stop at first failure within suite) | Yes (run all tests) | No |
| **Retry** | Enabled | Enabled | Enabled | | **Retry** | Enabled | Enabled | Enabled |
| **max_parallel** | 3 (default), 14 if `high priority` label | 14 | 3 (default), 14 if `high priority` | | **max_parallel** | 3 (default), 14 if `max-concurrency` label | 14 | 3 (default), 14 if `max-concurrency` |
| **PR gate** | Yes (draft, label, rate limit) | Skipped | Skipped | | **PR gate** | Yes (draft, label, rate limit) | Skipped | Skipped |
| **Concurrency** | `cancel-in-progress: true` per branch | Queue (no cancel) | Isolated per stage+SHA | | **Concurrency** | `cancel-in-progress: true` per PR | Queue (no cancel) | Isolated per stage+SHA |
Four labels relax these limits for one PR: `bypass-fail-fast`, `parallel-stages`,
`max-concurrency`, and `highest-priority` (all three). `.github/scripts/ci-labels.cjs`
resolves them; the [contribution guide](https://docs.sglang.io/developer_guide/contribution_guide.html#ci-control-labels)
describes what each one does.
--- ---
@@ -158,7 +164,7 @@ This skill covers the CI **infrastructure** layer — how tests are dispatched,
**How it works:** **How it works:**
1. Calls `listJobsForWorkflowRun` to list all jobs in the current run 1. Calls `listJobsForWorkflowRun` to list all jobs in the current run
2. Matches jobs by exact name or prefix (for matrix jobs, e.g., `base-b-test-1-gpu-small (3)`) 2. Matches jobs by exact name or prefix (for matrix jobs, e.g., `base-b-test-1-gpu-small (3)`)
3. If any matched job has `conclusion === 'failure'` → fail immediately (fast-fail) 3. If any matched job has `conclusion === 'failure'` → fail immediately (fail-fast)
4. If all matched jobs are completed and count matches `expected_count` → success 4. If all matched jobs are completed and count matches `expected_count` → success
5. Otherwise → sleep `poll-interval-seconds` (default: 60s) and retry 5. Otherwise → sleep `poll-interval-seconds` (default: 60s) and retry
6. Timeout after `max-wait-minutes` (240 min for base-a, 480 min for base-b) 6. Timeout after `max-wait-minutes` (240 min for base-a, 480 min for base-b)
@@ -179,7 +185,7 @@ This skill covers the CI **infrastructure** layer — how tests are dispatched,
--- ---
## Cross-Job Fast-Fail (`check-pr-test-health` action) ## Cross-Job Fail-Fast (`check-pr-test-health` action)
Composite action called after checkout in every stage test job (21 jobs total across `pr-test.yml`, `pr-test-multimodal-gen.yml`, `pr-test-sgl-kernel.yml`, `pr-test-jit-kernel.yml`). Composite action called after checkout in every stage test job (21 jobs total across `pr-test.yml`, `pr-test-multimodal-gen.yml`, `pr-test-sgl-kernel.yml`, `pr-test-jit-kernel.yml`).
@@ -189,7 +195,7 @@ Composite action called after checkout in every stage test job (21 jobs total ac
3. If root cause failures found → calls `core.setFailed()` with the list of root cause job names 3. If root cause failures found → calls `core.setFailed()` with the list of root cause job names
4. If none → does nothing (step succeeds) 4. If none → does nothing (step succeeds)
**Cascade filtering**: When job A fast-fails due to health check, it also has `conclusion: failure`. Without filtering, job B would list both the original failure AND job A's fast-fail. The filter checks each failed job's `steps` array — if the failing step name contains `check-pr-test-health` or `Check PR test health`, it's excluded from the root cause list. **Cascade filtering**: When job A fails fast due to the health check, it also has `conclusion: failure`. Without filtering, job B would list both the original failure AND job A's fail-fast. The filter checks each failed job's `steps` array — if the failing step name contains `check-pr-test-health` or `Check PR test health`, it's excluded from the root cause list.
**Usage pattern:** **Usage pattern:**
```yaml ```yaml
@@ -210,11 +216,11 @@ steps:
**Visual effect**: Job shows **red X** (failure) with error annotation showing root cause job names. Subsequent steps are naturally skipped (default `if: success()` is false after a failed step). No per-step `if` guards needed. **Visual effect**: Job shows **red X** (failure) with error annotation showing root cause job names. Subsequent steps are naturally skipped (default `if: success()` is false after a failed step). No per-step `if` guards needed.
**No stage filtering**: Checks ALL jobs in the run, not just the current stage. Any failure anywhere triggers fast-fail. **No stage filtering**: Checks ALL jobs in the run, not just the current stage. Any failure anywhere triggers fail-fast.
**Error message example:** **Error message example:**
``` ```
Fast-fail: skipping — root cause job(s): base-b-test-1-gpu-small (0), base-b-test-1-gpu-small (1) Fail-fast: skipping — root cause job(s): base-b-test-1-gpu-small (0), base-b-test-1-gpu-small (1)
``` ```
--- ---
+100 -277
View File
@@ -1,282 +1,105 @@
--- ---
name: clean-startup-log name: clean-startup-log
description: Clean up noisy startup warnings and spurious prints in SGLang server logs. Use when users ask to clean up unwanted warnings, deprecation messages, or third-party noise in the server startup output. description: Audit SGLang startup logs, save evidence, and propose cleanup for user review. With no arguments, run Qwen3-8B at TP1 and TP2 plus gpt-oss-20b at TP1.
disable-model-invocation: true disable-model-invocation: true
--- ---
# Clean Up SGLang Server Startup Logs # Audit SGLang Startup Logs
Goal: ensure the server startup log is clean and minimal, with no spurious warnings, deprecation messages, or unformatted prints from third-party libraries. The default outcome is saved logs and a findings report. Apply runtime changes
only after the user selects them. A request to edit this skill does not itself
## Workflow launch servers.
### 1. Launch a server and capture the log ## Default runs
```bash A bare `$clean-startup-log` invocation runs these cases sequentially without
uv run sglang serve --model-path Qwen/Qwen3-8B 2>&1 | tee /tmp/startup_log.txt asking for commands. Explicit commands, models, or TP sizes replace this matrix.
```
| Case / log filename | Command |
Wait until the server prints `The server is fired up and ready to roll!`, then Ctrl-C. |---|---|
| `qwen3-8b-tp1.log` | `uv run sglang serve --model-path Qwen/Qwen3-8B` |
For TP>1 testing: | `qwen3-8b-tp2.log` | `uv run sglang serve --model-path Qwen/Qwen3-8B --tp 2` |
```bash | `gpt-oss-20b-tp1.log` | `uv run sglang serve --model-path openai/gpt-oss-20b` |
uv run sglang serve --model-path Qwen/Qwen3-8B --tp 2 2>&1 | tee /tmp/startup_log.txt
``` These cover dense, tensor-parallel, and MoE/hybrid sliding-window attention
startup. Reuse complete captures from the current audit when code and environment
For MoE / hybrid-SWA models (e.g. gpt-oss), test separately — they exercise different code paths: have not changed.
```bash
uv run sglang serve --model-path openai/gpt-oss-20b 2>&1 | tee /tmp/startup_log.txt ## Capture logs
```
1. Check the checkout, free GPUs, and ports once. Use the requested command when
### 2. Compare against the clean reference log resources are free; otherwise select free GPUs with `CUDA_VISIBLE_DEVICES` and
an unused `--port`, recording the adjustments. Leave existing servers alone.
Read `/tmp/startup_log.txt` and compare it against the reference log at the bottom of this file. Identify lines that: 2. Create a unique directory with `mktemp -d /tmp/sglang-startup-audit-XXXXXX`.
Save raw stdout and stderr together in a separate log for each case, for
- Do NOT have the `[timestamp]` or `[timestamp TPx]` logger prefix example with `set -o pipefail` and `COMMAND 2>&1 | tee LOG_PATH`. Record commands,
- Contain `WARNING`, `deprecated`, `is deprecated`, or similar noise GPU IDs, ports, commit, relevant overrides, and readiness status.
- Are printed by third-party libraries (transformers, torchao, NCCL, Gloo, tqdm, etc.) 3. Wait for `The server is fired up and ready to roll!`, then stop that server and
- Are duplicate/redundant with information already logged by SGLang its workers before the next case. First runs can spend many minutes downloading
- Appear multiple times due to `ModelConfig` being constructed in multiple processes weights or compiling FlashInfer kernels; check download/compiler activity
before treating a quiet log as a hang. Preserve partial logs for failed or
### 3. Classify each noisy line stalled starts and report the last stage. Continue independent cases when possible.
4. Preserve the user's logging configuration, including `NCCL_DEBUG`. If NCCL
For each noisy line, determine: verbosity needs explaining, inspect relevant shell settings, `NCCL_CONF_FILE`,
and `/etc/nccl.conf`. Do not override intentional diagnostics or recommend
| Category | Action | `NCCL_DEBUG=WARN` solely because the output is long. Avoid full environment dumps.
|----------|--------|
| **SGLang code using wrong API** | Fix the SGLang code (e.g., replace deprecated API with new one) | ## Investigate efficiently
| **SGLang code logging at wrong level** | Change log level (e.g., warning -> debug for non-actionable messages) |
| **Duplicated across processes** | Downgrade to debug — info logged in one process becomes noise in 3-4 | - Scan for deprecations, duplicate handler output, unrelated import failures,
| **Third-party lib prints at import time** | Suppress the logger or redirect stdout during that import | unformatted prints, and unexpected warnings. Read representative excerpts and
| **C-level print from .so library** | Redirect fd 1 during the specific C call, or accept it if too invasive | counts instead of repeatedly dumping `server_args`, progress redraws, or NCCL
| **Real warning the user should see** | Keep it | diagnostics. Normalize carriage returns for analysis only; preserve raw logs.
- Trace each candidate to its actual emitter with focused `rg` searches. Inspect
### 4. Present findings before fixing its log level: SGLang's formatter may omit severity. Group shared signatures
across cases and distinguish handler duplication from separate GPU/process calls.
List all noisy lines with their source and proposed fix. Ask the user to review before making changes. - Repetition, WARNING severity, or a different third-party format alone does not
establish a cleanup need. Consider whether the message explains configuration,
### 5. Apply fixes and verify progress, resource use, or an operational limitation.
- Consult [noise-source hints](references/noise-sources.md) only for a matching
After approval, apply fixes one at a time, re-launch the server, and verify each fix works. signature or an unresolved emitter. Verify current code rather than trusting
historical line numbers, fix status, or assumptions about unrelated models.
## Key Architecture: Why Logs Repeat
## Accepted output
`ModelConfig` is constructed **3-4 times** during startup across different processes:
1. Main process: `ServerArgs.__post_init__()``get_model_config()``ModelConfig()` Preserve these reviewed messages unless the user requests a different policy:
2. Scheduler subprocess: `Scheduler.init_model_config()``ModelConfig.from_server_args()`
3. Scheduler subprocess: `TpModelWorker._init_model_config()``ModelConfig.from_server_args()` - NCCL diagnostics enabled by the user's environment or host configuration.
4. Main process: `TokenizerManager.init_model_config()``ModelConfig.from_server_args()` - NUMA permission warnings, including one check per GPU in TP runs.
- GPT-OSS MXFP4 backend-selection warnings and default page-size selection warnings.
Similarly, `get_tokenizer()` is called **5 times** across processes: - `Init Unified Radix Cache. Components: ... Tree Core: ...`, tree-cache summaries,
1. `resolve_auto_parsers` (main) — `template_detection.py` SWA allocation details, and per-rank memory/timing records.
2. `Scheduler.init_tokenizer()` (scheduler subprocess) — `scheduler.py` - Useful progress bars, warmup HTTP access logs, uv synchronization messages,
3. `DetokenizerManager` (detokenizer subprocess) — `detokenizer_manager.py` isolated NCCL/Gloo startup lines, and one timestamped HF authentication warning.
4. `TpModelWorker.__init__()` (scheduler subprocess) — `tp_worker.py`
5. `TokenizerManager` (main) — `tokenizer_manager.py` These can appear in a clean startup log. Do not repeatedly propose the declined
NUMA deduplication, backend/page-size level changes, or NCCL verbosity override.
Any `logger.info()` or `logger.warning()` in `ModelConfig.__init__()` or `get_tokenizer()` will appear 3-5 times. **Keep these at `logger.debug()`.** Keep real operational warnings visible: for example, a Harmony vocabulary failure
can disable `/v1/responses` even when server readiness and `/generate` succeed.
## Known Noise Sources and Fixes (from past sessions)
## Report before changing code
### 1. torchao "Skipping import of cpp extensions due to incompatible torch version"
Return a compact run table with readiness status and clickable raw-log links.
- **Source:** `torchao/__init__.py` — printed via `logger.warning()` when torch version < 2.11.0 For each actual cleanup candidate, give an exact representative message, affected
- **Trigger:** `sglang/__init__.py` -> `_apply_hf_patches()` -> `_patch_removed_symbols()` -> `from transformers.models.llama import modeling_llama` -> deep import chain -> `transformers/quantizers/auto.py` -> `from .quantizer_torchao import TorchAoHfQuantizer` -> imports torchao cases/counts, source file/function, and specific proposed behavior. Distinguish
- **Fix:** In `hf_transformers_patches.py::_patch_removed_symbols()`, temporarily set the `torchao` logger level to `ERROR` around the `modeling_llama` import: confirmed findings from suspicions and operational failures from logging noise.
```python
_torchao_logger = logging.getLogger("torchao") If there are no actionable cleanup findings, say the logs are clean and no
_prev_level = _torchao_logger.level further cleanup is needed. Otherwise, ask which numbered changes to adopt and
_torchao_logger.setLevel(logging.ERROR) wait for the user's selections before editing runtime code or preparing patches.
try: Honor existing approvals and declined items without asking again.
from transformers.models.llama import modeling_llama
finally: ## Apply selected changes
_torchao_logger.setLevel(_prev_level)
``` - Batch compatible approved edits, then verify affected cases once. Repeat a
startup only for a new change, failure, or unresolved concern; do not relaunch
### 2. "`torch_dtype` is deprecated! Use `dtype` instead!" (PARTIALLY FIXED) after every one-line edit. Save verification logs separately from baselines.
- Preserve useful warnings and application handlers. HF can warn during early
- **Source:** `transformers/configuration_utils.py` — the `torch_dtype` property warns via `logger.warning_once()` CLI model detection before `configure_logger()`, and spawned processes have
- **Trigger:** Model files accessing `config.torch_dtype` instead of `config.dtype` independent logger state. Keep `configure_hf_hub_logger()` in both
- **Fix applied so far:** Only `models/gpt_oss.py` (lines 222, 471) — tested with `openai/gpt-oss-20b`. `suppress_noisy_warnings()` and `configure_logger()`; make repeated setup safe.
- **Remaining files that still use `config.torch_dtype`** (fix each only after testing with the corresponding model): - Keep the legacy compiled-kernel cache migration notice at DEBUG. Avoid broad
- `models/bailing_moe.py` (line 302) library-level suppression or fd redirection for a narrow logging problem.
- `models/llada2.py` (line 313) - Run relevant formatting and focused existing checks. Add tests only when they
- `models/qwen3_next.py` (lines 192, 209) verify meaningful behavior, not a log-level spelling. Report changes and
- `models/qwen3_5.py` (line 245) verification; create branches, commits, and PRs when requested.
- `models/nano_nemotron_vl.py` (lines 79, 102, 284)
- `models/llava.py` (lines 732, 734-737)
- `model_loader/loader.py` (line 649)
- **Note:** `common.py` was already fixed in a prior session. If new model files are added with `config.torch_dtype`, the warning will reappear — grep for `\.torch_dtype` to find them.
- **Important:** Only change `config.torch_dtype` → `config.dtype` for models you have actually tested. The `dtype` property should return the same value, but verify per-model to avoid regressions.
### 3. "`BaseImageProcessorFast` is deprecated"
- **Source:** `transformers/utils/import_utils.py` — the lazy module `__getattr__` warns when `BaseImageProcessorFast` is accessed
- **Trigger:** `base_processor.py` and `ernie45_vl.py` have `from transformers import BaseImageProcessorFast` at top level. These are imported eagerly via `tokenizer_manager.py` -> `multimodal_processor.py` -> `base_processor.py`, even for non-multimodal models.
- **Fix:** Replace `from transformers import BaseImageProcessorFast` with `from transformers import BaseImageProcessor` and update all `isinstance(..., BaseImageProcessorFast)` checks to `isinstance(..., BaseImageProcessor)`
### 4. "No platform detected. Using base SRTPlatform with defaults."
- **Source:** `sglang/srt/platforms/__init__.py` — `logger.warning()`
- **Fix:** Change to `logger.debug()` — this is expected on machines without a platform plugin and not actionable.
### 5. `NCCL version 2.27.7+cuda13.0`
- **Source:** C-level print from `libnccl.so` during `ncclCommInitRank()` call
- **Status:** Accepted as-is. SGLang already logs the version via `sglang is using nccl==X.Y.Z`. The C-level print cannot be suppressed without redirecting stdout fd, which is too invasive. `NCCL_DEBUG=WARN` does not suppress it in NCCL 2.27+.
### 6. `[Gloo] Rank X is connected to Y peer ranks`
- **Source:** C++ Gloo library print during process group init
- **Status:** Accepted as-is. From C++ code inside PyTorch's Gloo backend.
### 7. `torchao SyntaxWarning: invalid escape sequence`
- **Source:** `torchao/quantization/quant_api.py` — a raw string with unescaped `\.`
- **Status:** Upstream torchao bug. Cannot fix from SGLang side.
### 8. tqdm progress bars (e.g., `Multi-thread loading shards`, `Capturing batches`)
- **Status:** These are expected and useful. They show progress during weight loading and CUDA graph capture. Keep them.
### 9. CUTE_DSL "Unexpected error during package walk" — double-logged (FIXED)
- **Source:** `nvidia-cutlass-dsl` package at `.venv/.../cutlass/cutlass_dsl/cutlass.py`, line 391. Logger named `CUTE_DSL` with its own `StreamHandler`.
- **Trigger:** During CUDA graph capture, cutlass DSL walks packages and hits an unexpected error for `cutlass.cute.experimental`.
- **Root cause of double-logging:** The CUTE_DSL logger has `propagate=True` (default), so the warning is emitted by both the CUTE_DSL handler (with its format) and the root logger (SGLang's format).
- **Fix applied:** In `entrypoints/engine.py`, changed `CUTE_DSL_LOG_LEVEL` from `"30"` (WARNING) to `"40"` (ERROR). This suppresses the WARNING at both the CUTE_DSL logger and root propagation levels. The env var controls both `logger.setLevel()` and `console_handler.setLevel()` in cutlass's `setup_log()`.
### 10. ModelConfig init logs repeated 3x (FIXED)
- **Lines:** `"Downcasting torch.float32 to ..."`, `"Hybrid swa model: ..."`, `"DeepGemm is enabled but ..."`
- **Source:** `configs/model_config.py` — `_get_and_verify_dtype()` (line 1457), `_derive_hybrid_model()` (line 497), `_verify_quantization()` (line 1236)
- **Root cause:** `ModelConfig.__init__()` is called 3-4 times in different processes (see "Key Architecture" above). Each construction fires the same log lines.
- **Fix applied:** Downgraded all three from `logger.info()`/`logger.warning()` to `logger.debug()`. The dtype is already visible in `server_args` and `Load weight end`. Hybrid SWA info appears in `Tree cache initialized`. DeepGemm is not actionable.
### 11. Tokenizer retry/fallback messages repeated 3-4x (FIXED)
- **Lines:** `"Tokenizer loaded as generic TokenizersBackend ... retrying"`, `"Loading tokenizer ... directly as PreTrainedTokenizerFast"`, `"Tokenizer for ... loaded as generic TokenizersBackend. Set --trust-remote-code"`
- **Source:** `utils/hf_transformers/tokenizer.py` — `_resolve_tokenizers_backend()` (line 215), `_load_tokenizer_by_declared_class()` (line 110), final warning (line 244)
- **Root cause:** 5 separate `get_tokenizer()` calls across processes (see "Key Architecture" above). Each produces 3 log lines. Concurrent subprocess launches cause interleaved/doubled output.
- **Fix applied:** Downgraded all three from `logger.warning()`/`logger.info()` to `logger.debug()`.
### 12. Template detection logs — 5 lines consolidated to 1 (FIXED)
- **Lines:** `"Detected reasoning config '...' from template rule '...'"`, `"Detected reasoning parser '...' from template rule '...'"`, `"Detected tool-call parser '...' from template rule '...'"`, `"Auto-detected reasoning parser: ..."`, `"Auto-detected tool-call parser: ..."`
- **Source:** `managers/template_detection.py` (lines 337, 370) logged each detection rule match. `managers/template_manager.py` (lines 177-182) logged summary lines that duplicated the detection logs.
- **Fix applied:** Removed per-rule logs from `template_detection.py`. Consolidated the 5 lines in `template_manager.py` into a single summary: `"Auto-detected template features: reasoning_config=..., reasoning_parser=..., tool_call_parser=..."`
### 13. KV cache dtype logged separately from allocation (FIXED)
- **Lines:** `"Using KV cache dtype: torch.bfloat16"` then `"KV Cache is allocated. #tokens: ..., K size: ..., V size: ..."`
- **Source:** `model_executor/model_runner.py` (line 2217) and `mem_cache/memory_pool.py` (line 740)
- **Fix applied:** Removed the standalone dtype log from `model_runner.py`. Added `dtype` field to the allocation log in `memory_pool.py`: `"KV Cache is allocated. dtype: torch.bfloat16, #tokens: ..., K size: ..., V size: ..."`
### 14. CUTLASS backend warning — B200 → SM100, warning → info (FIXED)
- **Line:** `"CUTLASS backend is disabled when piecewise cuda graph is enabled due to TMA descriptor initialization issues on B200."`
- **Source:** `layers/attention/flashinfer_backend.py` (line 249)
- **Fix applied:** Changed "B200" to "SM100 GPUs" (the condition checks `is_sm100_supported()` which matches SM10x, not just B200). Downgraded from `logger.warning()` to `logger.info()` since it's an expected automatic fallback.
### 15. `max_total_num_tokens` and `Tree cache initialized` log ordering
- **Issue:** `max_total_num_tokens=...` appears before `Tree cache initialized:...` even though tree cache is conceptually part of memory setup.
- **Root cause:** `max_total_num_tokens` is logged inside `init_model_worker()` (scheduler.py:972), which runs before `build_kv_cache()` (scheduler.py:425) where tree cache is created.
- **Status:** Not fixed — reordering was reverted. Acceptable as-is.
### 16. `Ignore import error when loading sglang.srt.models.midashenglm`
- **Source:** `models/registry.py` (line 109) — `logger.warning()` during `import_model_classes()` which iterates all model modules via `pkgutil.iter_modules`
- **Trigger:** The `midashenglm` model depends on `torchaudio`, which fails to load
- **Status:** Should be downgraded to `logger.debug()` — not actionable when loading an unrelated model. Same pattern exists in `managers/multimodal_processor.py`, `dllm/algorithm/__init__.py`, `multimodal_gen/runtime/models/registry.py`.
### 17. `Multiple NUMA nodes found for GPU X`
- **Source:** `utils/numa_utils.py` (line 112) — `logger.warning()`
- **Status:** Could be downgraded to `logger.info()`. The situation is handled gracefully ("Using the first one") and not actionable.
### 18. Warmup `/model_info` access log
- **Source:** Uvicorn access log, triggered by SGLang's own warmup at `entrypoints/http_server.py` (line 1877)
- **Status:** SGLang talking to itself. Could suppress uvicorn access logger during warmup, or exclude `/model_info` from warmup access logging.
## Investigation Techniques
### Trace what triggers an import
```python
import sys
_real_import = __builtins__.__import__
def _tracing_import(name, *args, **kwargs):
if 'TARGET_MODULE' in name:
import traceback
print(f'=== Importing {name} ===')
traceback.print_stack()
return _real_import(name, *args, **kwargs)
__builtins__.__import__ = _tracing_import
```
### Trace what triggers a logger warning
```python
import logging, traceback
class TraceHandler(logging.Handler):
def emit(self, record):
if 'SEARCH_STRING' in record.getMessage():
traceback.print_stack()
h = TraceHandler()
h.setLevel(logging.WARNING)
logging.getLogger('TARGET_LOGGER_NAME').addHandler(h)
```
### Find C-level prints in .so files
```bash
strings /path/to/library.so | grep "SEARCH_STRING"
```
### Find all config.torch_dtype accesses (for deprecation warning)
```bash
grep -rn '\.torch_dtype' python/sglang/srt/models/ python/sglang/srt/model_loader/ python/sglang/srt/utils/hf_transformers/
```
## Reference: Clean Startup Log (TP=1, Qwen3-8B)
```
[2026-05-24 00:52:39] Attention backend not specified. Use trtllm_mha backend by default.
[2026-05-24 00:52:39] TensorRT-LLM MHA only supports page_size of 16, 32 or 64, changing page_size from None to 64.
[2026-05-24 00:52:40] server_args=ServerArgs(model_path='Qwen/Qwen3-8B', ...)
[2026-05-24 00:52:40] Multiple NUMA nodes found for GPU 0: [...]. Using the first one.
[2026-05-24 00:52:42] Using default HuggingFace chat template with detected content format: string
[2026-05-24 00:52:42] Auto-detected template features: reasoning_config=..., reasoning_parser=qwen3, tool_call_parser=qwen
[2026-05-24 00:52:50] Init torch distributed begin.
[Gloo] Rank 0 is connected to 0 peer ranks. Expected number of connected peer ranks is : 0
[Gloo] Rank 0 is connected to 0 peer ranks. Expected number of connected peer ranks is : 0
[Gloo] Rank 0 is connected to 0 peer ranks. Expected number of connected peer ranks is : 0
[2026-05-24 00:52:50] Init torch distributed ends. elapsed=0.21 s, mem usage=0.10 GB
[2026-05-24 00:52:51] Load weight begin. avail mem=275.75 GB
[2026-05-24 00:52:51] Found local HF snapshot for Qwen/Qwen3-8B at ...; skipping download.
Multi-thread loading shards: 100% Completed | 5/5 [00:01<00:00, 2.62it/s]
[2026-05-24 00:52:54] Load weight end. elapsed=2.62 s, type=Qwen3ForCausalLM, avail mem=260.48 GB, mem usage=15.28 GB.
[2026-05-24 00:52:54] KV Cache is allocated. dtype: torch.bfloat16, #tokens: 1707904, K size: 117.28 GB, V size: 117.28 GB
[2026-05-24 00:52:54] Memory pool end. avail mem=25.28 GB
[2026-05-24 00:52:54] CUTLASS backend is disabled when piecewise cuda graph is enabled due to TMA descriptor initialization issues on SM100 GPUs. Using auto backend instead for stability.
[2026-05-24 00:52:54] Capture cuda graph begin. This can take up to several minutes. avail mem=24.16 GB
[2026-05-24 00:52:54] Capture cuda graph bs [1, 2, 4, ...]
Capturing batches (bs=1 avail_mem=23.56 GB): 100% | 52/52 [00:05<00:00, 10.36it/s]
[2026-05-24 00:53:00] Capture cuda graph end. Time elapsed: 5.38 s. mem usage=0.60 GB. avail mem=23.56 GB.
[2026-05-24 00:53:00] Capture piecewise CUDA graph begin. avail mem=23.56 GB
[2026-05-24 00:53:00] Capture cuda graph num tokens [4, 8, 12, ...]
Compiling num tokens (num_tokens=4): 100% | 74/74 [00:09<00:00, 7.44it/s]
Capturing num tokens (num_tokens=4 avail_mem=21.24 GB): 100% | 74/74 [00:07<00:00, 10.44it/s]
[2026-05-24 00:53:18] Capture piecewise CUDA graph end. Time elapsed: 18.18 s. mem usage=2.32 GB. avail mem=21.24 GB.
[2026-05-24 00:53:20] Tree cache initialized: source=default impl=RadixCache hybrid_swa=False hybrid_ssm=False hierarchical=False streaming_wrapped=False
[2026-05-24 00:53:20] max_total_num_tokens=1707904, chunked_prefill_size=16384, max_prefill_tokens=16384, max_running_requests=4096, context_len=40960, available_gpu_mem=21.24 GB
[2026-05-24 00:53:20] INFO: Started server process [1964249]
[2026-05-24 00:53:20] INFO: Waiting for application startup.
[2026-05-24 00:53:20] Using default chat sampling params from model generation config: {'temperature': 0.6, 'top_k': 20, 'top_p': 0.95}
[2026-05-24 00:53:20] INFO: Application startup complete.
[2026-05-24 00:53:20] INFO: Uvicorn running on http://127.0.0.1:30000 (Press CTRL+C to quit)
[2026-05-24 00:53:21] Prefill batch, #new-seq: 1, #new-token: 64, ...
[2026-05-24 00:53:21] INFO: 127.0.0.1:... - "POST /generate HTTP/1.1" 200 OK
[2026-05-24 00:53:21] The server is fired up and ready to roll!
```
Note: `[Gloo]` messages and tqdm progress bars are acceptable. The key is no warnings or deprecation messages from transformers, torchao, or other third-party libraries. The `CUTLASS backend is disabled` message is now `info` level, not a warning.
@@ -0,0 +1,70 @@
# Startup noise-source hints
Read this only for an observed signature that needs investigation. Paths and
behaviors can change; search the current checkout and installed dependency before
proposing a fix. The accepted-output policy in `../SKILL.md` takes precedence.
These are investigation hints, not a list of changes to apply.
## Common signatures
| Observed output | Where to investigate | Decision to verify |
|---|---|---|
| HF warning printed in two formats | `utils/common.py`, HF logger handlers and propagation | A library handler plus root propagation can emit the same record twice. Keep one timestamped copy, including before full server logging setup. |
| `Skipping import of cpp extensions due to incompatible torch version` | `torchao/__init__.py`; imports through `hf_transformers_patches.py` and transformers quantizers | Determine whether torchao is needed for the requested model before proposing narrow import-time suppression. |
| `torch_dtype` is deprecated | Model code, loader, and HF utilities accessing `config.torch_dtype` | Prefer `config.dtype` where supported; change and verify only affected, tested models. |
| `BaseImageProcessorFast` is deprecated | Multimodal processor imports and type checks | Check the installed transformers replacement and compatible `isinstance` behavior. |
| `No platform detected. Using base SRTPlatform with defaults.` | Platform detection | Decide whether fallback is expected on this machine or indicates a missing plugin. |
| `Unexpected error during package walk` in CUTE_DSL | Installed cutlass package, its handler, root propagation, and `CUTE_DSL_LOG_LEVEL` | Inspect the actual exception and duplicate-handler path; do not assume the current log level or hide all warnings. |
| Repeated dtype, hybrid-model, or tokenizer fallback messages | Model-config and tokenizer construction in launcher, scheduler, and detokenizer | Multiple constructors may explain repetition. Retain meaningful per-process information and distinguish these from handler duplication. |
| Repeated template detection messages | `managers/template_detection.py` and `managers/template_manager.py` | Check whether a summary already contains the same information. |
| KV cache dtype logged separately from allocation | Model runner and memory pool | Check whether the allocation summary already carries dtype; retain useful allocation and SWA details. |
| CUTLASS backend disabled during graph capture | Attention backend selection | Expected fallback may be useful information; retain the reason and selected behavior. |
| `Ignore import error when loading ...` | Model, multimodal, or algorithm registries | Distinguish an unused optional model dependency from failure to load the requested model. |
| `Multiple NUMA nodes found for GPU ...` | `utils/numa_utils.py` | This differs from the accepted NUMA permission warning; evaluate its context separately. |
| `OpenAI Responses API (/v1/responses) disabled` | `entrypoints/http_server.py`, serving responses, and Harmony vocabulary loading | A real endpoint limitation should remain visible even if generation warmup succeeds. |
| `SyntaxWarning: invalid escape sequence` from a dependency | Installed package file named by the warning | Identify the upstream issue; avoid editing the installed package as a repository fix. |
Repeated constructors and separate processes are not blanket reasons to downgrade
messages. Constructor counts and import paths vary by model and checkout. Likewise,
cache-summary ordering and native library formats need no change merely because
they differ from a historical reference log.
## Targeted tracing
Start with source searches. For example:
```bash
rg -n 'SEARCH_STRING' python/sglang/srt/FOCUSED_DIRECTORY
rg -n '\.torch_dtype' python/sglang/srt/models/MODEL.py
```
If the emitter is clear but its call path is not, attach a temporary handler in an
isolated reproduction:
```python
import logging
import traceback
class TraceHandler(logging.Handler):
def emit(self, record):
if "SEARCH_STRING" in record.getMessage():
traceback.print_stack()
target = logging.getLogger("TARGET_LOGGER_NAME")
handler = TraceHandler()
target.addHandler(handler)
try:
reproduce_observed_warning()
finally:
target.removeHandler(handler)
```
For suspected native output, search only the identified shared library:
```bash
strings /path/to/library.so | rg -F 'SEARCH_STRING'
```
Keep diagnostic instrumentation out of committed fixes and baseline logs.
@@ -169,6 +169,27 @@ least one real deployment or capability boundary. Put orthogonal runtime feature
variant/quant/strategy needs its own image), `multiNodeHints` only for fabric-specific hw variant/quant/strategy needs its own image), `multiNodeHints` only for fabric-specific hw
(e.g. gb200). (e.g. gb200).
5. **Diffusion pages: add the ComfyUI section.** Every diffusion cookbook page ends with
a `## <n>. Run in ComfyUI` section so a reader never has to guess whether the model is
reachable from ComfyUI. It is one component; the per-model facts live in the component,
not the page:
```mdx
## <n>. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="<key>" />
```
Pick `model` from the table in
`docs/src/snippets/diffusion/comfyui-support.jsx`. Use the model's own key when it has
an entry (its executor or dedicated node differs); otherwise use the generic `image` or
`video`. A model gets its own key only when the plugin actually treats it specially —
an entry in `executor_class_dict`
(`python/sglang/multimodal_gen/apps/ComfyUI_SGLDiffusion/core/generator.py`) or a
dedicated node. Adding a key without the matching plugin support makes the page lie.
### Site-wiring (do all three) ### Site-wiring (do all three)
- **`docs/docs.json`** — add the page under Cookbook → `<category>` → `<Vendor>`, at - **`docs/docs.json`** — add the page under Cookbook → `<category>` → `<Vendor>`, at
@@ -147,6 +147,18 @@ than restating.
equal what the engine emits from the corresponding cell — same flags, same order. Drift equal what the engine emits from the corresponding cell — same flags, same order. Drift
here is the most common review miss. here is the most common review miss.
### 5b. ComfyUI section (diffusion pages)
- A diffusion page ends with `## <n>. Run in ComfyUI` rendering `<ComfyUISupport />`. A
reader must not have to guess whether the model is reachable from ComfyUI.
- The `model` prop is a key that exists in `docs/src/snippets/diffusion/comfyui-support.jsx`.
A model-specific key is only correct when the plugin really treats it specially — an entry
in `executor_class_dict`
(`python/sglang/multimodal_gen/apps/ComfyUI_SGLDiffusion/core/generator.py`) or a dedicated
node. Otherwise the generic `image` / `video` key is the honest one; a model-specific key
without matching plugin support makes the page claim support that does not exist.
- Prose describing ComfyUI support inline instead of using the component is a finding: the
facts drift from the plugin.
### 6. Commands / port ### 6. Commands / port
- Launch uses `sglang serve` — flag any `python -m sglang.launch_server` / - Launch uses `sglang serve` — flag any `python -m sglang.launch_server` /
`python3 -m sglang.launch_server` (deprecated). The engine already emits `sglang serve`; `python3 -m sglang.launch_server` (deprecated). The engine already emits `sglang serve`;
+1 -1
View File
@@ -5,7 +5,7 @@ description: Guide for writing SGLang CI/UT tests. Covers CustomTestCase, CI reg
# Writing SGLang CI / UT Tests # Writing SGLang CI / UT Tests
This skill covers **how to write and register tests**. For CI pipeline internals (stage ordering, fast-fail, gating, partitioning, debugging CI failures), see the [CI workflow guide](../ci-workflow-guide/SKILL.md). Whether a case is worth adding at all is decided by [`unit-test-admission`](../../rules/unit-test-admission.md) — read it before writing the case, not after. This skill covers **how to write and register tests**. For CI pipeline internals (stage ordering, fail-fast, gating, partitioning, debugging CI failures), see the [CI workflow guide](../ci-workflow-guide/SKILL.md). Whether a case is worth adding at all is decided by [`unit-test-admission`](../../rules/unit-test-admission.md) — read it before writing the case, not after.
## Core Rules ## Core Rules
+24
View File
@@ -731,6 +731,12 @@
"cooldown_interval_minutes": 0, "cooldown_interval_minutes": 0,
"reason": "top contributor" "reason": "top contributor"
}, },
"iyastreb": {
"can_tag_run_ci_label": true,
"can_rerun_failed_ci": true,
"cooldown_interval_minutes": 0,
"reason": "custom override"
},
"jason-fxz": { "jason-fxz": {
"can_tag_run_ci_label": true, "can_tag_run_ci_label": true,
"can_rerun_failed_ci": true, "can_rerun_failed_ci": true,
@@ -900,6 +906,12 @@
"cooldown_interval_minutes": 60, "cooldown_interval_minutes": 60,
"reason": "custom override" "reason": "custom override"
}, },
"lluki": {
"can_tag_run_ci_label": true,
"can_rerun_failed_ci": true,
"cooldown_interval_minutes": 0,
"reason": "custom override"
},
"luccafong": { "luccafong": {
"can_tag_run_ci_label": true, "can_tag_run_ci_label": true,
"can_rerun_failed_ci": true, "can_rerun_failed_ci": true,
@@ -978,6 +990,12 @@
"cooldown_interval_minutes": 0, "cooldown_interval_minutes": 0,
"reason": "custom override" "reason": "custom override"
}, },
"niehen6174": {
"can_tag_run_ci_label": true,
"can_rerun_failed_ci": true,
"cooldown_interval_minutes": 60,
"reason": "custom override"
},
"nvcastet": { "nvcastet": {
"can_tag_run_ci_label": true, "can_tag_run_ci_label": true,
"can_rerun_failed_ci": true, "can_rerun_failed_ci": true,
@@ -1014,6 +1032,12 @@
"cooldown_interval_minutes": 0, "cooldown_interval_minutes": 0,
"reason": "custom override" "reason": "custom override"
}, },
"ovidiusm": {
"can_tag_run_ci_label": true,
"can_rerun_failed_ci": true,
"cooldown_interval_minutes": 0,
"reason": "custom override"
},
"pansicheng": { "pansicheng": {
"can_tag_run_ci_label": true, "can_tag_run_ci_label": true,
"can_rerun_failed_ci": true, "can_rerun_failed_ci": true,
+3
View File
@@ -18,6 +18,7 @@
/python/sglang/srt/disaggregation/ascend @ping1jing2 @iforgetmyname /python/sglang/srt/disaggregation/ascend @ping1jing2 @iforgetmyname
/python/sglang/srt/disaggregation/encoder @ShangmingCai @liusy58 @ZhengWG @gty111 /python/sglang/srt/disaggregation/encoder @ShangmingCai @liusy58 @ZhengWG @gty111
/python/sglang/srt/disaggregation/mori @Duyi-Wang @kkHuang-amd @HaiShaw @Lzy17 @billishyahao /python/sglang/srt/disaggregation/mori @Duyi-Wang @kkHuang-amd @HaiShaw @Lzy17 @billishyahao
/python/sglang/srt/disaggregation/nixl @iyastreb @ovidiusm @lluki
/python/sglang/srt/distributed @yizhang2077 @merrymercy @ch-wan /python/sglang/srt/distributed @yizhang2077 @merrymercy @ch-wan
/python/sglang/srt/distributed/device_communicators/mooncake_transfer_engine.py @ShangmingCai @stmatengss /python/sglang/srt/distributed/device_communicators/mooncake_transfer_engine.py @ShangmingCai @stmatengss
/python/sglang/srt/dllm @ClawSeven @btw616 @rwang5203 /python/sglang/srt/dllm @ClawSeven @btw616 @rwang5203
@@ -55,6 +56,7 @@
/python/sglang/srt/mem_cache/allocator @hnyls2002 @hzh0425 @xiezhq-hermann @ispobock @alphabetc1 @huangtingwei9988 /python/sglang/srt/mem_cache/allocator @hnyls2002 @hzh0425 @xiezhq-hermann @ispobock @alphabetc1 @huangtingwei9988
/python/sglang/srt/mem_cache/rust_tree_core @Jialin @hzh0425 @xiezhq-hermann @ispobock @alphabetc1 /python/sglang/srt/mem_cache/rust_tree_core @Jialin @hzh0425 @xiezhq-hermann @ispobock @alphabetc1
/python/sglang/srt/mem_cache/storage/mooncake_store @huangtingwei9988 @stmatengss /python/sglang/srt/mem_cache/storage/mooncake_store @huangtingwei9988 @stmatengss
/python/sglang/srt/mem_cache/storage/nixl @iyastreb @ovidiusm @lluki
/python/sglang/srt/mem_cache/embedding_*.py @liusy58 /python/sglang/srt/mem_cache/embedding_*.py @liusy58
/python/sglang/srt/mem_cache/storage/mooncake_store/mooncake_embedding_store.py @liusy58 /python/sglang/srt/mem_cache/storage/mooncake_store/mooncake_embedding_store.py @liusy58
/python/sglang/srt/model_executor @merrymercy @Ying1123 @hnyls2002 @Fridge003 @ispobock /python/sglang/srt/model_executor @merrymercy @Ying1123 @hnyls2002 @Fridge003 @ispobock
@@ -73,6 +75,7 @@
/python/sglang/kernels/aot @ispobock @BBuf @yizhang2077 @merrymercy @FlamingoPg @HaiShaw /python/sglang/kernels/aot @ispobock @BBuf @yizhang2077 @merrymercy @FlamingoPg @HaiShaw
/python/sglang/kernels/aot/csrc/musa @yeahdongcn /python/sglang/kernels/aot/csrc/musa @yeahdongcn
/rust/sglang-radix-tree @Jialin @hzh0425 @xiezhq-hermann @ispobock @alphabetc1 /rust/sglang-radix-tree @Jialin @hzh0425 @xiezhq-hermann @ispobock @alphabetc1
/rust/sglang-renderer @sagearc
/sgl-model-gateway @slin1237 @CatherineSue /sgl-model-gateway @slin1237 @CatherineSue
/sgl-model-gateway/benches @slin1237 /sgl-model-gateway/benches @slin1237
/sgl-model-gateway/bindings/python @CatherineSue @key4ng @slin1237 /sgl-model-gateway/bindings/python @CatherineSue @key4ng @slin1237
+11
View File
@@ -143,6 +143,17 @@ This section lists the oncalls for each hardware platform. The format is @github
This list is based on the current situation. If you or someone you know would like to donate machines for CI, they can serve as the CI oncalls for their machines. Please ping [Lianmin Zheng](https://github.com/merrymercy) and [Ying Sheng](https://github.com/Ying1123) in the Slack channel. They will start a nomination and internal review process. This list is based on the current situation. If you or someone you know would like to donate machines for CI, they can serve as the CI oncalls for their machines. Please ping [Lianmin Zheng](https://github.com/merrymercy) and [Ying Sheng](https://github.com/Ying1123) in the Slack channel. They will start a nomination and internal review process.
## CI Control Labels
`bypass-fail-fast`, `parallel-stages`, `max-concurrency` and `highest-priority`
each relax one of the limits that keep a single PR from monopolizing the
self-hosted GPU runners; `highest-priority` relaxes all of them at once. The
[contribution guide](https://docs.sglang.io/developer_guide/contribution_guide.html#ci-control-labels)
describes what each one does.
Applying one spends other PRs' runner capacity. `parallel-stages` is the
expensive one: a PR that cannot pass now runs its whole matrix.
## CI Maintenance Mode ## CI Maintenance Mode
When the CI is unhealthy (e.g., the scheduled pr-test on `main` is broken for consecutive runs), the project enters **CI Maintenance Mode** by opening [issue #21065](https://github.com/sgl-project/sglang/issues/21065). While active: When the CI is unhealthy (e.g., the scheduled pr-test on `main` is broken for consecutive runs), the project enters **CI Maintenance Mode** by opening [issue #21065](https://github.com/sgl-project/sglang/issues/21065). While active:
- All PR CI runs are paused. Resources are allocated to PRs that fix the CI. - All PR CI runs are paused. Resources are allocated to PRs that fix the CI.
@@ -0,0 +1,76 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { test } = require('node:test');
const yaml = fs.readFileSync(path.join(__dirname, 'action.yml'), 'utf8');
const script = yaml.split(' script: |\n')[1]
.split('\n').map(line => line.replace(/^ /, '')).join('\n');
// `require` and GITHUB_WORKSPACE mirror what actions/github-script injects.
const WORKSPACE = path.join(__dirname, '..', '..', '..');
const run = new (Object.getPrototypeOf(async function () {}).constructor)(
'github', 'context', 'core', 'process', 'require', script,
);
async function check({ snapshot = [], live = [], lint = 'success', event = 'pull_request' } = {}) {
const failures = [];
let labelReads = 0;
let jobReads = 0;
const labels = names => names.map(name => ({ name }));
const github = {
rest: {
checks: { listForRef: async () => ({ data: { check_runs: [
{ app: { slug: 'github-actions' }, status: 'completed', conclusion: lint },
] } }) },
pulls: { get: async args => {
assert.equal(args.pull_number, 42);
labelReads++;
return { data: { labels: labels(live) } };
} },
repos: { listPullRequestsAssociatedWithCommit: async () => {
labelReads++;
return { data: [{ labels: labels(live) }] };
} },
actions: { listJobsForWorkflowRun: () => {} },
},
paginate: async () => {
jobReads++;
return [{ name: 'model-test', status: 'completed', conclusion: 'failure', steps: [] }];
},
};
await run(github, {
eventName: event, repo: { owner: 'owner', repo: 'repo' }, sha: 'head', runId: 1,
payload: event === 'pull_request'
? { pull_request: { number: 42, head: { sha: 'head' }, labels: labels(snapshot) } }
: {},
}, { info: () => {}, setFailed: message => failures.push(message) },
{ env: { GITHUB_WORKSPACE: WORKSPACE } }, require);
return { failures, labelReads, jobReads };
}
test('a label added after the event bypasses sibling failures on rerun', async () => {
assert.deepEqual(await check({ live: ['bypass-fail-fast'] }),
{ failures: [], labelReads: 1, jobReads: 0 });
});
test('a removed label does not continue bypassing sibling failures', async () => {
const result = await check({ snapshot: ['bypass-fail-fast'] });
assert.equal(result.labelReads, 1);
assert.equal(result.jobReads, 1);
assert.match(result.failures[0], /root cause job\(s\): model-test/);
});
test('bypass never skips a failed lint check', async () => {
assert.deepEqual(await check({ live: ['bypass-fail-fast'], lint: 'failure' }),
{ failures: ['Fail-fast: lint check failed'], labelReads: 0, jobReads: 0 });
});
test('non-PR events retain associated-PR label lookup', async () => {
assert.deepEqual(await check({ event: 'workflow_dispatch', live: ['bypass-fail-fast'] }),
{ failures: [], labelReads: 1, jobReads: 0 });
});
test('scheduled runs remain exempt', async () => {
assert.deepEqual(await check({ event: 'schedule' }),
{ failures: [], labelReads: 0, jobReads: 0 });
});
+36 -26
View File
@@ -1,5 +1,5 @@
name: Check PR Test Health name: Check PR Test Health
description: Fail fast if any job in the current workflow run has already failed, or if the lint check (from lint.yml) has failed. Auto-skips for scheduled runs. The jobs-failed check (but not the lint check) is bypassed when the PR carries the `bypass-fastfail` label. description: Fail fast if any job in the current workflow run has already failed, or if the lint check (from lint.yml) has failed. Auto-skips for scheduled runs. The jobs-failed check (but not the lint check) is bypassed when the PR carries the `bypass-fail-fast` label (or `highest-priority`, which implies it).
inputs: inputs:
github-token: github-token:
@@ -17,15 +17,17 @@ runs:
with: with:
github-token: ${{ inputs.github-token }} github-token: ${{ inputs.github-token }}
script: | script: |
core.info(`[health-check] START -- event=${context.eventName}, runId=${context.runId}`);
// Skip when explicitly requested via env var (e.g. release branch cut) // Skip when explicitly requested via env var (e.g. release branch cut)
if (process.env.SKIP_PR_TEST_HEALTH_CHECK === 'true') { if (process.env.SKIP_PR_TEST_HEALTH_CHECK === 'true') {
core.info('Skipping health check (SKIP_PR_TEST_HEALTH_CHECK=true)'); core.info('[health-check] SKIP: SKIP_PR_TEST_HEALTH_CHECK=true');
return; return;
} }
// Skip for scheduled runs they should collect all failures, not fast-fail // Skip for scheduled runs -- they should collect all failures, not fail-fast
if (context.eventName === 'schedule') { if (context.eventName === 'schedule') {
core.info('Skipping health check for scheduled run'); core.info('[health-check] SKIP: scheduled run');
return; return;
} }
@@ -33,6 +35,7 @@ runs:
// listJobsForWorkflowRun only sees jobs within the SAME run, so we use // listJobsForWorkflowRun only sees jobs within the SAME run, so we use
// checks.listForRef which queries by commit SHA across ALL workflows. // checks.listForRef which queries by commit SHA across ALL workflows.
const ref = context.payload.pull_request?.head?.sha || context.sha; const ref = context.payload.pull_request?.head?.sha || context.sha;
core.info(`[health-check] Checking lint for ref=${ref}`);
const { data } = await github.rest.checks.listForRef({ const { data } = await github.rest.checks.listForRef({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
@@ -42,28 +45,20 @@ runs:
const lintRun = data.check_runs.find( const lintRun = data.check_runs.find(
cr => cr.app?.slug === 'github-actions' cr => cr.app?.slug === 'github-actions'
); );
core.info(`[health-check] Lint check: status=${lintRun?.status}, conclusion=${lintRun?.conclusion}`);
if (lintRun?.status === 'completed' && lintRun?.conclusion === 'failure') { if (lintRun?.status === 'completed' && lintRun?.conclusion === 'failure') {
core.setFailed('Fast-fail: lint check failed'); core.setFailed('Fail-fast: lint check failed');
return; return;
} }
// Skip the jobs-failed check when the PR carries the bypass-fastfail label. // The lint check above is never bypassed; only sibling failures are.
// Lint check above still runs. const { resolveCiLabels } = require(
let labels = []; `${process.env.GITHUB_WORKSPACE}/.github/scripts/ci-labels.cjs`
if (context.payload.pull_request?.labels) { );
labels = context.payload.pull_request.labels.map(l => l.name); const axes = await resolveCiLabels(github, context);
} else { core.info(`[health-check] PR labels: [${axes.labels.join(', ')}]`);
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ if (axes.bypassFailFast) {
owner: context.repo.owner, core.info('[health-check] SKIP jobs-failed check: bypass-fail-fast label present');
repo: context.repo.repo,
commit_sha: ref,
});
if (prs.length > 0) {
labels = prs[0].labels.map(l => l.name);
}
}
if (labels.includes('bypass-fastfail')) {
core.info('Skipping jobs-failed check (bypass-fastfail label present)');
return; return;
} }
@@ -73,26 +68,41 @@ runs:
run_id: context.runId, run_id: context.runId,
per_page: 100, per_page: 100,
}); });
// Find jobs that failed from a real error, not from fast-fail cascade core.info(`[health-check] Total jobs in run: ${jobs.length}`);
const failedJobs = jobs.filter(j => j.status === 'completed' && j.conclusion === 'failure');
core.info(`[health-check] Failed jobs (before filtering): ${failedJobs.map(j => `${j.name}(${j.conclusion})`).join(', ') || 'none'}`);
// Find jobs that failed from a real error, not from fail-fast cascade
const rootCauseFailures = jobs.filter(j => { const rootCauseFailures = jobs.filter(j => {
if (j.status !== 'completed' || j.conclusion !== 'failure') return false; if (j.status !== 'completed' || j.conclusion !== 'failure') return false;
// h20 runners are flaky (dirty GPU state from prior runs); their failures // h20 runners are flaky (dirty GPU state from prior runs); their failures
// should not cascade fast-fail to other stages. j.name shape from // should not cascade fail-fast to other stages. j.name shape from
// listJobsForWorkflowRun: "<job-key>" + optional " / <reusable-job>" // listJobsForWorkflowRun: "<job-key>" + optional " / <reusable-job>"
// + optional " (<matrix>)". Split off the base job key before exact // + optional " (<matrix>)". Split off the base job key before exact
// match so we cover both inline + reusable forms without confusing // match so we cover both inline + reusable forms without confusing
// 'h20' with the 'h200' prefix. // 'h20' with the 'h200' prefix.
const baseName = j.name.split(/[ /]/)[0]; const baseName = j.name.split(/[ /]/)[0];
if (baseName === 'base-c-test-8-gpu-h20') { if (baseName === 'base-c-test-8-gpu-h20') {
core.info(`[health-check] Filtered out h20 job: ${j.name}`);
return false; return false;
} }
// If the failing step is the health check, it's a cascade — skip it // multimodal-gen NPU tests should not cascade fail-fast to perf/accuracy stages.
if (baseName === 'multimodal-gen-test-1-npu-a3' || baseName === 'multimodal-gen-test-2-npu-a3') {
core.info(`[health-check] Filtered out multimodal-gen NPU job: ${j.name}`);
return false;
}
// If the failing step is the health check, it's a cascade -- skip it
const failedStep = (j.steps || []).find(s => s.conclusion === 'failure'); const failedStep = (j.steps || []).find(s => s.conclusion === 'failure');
if (failedStep && (failedStep.name.includes('check-pr-test-health') || failedStep.name.includes('Check PR test health'))) { if (failedStep && (failedStep.name.includes('check-pr-test-health') || failedStep.name.includes('Check PR test health'))) {
core.info(`[health-check] Filtered out cascade failure: ${j.name} (failed step: ${failedStep.name})`);
return false; return false;
} }
return true; return true;
}); });
core.info(`[health-check] Root cause failures (after filtering): ${rootCauseFailures.map(j => j.name).join(', ') || 'none'}`);
if (rootCauseFailures.length > 0) { if (rootCauseFailures.length > 0) {
core.setFailed(`Fast-fail: skipping — root cause job(s): ${rootCauseFailures.map(j => j.name).join(', ')}`); core.setFailed(`Fail-fast: skipping — root cause job(s): ${rootCauseFailures.map(j => j.name).join(', ')}`);
} else {
core.info('[health-check] PASS: no root cause failures detected');
} }
+7 -23
View File
@@ -1,5 +1,5 @@
name: Wait for Jobs name: Wait for Jobs
description: Poll and wait for specified jobs in the current workflow run to complete. Returns success immediately when the PR carries the `bypass-fastfail` label, letting downstream stages dispatch in parallel (same effect as scheduled runs). description: Poll and wait for specified jobs in the current workflow run to complete. Returns success immediately when the PR carries the `parallel-stages` label (or `highest-priority`, which implies it), letting downstream stages dispatch in parallel (same effect as scheduled runs).
inputs: inputs:
stage-name: stage-name:
@@ -49,28 +49,12 @@ runs:
const pollIntervalSeconds = parseInt(process.env.INPUT_POLL_INTERVAL_SECONDS); const pollIntervalSeconds = parseInt(process.env.INPUT_POLL_INTERVAL_SECONDS);
const maxAttempts = (maxWaitMinutes * 60) / pollIntervalSeconds; const maxAttempts = (maxWaitMinutes * 60) / pollIntervalSeconds;
// bypass-fastfail label opts the PR out of stage-to-stage waiting, const { resolveCiLabels } = require(
// letting all stages dispatch in parallel like scheduled runs do. `${process.env.GITHUB_WORKSPACE}/.github/scripts/ci-labels.cjs`
let labels = []; );
if (context.payload.pull_request?.labels) { const { parallelStages } = await resolveCiLabels(github, context);
labels = context.payload.pull_request.labels.map(l => l.name); if (parallelStages) {
} else { console.log(`Skipping ${stageName} wait (parallel-stages label present)`);
const ref = context.payload.pull_request?.head?.sha || context.sha;
try {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: ref,
});
if (prs.length > 0) {
labels = prs[0].labels.map(l => l.name);
}
} catch (e) {
console.log(`Could not fetch PR labels for ${ref}: ${e.message}`);
}
}
if (labels.includes('bypass-fastfail')) {
console.log(`Skipping ${stageName} wait (bypass-fastfail label present)`);
core.setOutput('result', 'success'); core.setOutput('result', 'success');
return; return;
} }
+62
View File
@@ -0,0 +1,62 @@
"use strict";
/**
* Labels come from the API, not `context.payload`: a rerun replays the original
* event, so the payload carries the label set from when the run was created.
*/
const BYPASS_FAIL_FAST = "bypass-fail-fast";
const PARALLEL_STAGES = "parallel-stages";
const MAX_CONCURRENCY = "max-concurrency";
const HIGHEST_PRIORITY = "highest-priority";
// Callers gate the whole run on this, so a transient API error must not fail them.
async function readLabels(github, context) {
try {
return await fetchLabels(github, context);
} catch (e) {
console.warn(`Could not read PR labels: ${e.message}`);
return [];
}
}
async function fetchLabels(github, context) {
const prNumber = context.payload.pull_request?.number;
if (prNumber) {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
return pr.labels.map((l) => l.name);
}
const sha = context.payload.pull_request?.head?.sha || context.sha;
const { data: prs } =
await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: sha,
});
return prs.length > 0 ? prs[0].labels.map((l) => l.name) : [];
}
async function resolveCiLabels(github, context) {
const labels = await readLabels(github, context);
const has = (name) => labels.includes(name);
const highestPriority = has(HIGHEST_PRIORITY);
return {
labels,
bypassFailFast: highestPriority || has(BYPASS_FAIL_FAST),
parallelStages: highestPriority || has(PARALLEL_STAGES),
maxConcurrency: highestPriority || has(MAX_CONCURRENCY),
highestPriority,
};
}
module.exports = {
resolveCiLabels,
BYPASS_FAIL_FAST,
PARALLEL_STAGES,
MAX_CONCURRENCY,
HIGHEST_PRIORITY,
};
@@ -0,0 +1,96 @@
# Reusable workflow: cross-reference CI test failures with coverage-based
# test recommendations. Called by pr-test-npu.yml's analyze-failure-report job.
name: Analyze Failure Report
on:
workflow_call:
inputs:
log_artifact_pattern:
type: string
required: true
description: 'Glob pattern for test log artifacts (e.g. selected-test-logs-*)'
recommendations_content:
type: string
required: false
default: ''
description: 'Content of recommendations from upstream job output'
defaults:
run:
shell: bash -el {0}
jobs:
analyze:
name: cross-reference failures with recommendations
continue-on-error: true # Non-blocking on parse/read failure.
runs-on: ubuntu-latest
steps:
- name: Checkout sglang repo
uses: actions/checkout@v4
# ----- Step 1: Locate recommendations -----
- name: Locate recommendations file
id: locate-recs
run: |
mkdir -p ./test-logs
if [ -n "${{ inputs.recommendations_content }}" ]; then
echo "has_recommendations=true" >> $GITHUB_OUTPUT
echo "Using workflow output recommendations"
echo "${{ inputs.recommendations_content }}" > ./test-logs/_recommended.txt
echo "RECS_FILE=./test-logs/_recommended.txt" >> $GITHUB_ENV
echo "RECS_SOURCE=output" >> $GITHUB_ENV
COUNT=$(wc -l < ./test-logs/_recommended.txt)
echo "Recommendations count: ${COUNT}"
else
echo "has_recommendations=false" >> $GITHUB_OUTPUT
echo "::notice::No recommended test cases detected, skipping subsequent analysis tasks."
fi
# ----- Step 2: Download test logs -----
- name: Download test logs
uses: actions/download-artifact@v7
with:
pattern: ${{ inputs.log_artifact_pattern }}
path: ./test-logs
merge-multiple: true
continue-on-error: true
- name: Show downloaded logs structure
run: |
echo "Downloaded log files:"
find ./test-logs -type f | head -50 || echo " (no logs found)"
# ----- Step 3: Run analysis -----
- name: Ensure regex is available
if: steps.locate-recs.outputs.has_recommendations != 'false'
run: |
set -euo pipefail
if ! python3 -c "import regex" >/dev/null 2>&1; then
echo "regex not found for $(python3 -V); bootstrapping pip and installing regex"
curl -fsSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py --break-system-packages
python3 -m pip install --break-system-packages regex
fi
python3 -c "import regex; print('regex ok:', regex.__file__)"
- name: Run failure analysis
if: steps.locate-recs.outputs.has_recommendations != 'false'
id: analysis
continue-on-error: true
run: |
python3 scripts/ci/npu/precise-test/analyze_failure_report.py \
--log-dir ./test-logs \
--recommendations-file "${RECS_FILE}" \
--recommendations-source "${RECS_SOURCE:-none}" \
--output ./test-logs/failure_report.md
# ----- Step 4: Upload report -----
- name: Upload failure analysis report
if: always()
uses: actions/upload-artifact@v7
with:
name: failure-analysis-report
path: ./test-logs/failure_report.md
if-no-files-found: ignore
retention-days: 14
+55 -113
View File
@@ -1,5 +1,7 @@
name: PR Test Stage for NPU name: PR Test Stage for NPU
# Reusable workflow for one CUDA test stage. Caller pr-test-npu.yml forwards # Reusable workflow for one NPU test stage. Caller pr-test-npu.yml forwards
# job parameters (runner, image, partitions) and optionally delegates test
# execution to the coverage runner (use_coverage_runner=true).
on: on:
workflow_call: workflow_call:
@@ -33,17 +35,25 @@ on:
type: string type: string
default: '{"size":1,"arr":[0]}' default: '{"size":1,"arr":[0]}'
ref: ref:
description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the default branch.' description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the event commit SHA.'
type: string type: string
default: '' default: ''
skip_pr_test_health_check: skip_pr_test_health_check:
description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the default branch.' description: 'Set to true to skip the PR test health check (fail-fast gate).'
type: string type: string
default: 'false' default: 'false'
is_nightly_pipeline_job: is_nightly_pipeline_job:
description: 'Run the test suite with --nightly (collects nightly-registered tests) and --continue-on-error' description: 'Run the test suite with --nightly (collects nightly-registered tests) and --continue-on-error'
type: boolean type: boolean
default: false default: false
use_coverage_runner:
description: 'If true, delegate test execution to scripts/ci/npu/precise-test/run_tests_with_coverage.sh instead of running via run_suite.py directly'
type: boolean
default: false
upload_test_logs:
description: 'If true, upload the test logs as a GitHub Actions artifact (only enabled by pr-test-npu)'
type: boolean
default: false
github-token: github-token:
description: 'GitHub token for API calls' description: 'GitHub token for API calls'
type: string type: string
@@ -72,118 +82,15 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ inputs.ref || github.ref }} # Pin to the event SHA (not the branch name) so every job in this run
# checks out the same commit even if the branch advances mid-run.
ref: ${{ inputs.ref || github.sha }}
- name: Mark repository safe - name: Mark repository safe
run: | run: |
git config --system --add safe.directory ${GITHUB_WORKSPACE} git config --system --add safe.directory ${GITHUB_WORKSPACE}
- name: Check PR test health - uses: ./.github/actions/check-pr-test-health
uses: actions/github-script@v8
env:
SKIP_PR_TEST_HEALTH_CHECK: ${{ env.SKIP_PR_TEST_HEALTH_CHECK }}
with:
github-token: ${{ inputs.github-token || github.token }}
script: |
core.notice(`[health-check] START — event=${context.eventName}, runId=${context.runId}`);
// Skip when explicitly requested via env var (e.g. release branch cut)
if (process.env.SKIP_PR_TEST_HEALTH_CHECK === 'true') {
core.notice('[health-check] SKIP: SKIP_PR_TEST_HEALTH_CHECK=true');
return;
}
// Skip for scheduled runs — they should collect all failures, not fast-fail
if (context.eventName === 'schedule') {
core.notice('[health-check] SKIP: scheduled run');
return;
}
// Check lint status from the separate Lint workflow (lint.yml).
// listJobsForWorkflowRun only sees jobs within the SAME run, so we use
// checks.listForRef which queries by commit SHA across ALL workflows.
const ref = context.payload.pull_request?.head?.sha || context.sha;
core.info(`[health-check] Checking lint for ref=${ref}`);
const { data } = await github.rest.checks.listForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: ref,
check_name: 'lint',
});
const lintRun = data.check_runs.find(
cr => cr.app?.slug === 'github-actions'
);
core.info(`[health-check] Lint check: status=${lintRun?.status}, conclusion=${lintRun?.conclusion}`);
if (lintRun?.status === 'completed' && lintRun?.conclusion === 'failure') {
core.setFailed('Fast-fail: lint check failed');
return;
}
// Skip the jobs-failed check when the PR carries the bypass-fastfail label.
// Lint check above still runs.
let labels = [];
if (context.payload.pull_request?.labels) {
labels = context.payload.pull_request.labels.map(l => l.name);
} else {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: ref,
});
if (prs.length > 0) {
labels = prs[0].labels.map(l => l.name);
}
}
core.info(`[health-check] PR labels: [${labels.join(', ')}]`);
if (labels.includes('bypass-fastfail')) {
core.notice('[health-check] SKIP jobs-failed check: bypass-fastfail label present');
return;
}
const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
per_page: 100,
});
core.info(`[health-check] Total jobs in run: ${jobs.length}`);
const failedJobs = jobs.filter(j => j.status === 'completed' && j.conclusion === 'failure');
core.info(`[health-check] Failed jobs (before filtering): ${failedJobs.map(j => `${j.name}(${j.conclusion})`).join(', ') || 'none'}`);
// Find jobs that failed from a real error, not from fast-fail cascade
const rootCauseFailures = jobs.filter(j => {
if (j.status !== 'completed' || j.conclusion !== 'failure') return false;
// h20 runners are flaky (dirty GPU state from prior runs); their failures
// should not cascade fast-fail to other stages. j.name shape from
// listJobsForWorkflowRun: "<job-key>" + optional " / <reusable-job>"
// + optional " (<matrix>)". Split off the base job key before exact
// match so we cover both inline + reusable forms without confusing
// 'h20' with the 'h200' prefix.
const baseName = j.name.split(/[ /]/)[0];
if (baseName === 'base-c-test-8-gpu-h20') {
core.info(`[health-check] Filtered out h20 job: ${j.name}`);
return false;
}
// multimodal-gen NPU tests should not cascade fast-fail to perf/accuracy stages.
if (baseName === 'multimodal-gen-test-1-npu-a3' || baseName === 'multimodal-gen-test-2-npu-a3') {
core.info(`[health-check] Filtered out multimodal-gen NPU job: ${j.name}`);
return false;
}
// If the failing step is the health check, it's a cascade — skip it
const failedStep = (j.steps || []).find(s => s.conclusion === 'failure');
if (failedStep && (failedStep.name.includes('check-pr-test-health') || failedStep.name.includes('Check PR test health'))) {
core.info(`[health-check] Filtered out cascade failure: ${j.name} (failed step: ${failedStep.name})`);
return false;
}
return true;
});
core.info(`[health-check] Root cause failures (after filtering): ${rootCauseFailures.map(j => j.name).join(', ') || 'none'}`);
if (rootCauseFailures.length > 0) {
core.setFailed(`Fast-fail: skipping — root cause job(s): ${rootCauseFailures.map(j => j.name).join(', ')}`);
} else {
core.notice('[health-check] PASS: no root cause failures detected');
}
- name: Install dependencies - name: Install dependencies
env: env:
@@ -228,10 +135,12 @@ jobs:
timeout-minutes: ${{ fromJson(inputs.run_timeout_minutes) }} timeout-minutes: ${{ fromJson(inputs.run_timeout_minutes) }}
env: env:
CONTINUE_ON_ERROR_FLAG: ${{ inputs.continue_on_error == 'true' && '--continue-on-error' || '' }} CONTINUE_ON_ERROR_FLAG: ${{ inputs.continue_on_error == 'true' && '--continue-on-error' || '' }}
TEST_LOG_DIR: /tmp/test-logs
shell: bash shell: bash
run: | run: |
# Fail fast on any command error, undefined variable, or pipe failure. # Fail fast on any command error, undefined variable, or pipe failure.
set -euo pipefail set -euo pipefail
mkdir -p "${TEST_LOG_DIR}"
# Install missing python deps (skip if already importable). # Install missing python deps (skip if already importable).
PYTHON_FOR_SGLANG="python" PYTHON_FOR_SGLANG="python"
@@ -255,8 +164,11 @@ jobs:
install_pkg "${pkg}" install_pkg "${pkg}"
done done
# Install sgl-eval (CLI used by run_eval.py's `sgl-eval run ...`) if missing. # Fallback for images that predate the pin in pyproject_npu.toml. Checking
command -v sgl-eval >/dev/null 2>&1 || install_pkg "sgl-eval==0.1.0" # the version, not the command, so a stale wheel is replaced rather than kept.
SGL_EVAL_VERSION=0.1.2
${PYTHON_FOR_SGLANG} -c "import sgl_eval, sys; sys.exit(sgl_eval.__version__ != '${SGL_EVAL_VERSION}')" 2>/dev/null \
|| install_pkg "sgl-eval==${SGL_EVAL_VERSION}"
cd test cd test
# Append `--nightly` and `--continue-on-error` when the job belongs to the nightly pipeline. # Append `--nightly` and `--continue-on-error` when the job belongs to the nightly pipeline.
@@ -266,8 +178,38 @@ jobs:
if [ "${{ inputs.is_nightly_pipeline_job }}" = "true" ]; then if [ "${{ inputs.is_nightly_pipeline_job }}" = "true" ]; then
NIGHTLY_FLAG="--nightly --continue-on-error" NIGHTLY_FLAG="--nightly --continue-on-error"
fi fi
# Optional: delegate execution to scripts/ci/npu/precise-test/run_tests_with_coverage.sh when the
# caller sets use_coverage_runner=true. list_tests.py (same directory) enumerates the
# selected test files; the original run_suite.py execution below is preserved
# unchanged for the default (false) path.
LOG_FILE="${TEST_LOG_DIR}/${{ inputs.self_name }}-part${{ matrix.partition }}.log"
if [[ "${{ inputs.use_coverage_runner }}" == "true" ]]; then
pip install coverage==7.8.*
python3 ../scripts/ci/npu/precise-test/list_tests.py --hw npu --suite ${{ inputs.self_name }} \
--auto-partition-id ${{ matrix.partition }} \
--auto-partition-size ${{ fromJson(inputs.partitions).size }} \
-o /tmp/selected_tests.txt
echo "Selected test files:"
cat /tmp/selected_tests.txt
if [ -s /tmp/selected_tests.txt ]; then
bash ../scripts/ci/npu/precise-test/run_tests_with_coverage.sh $(cat /tmp/selected_tests.txt)
fi
exit 0
fi
python3 run_suite.py --hw npu --suite ${{ inputs.self_name }} \ python3 run_suite.py --hw npu --suite ${{ inputs.self_name }} \
--auto-partition-id ${{ matrix.partition }} \ --auto-partition-id ${{ matrix.partition }} \
--auto-partition-size ${{ fromJson(inputs.partitions).size }} \ --auto-partition-size ${{ fromJson(inputs.partitions).size }} \
${{ inputs.timeout_per_file && format('--timeout-per-file {0}', inputs.timeout_per_file) || '' }} \ ${{ inputs.timeout_per_file && format('--timeout-per-file {0}', inputs.timeout_per_file) || '' }} \
$NIGHTLY_FLAG $CONTINUE_ON_ERROR_FLAG $NIGHTLY_FLAG $CONTINUE_ON_ERROR_FLAG 2>&1 | tee "${LOG_FILE}"
- name: Upload test logs
if: always() && inputs.use_coverage_runner != true && inputs.upload_test_logs == true
uses: actions/upload-artifact@v7
with:
name: selected-test-logs-${{ inputs.self_name }}-part${{ matrix.partition }}
path: /tmp/test-logs/
if-no-files-found: ignore
retention-days: 14
+48 -110
View File
@@ -43,7 +43,7 @@ on:
default: '{}' default: '{}'
description: 'JSON run metadata {branch_label, workflow_name, create_time}, recorded once at workflow start' description: 'JSON run metadata {branch_label, workflow_name, create_time}, recorded once at workflow start'
skip_pr_test_health_check: skip_pr_test_health_check:
description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the default branch.' description: 'Set to true to skip the PR test health check (fail-fast gate).'
type: string type: string
default: 'false' default: 'false'
is_nightly_pipeline_job: is_nightly_pipeline_job:
@@ -54,6 +54,10 @@ on:
description: 'Partition config to parallelize a suite across jobs, e.g. {"size":3,"arr":[0,1,2]}. Each element of arr is a partition id; the template expands one matrix job per element. size is passed to run_suite.py --auto-partition-size.' description: 'Partition config to parallelize a suite across jobs, e.g. {"size":3,"arr":[0,1,2]}. Each element of arr is a partition id; the template expands one matrix job per element. size is passed to run_suite.py --auto-partition-size.'
type: string type: string
default: '{"size":1,"arr":[0]}' default: '{"size":1,"arr":[0]}'
ref:
description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the event SHA.'
type: string
default: ''
github-token: github-token:
description: 'GitHub token for API calls' description: 'GitHub token for API calls'
type: string type: string
@@ -63,6 +67,14 @@ on:
type: string type: string
default: '300' default: '300'
description: 'timeout-minutes for the Run test step' description: 'timeout-minutes for the Run test step'
use_coverage_runner:
description: 'If true, delegate test execution to scripts/ci/npu/precise-test/run_tests_with_coverage.sh instead of running via run_suite.py directly'
type: boolean
default: false
upload_test_logs:
description: 'If true, upload the test logs as a GitHub Actions artifact (only enabled by pr-test-npu)'
type: boolean
default: false
env: env:
SKIP_PR_TEST_HEALTH_CHECK: ${{ inputs.skip_pr_test_health_check }} SKIP_PR_TEST_HEALTH_CHECK: ${{ inputs.skip_pr_test_health_check }}
@@ -84,113 +96,12 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Check PR test health
uses: actions/github-script@v8
env:
SKIP_PR_TEST_HEALTH_CHECK: ${{ env.SKIP_PR_TEST_HEALTH_CHECK }}
with: with:
github-token: ${{ inputs.github-token || github.token }} # Pin to the event SHA (not the branch name) so every job in this run
script: | # checks out the same commit even if the branch advances mid-run.
core.notice(`[health-check] START — event=${context.eventName}, runId=${context.runId}`); ref: ${{ inputs.ref || github.sha }}
// Skip when explicitly requested via env var (e.g. release branch cut) - uses: ./.github/actions/check-pr-test-health
if (process.env.SKIP_PR_TEST_HEALTH_CHECK === 'true') {
core.notice('[health-check] SKIP: SKIP_PR_TEST_HEALTH_CHECK=true');
return;
}
// Skip for scheduled runs — they should collect all failures, not fast-fail
if (context.eventName === 'schedule') {
core.notice('[health-check] SKIP: scheduled run');
return;
}
// Check lint status from the separate Lint workflow (lint.yml).
// listJobsForWorkflowRun only sees jobs within the SAME run, so we use
// checks.listForRef which queries by commit SHA across ALL workflows.
const ref = context.payload.pull_request?.head?.sha || context.sha;
core.info(`[health-check] Checking lint for ref=${ref}`);
const { data } = await github.rest.checks.listForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: ref,
check_name: 'lint',
});
const lintRun = data.check_runs.find(
cr => cr.app?.slug === 'github-actions'
);
core.info(`[health-check] Lint check: status=${lintRun?.status}, conclusion=${lintRun?.conclusion}`);
if (lintRun?.status === 'completed' && lintRun?.conclusion === 'failure') {
core.setFailed('Fast-fail: lint check failed');
return;
}
// Skip the jobs-failed check when the PR carries the bypass-fastfail label.
// Lint check above still runs.
let labels = [];
if (context.payload.pull_request?.labels) {
labels = context.payload.pull_request.labels.map(l => l.name);
} else {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: ref,
});
if (prs.length > 0) {
labels = prs[0].labels.map(l => l.name);
}
}
core.info(`[health-check] PR labels: [${labels.join(', ')}]`);
if (labels.includes('bypass-fastfail')) {
core.notice('[health-check] SKIP jobs-failed check: bypass-fastfail label present');
return;
}
const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
per_page: 100,
});
core.info(`[health-check] Total jobs in run: ${jobs.length}`);
const failedJobs = jobs.filter(j => j.status === 'completed' && j.conclusion === 'failure');
core.info(`[health-check] Failed jobs (before filtering): ${failedJobs.map(j => `${j.name}(${j.conclusion})`).join(', ') || 'none'}`);
// Find jobs that failed from a real error, not from fast-fail cascade
const rootCauseFailures = jobs.filter(j => {
if (j.status !== 'completed' || j.conclusion !== 'failure') return false;
// h20 runners are flaky (dirty GPU state from prior runs); their failures
// should not cascade fast-fail to other stages. j.name shape from
// listJobsForWorkflowRun: "<job-key>" + optional " / <reusable-job>"
// + optional " (<matrix>)". Split off the base job key before exact
// match so we cover both inline + reusable forms without confusing
// 'h20' with the 'h200' prefix.
const baseName = j.name.split(/[ /]/)[0];
if (baseName === 'base-c-test-8-gpu-h20') {
core.info(`[health-check] Filtered out h20 job: ${j.name}`);
return false;
}
// multimodal-gen NPU tests should not cascade fast-fail to perf/accuracy stages.
if (baseName === 'multimodal-gen-test-1-npu-a3' || baseName === 'multimodal-gen-test-2-npu-a3') {
core.info(`[health-check] Filtered out multimodal-gen NPU job: ${j.name}`);
return false;
}
// If the failing step is the health check, it's a cascade — skip it
const failedStep = (j.steps || []).find(s => s.conclusion === 'failure');
if (failedStep && (failedStep.name.includes('check-pr-test-health') || failedStep.name.includes('Check PR test health'))) {
core.info(`[health-check] Filtered out cascade failure: ${j.name} (failed step: ${failedStep.name})`);
return false;
}
return true;
});
core.info(`[health-check] Root cause failures (after filtering): ${rootCauseFailures.map(j => j.name).join(', ') || 'none'}`);
if (rootCauseFailures.length > 0) {
core.setFailed(`Fast-fail: skipping — root cause job(s): ${rootCauseFailures.map(j => j.name).join(', ')}`);
} else {
core.notice('[health-check] PASS: no root cause failures detected');
}
- name: Check npu info - name: Check npu info
run: | run: |
@@ -227,6 +138,9 @@ jobs:
# Fail fast on any command error, undefined variable, or pipe failure. # Fail fast on any command error, undefined variable, or pipe failure.
set -euo pipefail set -euo pipefail
TEST_LOG_DIR="/tmp/test-logs"
mkdir -p "${TEST_LOG_DIR}"
sglang_source_path=$(pwd) sglang_source_path=$(pwd)
echo "Source code path: ${sglang_source_path}" echo "Source code path: ${sglang_source_path}"
ln -sf ${sglang_source_path} /root/sglang ln -sf ${sglang_source_path} /root/sglang
@@ -390,14 +304,29 @@ jobs:
fi fi
# Nightly additionally persists the full suite log to ${log_path}/${tc_name}.log # Nightly additionally persists the full suite log to ${log_path}/${tc_name}.log
# under the structured path; PR runs only tee to /tmp/test_output.log. # under the structured path; PR runs tee to ${TEST_LOG_DIR}/${tc_name}.log.
# Use an array so the extra target is omitted when empty: passing an empty # Use an array so the extra target is omitted when empty: passing an empty
# string arg makes GNU tee fail (exit code 1) and flips the pipeline exit code. # string arg makes GNU tee fail (exit code 1) and flips the pipeline exit code.
LOG_TEE_TARGETS=("/tmp/test_output.log") LOG_TEE_TARGETS=("${TEST_LOG_DIR}/${tc_name}.log")
if [ "${{ inputs.is_nightly_pipeline_job }}" = "true" ]; then if [ "${{ inputs.is_nightly_pipeline_job }}" = "true" ]; then
LOG_TEE_TARGETS+=("${log_path}/${tc_name}.log") LOG_TEE_TARGETS+=("${log_path}/${tc_name}.log")
fi fi
# Optional: delegate execution to scripts/ci/npu/precise-test/run_tests_with_coverage.sh when the
# caller sets use_coverage_runner=true. list_tests.py (same directory) enumerates the
# selected test files; the original run_suite.py execution below is preserved
# unchanged for the default (false) path.
if [[ "${{ inputs.use_coverage_runner }}" == "true" ]]; then
pip install coverage==7.8.*
${PYTHON_FOR_SGLANG} -u ../scripts/ci/npu/precise-test/list_tests.py --hw npu --suite ${test_suite} ${PARTITION_ARGS} -o /tmp/selected_tests.txt
echo "Selected test files:"
cat /tmp/selected_tests.txt
if [ -s /tmp/selected_tests.txt ]; then
bash ../scripts/ci/npu/precise-test/run_tests_with_coverage.sh $(cat /tmp/selected_tests.txt)
fi
exit 0
fi
# Run NPU test suite with run_suite.py. # Run NPU test suite with run_suite.py.
# Capture mode (--enable-retry --max-attempts 1) keeps test subprocesses off the # Capture mode (--enable-retry --max-attempts 1) keeps test subprocesses off the
# tee pipe, so leftover server processes cannot block the pipeline on exit. # tee pipe, so leftover server processes cannot block the pipeline on exit.
@@ -418,11 +347,11 @@ jobs:
echo "## ${tc_name} ${status_icon} ${test_status}" >> $GITHUB_STEP_SUMMARY echo "## ${tc_name} ${status_icon} ${test_status}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ inputs.is_nightly_pipeline_job }}" != "true" ]; then if [ "${{ inputs.is_nightly_pipeline_job }}" != "true" ]; then
metric_count=$(grep -c '\[METRIC\]' /tmp/test_output.log 2>/dev/null || echo 0) metric_count=$(grep -c '\[METRIC\]' "${TEST_LOG_DIR}/${tc_name}.log" 2>/dev/null || echo 0)
if [ "${metric_count}" -gt 0 ]; then if [ "${metric_count}" -gt 0 ]; then
echo "| Metric | Value | Pass |" >> $GITHUB_STEP_SUMMARY echo "| Metric | Value | Pass |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|------|" >> $GITHUB_STEP_SUMMARY echo "|--------|-------|------|" >> $GITHUB_STEP_SUMMARY
grep '\[METRIC\]' /tmp/test_output.log | while IFS= read -r line; do grep '\[METRIC\]' "${TEST_LOG_DIR}/${tc_name}.log" | while IFS= read -r line; do
metric_name=$(echo "$line" | sed -E 's/.*\[METRIC\] ([^=]+)=.*/\1/') metric_name=$(echo "$line" | sed -E 's/.*\[METRIC\] ([^=]+)=.*/\1/')
metric_value=$(echo "$line" | sed -E 's/.*\[METRIC\] [^=]+=([^ ]+).*/\1/') metric_value=$(echo "$line" | sed -E 's/.*\[METRIC\] [^=]+=([^ ]+).*/\1/')
echo "| ${metric_name} | ${metric_value} | ${status_icon} |" >> $GITHUB_STEP_SUMMARY echo "| ${metric_name} | ${metric_value} | ${status_icon} |" >> $GITHUB_STEP_SUMMARY
@@ -433,3 +362,12 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
fi fi
exit ${test_exit_code} exit ${test_exit_code}
- name: Upload test logs
if: always() && inputs.use_coverage_runner != true && inputs.upload_test_logs == true
uses: actions/upload-artifact@v7
with:
name: selected-test-logs-${{ inputs.test_suite }}-part${{ matrix.partition }}
path: /tmp/test-logs/
if-no-files-found: ignore
retention-days: 14
+17 -11
View File
@@ -136,15 +136,24 @@ jobs:
- "test/registered/rust/**" - "test/registered/rust/**"
- ".github/workflows/_pr-test-*.yml" - ".github/workflows/_pr-test-*.yml"
- name: Resolve CI labels
id: ci-labels
if: github.event_name == 'pull_request'
uses: actions/github-script@v8
with:
script: |
const { resolveCiLabels } = require(
`${process.env.GITHUB_WORKSPACE}/.github/scripts/ci-labels.cjs`
);
const axes = await resolveCiLabels(github, context);
core.info(`CI labels: [${axes.labels.join(', ')}]`);
core.setOutput('max_concurrency', String(axes.maxConcurrency));
- name: Determine full-parallel mode - name: Determine full-parallel mode
id: parallel-mode id: parallel-mode
run: | run: |
# `full=true` lifts the matrix-fanout throttle so each suite's # `full=true` lifts the matrix-fanout throttle so each suite's
# max_parallel = size. Conditions: # max_parallel = size.
# 1. Scheduled cron run.
# 2. run_all_tests run (manual full dispatch / release) -- a full
# run should mirror the cron's parallelism, not just its test set.
# 3. pull_request event with the `high priority` label.
FULL=false FULL=false
if [[ "${{ github.event_name }}" == "schedule" ]]; then if [[ "${{ github.event_name }}" == "schedule" ]]; then
FULL=true FULL=true
@@ -152,9 +161,9 @@ jobs:
elif [[ "${{ inputs.run_all_tests }}" == "true" ]]; then elif [[ "${{ inputs.run_all_tests }}" == "true" ]]; then
FULL=true FULL=true
echo "run_all_tests -> full parallelism" echo "run_all_tests -> full parallelism"
elif [[ "${{ github.event_name }}" == "pull_request" && "${{ contains(github.event.pull_request.labels.*.name, 'high priority') }}" == "true" ]]; then elif [[ "${{ steps.ci-labels.outputs.max_concurrency }}" == "true" ]]; then
FULL=true FULL=true
echo "high priority PR -> full parallelism" echo "max-concurrency PR -> full parallelism"
fi fi
echo "full=$FULL" >> "$GITHUB_OUTPUT" echo "full=$FULL" >> "$GITHUB_OUTPUT"
@@ -189,10 +198,7 @@ jobs:
id: partitions id: partitions
run: | run: |
# Emit a single JSON output `partitions` keyed by suite name with # Emit a single JSON output `partitions` keyed by suite name with
# {size, arr, max_parallel} fields per suite. Replaces the prior # {size, arr, max_parallel} fields per suite.
# full/low max-parallel presets; `--full-parallel` keeps the
# `high priority` PR / scheduled cron escape hatch.
# See scripts/ci/utils/compute_partitions.py.
python3 scripts/ci/utils/compute_partitions.py \ python3 scripts/ci/utils/compute_partitions.py \
--full-parallel ${{ steps.parallel-mode.outputs.full }} \ --full-parallel ${{ steps.parallel-mode.outputs.full }} \
--partition-model-file /tmp/partition-model.json \ --partition-model-file /tmp/partition-model.json \
@@ -88,6 +88,7 @@ jobs:
MALLOC_ARENA_MAX: "2" MALLOC_ARENA_MAX: "2"
MAX_JOBS: "1" MAX_JOBS: "1"
PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1"
PYTEST_ADDOPTS: "-s"
TORCH_EXTENSIONS_DIR: ${{ runner.temp }}/torch-extensions TORCH_EXTENSIONS_DIR: ${{ runner.temp }}/torch-extensions
run: | run: |
python3 -m pytest -q tools/sglang-simulator/test/test_simulation_sglang_runner.py python3 -m pytest -q tools/sglang-simulator/test/test_simulation_sglang_runner.py
@@ -8,8 +8,8 @@ on:
required: true required: true
type: string type: string
default: 'pr-test.yml pr-test-extra.yml' default: 'pr-test.yml pr-test-extra.yml'
include_high_priority: include_highest_priority:
description: 'Also cancel runs from high-priority PRs' description: 'Also cancel runs from PRs labelled highest-priority'
required: false required: false
type: boolean type: boolean
default: false default: false
@@ -36,7 +36,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
WORKFLOWS: ${{ github.event.inputs.workflows }} WORKFLOWS: ${{ github.event.inputs.workflows }}
INCLUDE_HIGH_PRIORITY: ${{ github.event.inputs.include_high_priority }} INCLUDE_HIGHEST_PRIORITY: ${{ github.event.inputs.include_highest_priority }}
INCLUDE_RERUN_TEST: ${{ github.event.inputs.include_rerun_test }} INCLUDE_RERUN_TEST: ${{ github.event.inputs.include_rerun_test }}
shell: bash shell: bash
run: | run: |
@@ -49,7 +49,7 @@ jobs:
fi fi
echo "Targeting ${#WORKFLOW_FILES[@]} workflow(s): ${WORKFLOW_FILES[*]}" echo "Targeting ${#WORKFLOW_FILES[@]} workflow(s): ${WORKFLOW_FILES[*]}"
echo "include_high_priority=$INCLUDE_HIGH_PRIORITY, include_rerun_test=$INCLUDE_RERUN_TEST" echo "include_highest_priority=$INCLUDE_HIGHEST_PRIORITY, include_rerun_test=$INCLUDE_RERUN_TEST"
echo "" echo ""
# Decide whether to cancel run_id given a PR-lookup endpoint. # Decide whether to cancel run_id given a PR-lookup endpoint.
@@ -99,12 +99,12 @@ jobs:
return return
fi fi
if echo "$labels" | grep -Fxq "high priority"; then if echo "$labels" | grep -Fxq "highest-priority"; then
if [ "$INCLUDE_HIGH_PRIORITY" != "true" ]; then if [ "$INCLUDE_HIGHEST_PRIORITY" != "true" ]; then
echo " 🛑 Skipping (high priority label)" echo " 🛑 Skipping (highest-priority label)"
return return
fi fi
echo " ⚠️ High priority PR, but include_high_priority is enabled" echo " ⚠️ highest-priority PR, but include_highest_priority is enabled"
fi fi
echo " 🚫 Cancelling..." echo " 🚫 Cancelling..."
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
const QUOTA_IDLE = 7; const QUOTA_IDLE = 7;
const STALE_DAYS = 90; const STALE_DAYS = 90;
const KEEP_LABELS = new Set(['high priority', 'keep-open', 'good first issue']); const KEEP_LABELS = new Set(['highest-priority', 'keep-open', 'good first issue']);
const WIP_MARKER = /^\s*\[?\s*(wip|do[ _-]?not[ _-]?merge|dnm|draft)\s*\]?/i; const WIP_MARKER = /^\s*\[?\s*(wip|do[ _-]?not[ _-]?merge|dnm|draft)\s*\]?/i;
// Scheduled runs always act; only manual runs can be dry. // Scheduled runs always act; only manual runs can be dry.
@@ -0,0 +1,27 @@
# Coverage data collection pipeline for the NPU precision test selection.
# Delegates to pr-test-npu.yml with coverage_mode=true, which switches:
# - Runners to the coverage pool (shared /root/.cache with setup-covstub)
# - use_coverage_runner=true on all test jobs
# - Skips pr-gate, base-a (910b), multimodal-gen, recommend, analyze
# - Runs setup-covstub to assemble coverage data and publish it to the
# shared disk at a fixed path (/root/.cache/tests/precise-test/coverage)
# pr-test-npu-finish is the final gate in the called workflow.
#
# Manually triggered only (workflow_dispatch): PRs run the standard
# pr-test-npu.yml instead; this pipeline builds the full coverage baseline
# on demand.
name: Coverage Collection (NPU)
on:
workflow_dispatch:
concurrency:
group: npu-coverage-collection-${{ github.ref }}
cancel-in-progress: true
jobs:
coverage:
uses: ./.github/workflows/pr-test-npu.yml
with:
coverage_mode: true
secrets: inherit
+3
View File
@@ -76,6 +76,9 @@ jobs:
- name: Check cookbook authoring contracts - name: Check cookbook authoring contracts
run: node docs/scripts/check_cookbook_configs.mjs run: node docs/scripts/check_cookbook_configs.mjs
- name: Test PR health action
run: node --test .github/actions/check-pr-test-health/action.test.cjs
- name: Cache mint - name: Cache mint
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
+8 -8
View File
@@ -127,23 +127,23 @@ jobs:
# Get unique PR numbers (exclude NO_PR entries) # Get unique PR numbers (exclude NO_PR entries)
pr_numbers=$(cut -d'|' -f1 < "$pr_data_file" | grep -v '^NO_PR$' | sort -u || true) pr_numbers=$(cut -d'|' -f1 < "$pr_data_file" | grep -v '^NO_PR$' | sort -u || true)
# Separate high priority and normal PRs # Separate highest-priority and normal PRs
high_priority_prs=() highest_priority_prs=()
normal_prs=() normal_prs=()
for pr_num in $pr_numbers; do for pr_num in $pr_numbers; do
labels=$(gh pr view "$pr_num" --repo "$REPO" --json labels \ labels=$(gh pr view "$pr_num" --repo "$REPO" --json labels \
| jq -r '.labels[].name' 2>/dev/null || true) | jq -r '.labels[].name' 2>/dev/null || true)
if echo "$labels" | grep -Fxq "high priority"; then if echo "$labels" | grep -Fxq "highest-priority"; then
high_priority_prs+=($pr_num) highest_priority_prs+=($pr_num)
else else
normal_prs+=($pr_num) normal_prs+=($pr_num)
fi fi
done done
# Combine: high priority first, then normal # Combine: highest-priority first, then normal
sorted_pr_numbers=("${high_priority_prs[@]}" "${normal_prs[@]}") sorted_pr_numbers=("${highest_priority_prs[@]}" "${normal_prs[@]}")
pr_count=0 pr_count=0
total_running=0 total_running=0
@@ -170,8 +170,8 @@ jobs:
# Add priority indicator # Add priority indicator
priority_indicator="" priority_indicator=""
if echo "$pr_labels" | grep -q "high priority"; then if echo "$pr_info" | jq -e '[.labels[].name] | index("highest-priority")' >/dev/null; then
priority_indicator="🔴 [HIGH PRIORITY] " priority_indicator="🔴 [HIGHEST PRIORITY] "
fi fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -156,11 +156,14 @@ jobs:
# mi355x-ci-<RUNNER_NAME>-<GITHUB_RUN_ID>-<config> # mi355x-ci-<RUNNER_NAME>-<GITHUB_RUN_ID>-<config>
# Here we clear THIS runner's leftovers from a crashed previous run, so # Here we clear THIS runner's leftovers from a crashed previous run, so
# we match the RUNNER_NAME prefix (older runs have a different run id). # we match the RUNNER_NAME prefix (older runs have a different run id).
# Never a blanket `squeue --me`, which would kill a concurrent leg. # Never a blanket match on all jobs, which would kill a concurrent leg.
# %200j: squeue truncates the job name (%j) by default -- widen it so # %200j: squeue truncates the job name (%j) by default -- widen it so
# the grep sees the full name. # the grep sees the full name.
# No --me: Spur's squeue does not accept it. The RUNNER_NAME prefix is
# what makes this selective, and that is unique per runner, so the
# filter is equally precise without it.
if [ -z "${RUNNER_NAME:-}" ]; then echo "RUNNER_NAME unset; skipping"; exit 0; fi if [ -z "${RUNNER_NAME:-}" ]; then echo "RUNNER_NAME unset; skipping"; exit 0; fi
STALE_JOBS=$(squeue --me --noheader --format="%i %200j" | grep -F "mi355x-ci-${RUNNER_NAME}-" | awk '{print $1}' || true) STALE_JOBS=$(squeue --noheader --format="%i %200j" | grep -F "mi355x-ci-${RUNNER_NAME}-" | awk '{print $1}' || true)
if [ -n "$STALE_JOBS" ]; then if [ -n "$STALE_JOBS" ]; then
echo "Cancelling stale jobs for ${RUNNER_NAME}: $STALE_JOBS" echo "Cancelling stale jobs for ${RUNNER_NAME}: $STALE_JOBS"
scancel $STALE_JOBS scancel $STALE_JOBS
@@ -237,7 +240,7 @@ jobs:
# config are unique per matrix leg, so a concurrent leg is never hit. # config are unique per matrix leg, so a concurrent leg is never hit.
if [ -z "${RUNNER_NAME:-}" ]; then echo "RUNNER_NAME unset; skipping"; exit 0; fi if [ -z "${RUNNER_NAME:-}" ]; then echo "RUNNER_NAME unset; skipping"; exit 0; fi
JOB_TAG="mi355x-ci-${RUNNER_NAME}-${GITHUB_RUN_ID}-${MATRIX_CONFIG_NAME}" JOB_TAG="mi355x-ci-${RUNNER_NAME}-${GITHUB_RUN_ID}-${MATRIX_CONFIG_NAME}"
ACTIVE_JOBS=$(squeue --me --noheader --format="%i %200j" | grep -F "$JOB_TAG" | awk '{print $1}' || true) ACTIVE_JOBS=$(squeue --noheader --format="%i %200j" | grep -F "$JOB_TAG" | awk '{print $1}' || true)
if [ -n "$ACTIVE_JOBS" ]; then if [ -n "$ACTIVE_JOBS" ]; then
echo "Cancelling jobs for ${JOB_TAG}: $ACTIVE_JOBS" echo "Cancelling jobs for ${JOB_TAG}: $ACTIVE_JOBS"
scancel $ACTIVE_JOBS scancel $ACTIVE_JOBS
+8 -1
View File
@@ -243,7 +243,14 @@ jobs:
extra-a-test-1-gpu-large-amd, extra-a-test-1-gpu-large-amd,
extra-a-test-2-gpu-large-amd, extra-a-test-2-gpu-large-amd,
] ]
if: always() # Same `labeled` guard as call-gate: an unrelated label would otherwise finish
# green with nothing executed, over the real run's result.
if: |
always() &&
(github.event_name != 'pull_request' ||
github.event.action != 'labeled' ||
github.event.label.name == 'run-ci' ||
github.event.label.name == 'run-ci-extra')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check all dependent job statuses - name: Check all dependent job statuses
+16 -6
View File
@@ -187,19 +187,29 @@ jobs:
echo "Run mode: FILTERED (triggered by ${{ github.event_name }})" echo "Run mode: FILTERED (triggered by ${{ github.event_name }})"
fi fi
- name: Resolve CI labels
id: ci-labels
if: github.event_name == 'pull_request'
uses: actions/github-script@v8
with:
script: |
const { resolveCiLabels } = require(
`${process.env.GITHUB_WORKSPACE}/.github/scripts/ci-labels.cjs`
);
const axes = await resolveCiLabels(github, context);
core.info(`CI labels: [${axes.labels.join(', ')}]`);
core.setOutput('bypass_fail_fast', String(axes.bypassFailFast));
- name: Set continue-on-error for schedule/full runs - name: Set continue-on-error for schedule/full runs
id: set-continue-on-error id: set-continue-on-error
env: env:
# `bypass-fastfail` PR label: also disable within-suite fast-fail BYPASS_FAIL_FAST: ${{ steps.ci-labels.outputs.bypass_fail_fast }}
# here. The shared actions/wait-for-jobs already honors the same
# label to skip cross-stage waits.
BYPASS_FASTFAIL_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'bypass-fastfail') }}
run: | run: |
if [[ "${{ steps.run-mode.outputs.run_all_tests }}" == "true" \ if [[ "${{ steps.run-mode.outputs.run_all_tests }}" == "true" \
|| "${{ inputs.continue_on_error }}" == "true" \ || "${{ inputs.continue_on_error }}" == "true" \
|| "$BYPASS_FASTFAIL_LABEL" == "true" ]]; then || "$BYPASS_FAIL_FAST" == "true" ]]; then
echo "continue_on_error=true" >> $GITHUB_OUTPUT echo "continue_on_error=true" >> $GITHUB_OUTPUT
echo "Continue-on-error: ENABLED (run_all_tests=${{ steps.run-mode.outputs.run_all_tests }}, input=${{ inputs.continue_on_error }}, bypass-fastfail=$BYPASS_FASTFAIL_LABEL)" echo "Continue-on-error: ENABLED (run_all_tests=${{ steps.run-mode.outputs.run_all_tests }}, input=${{ inputs.continue_on_error }}, bypass-fail-fast=$BYPASS_FAIL_FAST)"
else else
echo "continue_on_error=false" >> $GITHUB_OUTPUT echo "continue_on_error=false" >> $GITHUB_OUTPUT
echo "Continue-on-error: DISABLED" echo "Continue-on-error: DISABLED"
+35 -88
View File
@@ -50,13 +50,14 @@ on:
type: boolean type: boolean
default: false default: false
skip_pr_test_health_check: skip_pr_test_health_check:
description: "Skip PR test health check fast-fail (e.g. for release branch cuts)" description: "Skip PR test health check fail-fast (e.g. for release branch cuts)"
required: false required: false
type: boolean type: boolean
default: false default: false
concurrency: concurrency:
group: pr-test-extra-${{ github.event_name }}-${{ github.head_ref || github.ref_name || 'default' }}-${{ inputs.git_ref || 'all' }} # Keys on the PR number: two forks can share a head_ref and would cancel each other.
group: pr-test-extra-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref_name || 'default' }}-${{ inputs.git_ref || 'all' }}
cancel-in-progress: ${{ github.event_name != 'workflow_call' }} cancel-in-progress: ${{ github.event_name != 'workflow_call' }}
env: env:
@@ -159,92 +160,32 @@ jobs:
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
# No `secrets: inherit`: this hosted CPU job has no secret consumer. # No `secrets: inherit`: this hosted CPU job has no secret consumer.
# =============================================== extra-a (1-/2-gpu) =============================================== # =============================================== extra-a / extra-b ===============================================
extra-a-test-1-gpu-small: # `name:` keeps the job names wait-for-jobs and check-pr-test-health match on.
extra-test:
name: ${{ matrix.stage }}-test-${{ matrix.runner_config }}
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build] needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }} if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
strategy:
fail-fast: false
matrix:
include:
- { stage: extra-a, runner_config: 1-gpu-small, timeout_per_file: '' }
- { stage: extra-a, runner_config: 1-gpu-large, timeout_per_file: '1800' }
- { stage: extra-a, runner_config: 2-gpu-large, timeout_per_file: '' }
- { stage: extra-b, runner_config: 4-gpu-h100, timeout_per_file: '3600' }
- { stage: extra-b, runner_config: 4-gpu-b200, timeout_per_file: '1800' }
- { stage: extra-b, runner_config: 8-gpu-h200, timeout_per_file: '' }
- { stage: extra-b, runner_config: 8-gpu-b300, timeout_per_file: '3600' }
uses: ./.github/workflows/_pr-test-stage.yml uses: ./.github/workflows/_pr-test-stage.yml
with: with:
self_name: extra-a-test-1-gpu-small self_name: ${{ matrix.stage }}-test-${{ matrix.runner_config }}
runner_config: 1-gpu-small runner_config: ${{ matrix.runner_config }}
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
extra-a-test-1-gpu-large:
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: extra-a-test-1-gpu-large
runner_config: 1-gpu-large
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
timeout_per_file: '1800'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
extra-a-test-2-gpu-large:
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: extra-a-test-2-gpu-large
runner_config: 2-gpu-large
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
# =============================================== extra-b (4-/8-gpu) ===============================================
extra-b-test-4-gpu-h100:
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: extra-b-test-4-gpu-h100
runner_config: 4-gpu-h100
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
timeout_per_file: '3600'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
extra-b-test-4-gpu-b200:
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: extra-b-test-4-gpu-b200
runner_config: 4-gpu-b200
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
timeout_per_file: '1800'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
extra-b-test-8-gpu-h200:
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() && needs.check-changes.result == 'success' && (needs.call-gate.result == 'success' || needs.call-gate.result == 'skipped') }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: extra-b-test-8-gpu-h200
runner_config: 8-gpu-h200
check_changes: ${{ toJson(needs.check-changes.outputs) }} check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }} caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }} partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60' run_timeout_minutes: '60'
timeout_per_file: ${{ matrix.timeout_per_file }}
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit secrets: inherit
@@ -260,14 +201,16 @@ jobs:
sgl-kernel-build-wheels, sgl-kernel-build-wheels,
rust-ext-build, rust-ext-build,
simulator-test-cpu, simulator-test-cpu,
extra-a-test-1-gpu-small, extra-test,
extra-a-test-1-gpu-large,
extra-a-test-2-gpu-large,
extra-b-test-4-gpu-h100,
extra-b-test-4-gpu-b200,
extra-b-test-8-gpu-h200,
] ]
if: always() # Without this guard an unrelated label starts a run that finishes green with
# nothing executed, over the real failure; `skipped` is what pr-states.yml reads.
if: |
always() &&
(github.event_name != 'pull_request' ||
github.event.action != 'labeled' ||
github.event.label.name == 'run-ci' ||
github.event.label.name == 'run-ci-extra')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check all dependent job statuses - name: Check all dependent job statuses
@@ -297,10 +240,14 @@ jobs:
# there (initial completion) and pull_request_target (push / label). # there (initial completion) and pull_request_target (push / label).
notify-pr-states: notify-pr-states:
needs: [pr-test-extra-finish] needs: [pr-test-extra-finish]
# Same guard; pr-states.yml subscribes to labeled/unlabeled itself, so nothing is lost.
if: | if: |
always() && always() &&
github.event_name == 'pull_request' && github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository github.event.pull_request.head.repo.full_name == github.repository &&
(github.event.action != 'labeled' ||
github.event.label.name == 'run-ci' ||
github.event.label.name == 'run-ci-extra')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Dispatch pr-states refresh - name: Dispatch pr-states refresh
+27 -141
View File
@@ -13,9 +13,6 @@ on:
description: 'Artifact of prebuilt Rust extension modules, from rust-ext-build. Empty, or a download that fails, falls back to the cache; a miss there compiles during install.' description: 'Artifact of prebuilt Rust extension modules, from rust-ext-build. Empty, or a download that fails, falls back to the cache; a miss there compiles during install.'
type: string type: string
default: '' default: ''
runner_config:
required: true
type: string
runs_on_map: runs_on_map:
required: true required: true
type: string type: string
@@ -43,15 +40,37 @@ env:
SKIP_PR_TEST_HEALTH_CHECK: ${{ inputs.skip_pr_test_health_check == true && 'true' || 'false' }} SKIP_PR_TEST_HEALTH_CHECK: ${{ inputs.skip_pr_test_health_check == true && 'true' || 'false' }}
jobs: jobs:
jit-kernel-unit-test: # `name:` reproduces the job names this workflow published before the table.
jit-kernel-test:
# The job name is the suite it runs, with the stage prefix swapped for this
# workflow's, so a row declares a (suite, runner) pair and nothing else.
name: jit-kernel-${{ matrix.suite }}${{ matrix.shard }}
# Runs whenever call-jit-kernel-tests dispatches this workflow. That caller is the # Runs whenever call-jit-kernel-tests dispatches this workflow. That caller is the
# single gate (PR jit_kernel changes, or scheduled/parallel-dispatch full runs), so # single gate (PR jit_kernel changes, or scheduled/parallel-dispatch full runs), so
# the sub-jobs no longer re-exclude schedule/parallel-dispatch here. # the sub-jobs no longer re-exclude schedule/parallel-dispatch here.
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
partition: [0, 1] include:
runs-on: 1-gpu-h100 - suite: unit-test-1-gpu-large
runner_config: 1-gpu-large
shard: " (0)"
suite_args: --auto-partition-id 0 --auto-partition-size 2 --fork-worker-batch-size 20
- suite: unit-test-1-gpu-large
runner_config: 1-gpu-large
shard: " (1)"
suite_args: --auto-partition-id 1 --auto-partition-size 2 --fork-worker-batch-size 20
- suite: unit-test-1-gpu-small
runner_config: 1-gpu-small
- suite: unit-test-4-gpu-b200
runner_config: 4-gpu-b200
- suite: unit-test-8-gpu-h200
runner_config: 8-gpu-h200
# Alone among these, it has never run the health check.
skip_health_check: true
- suite: benchmark-test-1-gpu-large
runner_config: 1-gpu-large
runs-on: ${{ fromJson(inputs.runs_on_map)[matrix.runner_config] }}
timeout-minutes: 240 timeout-minutes: 240
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -59,6 +78,7 @@ jobs:
ref: ${{ inputs.git_ref || github.sha }} ref: ${{ inputs.git_ref || github.sha }}
- uses: ./.github/actions/check-pr-test-health - uses: ./.github/actions/check-pr-test-health
if: ${{ !matrix.skip_health_check }}
- uses: ./.github/actions/check-maintenance - uses: ./.github/actions/check-maintenance
@@ -89,138 +109,4 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
run: | run: |
cd test/ cd test/
python3 run_suite.py --hw cuda \ python3 run_suite.py --hw cuda --suite base-b-kernel-${{ matrix.suite }} ${{ matrix.suite_args }}
--suite base-b-kernel-unit-test-1-gpu-large \
--auto-partition-id ${{ matrix.partition }} \
--auto-partition-size 2 \
--fork-worker-batch-size 20
jit-kernel-multigpu-unit-test:
# Runs whenever call-jit-kernel-tests dispatches this workflow. That caller is the
# single gate (PR jit_kernel changes, or scheduled/parallel-dispatch full runs), so
# the sub-jobs no longer re-exclude schedule/parallel-dispatch here.
runs-on: 8-gpu-h200
timeout-minutes: 240
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.git_ref || github.sha }}
- uses: ./.github/actions/check-maintenance
- name: Cleanup
if: inputs.sgl_kernel == 'true'
run: |
ls -alh python/sglang/kernels/aot/dist || true
rm -rf python/sglang/kernels/aot/dist/* || true
- name: Download artifacts
if: inputs.sgl_kernel == 'true'
uses: actions/download-artifact@v4
with:
path: python/sglang/kernels/aot/dist/
merge-multiple: true
pattern: wheel-python3.10-cuda13.0
- uses: ./.github/actions/download-rust-ext
with:
artifact_name: ${{ inputs.rust_ext_artifact }}
- name: Install dependencies
timeout-minutes: 20
run: |
CUSTOM_BUILD_SGL_KERNEL=${{ inputs.sgl_kernel }} bash scripts/ci/cuda/ci_install_dependency.sh diffusion
- name: Run multi-GPU test
timeout-minutes: 45
run: |
cd test/
python3 run_suite.py --hw cuda --suite base-b-kernel-unit-test-8-gpu-h200
jit-kernel-benchmark-test:
# Runs whenever call-jit-kernel-tests dispatches this workflow. That caller is the
# single gate (PR jit_kernel changes, or scheduled/parallel-dispatch full runs), so
# the sub-jobs no longer re-exclude schedule/parallel-dispatch here.
runs-on: 1-gpu-h100
timeout-minutes: 240
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.git_ref || github.sha }}
- uses: ./.github/actions/check-pr-test-health
- uses: ./.github/actions/check-maintenance
- name: Cleanup
if: inputs.sgl_kernel == 'true'
run: |
ls -alh python/sglang/kernels/aot/dist || true
rm -rf python/sglang/kernels/aot/dist/* || true
- name: Download artifacts
if: inputs.sgl_kernel == 'true'
uses: actions/download-artifact@v4
with:
path: python/sglang/kernels/aot/dist/
merge-multiple: true
pattern: wheel-python3.10-cuda13.0
- uses: ./.github/actions/download-rust-ext
with:
artifact_name: ${{ inputs.rust_ext_artifact }}
- name: Install dependencies
timeout-minutes: 20
run: |
CUSTOM_BUILD_SGL_KERNEL=${{ inputs.sgl_kernel }} bash scripts/ci/cuda/ci_install_dependency.sh diffusion
- name: Run benchmark tests
timeout-minutes: 45
run: |
cd test/
python3 run_suite.py --hw cuda --suite base-b-kernel-benchmark-test-1-gpu-large
jit-kernel-b200-test:
# Runs whenever call-jit-kernel-tests dispatches this workflow. That caller is the
# single gate (PR jit_kernel changes, or scheduled/parallel-dispatch full runs), so
# the sub-jobs no longer re-exclude schedule/parallel-dispatch here.
runs-on: ${{ fromJson(inputs.runs_on_map)[inputs.runner_config] }}
timeout-minutes: 240
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.git_ref || github.sha }}
- uses: ./.github/actions/check-pr-test-health
- uses: ./.github/actions/check-maintenance
- name: Cleanup
if: inputs.sgl_kernel == 'true'
run: |
ls -alh python/sglang/kernels/aot/dist || true
rm -rf python/sglang/kernels/aot/dist/* || true
- name: Download artifacts
if: inputs.sgl_kernel == 'true'
uses: actions/download-artifact@v4
with:
path: python/sglang/kernels/aot/dist/
merge-multiple: true
pattern: wheel-python3.10-cuda13.0
- uses: ./.github/actions/download-rust-ext
with:
artifact_name: ${{ inputs.rust_ext_artifact }}
- name: Install dependencies
timeout-minutes: 20
run: |
CUSTOM_BUILD_SGL_KERNEL=${{ inputs.sgl_kernel }} bash scripts/ci/cuda/ci_install_dependency.sh diffusion
- name: Run B200 diffusion test
timeout-minutes: 30
run: |
cd test/
python3 run_suite.py --hw cuda --suite base-b-kernel-unit-test-4-gpu-b200
+5 -2
View File
@@ -130,7 +130,7 @@ jobs:
path: | path: |
python/sglang/multimodal_gen/test/execution_report_*.json python/sglang/multimodal_gen/test/execution_report_*.json
python/diffusion-results.json python/diffusion-results.json
retention-days: 1 retention-days: 7
- name: Upload diffusion failure artifacts - name: Upload diffusion failure artifacts
if: always() if: always()
@@ -280,6 +280,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: ${{ fromJson(needs.compute-diffusion-partitions.outputs.matrix-2gpu) }} matrix: ${{ fromJson(needs.compute-diffusion-partitions.outputs.matrix-2gpu) }}
steps: steps:
- name: Record retry deadline
run: echo "SGLANG_DIFFUSION_RETRY_DEADLINE=$(( $(date +%s) + 2400 ))" >> "$GITHUB_ENV"
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
@@ -327,7 +330,7 @@ jobs:
path: | path: |
python/sglang/multimodal_gen/test/execution_report_*.json python/sglang/multimodal_gen/test/execution_report_*.json
python/diffusion-results.json python/diffusion-results.json
retention-days: 1 retention-days: 7
- name: Upload diffusion failure artifacts - name: Upload diffusion failure artifacts
if: always() if: always()
+319 -26
View File
@@ -7,10 +7,16 @@ on:
- cron: '0 12 * * *' # Run daily at 12:00 UTC - cron: '0 12 * * *' # Run daily at 12:00 UTC
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
inputs:
coverage_mode:
description: 'Run in coverage collection mode (uses coverage runners, enables coverage instrumentation, runs setup-covstub)'
required: false
type: boolean
default: false
workflow_call: workflow_call:
inputs: inputs:
ref: ref:
description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the default branch.' description: 'Git ref (branch, tag, or SHA) to test. If not provided, uses the event commit SHA.'
required: false required: false
type: string type: string
default: '' default: ''
@@ -19,9 +25,14 @@ on:
required: false required: false
type: boolean type: boolean
default: false default: false
coverage_mode:
description: 'Run in coverage collection mode (uses coverage runners, enables coverage instrumentation, runs setup-covstub)'
required: false
type: boolean
default: false
concurrency: concurrency:
group: pr-test-npu-${{ inputs.ref || github.ref }} group: pr-test-npu-${{ inputs.coverage_mode == true && 'coverage-' || '' }}${{ inputs.ref || github.ref }}
cancel-in-progress: ${{ github.event_name != 'workflow_call' }} cancel-in-progress: ${{ github.event_name != 'workflow_call' }}
jobs: jobs:
@@ -36,7 +47,9 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ inputs.ref || github.ref }} # Pin to the event SHA (not the branch name) so every job in this run
# checks out the same commit even if the branch advances mid-run.
ref: ${{ inputs.ref || github.sha }}
- name: Determine run mode - name: Determine run mode
id: run-mode id: run-mode
@@ -57,10 +70,18 @@ jobs:
if: steps.run-mode.outputs.run_all_tests != 'true' if: steps.run-mode.outputs.run_all_tests != 'true'
with: with:
filters: | filters: |
# Everything under python/sglang except multimodal_gen/ and kernels/ops/diffusion/.
# Exclusions have to be extglobs inside a single pattern: paths-filter ORs the
# patterns of a filter (predicate-quantifier defaults to "some"), so a standalone
# "!some/path/**" entry matches every file outside that path and makes the whole
# filter unconditionally true.
main_package: main_package:
- "python/sglang/**/!(*.md)" - "python/sglang/!(*.md)"
- "!python/sglang/multimodal_gen/**" - "python/sglang/!(multimodal_gen|kernels)/**/!(*.md)"
- "!python/sglang/kernels/ops/diffusion/**" - "python/sglang/kernels/!(*.md)"
- "python/sglang/kernels/!(ops)/**/!(*.md)"
- "python/sglang/kernels/ops/!(*.md)"
- "python/sglang/kernels/ops/!(diffusion)/**/!(*.md)"
- "python/pyproject_npu.toml" - "python/pyproject_npu.toml"
- "scripts/ci/npu/npu_ci_install_dependency.sh" - "scripts/ci/npu/npu_ci_install_dependency.sh"
- "test/registered/npu/**" - "test/registered/npu/**"
@@ -77,7 +98,7 @@ jobs:
# ==================== PR Gate ==================== # # ==================== PR Gate ==================== #
pr-gate: pr-gate:
needs: check-changes needs: check-changes
if: needs.check-changes.outputs.changes_exist == 'true' if: ${{ inputs.coverage_mode != true && needs.check-changes.outputs.changes_exist == 'true' }}
uses: ./.github/workflows/pr-gate.yml uses: ./.github/workflows/pr-gate.yml
secrets: inherit secrets: inherit
@@ -96,7 +117,7 @@ jobs:
base-a-test-1-npu-a2: base-a-test-1-npu-a2:
needs: [check-changes, pr-gate, set-image-config] needs: [check-changes, pr-gate, set-image-config]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && inputs.coverage_mode != true && needs.check-changes.outputs.main_package == 'true' }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-a-test-1-npu-a2 self_name: base-a-test-1-npu-a2
@@ -108,68 +129,78 @@ jobs:
base-b-test-1-npu-a3: base-b-test-1-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-b-test-1-npu-a3 self_name: base-b-test-1-npu-a3
runner_config: linux-aarch64-a3-2- runner_config: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-2' || 'linux-aarch64-a3-2-' }}
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
run_timeout_minutes: '60' run_timeout_minutes: '60'
timeout_per_file: '3600' timeout_per_file: '3600'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
secrets: inherit secrets: inherit
base-b-test-2-npu-a3: base-b-test-2-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-b-test-2-npu-a3 self_name: base-b-test-2-npu-a3
runner_config: linux-aarch64-a3-2- runner_config: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-2' || 'linux-aarch64-a3-2-' }}
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
run_timeout_minutes: '60' run_timeout_minutes: '60'
timeout_per_file: '3600' timeout_per_file: '3600'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
secrets: inherit secrets: inherit
base-b-test-4-npu-a3: base-b-test-4-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-b-test-4-npu-a3 self_name: base-b-test-4-npu-a3
runner_config: linux-aarch64-a3-4- runner_config: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-4' || 'linux-aarch64-a3-4-' }}
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
run_timeout_minutes: '120' run_timeout_minutes: '120'
timeout_per_file: '3600' timeout_per_file: '3600'
partitions: '{"size":2,"arr":[0, 1]}' partitions: '{"size":2,"arr":[0, 1]}'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
secrets: inherit secrets: inherit
base-b-test-8-npu-a3: base-b-test-8-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-b-test-8-npu-a3 self_name: base-b-test-8-npu-a3
runner_config: linux-aarch64-a3-8- runner_config: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-8' || 'linux-aarch64-a3-8-' }}
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
run_timeout_minutes: '60' run_timeout_minutes: '60'
timeout_per_file: '3600' timeout_per_file: '3600'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
secrets: inherit secrets: inherit
base-b-test-16-npu-a3: base-b-test-16-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-pr-test-stage.yml uses: ./.github/workflows/_npu-pr-test-stage.yml
with: with:
self_name: base-b-test-16-npu-a3 self_name: base-b-test-16-npu-a3
runner_config: linux-aarch64-a3-16- runner_config: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-16' || 'linux-aarch64-a3-16-' }}
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
run_timeout_minutes: '120' run_timeout_minutes: '120'
timeout_per_file: '3600' timeout_per_file: '3600'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
secrets: inherit secrets: inherit
multimodal-gen-test-1-npu-a3: multimodal-gen-test-1-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.multimodal_gen == 'true' }} if: ${{ !failure() && !cancelled() && inputs.coverage_mode != true && needs.check-changes.outputs.multimodal_gen == 'true' }}
runs-on: linux-aarch64-a3-800t-2 runs-on: linux-aarch64-a3-800t-2
strategy: strategy:
fail-fast: false fail-fast: false
@@ -233,7 +264,7 @@ jobs:
multimodal-gen-test-4-npu-a3: multimodal-gen-test-4-npu-a3:
needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2] needs: [check-changes, pr-gate, set-image-config, base-a-test-1-npu-a2]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.multimodal_gen == 'true' }} if: ${{ !failure() && !cancelled() && inputs.coverage_mode != true && needs.check-changes.outputs.multimodal_gen == 'true' }}
runs-on: linux-aarch64-a3-800t-4 runs-on: linux-aarch64-a3-800t-4
strategy: strategy:
fail-fast: false fail-fast: false
@@ -298,34 +329,38 @@ jobs:
base-c-test-acc-2-npu-a3: base-c-test-acc-2-npu-a3:
name: base-c-test-acc-2-npu-a3 name: base-c-test-acc-2-npu-a3
needs: [ check-changes, pr-gate, set-image-config, base-b-test-1-npu-a3, base-b-test-2-npu-a3, base-b-test-4-npu-a3, base-b-test-8-npu-a3, base-b-test-16-npu-a3 ] needs: [ check-changes, pr-gate, set-image-config, base-b-test-1-npu-a3, base-b-test-2-npu-a3, base-b-test-4-npu-a3, base-b-test-8-npu-a3, base-b-test-16-npu-a3 ]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-single-node-test-stage.yml uses: ./.github/workflows/_npu-single-node-test-stage.yml
with: with:
runner: linux-aarch64-a3-2- runner: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-2' || 'linux-aarch64-a3-2-' }}
test_type: 'accuracy' test_type: 'accuracy'
test_suite: base-c-test-acc-2-npu-a3 test_suite: base-c-test-acc-2-npu-a3
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
install_sglang_deps: true install_sglang_deps: true
device_type_for_deps: 'a3' device_type_for_deps: 'a3'
partitions: '{"size":2,"arr":[0,1]}' partitions: '{"size":2,"arr":[0,1]}'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
base-c-test-acc-16-npu-a3: base-c-test-acc-16-npu-a3:
name: base-c-test-acc-16-npu-a3 name: base-c-test-acc-16-npu-a3
needs: [ check-changes, pr-gate, set-image-config, base-b-test-1-npu-a3, base-b-test-2-npu-a3, base-b-test-4-npu-a3, base-b-test-8-npu-a3, base-b-test-16-npu-a3 ] needs: [ check-changes, pr-gate, set-image-config, base-b-test-1-npu-a3, base-b-test-2-npu-a3, base-b-test-4-npu-a3, base-b-test-8-npu-a3, base-b-test-16-npu-a3 ]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-single-node-test-stage.yml uses: ./.github/workflows/_npu-single-node-test-stage.yml
with: with:
runner: linux-aarch64-a3-16- runner: ${{ inputs.coverage_mode == true && 'linux-aarch64-a3-800t-16' || 'linux-aarch64-a3-16-' }}
test_type: 'accuracy' test_type: 'accuracy'
test_suite: base-c-test-acc-16-npu-a3 test_suite: base-c-test-acc-16-npu-a3
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
install_sglang_deps: true install_sglang_deps: true
device_type_for_deps: 'a3' device_type_for_deps: 'a3'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
base-c-test-perf-2-npu-a3: base-c-test-perf-2-npu-a3:
name: base-c-test-perf-2-npu-a3 name: base-c-test-perf-2-npu-a3
needs: [ check-changes, pr-gate, set-image-config, base-c-test-acc-2-npu-a3, base-c-test-acc-16-npu-a3 ] needs: [ check-changes, pr-gate, set-image-config, base-c-test-acc-2-npu-a3, base-c-test-acc-16-npu-a3 ]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-single-node-test-stage.yml uses: ./.github/workflows/_npu-single-node-test-stage.yml
with: with:
runner: linux-aarch64-a3-800t-2 runner: linux-aarch64-a3-800t-2
@@ -334,11 +369,13 @@ jobs:
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }} image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
install_sglang_deps: true install_sglang_deps: true
device_type_for_deps: 'a3' device_type_for_deps: 'a3'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
base-c-test-perf-16-npu-a3: base-c-test-perf-16-npu-a3:
name: base-c-test-perf-16-npu-a3 name: base-c-test-perf-16-npu-a3
needs: [ check-changes, pr-gate, set-image-config, base-c-test-acc-2-npu-a3, base-c-test-acc-16-npu-a3 ] needs: [ check-changes, pr-gate, set-image-config, base-c-test-acc-2-npu-a3, base-c-test-acc-16-npu-a3 ]
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }} if: ${{ !failure() && !cancelled() && (inputs.coverage_mode == true || needs.check-changes.outputs.main_package == 'true') }}
uses: ./.github/workflows/_npu-single-node-test-stage.yml uses: ./.github/workflows/_npu-single-node-test-stage.yml
with: with:
runner: linux-aarch64-a3-800t-16 runner: linux-aarch64-a3-800t-16
@@ -348,8 +385,262 @@ jobs:
install_sglang_deps: true install_sglang_deps: true
device_type_for_deps: 'a3' device_type_for_deps: 'a3'
test_timeout_minutes: '180' test_timeout_minutes: '180'
use_coverage_runner: ${{ inputs.coverage_mode == true }}
upload_test_logs: ${{ inputs.coverage_mode != true }}
# ==================== Recommend Tests from Coverage ==================== #
# Recommends PR-affected test cases from the pre-built coverage baseline.
# continue-on-error so it never blocks the PR gate.
recommend-tests-from-coverage:
name: Recommend tests from coverage
runs-on: ubuntu-latest
continue-on-error: true
if: ${{ !cancelled() && github.event_name == 'pull_request' && inputs.coverage_mode != true }}
outputs:
coverage_paths: ${{ steps.export-paths.outputs.coverage_paths }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout sglang scripts
uses: actions/checkout@v4
with:
sparse-checkout: |
scripts
sparse-checkout-cone-mode: false
- name: Download test case map
run: |
set -euo pipefail
curl -fsSL \
--retry 3 \
--retry-delay 5 \
--retry-all-errors \
"https://sglang-npu.obs.cn-southwest-2.myhuaweicloud.com/coverage/test_case_map.json" \
-o test_case_map.json
test -s test_case_map.json
echo "Downloaded test_case_map.json"
du -h test_case_map.json
- name: Download coverage package and extract covstub
run: |
set -euo pipefail
curl -fsSL \
--retry 3 \
--retry-delay 5 \
--retry-all-errors \
"https://sglang-npu.obs.cn-southwest-2.myhuaweicloud.com/coverage/outputs.tar.gz" \
-o outputs.tar.gz
test -s outputs.tar.gz
echo "Downloaded outputs.tar.gz"
du -h outputs.tar.gz
tar xzf outputs.tar.gz outputs/covstub
test -d outputs/covstub
echo "Extracted outputs/covstub"
find outputs/covstub -maxdepth 2 -type d | head -20 || true
- name: Recommend tests for current PR
run: |
set -euo pipefail
if ! python3 -c "import regex" >/dev/null 2>&1; then
echo "regex not found for $(python3 -V); bootstrapping pip and installing regex"
curl -fsSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py --break-system-packages
python3 -m pip install --break-system-packages regex
fi
python3 -c "import regex; print('regex ok:', regex.__file__)"
PR_SPEC="${{ github.repository }}#${{ github.event.pull_request.number }}"
echo "Selecting tests for PR: ${PR_SPEC}"
# test_selector.py resolves relative paths against the script's own dir,
# so pass absolute paths for files downloaded to the workspace root.
python3 scripts/ci/npu/precise-test/test_selector.py \
--github-pr "${PR_SPEC}" \
--source-dir "$PWD/outputs/covstub" \
--map-file "$PWD/test_case_map.json"
- name: Export coverage paths output
id: export-paths
if: always()
run: |
FILE="scripts/ci/npu/precise-test/recommended_pytest_paths.txt"
if [ -f "$FILE" ]; then
{
echo "coverage_paths<<EOF"
cat "$FILE"
echo "EOF"
} >> "$GITHUB_OUTPUT"
echo "Exported $(wc -l < "$FILE") recommended paths"
else
echo "coverage_paths=" >> "$GITHUB_OUTPUT"
echo "::notice::recommended_pytest_paths.txt not found"
fi
# ==================== Analyze Failure Report ==================== #
# Cross-references test failures with coverage recommendations.
# !cancelled() only: skipped jobs in needs poison success().
analyze-failure-report:
name: Analyze failure report
needs:
[
base-a-test-1-npu-a2,
base-b-test-1-npu-a3,
base-b-test-2-npu-a3,
base-b-test-4-npu-a3,
base-b-test-8-npu-a3,
base-b-test-16-npu-a3,
multimodal-gen-test-1-npu-a3,
multimodal-gen-test-4-npu-a3,
base-c-test-acc-2-npu-a3,
base-c-test-acc-16-npu-a3,
base-c-test-perf-2-npu-a3,
base-c-test-perf-16-npu-a3,
recommend-tests-from-coverage,
]
if: ${{ !cancelled() && inputs.coverage_mode != true }}
uses: ./.github/workflows/_npu-analyze-failure.yml
with:
log_artifact_pattern: selected-test-logs-*
recommendations_content: ${{ needs.recommend-tests-from-coverage.outputs.coverage_paths || '' }}
# ==================== Coverage Assembly & Publish ==================== #
# Assembles coverage data and publishes the baseline to the shared disk.
# !failure() && !cancelled() (not success()): skipped upstream poisons success().
setup-covstub:
needs:
[
set-image-config,
base-b-test-1-npu-a3,
base-b-test-2-npu-a3,
base-b-test-4-npu-a3,
base-b-test-8-npu-a3,
base-b-test-16-npu-a3,
base-c-test-acc-2-npu-a3,
base-c-test-acc-16-npu-a3,
base-c-test-perf-2-npu-a3,
base-c-test-perf-16-npu-a3,
]
if: ${{ !failure() && !cancelled() && inputs.coverage_mode == true }}
runs-on: linux-aarch64-a3-800t-2
container:
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# Pin to the event SHA (not the branch name) so every job in this run
# checks out the same commit even if the branch advances mid-run.
ref: ${{ inputs.ref || github.sha }}
- name: Copy sglang source to covstub
run: |
RUN_DIR="/root/.cache/tests/precise-test/${{ github.run_id }}-attempt-${{ github.run_attempt }}"
COVSTUB="${RUN_DIR}/outputs/covstub"
mkdir -p "${COVSTUB}"
cp -r python/sglang "${COVSTUB}/"
- name: Build test case map
shell: bash
run: |
set -euo pipefail
RUN_DIR="/root/.cache/tests/precise-test/${{ github.run_id }}-attempt-${{ github.run_attempt }}"
# Date tag derivation must match scripts/ci/npu/precise-test/run_tests_with_coverage.sh.
# The custom k8s runner only injects explicitly-set env vars, so
# GITHUB_RUN_STARTED_AT may be absent (set -u would abort); fall back
# to local date exactly like run_tests_with_coverage.sh does, so both
# sides derive the same tag.
if [ -n "${GITHUB_RUN_STARTED_AT:-}" ]; then
COV_DATE_TAG="${GITHUB_RUN_STARTED_AT:0:10}"
COV_DATE_TAG="${COV_DATE_TAG//-/}"
else
COV_DATE_TAG="$(date +%Y%m%d)"
fi
COV_ROOT="${RUN_DIR}/outputs/sglang@${COV_DATE_TAG}"
COVSTUB="${RUN_DIR}/outputs/covstub"
# Placed outside outputs/ so it is NOT included in outputs.tar.gz.
MAP_FILE="${RUN_DIR}/test_case_map.json"
echo "Coverage dir: ${COV_ROOT}"
echo "Source dir: ${COVSTUB}"
# Coverage data may be absent when all test jobs were skipped
# (e.g. by the changes filter). Skip map building in that case.
if [ ! -d "${COV_ROOT}" ]; then
echo "::warning::Coverage dir not found: ${COV_ROOT}, skip building test case map"
exit 0
fi
COVERAGE_FILE_COUNT=$(find "${COV_ROOT}" -name 'coverage.*' -type f | wc -l)
echo "Found ${COVERAGE_FILE_COUNT} coverage files"
if [ "${COVERAGE_FILE_COUNT}" -eq 0 ]; then
echo "::warning::No coverage files found under ${COV_ROOT}, skip building test case map"
exit 0
fi
if ! python3 -c "import regex" 2>/dev/null; then
pip install regex \
-i http://cache-service.nginx-pypi-cache.svc.cluster.local/pypi/simple \
--trusted-host cache-service.nginx-pypi-cache.svc.cluster.local \
--retries 3 --timeout 60 \
|| pip install regex -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
fi
python3 scripts/ci/npu/precise-test/test_selector.py \
--build-map \
--coverage-dir "${COV_ROOT}" \
--source-dir "${COVSTUB}" \
--map-file "${MAP_FILE}"
test -s "${MAP_FILE}"
du -h "${MAP_FILE}"
- name: Archive outputs
run: |
RUN_DIR="/root/.cache/tests/precise-test/${{ github.run_id }}-attempt-${{ github.run_attempt }}"
tar -czf "${RUN_DIR}/outputs.tar.gz" -C "${RUN_DIR}" outputs
- name: Publish outputs and map to shared disk
shell: bash
run: |
set -euo pipefail
RUN_DIR="/root/.cache/tests/precise-test/${{ github.run_id }}-attempt-${{ github.run_attempt }}"
# Fixed publish dir with no RUN_DIR layer: other pipelines have a
# different GITHUB_RUN_ID and must be able to read the baseline.
# Must match PUBLISH_DIR in the recommend-tests-from-coverage job.
PUBLISH_DIR="/root/.cache/tests/precise-test/coverage"
# Skip publish when no coverage data was collected this run (map
# build was skipped), so we never overwrite the historical
# baseline on the shared disk with an incomplete one.
if [ ! -s "${RUN_DIR}/test_case_map.json" ]; then
echo "::warning::test_case_map.json missing (no coverage data this run), skip publish"
exit 0
fi
mkdir -p "${PUBLISH_DIR}"
# mv (rename) within the same shared filesystem is atomic, so a
# concurrent reader sees either the old or the new baseline,
# never a partial file.
mv "${RUN_DIR}/outputs.tar.gz" "${PUBLISH_DIR}/outputs.tar.gz"
mv "${RUN_DIR}/test_case_map.json" "${PUBLISH_DIR}/test_case_map.json"
echo "Published baseline to ${PUBLISH_DIR}"
du -h "${PUBLISH_DIR}/outputs.tar.gz" "${PUBLISH_DIR}/test_case_map.json"
- name: Clean up per-run directory
# Skipped on publish failure, keeping RUN_DIR for manual recovery.
shell: bash
run: |
set -euo pipefail
RUN_DIR="/root/.cache/tests/precise-test/${{ github.run_id }}-attempt-${{ github.run_attempt }}"
rm -rf "${RUN_DIR}"
echo "Removed ${RUN_DIR}"
pr-test-npu-finish: pr-test-npu-finish:
needs: needs:
[ [
@@ -369,6 +660,8 @@ jobs:
base-c-test-acc-16-npu-a3, base-c-test-acc-16-npu-a3,
base-c-test-perf-2-npu-a3, base-c-test-perf-2-npu-a3,
base-c-test-perf-16-npu-a3, base-c-test-perf-16-npu-a3,
setup-covstub,
] ]
if: always() if: always()
runs-on: ubuntu-latest runs-on: ubuntu-latest
+4 -1
View File
@@ -62,6 +62,7 @@ jobs:
- "python/sglang/test/!(ascend|observability|mock_model|manual|external_models|kernels)/**/!(*.md)" - "python/sglang/test/!(ascend|observability|mock_model|manual|external_models|kernels)/**/!(*.md)"
- "python/pyproject_xpu.toml" - "python/pyproject_xpu.toml"
- "test/registered/xpu/**/!(*.md)" - "test/registered/xpu/**/!(*.md)"
- "test/registered/disaggregation/test_disaggregation_xpu.py"
- "test/registered/attention/test_chunk_gated_delta_rule.py" - "test/registered/attention/test_chunk_gated_delta_rule.py"
- "test/registered/attention/test_deterministic.py" - "test/registered/attention/test_deterministic.py"
- "test/registered/lora/test_moe_lora_info.py" - "test/registered/lora/test_moe_lora_info.py"
@@ -251,7 +252,9 @@ jobs:
- name: Run diffusion server tests (1-GPU) - name: Run diffusion server tests (1-GPU)
timeout-minutes: 60 timeout-minutes: 60
run: | run: |
docker exec ci_sglang_xpu bash -c "source /opt/venv/bin/activate && cd /sglang-checkout/python && python3 sglang/multimodal_gen/test/run_suite.py --suite 1-gpu-xpu" # xpu_b60.json is seeded with this flag on; without it the SYCL queue
# is deep enough that step timings record host enqueue, not device time.
docker exec -e SGLANG_DIFFUSION_SYNC_STAGE_PROFILING=1 ci_sglang_xpu bash -c "source /opt/venv/bin/activate && cd /sglang-checkout/python && python3 sglang/multimodal_gen/test/run_suite.py --suite 1-gpu-xpu"
- name: Cleanup container - name: Cleanup container
if: always() if: always()
+81 -153
View File
@@ -41,17 +41,17 @@ on:
type: boolean type: boolean
default: false default: false
skip_pr_test_health_check: skip_pr_test_health_check:
description: "Skip PR test health check fast-fail (e.g. for release branch cuts)" description: "Skip PR test health check fail-fast (e.g. for release branch cuts)"
required: false required: false
type: boolean type: boolean
default: false default: false
concurrency: concurrency:
# Concurrency group structure: pr-test-{event}-{branch}-{git_ref}
# - event_name prevents scheduled runs from colliding with fork PRs whose branch is named 'main' # - event_name prevents scheduled runs from colliding with fork PRs whose branch is named 'main'
# (without it, both resolve the branch segment to 'main' and block each other) # (without it, both resolve the branch segment to 'main' and block each other)
# - github.head_ref (pull_request) or github.ref_name (workflow_dispatch) normalizes to branch name # - a PR keys on its number: github.head_ref is a bare branch name with no owner,
group: pr-test-${{ github.event_name }}-${{ github.head_ref || github.ref_name || 'default' }}-${{ inputs.git_ref || 'all' }} # so two forks using the same name would share one group and cancel each other
group: pr-test-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref_name || 'default' }}-${{ inputs.git_ref || 'all' }}
cancel-in-progress: ${{ github.event_name != 'workflow_call' }} cancel-in-progress: ${{ github.event_name != 'workflow_call' }}
env: env:
@@ -88,11 +88,6 @@ jobs:
secrets: inherit secrets: inherit
# =============================================== Wait Jobs for Sequential PR Execution ==================================================== # =============================================== Wait Jobs for Sequential PR Execution ====================================================
# These jobs poll GitHub API to wait for previous stages to complete.
# For PR runs: wait jobs run and enforce sequential execution via polling.
# For scheduled runs: wait jobs are skipped, enabling parallel execution for easier retry.
# For PRs with the `bypass-fastfail` label: wait jobs run but return success immediately
# (handled inside the wait-for-jobs action), so downstream stages dispatch in parallel.
wait-for-base-a: wait-for-base-a:
needs: [check-changes, call-gate] needs: [check-changes, call-gate]
@@ -290,7 +285,6 @@ jobs:
needs.check-changes.outputs.jit_kernel == 'true' needs.check-changes.outputs.jit_kernel == 'true'
uses: ./.github/workflows/pr-test-jit-kernel.yml uses: ./.github/workflows/pr-test-jit-kernel.yml
with: with:
runner_config: 4-gpu-b200
runs_on_map: ${{ needs.check-changes.outputs.runs_on_map }} runs_on_map: ${{ needs.check-changes.outputs.runs_on_map }}
jit_kernel: ${{ needs.check-changes.outputs.jit_kernel }} jit_kernel: ${{ needs.check-changes.outputs.jit_kernel }}
# On scheduled/parallel-dispatch runs sgl-kernel-build-wheels is skipped, so the wheel # On scheduled/parallel-dispatch runs sgl-kernel-build-wheels is skipped, so the wheel
@@ -305,18 +299,27 @@ jobs:
# =============================================== primary ==================================================== # =============================================== primary ====================================================
# Runs on 5090 (32GB, SM120) # `name:` is load-bearing: wait-for-jobs gates a stage by job-name prefix,
base-a-test-1-gpu-small: # and check-pr-test-health carves h20 out by the same name.
base-a-test:
name: base-a-test-${{ matrix.runner_config }}
needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build] needs: [check-changes, call-gate, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }} if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include:
# 5090 (32GB, SM120)
- { runner_config: 1-gpu-small, run_timeout: '10', timeout_per_file: '' }
uses: ./.github/workflows/_pr-test-stage.yml uses: ./.github/workflows/_pr-test-stage.yml
with: with:
self_name: base-a-test-1-gpu-small self_name: base-a-test-${{ matrix.runner_config }}
runner_config: 1-gpu-small runner_config: ${{ matrix.runner_config }}
check_changes: ${{ toJson(needs.check-changes.outputs) }} check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }} caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }} partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '10' run_timeout_minutes: ${{ matrix.run_timeout }}
timeout_per_file: ${{ matrix.timeout_per_file }}
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit secrets: inherit
@@ -337,62 +340,28 @@ jobs:
# No `secrets: inherit`: this stage has no secret consumer, unlike the GPU # No `secrets: inherit`: this stage has no secret consumer, unlike the GPU
# stages' coredump upload. GITHUB_TOKEN and permissions inherit regardless. # stages' coredump upload. GITHUB_TOKEN and permissions inherit regardless.
# Runs on 5090 (32GB, SM120) base-b-test:
base-b-test-1-gpu-small: name: base-b-test-${{ matrix.runner_config }}
needs: [check-changes, call-gate, wait-for-base-a, sgl-kernel-build-wheels, rust-ext-build] needs: [check-changes, call-gate, wait-for-base-a, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }} if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include:
# 5090 (32GB, SM120)
- { runner_config: 1-gpu-small, run_timeout: '30', timeout_per_file: '' }
- { runner_config: 1-gpu-large, run_timeout: '60', timeout_per_file: '1800' }
- { runner_config: 2-gpu-large, run_timeout: '30', timeout_per_file: '' }
- { runner_config: 4-gpu-b200, run_timeout: '40', timeout_per_file: '' }
uses: ./.github/workflows/_pr-test-stage.yml uses: ./.github/workflows/_pr-test-stage.yml
with: with:
self_name: base-b-test-1-gpu-small self_name: base-b-test-${{ matrix.runner_config }}
runner_config: 1-gpu-small runner_config: ${{ matrix.runner_config }}
check_changes: ${{ toJson(needs.check-changes.outputs) }} check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }} caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }} partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30' run_timeout_minutes: ${{ matrix.run_timeout }}
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} timeout_per_file: ${{ matrix.timeout_per_file }}
secrets: inherit
# Runs on H100 (80GB, SM90) - tests that don't pass on 5090 (FA3, FP8, high VRAM, etc.)
base-b-test-1-gpu-large:
needs: [check-changes, call-gate, wait-for-base-a, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-b-test-1-gpu-large
runner_config: 1-gpu-large
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
timeout_per_file: '1800'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
base-b-test-2-gpu-large:
needs: [check-changes, call-gate, wait-for-base-a, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-b-test-2-gpu-large
runner_config: 2-gpu-large
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
base-b-test-4-gpu-b200:
needs: [check-changes, call-gate, wait-for-base-a, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-b-test-4-gpu-b200
runner_config: 4-gpu-b200
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '40'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit secrets: inherit
@@ -416,104 +385,70 @@ jobs:
skip_pr_test_health_check: ${{ (inputs.skip_pr_test_health_check == true || inputs.test_parallel_dispatch == true || inputs.run_all_tests == true) && 'true' || 'false' }} skip_pr_test_health_check: ${{ (inputs.skip_pr_test_health_check == true || inputs.test_parallel_dispatch == true || inputs.run_all_tests == true) && 'true' || 'false' }}
secrets: inherit secrets: inherit
base-c-test-4-gpu-h100: base-c-test:
name: base-c-test-${{ matrix.runner_config }}
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build] needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }} if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include:
- {
runner_config: 4-gpu-h100,
run_timeout: '30',
timeout_per_file: '',
warmup_deep_gemm_models: 'lmsys/sglang-ci-dsv3-test:4',
warmup_server_models: 'lmsys/sglang-ci-dsv3-test:4',
}
- {
runner_config: 8-gpu-h200,
run_timeout: '30',
timeout_per_file: '1800',
warmup_deep_gemm_models: 'deepseek-ai/DeepSeek-V3-0324:8 deepseek-ai/DeepSeek-V3.2:8 zai-org/GLM-5-FP8:8 XiaomiMiMo/MiMo-V2-Flash:4 XiaomiMiMo/MiMo-V2.5:8',
warmup_timeout: '60',
}
- { runner_config: 8-gpu-h20, run_timeout: '30', timeout_per_file: '' }
- { runner_config: 4-gpu-b200, run_timeout: '30', timeout_per_file: '1800' }
- { runner_config: 8-gpu-b300, run_timeout: '60', timeout_per_file: '3600' }
uses: ./.github/workflows/_pr-test-stage.yml uses: ./.github/workflows/_pr-test-stage.yml
with: with:
self_name: base-c-test-4-gpu-h100 self_name: base-c-test-${{ matrix.runner_config }}
runner_config: 4-gpu-h100 runner_config: ${{ matrix.runner_config }}
check_changes: ${{ toJson(needs.check-changes.outputs) }} check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }} caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }} partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30' run_timeout_minutes: ${{ matrix.run_timeout }}
warmup_deep_gemm_models: 'lmsys/sglang-ci-dsv3-test:4' timeout_per_file: ${{ matrix.timeout_per_file }}
warmup_server_models: 'lmsys/sglang-ci-dsv3-test:4' warmup_deep_gemm_models: ${{ matrix.warmup_deep_gemm_models }}
warmup_server_models: ${{ matrix.warmup_server_models }}
# Rows that do not set it keep _pr-test-stage.yml's own default.
warmup_timeout_minutes: ${{ matrix.warmup_timeout || '25' }}
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit secrets: inherit
base-c-test-8-gpu-h200: # Separate only because `needs` cannot vary per matrix row: this stage takes
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build] # the aarch64 build, and neither table should wait on the other's.
if: ${{ !failure() && !cancelled() }} base-c-test-aarch64:
uses: ./.github/workflows/_pr-test-stage.yml name: base-c-test-${{ matrix.runner_config }}
with:
self_name: base-c-test-8-gpu-h200
runner_config: 8-gpu-h200
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30'
timeout_per_file: '1800'
# Per-model TP must match the test's launch in test/registered/ -- see
# FALLBACK_ARGS in scripts/ci/cuda/warmup_deep_gemm.py for extra dp/ep
# flags. Only models that actually invoke DeepGEMM kernels at runtime
# are listed. Cold-cache ~13 min; warm-cache <=30 s via marker file.
# Server CUDA Graph warmup is combined into this step (warmup_server_models unset).
warmup_deep_gemm_models: 'deepseek-ai/DeepSeek-V3-0324:8 deepseek-ai/DeepSeek-V3.2:8 zai-org/GLM-5-FP8:8 XiaomiMiMo/MiMo-V2-Flash:4 XiaomiMiMo/MiMo-V2.5:8'
warmup_timeout_minutes: '60'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
base-c-test-8-gpu-h20:
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-c-test-8-gpu-h20
runner_config: 8-gpu-h20
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
base-c-test-4-gpu-b200:
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-c-test-4-gpu-b200
runner_config: 4-gpu-b200
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30'
timeout_per_file: '1800'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
base-c-test-4-gpu-gb300:
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build-aarch64] needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build-aarch64]
if: ${{ !failure() && !cancelled() }} if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include:
- { runner_config: 4-gpu-gb300, run_timeout: '30', timeout_per_file: '1800' }
uses: ./.github/workflows/_pr-test-stage.yml uses: ./.github/workflows/_pr-test-stage.yml
with: with:
self_name: base-c-test-4-gpu-gb300 self_name: base-c-test-${{ matrix.runner_config }}
runner_config: 4-gpu-gb300 runner_config: ${{ matrix.runner_config }}
check_changes: ${{ toJson(needs.check-changes.outputs) }} check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }} caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }} partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '30' run_timeout_minutes: ${{ matrix.run_timeout }}
timeout_per_file: '1800' timeout_per_file: ${{ matrix.timeout_per_file }}
# The one aarch64 stage, so it takes the aarch64 build, not rust-ext-build's.
rust_ext_artifact: ${{ needs.rust-ext-build-aarch64.outputs.artifact_name }} rust_ext_artifact: ${{ needs.rust-ext-build-aarch64.outputs.artifact_name }}
secrets: inherit secrets: inherit
base-c-test-8-gpu-b300:
needs: [check-changes, call-gate, wait-for-base-b, sgl-kernel-build-wheels, rust-ext-build]
if: ${{ !failure() && !cancelled() }}
uses: ./.github/workflows/_pr-test-stage.yml
with:
self_name: base-c-test-8-gpu-b300
runner_config: 8-gpu-b300
check_changes: ${{ toJson(needs.check-changes.outputs) }}
caller_inputs: ${{ toJson(inputs) }}
partitions: ${{ needs.check-changes.outputs.partitions }}
run_timeout_minutes: '60'
timeout_per_file: '3600'
rust_ext_artifact: ${{ needs.rust-ext-build.outputs.artifact_name }}
secrets: inherit
# List every build and test job: `skipped` passes here, so an omission turns that # List every build and test job: `skipped` passes here, so an omission turns that
# job's failure into a green run with no tests. # job's failure into a green run with no tests.
pr-test-finish: pr-test-finish:
@@ -536,18 +471,11 @@ jobs:
call-multimodal-gen-tests, call-multimodal-gen-tests,
base-a-test-1-gpu-small, base-a-test,
base-a-test-cpu, base-a-test-cpu,
base-b-test-1-gpu-small, base-b-test,
base-b-test-1-gpu-large, base-c-test,
base-b-test-2-gpu-large, base-c-test-aarch64,
base-b-test-4-gpu-b200,
base-c-test-4-gpu-h100,
base-c-test-8-gpu-h20,
base-c-test-8-gpu-h200,
base-c-test-4-gpu-b200,
base-c-test-4-gpu-gb300,
base-c-test-8-gpu-b300,
] ]
if: always() if: always()
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1 -1
View File
@@ -62,7 +62,7 @@ runtime_common = [
"transformers==5.12.1", "transformers==5.12.1",
"uvicorn", "uvicorn",
"uvloop", "uvloop",
"xgrammar==0.2.1", "xgrammar==0.2.7",
"smg-grpc-servicer>=0.9.0", "smg-grpc-servicer>=0.9.0",
] ]
@@ -0,0 +1,113 @@
"""Measure PD TTFT with a shared cached prefix and unique request suffixes.
Start a PD router and workers before running this client. Both worker caches are
flushed after warmup; the shared prefix is then warmed on prefill. Decode radix
caching should be disabled to exercise transfer of the entire missing KV range.
Saves per-request responses and client-observed TTFT to the requested JSON file.
"""
import argparse
import asyncio
import json
import random
import time
from pathlib import Path
import aiohttp
import numpy as np
async def main(args):
rng = random.Random(35762)
prefix = [rng.randrange(1000, 30000) for _ in range(args.prefix)]
prompts = [
prefix + [rng.randrange(1000, 30000) for _ in range(args.unique)]
for _ in range(args.requests)
]
timeout = aiohttp.ClientTimeout(total=1800)
async with aiohttp.ClientSession(timeout=timeout) as session:
async def generate(ids, stream=True):
start = time.perf_counter()
first = None
result = None
body = {
"input_ids": ids,
"sampling_params": {
"temperature": 0,
"max_new_tokens": args.output,
"ignore_eos": True,
},
"stream": stream,
}
async with session.post(args.url + "/generate", json=body) as response:
response.raise_for_status()
if stream:
async for line in response.content:
if not line.startswith(b"data:"):
continue
payload = line[5:].strip()
if payload == b"[DONE]":
continue
result = json.loads(payload)
if first is None:
first = time.perf_counter() - start
else:
result = await response.json()
elapsed = time.perf_counter() - start
if result is None or "error" in result:
raise RuntimeError(result)
return {"ttft_s": first, "elapsed_s": elapsed, "response": result}
# Warm both cold-prefill and cached-prefix/concurrent shapes. Clear both
# sides afterwards so measured requests never reuse a unique suffix.
await generate(prompts[0])
await asyncio.gather(*(generate(p) for p in prompts[1 : args.concurrency + 1]))
for url in args.workers:
async with session.post(url + "/flush_cache", params={"timeout": 30}) as r:
r.raise_for_status()
if prefix:
await generate(prefix)
sem = asyncio.Semaphore(args.concurrency)
async def run(ids):
async with sem:
return await generate(ids)
start = time.perf_counter()
records = await asyncio.gather(*(run(ids) for ids in prompts))
elapsed = time.perf_counter() - start
ttfts = [r["ttft_s"] for r in records]
summary = {
"args": vars(args),
"elapsed_s": elapsed,
"requests_per_s": len(records) / elapsed,
"ttft_mean_ms": float(np.mean(ttfts) * 1000),
"ttft_p50_ms": float(np.percentile(ttfts, 50) * 1000),
"ttft_p99_ms": float(np.percentile(ttfts, 99) * 1000),
"records": records,
}
Path(args.result).write_text(json.dumps(summary, indent=2))
print(json.dumps({k: v for k, v in summary.items() if k != "records"}), flush=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--url", default="http://127.0.0.1:30000")
parser.add_argument(
"--workers",
nargs="+",
default=["http://127.0.0.1:30001", "http://127.0.0.1:30002"],
)
parser.add_argument("--prefix", type=int, default=83264)
parser.add_argument("--unique", type=int, default=6720)
parser.add_argument("--output", type=int, default=1)
parser.add_argument("--requests", type=int, default=16)
parser.add_argument("--concurrency", type=int, default=1)
parser.add_argument("--result", required=True)
args = parser.parse_args()
if args.prefix < 0 or args.unique <= 0:
parser.error("--prefix must be nonnegative and --unique must be positive")
if min(args.output, args.requests, args.concurrency) <= 0:
parser.error("--output, --requests and --concurrency must be positive")
asyncio.run(main(args))
+4 -5
View File
@@ -5,10 +5,10 @@ import torch
from tqdm import tqdm from tqdm import tqdm
from sglang.srt.distributed import ( from sglang.srt.distributed import (
get_world_group,
init_distributed_environment, init_distributed_environment,
initialize_model_parallel, initialize_model_parallel,
) )
from sglang.srt.distributed.parallel_state import get_world_group
from sglang.srt.managers.cache_controller import ( from sglang.srt.managers.cache_controller import (
HiCacheController, HiCacheController,
PrefetchOperation, PrefetchOperation,
@@ -17,6 +17,7 @@ from sglang.srt.managers.cache_controller import (
from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator from sglang.srt.mem_cache.allocator import TokenToKVPoolAllocator
from sglang.srt.mem_cache.memory_pool import MHATokenToKVPool from sglang.srt.mem_cache.memory_pool import MHATokenToKVPool
from sglang.srt.mem_cache.pool_host.mha import MHATokenToKVPoolHost from sglang.srt.mem_cache.pool_host.mha import MHATokenToKVPoolHost
from sglang.test.test_utils import publish_build_topology
init_distributed_environment( init_distributed_environment(
world_size=1, world_size=1,
@@ -26,10 +27,8 @@ init_distributed_environment(
backend="gloo", backend="gloo",
) )
initialize_model_parallel( publish_build_topology()
tensor_model_parallel_size=1, initialize_model_parallel()
pipeline_model_parallel_size=1,
)
group = get_world_group().cpu_group group = get_world_group().cpu_group
@@ -21,6 +21,7 @@ from sglang.srt.distributed.parallel_state import (
init_distributed_environment, init_distributed_environment,
initialize_model_parallel, initialize_model_parallel,
) )
from sglang.test.test_utils import publish_build_topology
def parse_args(): def parse_args():
@@ -85,7 +86,8 @@ def init_dist(backend: str):
distributed_init_method=distributed_init_method, distributed_init_method=distributed_init_method,
local_rank=rank, local_rank=rank,
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
return dist.group.WORLD return dist.group.WORLD
@@ -41,6 +41,7 @@ from sglang.srt.distributed.parallel_state import (
initialize_model_parallel, initialize_model_parallel,
set_custom_all_reduce, set_custom_all_reduce,
) )
from sglang.test.test_utils import publish_build_topology
Shape = Tuple[int, int] Shape = Tuple[int, int]
@@ -381,7 +382,8 @@ def main():
distributed_init_method="env://", distributed_init_method="env://",
backend="nccl", backend="nccl",
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
prefill_shapes = parse_shapes(args.prefill_shapes) prefill_shapes = parse_shapes(args.prefill_shapes)
decode_shapes = parse_shapes(args.decode_shapes) decode_shapes = parse_shapes(args.decode_shapes)
@@ -47,6 +47,7 @@ from sglang.srt.distributed.parallel_state import (
initialize_model_parallel, initialize_model_parallel,
set_custom_all_reduce, set_custom_all_reduce,
) )
from sglang.test.test_utils import publish_build_topology
Shape = Tuple[int, int] Shape = Tuple[int, int]
FP8_DTYPE = torch.float8_e4m3fnuz FP8_DTYPE = torch.float8_e4m3fnuz
@@ -400,7 +401,8 @@ def main() -> None:
distributed_init_method="env://", distributed_init_method="env://",
backend="nccl", backend="nccl",
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
if rank == 0: if rank == 0:
print( print(
@@ -30,6 +30,7 @@ from sglang.srt.distributed.parallel_state import (
initialize_model_parallel, initialize_model_parallel,
set_mscclpp_all_reduce, set_mscclpp_all_reduce,
) )
from sglang.test.test_utils import publish_build_topology
def torch_allreduce(torch_input: torch.Tensor, group: ProcessGroup) -> torch.Tensor: def torch_allreduce(torch_input: torch.Tensor, group: ProcessGroup) -> torch.Tensor:
@@ -173,7 +174,8 @@ if __name__ == "__main__":
rank=rank, rank=rank,
local_rank=rank % 8, local_rank=rank % 8,
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
group = get_tensor_model_parallel_group().device_group group = get_tensor_model_parallel_group().device_group
cpu_group = get_tensor_model_parallel_group().cpu_group cpu_group = get_tensor_model_parallel_group().cpu_group
pynccl_comm = get_tensor_model_parallel_group().pynccl_comm pynccl_comm = get_tensor_model_parallel_group().pynccl_comm
@@ -44,6 +44,7 @@ from sglang.srt.distributed.parallel_state import (
initialize_model_parallel, initialize_model_parallel,
set_torch_symm_mem_all_reduce, set_torch_symm_mem_all_reduce,
) )
from sglang.test.test_utils import publish_build_topology
from sglang.utils import is_in_ci from sglang.utils import is_in_ci
IS_CI = is_in_ci() IS_CI = is_in_ci()
@@ -188,7 +189,8 @@ if __name__ == "__main__":
rank=rank, rank=rank,
local_rank=rank % 8, local_rank=rank % 8,
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
group = get_tensor_model_parallel_group().device_group group = get_tensor_model_parallel_group().device_group
cpu_group = get_tensor_model_parallel_group().cpu_group cpu_group = get_tensor_model_parallel_group().cpu_group
pynccl_comm = get_tensor_model_parallel_group().pynccl_comm pynccl_comm = get_tensor_model_parallel_group().pynccl_comm
@@ -30,14 +30,16 @@ import torch.distributed as dist # type: ignore
from sglang.kernels.ops.quantization.fp8_kernel import fp8_dtype as SGLANG_FP8_DTYPE from sglang.kernels.ops.quantization.fp8_kernel import fp8_dtype as SGLANG_FP8_DTYPE
from sglang.kernels.ops.quantization.fp8_kernel import static_quant_fp8 from sglang.kernels.ops.quantization.fp8_kernel import static_quant_fp8
from sglang.srt.distributed import get_tp_group, tensor_model_parallel_all_reduce from sglang.srt.distributed import tensor_model_parallel_all_reduce
from sglang.srt.distributed.parallel_state import ( from sglang.srt.distributed.parallel_state import (
cleanup_dist_env_and_memory, cleanup_dist_env_and_memory,
get_tp_group,
graph_capture, graph_capture,
init_distributed_environment, init_distributed_environment,
initialize_model_parallel, initialize_model_parallel,
) )
from sglang.srt.layers.layernorm import RMSNorm # noqa from sglang.srt.layers.layernorm import RMSNorm # noqa
from sglang.test.test_utils import publish_build_topology
try: try:
from sgl_kernel import fused_add_rmsnorm as SGL_FUSED_ADD_RMS_NORM from sgl_kernel import fused_add_rmsnorm as SGL_FUSED_ADD_RMS_NORM
@@ -1178,7 +1180,8 @@ def main():
local_rank=rank, local_rank=rank,
backend="nccl", backend="nccl",
) )
initialize_model_parallel(tensor_model_parallel_size=world_size) publish_build_topology(world_rank=rank, tp_size=world_size)
initialize_model_parallel()
# Validate world size (must be > 1 for collective operations) # Validate world size (must be > 1 for collective operations)
if world_size <= 1: if world_size <= 1:
@@ -26,6 +26,7 @@ from sglang.srt.layers.moe.topk import (
select_experts, select_experts,
) )
from sglang.srt.server_args import ServerArgs, set_global_server_args_for_scheduler from sglang.srt.server_args import ServerArgs, set_global_server_args_for_scheduler
from sglang.test.test_utils import publish_build_topology
def fused_moe_triton_api( def fused_moe_triton_api(
@@ -227,10 +228,8 @@ def main():
backend="nccl" if torch.cuda.is_available() else "gloo", backend="nccl" if torch.cuda.is_available() else "gloo",
) )
initialize_model_parallel( publish_build_topology()
tensor_model_parallel_size=1, initialize_model_parallel()
expert_model_parallel_size=1,
)
model_config = get_model_config(args.model, args.tp_size, args.ep_size) model_config = get_model_config(args.model, args.tp_size, args.ep_size)
benchmark.run( benchmark.run(
@@ -15,6 +15,7 @@ from sglang.srt.distributed.parallel_state import (
from sglang.srt.layers.moe.moe_runner.triton_utils.fused_moe import ( from sglang.srt.layers.moe.moe_runner.triton_utils.fused_moe import (
fused_moe as fused_moe_sglang, fused_moe as fused_moe_sglang,
) )
from sglang.test.test_utils import publish_build_topology
from .common_utils import get_model_config from .common_utils import get_model_config
@@ -243,10 +244,8 @@ def main():
backend="nccl" if torch.cuda.is_available() else "gloo", backend="nccl" if torch.cuda.is_available() else "gloo",
) )
initialize_model_parallel( publish_build_topology()
tensor_model_parallel_size=1, initialize_model_parallel()
pipeline_model_parallel_size=1,
)
shape_configs = get_model_config(args.model, args.tp_size, args.ep_size) shape_configs = get_model_config(args.model, args.tp_size, args.ep_size)
benchmark.run( benchmark.run(
+89
View File
@@ -0,0 +1,89 @@
# syntax=docker/dockerfile:1
# Keep the compiler aligned with rust/rust-toolchain.toml. Pin image indexes
# rather than individual architecture manifests so both platforms use this file.
FROM rust:1.92.0-slim-bookworm@sha256:f1f73538ebe623fd3673a35aff3df358ae1084c64c55646516e5b17b321b6c9b AS build
ARG TARGETARCH
ARG CARGO_BUILD_JOBS=4
ENV RUSTUP_TOOLCHAIN=1.92.0 \
CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS} \
PCRE2_SYS_STATIC=1
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential pkg-config \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
COPY rust/Cargo.toml rust/Cargo.lock rust/rust-toolchain.toml rust/
# Cargo loads every workspace member even when building only the renderer.
COPY rust/sglang-grpc/Cargo.toml rust/sglang-grpc/
COPY rust/sglang-grpc/src/ rust/sglang-grpc/src/
COPY rust/sglang-mm/Cargo.toml rust/sglang-mm/
COPY rust/sglang-mm/src/ rust/sglang-mm/src/
COPY rust/sglang-server/Cargo.toml rust/sglang-server/
COPY rust/sglang-server/src/ rust/sglang-server/src/
COPY rust/sglang-renderer/Cargo.toml rust/sglang-renderer/
COPY rust/sglang-renderer/src/ rust/sglang-renderer/src/
# Avoid rustup downloading development components from rust-toolchain.toml,
# but fail if the image's compiler and the workspace toolchain drift apart.
RUN channel=$(sed -n 's/^channel = "\([^"]*\)"/\1/p' rust/rust-toolchain.toml) \
&& case "${RUSTUP_TOOLCHAIN}" in "$channel"|"$channel".*) ;; *) exit 1 ;; esac
RUN --mount=type=cache,id=renderer-registry-${TARGETARCH},target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=renderer-git-${TARGETARCH},target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=renderer-target-${TARGETARCH},target=/build/rust/target,sharing=locked \
cargo build --manifest-path rust/Cargo.toml -p sglang-renderer \
--bin sglang-renderer --release --features http --locked \
&& install -D rust/target/release/sglang-renderer /out/sglang-renderer
# Run the existing unit suite in the same Linux toolchain used for the image.
# This sibling stage is selected by CI and is not a dependency of the runtime.
FROM build AS test
COPY rust/sglang-renderer/tests/ rust/sglang-renderer/tests/
COPY experimental/sgl-router/tests/fixtures/tiny_tokenizer.json experimental/sgl-router/tests/fixtures/tiny_tokenizer.json
RUN --mount=type=cache,id=renderer-registry-${TARGETARCH},target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=renderer-git-${TARGETARCH},target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=renderer-target-${TARGETARCH},target=/build/rust/target,sharing=locked \
cargo test --manifest-path rust/Cargo.toml -p sglang-renderer --features http --locked
FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates libgcc-s1 \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --gid 65532 sglang \
&& useradd --uid 65532 --gid 65532 --no-log-init --create-home \
--home-dir /home/sglang --shell /usr/sbin/nologin sglang \
&& mkdir -p /home/sglang/.cache/huggingface \
&& chown -R 65532:65532 /home/sglang
COPY --from=build /out/sglang-renderer /usr/local/bin/sglang-renderer
COPY LICENSE /usr/share/licenses/sglang-renderer/LICENSE
# Metadata changes must not invalidate compilation.
ARG SGLANG_BUILD_COMMIT=unknown
ARG SGLANG_BUILD_URL=
ARG SGLANG_IMAGE_TAG=local/sglang-renderer:dev
ENV HOME=/home/sglang \
HF_HOME=/home/sglang/.cache/huggingface \
SGLANG_BUILD_COMMIT=${SGLANG_BUILD_COMMIT} \
SGLANG_BUILD_URL=${SGLANG_BUILD_URL} \
SGLANG_IMAGE_TAG=${SGLANG_IMAGE_TAG}
LABEL org.opencontainers.image.source="https://github.com/sgl-project/sglang" \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.revision="${SGLANG_BUILD_COMMIT}" \
org.opencontainers.image.version="${SGLANG_IMAGE_TAG}" \
org.opencontainers.image.url="${SGLANG_BUILD_URL}" \
ai.sglang.build.commit="${SGLANG_BUILD_COMMIT}" \
ai.sglang.build.url="${SGLANG_BUILD_URL}" \
ai.sglang.image.tag="${SGLANG_IMAGE_TAG}"
USER 65532:65532
WORKDIR /home/sglang
EXPOSE 30000
# The renderer's existing graceful shutdown handler listens for Ctrl-C.
STOPSIGNAL SIGINT
ENTRYPOINT ["/usr/local/bin/sglang-renderer"]
CMD ["--help"]
+8 -8
View File
@@ -61,7 +61,7 @@ ENV BUILD_TRITON="0"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
# =============================== # ===============================
# Base image 942 with rocm720 and args # Base image 942 with rocm720 and args
@@ -71,7 +71,7 @@ ENV BUILD_TRITON="1"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
ENV TRITON_COMMIT_DEFAULT="42270451990532c67e69d753fbd026f28fcc4840" ENV TRITON_COMMIT_DEFAULT="42270451990532c67e69d753fbd026f28fcc4840"
# =============================== # ===============================
@@ -82,7 +82,7 @@ ENV BUILD_TRITON="1"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
# Pin the ROCm torch stack for every pip invocation in this flavor. The file is # Pin the ROCm torch stack for every pip invocation in this flavor. The file is
# filled in after the torch 2.11 upgrade below; it must already exist (empty is # filled in after the torch 2.11 upgrade below; it must already exist (empty is
# valid) because pip reads PIP_CONSTRAINT from the first pip call onwards. # valid) because pip reads PIP_CONSTRAINT from the first pip call onwards.
@@ -106,7 +106,7 @@ ENV BUILD_TRITON="0"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
# =============================== # ===============================
# Base image 950 with rocm720 and args # Base image 950 with rocm720 and args
@@ -116,7 +116,7 @@ ENV BUILD_TRITON="1"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
ENV TRITON_COMMIT_DEFAULT="42270451990532c67e69d753fbd026f28fcc4840" ENV TRITON_COMMIT_DEFAULT="42270451990532c67e69d753fbd026f28fcc4840"
# =============================== # ===============================
@@ -127,7 +127,7 @@ ENV BUILD_TRITON="1"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
# Pin the ROCm torch stack for every pip invocation in this flavor. The file is # Pin the ROCm torch stack for every pip invocation in this flavor. The file is
# filled in after the torch 2.11 upgrade below; it must already exist (empty is # filled in after the torch 2.11 upgrade below; it must already exist (empty is
# valid) because pip reads PIP_CONSTRAINT from the first pip call onwards. # valid) because pip reads PIP_CONSTRAINT from the first pip call onwards.
@@ -286,7 +286,7 @@ ENV BUILD_TRITON="0"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
# Same reasoning as the rocm724 stages: keep pip from resolving the image's # Same reasoning as the rocm724 stages: keep pip from resolving the image's
# ROCm torch away to a PyPI CUDA build. Populated after the stack is in place. # ROCm torch away to a PyPI CUDA build. Populated after the stack is in place.
ENV PIP_CONSTRAINT="/etc/sglang/constraints/torch-rocm.txt" ENV PIP_CONSTRAINT="/etc/sglang/constraints/torch-rocm.txt"
@@ -300,7 +300,7 @@ ENV BUILD_TRITON="0"
ENV BUILD_LLVM="0" ENV BUILD_LLVM="0"
ENV BUILD_AITER_ALL="1" ENV BUILD_AITER_ALL="1"
ENV BUILD_MOONCAKE="1" ENV BUILD_MOONCAKE="1"
ENV AITER_COMMIT_DEFAULT="4ad99832823dde2315b361cbd3b54b1c5c12acd5" ENV AITER_COMMIT_DEFAULT="acf8fdf9307431ece8ee275971c41cb3d1a7020b"
ENV PIP_CONSTRAINT="/etc/sglang/constraints/torch-rocm.txt" ENV PIP_CONSTRAINT="/etc/sglang/constraints/torch-rocm.txt"
RUN mkdir -p /etc/sglang/constraints && : > /etc/sglang/constraints/torch-rocm.txt RUN mkdir -p /etc/sglang/constraints && : > /etc/sglang/constraints/torch-rocm.txt
@@ -30,15 +30,15 @@ For more details, please refer to the [official DeepSeek-OCR-2 repository](https
Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions. Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions.
For SGLang CPU installation, please refer to the [CPU version installation guide](../../../docs/hardware-platforms/cpu_server#installation).
## 3. Model Deployment ## 3. Model Deployment
This section provides deployment configurations optimized for different hardware platforms and use cases. This section provides deployment configurations optimized for different hardware platforms and use cases.
### 3.1 Basic Configuration ### 3.1 Basic Configuration
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform, quantization method, and deployment strategy. SGLang supports serving DeepSeek-OCR-2 on NVIDIA H200 and B200, AMD MI300X, MI355X, and MI325X GPUs, as well as Intel Xeon CPUs. The DeepSeek-OCR-2 series offers models in various sizes and architectures, optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)), and Intel Xeon CPUs. The recommended launch configurations vary by hardware and model size.
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform, quantization method, and deployment strategy. SGLang supports serving DeepSeek-OCR-2 on NVIDIA H200 and B200, AMD MI300X, MI355X, and MI325X GPUs, and Intel Arc Pro B-Series GPUs, as well as Intel Xeon CPUs.
<DeepSeekOCR2Deployment /> <DeepSeekOCR2Deployment />
@@ -692,7 +692,7 @@ Larger blocks can improve decode latency when acceptance stays high, but they al
For every candidate, compare with the same recipe without `--speculative-algorithm DSPARK`. Restart the server between the DSpark and non-speculative legs, keep the request corpus, sampling, concurrency, and warmup identical, and give each `bench_serving` leg its own `--flush-cache`. Leave `--speculative-draft-attention-backend` unset unless a separate profiling run justifies an override. For every candidate, compare with the same recipe without `--speculative-algorithm DSPARK`. Restart the server between the DSpark and non-speculative legs, keep the request corpus, sampling, concurrency, and warmup identical, and give each `bench_serving` leg its own `--flush-cache`. Leave `--speculative-draft-attention-backend` unset unless a separate profiling run justifies an override.
DSpark requires `pp_size == 1`. It is not compatible with PD disaggregation on current SGLang releases; selecting a prefill or decode role in the Playground automatically removes the inherited DSpark flags. The MI355X Flash Official recipes therefore run target-only, and so do the DP-Attention recipes in the Deploy panel — for DP-Attention configurations that do run DSpark, see the [B200 agentic recipe](#3-6-agentic-long-context-with-hicache-dram-offload-b200-fp4-dspark) and the [MI355X agentic recipe](#3-7-agentic-long-context-with-hicache-dram-offload-mi355x-fp4-dspark). If a larger draft block or concurrency causes graph-capture OOM, lower `--mem-fraction-static`, the draft block size, or the configured maximum running requests, then rerun both performance and accuracy gates. DSpark requires `pp_size == 1`. Selecting a prefill or decode role in the Playground removes the inherited DSpark flags; for a PD-disaggregated configuration that runs DSpark on both roles, see the [B200 agentic recipe](#3-6-agentic-long-context-with-hicache-dram-offload-b200-fp4-dspark). The MI355X Flash Official recipes run target-only, and so do the DP-Attention recipes in the Deploy panel — for DP-Attention configurations that do run DSpark, see the [B200 agentic recipe](#3-6-agentic-long-context-with-hicache-dram-offload-b200-fp4-dspark) and the [MI355X agentic recipe](#3-7-agentic-long-context-with-hicache-dram-offload-mi355x-fp4-dspark). If a larger draft block or concurrency causes graph-capture OOM, lower `--mem-fraction-static`, the draft block size, or the configured maximum running requests, then rerun both performance and accuracy gates.
### 3.5 Vision (Image Inputs) ### 3.5 Vision (Image Inputs)
@@ -737,7 +737,7 @@ Pending update...
### 3.6 Agentic Long-Context with HiCache DRAM Offload (B200 FP4, DSpark) ### 3.6 Agentic Long-Context with HiCache DRAM Offload (B200 FP4, DSpark)
**TP8, concurrency 816:** **TP8, concurrency 18:**
```bash Command ```bash Command
SGLANG_ENABLE_UNIFIED_RADIX_TREE=1 \ SGLANG_ENABLE_UNIFIED_RADIX_TREE=1 \
python3 -m sglang.launch_server \ python3 -m sglang.launch_server \
@@ -761,7 +761,7 @@ python3 -m sglang.launch_server \
--hicache-mem-layout page_first_direct --hicache-mem-layout page_first_direct
``` ```
DSv4 HiCache sizes the host tier with `--hicache-ratio` (host/device token ratio), not `--hicache-size`. Concurrency 15 runs the same command without the HiCache flags. DSv4 HiCache sizes the host tier with `--hicache-ratio` (host/device token ratio), not `--hicache-size`.
**DEP8 (DP Attention), concurrency 64160:** **DEP8 (DP Attention), concurrency 64160:**
```bash Command ```bash Command
@@ -796,6 +796,87 @@ python3 -m sglang.launch_server \
`--chunked-prefill-size` is a global budget divided by `--dp`, so this keeps 6144 tokens per rank. `--chunked-prefill-size` is a global budget divided by `--dp`, so this keeps 6144 tokens per rank.
**Disaggregated 1P1D (DEP8 prefill / DEP8 decode), concurrency 64128.** Prefill, on one 8-GPU node:
```bash Command
SGLANG_DSV4_MHC_PREWARM=1 \
SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK=9216 \
NCCL_MNNVL_ENABLE=1 \
NCCL_CUMEM_ENABLE=1 \
python3 -m sglang.launch_server \
--model-path deepseek-ai/DeepSeek-V4-Pro-0813 \
--trust-remote-code \
--tp 8 \
--dp 8 \
--enable-dp-attention \
--enable-dp-lm-head \
--ep-size 8 \
--moe-dense-tp-size 1 \
--moe-a2a-backend megamoe \
--enable-w4a4-mxfp4-megamoe \
--enable-deepseek-v4-fp4-indexer \
--disable-flashinfer-autotune \
--mem-fraction-static 0.85 \
--page-size 256 \
--swa-full-tokens-ratio 0.01 \
--chunked-prefill-size 65536 \
--tool-call-parser deepseekv4 \
--reasoning-parser deepseek-v4 \
--speculative-algorithm DSPARK \
--speculative-dspark-block-size 6 \
--enable-hierarchical-cache \
--hicache-ratio 8 \
--hicache-write-policy write_back \
--hicache-io-backend direct \
--hicache-mem-layout page_first_direct \
--disaggregation-mode prefill \
--disaggregation-transfer-backend mooncake \
--disaggregation-ib-device mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_10,mlx5_11
```
Decode, on a second 8-GPU node:
```bash Command
SGLANG_DSV4_MHC_PREWARM=1 \
SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK=4096 \
NCCL_MNNVL_ENABLE=1 \
NCCL_CUMEM_ENABLE=1 \
python3 -m sglang.launch_server \
--model-path deepseek-ai/DeepSeek-V4-Pro-0813 \
--trust-remote-code \
--tp 8 \
--dp 8 \
--enable-dp-attention \
--enable-dp-lm-head \
--ep-size 8 \
--moe-dense-tp-size 1 \
--moe-a2a-backend megamoe \
--enable-w4a4-mxfp4-megamoe \
--enable-deepseek-v4-fp4-indexer \
--disable-flashinfer-autotune \
--mem-fraction-static 0.90 \
--page-size 256 \
--swa-full-tokens-ratio 0.02 \
--tool-call-parser deepseekv4 \
--reasoning-parser deepseek-v4 \
--speculative-algorithm DSPARK \
--speculative-dspark-block-size 6 \
--disaggregation-mode decode \
--disaggregation-transfer-backend mooncake \
--disaggregation-ib-device mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_10,mlx5_11
```
Router:
```bash Command
python3 -m sglang_router.launch_router \
--pd-disaggregation \
--prefill http://<prefill-host>:30000 8998 \
--decode http://<decode-host>:30000 \
--host 0.0.0.0 --port 8000
```
HiCache runs on the prefill role only, with `write_back` rather than the aggregate recipe's `write_through`. Set `--disaggregation-ib-device` to the node's own external HCAs.
Concurrency 256 uses 2P1D — two prefill workers, one node each, added with a second `--prefill` on the router — with `--swa-full-tokens-ratio 0.02` on prefill and `--mem-fraction-static 0.91 --swa-full-tokens-ratio 0.005` on decode.
### 3.7 Agentic Long-Context with HiCache DRAM Offload (MI355X FP4, DSpark) ### 3.7 Agentic Long-Context with HiCache DRAM Offload (MI355X FP4, DSpark)
DeepSeek-V4-Pro-0813 bundles the DSpark head, so `--speculative-draft-model-path` is not needed. `--speculative-dspark-block-size 6` is the AL-optimal draft length on the committed golden curve (verify window 7). DeepSeek-V4-Pro-0813 bundles the DSpark head, so `--speculative-draft-model-path` is not needed. `--speculative-dspark-block-size 6` is the AL-optimal draft length on the committed golden curve (verify window 7).
+1 -1
View File
@@ -124,7 +124,7 @@ import { Playground } from "/src/snippets/_playground.jsx";
**gfx950 block-FP8 accuracy: fixed as of the pinned MI355X image (`v0.5.13.post1-rocm720-mi35x-20260618`).** Earlier SGLang ROCm images miscompiled AMD aiter's `gemm_a8w8_blockscale_bpreshuffle` GEMM on gfx950 (ROCm 7.2): the error was small per layer but compounded across all 78 layers and silently corrupted output — in-context reasoning broke (GSM8K ≈ 0) while short factual prompts still looked fine. The root cause was a gfx950/ROCm-7.2 miscompile of the CK kernel (a packed illegal-type FMA that relied on an LLVM coercion pass removed in ROCm 7.2; non-deterministic wrong rows near tile boundaries). This is resolved in the pinned image and newer: GLM-5.2-FP8 on MI350X/MI355X (gfx950) was re-validated at TP4 and TP8 — **GSM8K ≈ 0.96 (0% invalid)** and **15/15 needle-in-haystack retrieval to ~118K tokens**. **MI300X / MI325X (gfx942) were never affected.** If you must run an older image, treat gfx950 FP8 output as unverified. Background: [sgl-project/sglang#28685](https://github.com/sgl-project/sglang/issues/28685) (analysis) and the upstream CK fix [ROCm/rocm-libraries#8639](https://github.com/ROCm/rocm-libraries/pull/8639) (scalar FMA + accumulator anchor; restores correctness and determinism at -O3). **gfx950 block-FP8 accuracy: fixed as of the pinned MI355X image (`v0.5.13.post1-rocm720-mi35x-20260618`).** Earlier SGLang ROCm images miscompiled AMD aiter's `gemm_a8w8_blockscale_bpreshuffle` GEMM on gfx950 (ROCm 7.2): the error was small per layer but compounded across all 78 layers and silently corrupted output — in-context reasoning broke (GSM8K ≈ 0) while short factual prompts still looked fine. The root cause was a gfx950/ROCm-7.2 miscompile of the CK kernel (a packed illegal-type FMA that relied on an LLVM coercion pass removed in ROCm 7.2; non-deterministic wrong rows near tile boundaries). This is resolved in the pinned image and newer: GLM-5.2-FP8 on MI350X/MI355X (gfx950) was re-validated at TP4 and TP8 — **GSM8K ≈ 0.96 (0% invalid)** and **15/15 needle-in-haystack retrieval to ~118K tokens**. **MI300X / MI325X (gfx942) were never affected.** If you must run an older image, treat gfx950 FP8 output as unverified. Background: [sgl-project/sglang#28685](https://github.com/sgl-project/sglang/issues/28685) (analysis) and the upstream CK fix [ROCm/rocm-libraries#8639](https://github.com/ROCm/rocm-libraries/pull/8639) (scalar FMA + accumulator anchor; restores correctness and determinism at -O3).
</Note> </Note>
- **MTP / EAGLE speculative decoding on AMD.** Five-step MTP is validated for `amd/GLM-5.2-MXFP4` on MI355X with `lmsysorg/sglang-rocm:v0.5.19-rocm720-mi35x-20260916`. Choose **Low-Latency** for the GPU-resident TP8/EP1 topology, or **High-Throughput** for the TP4/EP4 topology. Both emit `--speculative-num-steps 5 --speculative-eagle-topk 1 --speculative-num-draft-tokens 6` with the Triton DSA prefill and decode backends. The TP4/EP4 InferenceX benchmark adds HiCache DRAM offload, but those host-specific knobs are intentionally omitted from the portable cookbook command. MTP remains unvalidated for GLM-5.2 on MI300X/MI325X and for the other MI355X checkpoint precisions. - **MTP / EAGLE speculative decoding on AMD.** Five-step MTP is validated for `amd/GLM-5.2-MXFP4` on MI355X with `lmsysorg/sglang-rocm:v0.5.20-rocm720-mi35x-20260920`. Choose **Low-Latency** for TP8/EP1, or **High-Throughput** for TP4/EP4 with HiCache (`--enable-hierarchical-cache --hicache-ratio 1.0`). HiCache uses the default `kernel`, `page_first`, and `write_through` settings. MTP remains unvalidated for GLM-5.2 on MI300X/MI325X and for the other MI355X checkpoint precisions.
## 3. Advanced Usage ## 3. Advanced Usage
@@ -10,10 +10,10 @@ tag: NEW
<Accordion title="Install SGLang"> <Accordion title="Install SGLang">
Use an SGLang build that includes GLM-5.3-Flash support. Use an SGLang build that includes GLM-5.3-Flash support (v0.5.20 or later).
```bash Command ```bash Command
docker pull lmsysorg/sglang:glm-5.3-flash docker pull lmsysorg/sglang:latest
``` ```
The deployment panel can render a complete `docker run` command for the selected hardware and options. See [Install SGLang with Docker](/docs/get-started/install#method-3-using-docker) for host setup. The deployment panel can render a complete `docker run` command for the selected hardware and options. See [Install SGLang with Docker](/docs/get-started/install#method-3-using-docker) for host setup.
@@ -104,7 +104,7 @@ The **Speculative** card in the Playground changes the algorithm without leaving
- **EAGLE / MTP 5-1-6** is exactly what Low Latency serves, so a Low Latency base starts on this chip. Pick it from a High Throughput base to keep that recipe's other settings and add the MTP head. - **EAGLE / MTP 5-1-6** is exactly what Low Latency serves, so a Low Latency base starts on this chip. Pick it from a High Throughput base to keep that recipe's other settings and add the MTP head.
- **Off (greedy)** strips the whole `--speculative-*` family, which is what High Throughput already starts from. - **Off (greedy)** strips the whole `--speculative-*` family, which is what High Throughput already starts from.
- **DFlash2** swaps the in-checkpoint MTP head for the trained block-diffusion draft in [`incoai/GLM-5.3-Flash-DFlash2`](https://huggingface.co/incoai/GLM-5.3-Flash-DFlash2). The draft proposes a whole block per step and the target verifies it in one forward pass, so output quality stays the target's. Its block size comes from the draft checkpoint, and the draft runs on `fa4` rather than the target's DSA backends. It needs a build that carries the GLM-5.3-Flash hidden-state capture from [PR #36708](https://github.com/sgl-project/sglang/pull/36708), which is merged into the [PR #36507](https://github.com/sgl-project/sglang/pull/36507) support branch (`xinyuan/glm-5.3-flash-support`) rather than into `main`, so the image pinned above is not enough on its own — pull that branch at its current head, or add #36708's commit on top of an older checkout. The draft repository is also access-gated: request access on its model page, then download it alongside the target before serving. This combination is not yet measured on the cookbook hardware, so treat it as a starting point. - **DFlash2** swaps the in-checkpoint MTP head for the trained block-diffusion draft in [`incoai/GLM-5.3-Flash-DFlash2`](https://huggingface.co/incoai/GLM-5.3-Flash-DFlash2). The draft proposes a whole block per step and the target verifies it in one forward pass, so output quality stays the target's. Its block size comes from the draft checkpoint, and the draft runs on `fa4` rather than the target's DSA backends. The hidden-state capture it needs ([PR #36708](https://github.com/sgl-project/sglang/pull/36708)) shipped with the GLM-5.3-Flash support in v0.5.20, so the image pinned above is enough. The draft repository is access-gated: request access on its model page, then download it alongside the target before serving. This combination is not yet measured on the cookbook hardware, so treat it as a starting point.
Neither algorithm runs with DP-Attention; the card disables the affected chips and names the reason. Neither algorithm runs with DP-Attention; the card disables the affected chips and names the reason.
@@ -134,13 +134,13 @@ The default multimodal feature transport is automatic, and on a single CUDA node
### 3.1 Reasoning ### 3.1 Reasoning
Thinking is enabled by the checkpoint's generation configuration, and generated commands enable `--reasoning-parser glm45` by default. The OpenAI-compatible API then places thinking in `message.reasoning_content` and the final answer in `message.content`. You can disable **Reasoning Parser** in the Playground when an integration needs the raw response format. Thinking is enabled by the checkpoint's generation configuration, and generated commands enable `--reasoning-parser auto` (which resolves to `glm45` for GLM-5.3-Flash) by default. The OpenAI-compatible API then places thinking in `message.reasoning_content` and the final answer in `message.content`. You can disable **Reasoning Parser** in the Playground when an integration needs the raw response format.
To disable thinking for a request, pass `chat_template_kwargs: {"thinking": false}` in the request body. To disable thinking for a request, pass `chat_template_kwargs: {"thinking": false}` in the request body.
### 3.2 Tool calling ### 3.2 Tool calling
Generated commands enable `--tool-call-parser glm47` by default, so structured calls are returned in `message.tool_calls`. You can disable **Tool Call Parser** in the Playground when tool calling is not needed. On follow-up turns, read both `reasoning_content` and `content` because a thinking model can use either field around tool execution. Generated commands enable `--tool-call-parser auto` (which resolves to `glm47` for GLM-5.3-Flash) by default, so structured calls are returned in `message.tool_calls`. You can disable **Tool Call Parser** in the Playground when tool calling is not needed. On follow-up turns, read both `reasoning_content` and `content` because a thinking model can use either field around tool execution.
### 3.3 Multimodal serving ### 3.3 Multimodal serving
+4 -4
View File
@@ -103,7 +103,7 @@ import { Playground } from "/src/snippets/_playground.jsx";
- **DeepSeek Sparse Attention (DSA).** GLM-5.3 uses the `glm_moe_dsa` architecture; SGLang auto-selects the DSA attention backends (`flashmla_sparse` prefill, `fa3` decode, `sgl-kernel` indexer topk). No attention-backend flag is needed on the supported hardware. SGLang also auto-selects the KV-cache dtype for DSA models — `fp8_e4m3` on Blackwell (B200/GB300/B300, which then routes DSA through the TensorRT-LLM backend) and `bf16` on Hopper (H200) — so no `--kv-cache-dtype` flag is required. On Hopper, pairing `--kv-cache-dtype fp8_e4m3` with `--dsa-prefill-backend flashmla_sparse_q8 --dsa-decode-backend flashmla_kv` selects the native FP8 sparse prefill kernel (computes directly on the fp8 KV cache with no fp8→bf16 dequantization round-trip; GLM-5.3's 64 query heads match the kernel's native tile) — see the [DeepSeek-V3.2 page](../DeepSeek/DeepSeek-V3_2) for kernel details; the optional `SGLANG_ENABLE_DSA_Q8KV8_*` performance env vars are documented in `python/sglang/srt/environ.py`. - **DeepSeek Sparse Attention (DSA).** GLM-5.3 uses the `glm_moe_dsa` architecture; SGLang auto-selects the DSA attention backends (`flashmla_sparse` prefill, `fa3` decode, `sgl-kernel` indexer topk). No attention-backend flag is needed on the supported hardware. SGLang also auto-selects the KV-cache dtype for DSA models — `fp8_e4m3` on Blackwell (B200/GB300/B300, which then routes DSA through the TensorRT-LLM backend) and `bf16` on Hopper (H200) — so no `--kv-cache-dtype` flag is required. On Hopper, pairing `--kv-cache-dtype fp8_e4m3` with `--dsa-prefill-backend flashmla_sparse_q8 --dsa-decode-backend flashmla_kv` selects the native FP8 sparse prefill kernel (computes directly on the fp8 KV cache with no fp8→bf16 dequantization round-trip; GLM-5.3's 64 query heads match the kernel's native tile) — see the [DeepSeek-V3.2 page](../DeepSeek/DeepSeek-V3_2) for kernel details; the optional `SGLANG_ENABLE_DSA_Q8KV8_*` performance env vars are documented in `python/sglang/srt/environ.py`.
- **MTP / speculative decoding.** The checkpoint ships one nextn layer. Enable EAGLE MTP for lower latency (`--speculative-algorithm EAGLE --speculative-num-steps 5 --speculative-eagle-topk 1 --speculative-num-draft-tokens 6` for low-latency; `1-1-2` for balanced). The config's `index_share_for_mtp_iteration` reuses the DSA indexer's topk across draft steps (effective only at `--speculative-eagle-topk 1`). Watch the server's reported **accept length** and adjust `--speculative-num-steps` / `--speculative-num-draft-tokens`: lower the draft length when rejected draft tokens create excess verification work. - **MTP / speculative decoding.** The checkpoint ships one nextn layer. Enable EAGLE MTP for lower latency (`--speculative-algorithm EAGLE --speculative-num-steps 5 --speculative-eagle-topk 1 --speculative-num-draft-tokens 6` for low-latency; `1-1-2` for balanced). The config's `index_share_for_mtp_iteration` reuses the DSA indexer's topk across draft steps (effective only at `--speculative-eagle-topk 1`). Watch the server's reported **accept length** and adjust `--speculative-num-steps` / `--speculative-num-draft-tokens`: lower the draft length when rejected draft tokens create excess verification work.
- **DFlash2 (block-diffusion draft).** The **Speculative** card in the [Playground above](#playground) also offers **DFlash2**, which replaces the in-checkpoint MTP layer with the separately trained block-diffusion drafter [`incoai/GLM-5.3-DFlash2`](https://huggingface.co/incoai/GLM-5.3-DFlash2). It proposes a whole block per step and the target verifies the block in one forward pass, so output quality stays the target's. The block size — 8, i.e. 7 draft tokens per verification step — comes from the draft checkpoint's own `dflash_config`, so no `--speculative-num-draft-tokens` is passed; the draft is a small dense model and runs on `fa4` instead of the target's DSA backends. Two prerequisites: the DFlash2 drafter ([PR #35371](https://github.com/sgl-project/sglang/pull/35371)) merged **after v0.5.18**, so install SGLang from `main` (or use a nightly image) rather than the release this page pins; and DFLASH runs on **CUDA/NPU only** and rejects **DP-Attention**, so turn DP-Attention off in the **Attention** card before selecting it on a high-throughput base. The draft repository is public but licensed CC BY-NC-ND 4.0 for research and evaluation. - **DFlash2 (block-diffusion draft).** The **Speculative** card in the [Playground above](#playground) also offers **DFlash2**, which replaces the in-checkpoint MTP layer with the separately trained block-diffusion drafter [`incoai/GLM-5.3-DFlash2`](https://huggingface.co/incoai/GLM-5.3-DFlash2). It proposes a whole block per step and the target verifies the block in one forward pass, so output quality stays the target's. The block size — 8, i.e. 7 draft tokens per verification step — comes from the draft checkpoint's own `dflash_config`, so no `--speculative-num-draft-tokens` is passed; the draft is a small dense model and runs on `fa4` instead of the target's DSA backends. Note that DFLASH runs on **CUDA/NPU only** and rejects **DP-Attention**, so turn DP-Attention off in the **Attention** card before selecting it on a high-throughput base. The draft repository is public but licensed CC BY-NC-ND 4.0 for research and evaluation.
- **Memory.** The FP8 weights are large (MoE total, not active params). Start around `--mem-fraction-static 0.8` on H200 (TP8) and tune up; raise it for the 4-GPU GB300 single-node layout (TP4). - **Memory.** The FP8 weights are large (MoE total, not active params). Start around `--mem-fraction-static 0.8` on H200 (TP8) and tune up; raise it for the 4-GPU GB300 single-node layout (TP4).
- **DP-Attention + DeepEP** for the balanced/high-throughput strategies spreads attention across data-parallel ranks and routes MoE through DeepEP. - **DP-Attention + DeepEP** for the balanced/high-throughput strategies spreads attention across data-parallel ranks and routes MoE through DeepEP.
- **BF16 weights need more GPUs.** The full-precision build (`zai-org/GLM-5.3-BF16`, ~1.5 TB) does not fit a single 8×H200 / 8×B200 / 4×GB300 node. It fits single-node on **8×B300** (TP8, ~2.1 TB HBM); on the smaller GPUs it needs a **multi-node** layout (e.g. 2×8×H200 or 2×8×B200 at TP16, 2×4×GB300 at TP8). FP8 is the recommended deployment. Use the same DSA / MTP / chunked-prefill guidance as FP8. - **BF16 weights need more GPUs.** The full-precision build (`zai-org/GLM-5.3-BF16`, ~1.5 TB) does not fit a single 8×H200 / 8×B200 / 4×GB300 node. It fits single-node on **8×B300** (TP8, ~2.1 TB HBM); on the smaller GPUs it needs a **multi-node** layout (e.g. 2×8×H200 or 2×8×B200 at TP16, 2×4×GB300 at TP8). FP8 is the recommended deployment. Use the same DSA / MTP / chunked-prefill guidance as FP8.
@@ -117,7 +117,7 @@ import { Playground } from "/src/snippets/_playground.jsx";
### 3.1 Reasoning ### 3.1 Reasoning
GLM-5.3 is a reasoning model. Enable the `glm45` reasoning parser (toggle **Reasoning Parser** in the **Parsers** card of the [Playground above](#playground)) to separate thinking from the final answer — thinking lands in `message.reasoning_content`, the answer in `message.content`. The chat template defaults `clear_thinking` to `false`; for multi-turn chat, pass `chat_template_kwargs: {"clear_thinking": True}` so previous reasoning is cleared before the next response. GLM-5.3 is a reasoning model, and generated commands enable `--reasoning-parser auto` (which resolves to `glm45` for GLM-5.3) by default so thinking is separated from the final answer — thinking lands in `message.reasoning_content`, the answer in `message.content`. Without the parser the server returns the thinking and the answer as one `content` string with a stray `</think>` between them, because the chat template opens `<think>` in the generation prompt. You can disable **Reasoning Parser** in the **Parsers** card of the [Playground above](#playground) when an integration needs that raw format. The chat template defaults `clear_thinking` to `false`; for multi-turn chat, pass `chat_template_kwargs: {"clear_thinking": True}` so previous reasoning is cleared before the next response.
**Reasoning effort.** Pass `chat_template_kwargs: {"reasoning_effort": ...}` to select `low`, `high`, or `max`. If you omit it or pass another value, the template uses `max`. **Reasoning effort.** Pass `chat_template_kwargs: {"reasoning_effort": ...}` to select `low`, `high`, or `max`. If you omit it or pass another value, the template uses `max`.
@@ -164,7 +164,7 @@ Here is how you can calculate it:
### 3.2 Tool Calling ### 3.2 Tool Calling
Enable the `glm47` tool-call parser (toggle **Tool Call Parser** in the **Parsers** card of the [Playground above](#playground)) to surface structured tool calls via `message.tool_calls`. GLM-5.3 emits the newer `<tool_call>…<arg_key>…<arg_value>…` format, so it needs the **`glm47`** parser — the older `glm45` parser does not parse it (the call would be left as raw text in `content`). On thinking mode the turn also fills `reasoning_content`, so print both fields. Generated commands enable `--tool-call-parser auto` by default, so structured calls are returned in `message.tool_calls` with `finish_reason: "tool_calls"`. `auto` resolves to **`glm47`** for GLM-5.3: the model emits the newer `<tool_call>…<arg_key>…<arg_value>…` format, which the older `glm45` parser does not parse (the call would be left as raw text in `content`). Running with no tool-call parser fails the same way, and `finish_reason` stays `"stop"`, so an agent loop never sees the call. You can disable **Tool Call Parser** in the **Parsers** card of the [Playground above](#playground) when tool calling is not needed. On thinking mode the turn also fills `reasoning_content`, so print both fields.
<Accordion title="Tool Calling Example (Python)"> <Accordion title="Tool Calling Example (Python)">
@@ -218,7 +218,7 @@ For long-context, prefix-heavy workloads, enable hierarchical KV caching to spil
### 3.4 Claude Code Integration ### 3.4 Claude Code Integration
GLM-5.3's strong reasoning + tool-calling makes it a good backend for [Claude Code](https://code.claude.com/docs/en/overview), Anthropic's agentic CLI. SGLang exposes the Anthropic-compatible `/v1/messages` endpoint on every server, so Claude Code can talk to a GLM-5.3 server with only environment variables — no code change. Launch the server with `--reasoning-parser glm45 --tool-call-parser glm47` (any recipe from the Deployment panel above works), then: GLM-5.3's strong reasoning + tool-calling makes it a good backend for [Claude Code](https://code.claude.com/docs/en/overview), Anthropic's agentic CLI. SGLang exposes the Anthropic-compatible `/v1/messages` endpoint on every server, so Claude Code can talk to a GLM-5.3 server with only environment variables — no code change. Launch the server with `--reasoning-parser auto --tool-call-parser auto` (any recipe from the Deployment panel above works), then:
```bash Command ```bash Command
export ANTHROPIC_BASE_URL="http://127.0.0.1:30000" export ANTHROPIC_BASE_URL="http://127.0.0.1:30000"
@@ -97,6 +97,8 @@ For other installation methods, please refer to the [official SGLang installatio
### 3.1 Basic Configuration ### 3.1 Basic Configuration
The Gemma 4 series offers models in various sizes and architectures, optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, and Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)). The recommended launch configurations vary by hardware and model size.
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform and model variant. **Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform and model variant.
<Gemma4Deployment /> <Gemma4Deployment />
@@ -22,11 +22,9 @@ SGLang offers multiple installation methods. You can choose the most suitable in
Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions. Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions.
For SGLang CPU installation, please refer to the [CPU version installation guide](../../../docs/hardware-platforms/cpu_server#installation).
## 3. Model Deployment ## 3. Model Deployment
This section provides deployment configurations optimized for different hardware platforms and use cases. This section provides deployment configurations optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)), and Intel Xeon CPUs.
### 3.1 Basic Configuration ### 3.1 Basic Configuration
@@ -24,15 +24,13 @@ For more details, please refer to the [official Llama models repository](https:/
Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions. Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions.
For SGLang CPU installation, please refer to the [CPU version installation guide](../../../docs/hardware-platforms/cpu_server#installation).
## 3. Model Deployment ## 3. Model Deployment
This section provides deployment configurations optimized for AMD GPUs (MI300X, MI325X, MI355X) and Intel Xeon CPUs. This section provides deployment configurations optimized for AMD GPUs (MI300X, MI325X, MI355X), Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)) and Intel Xeon CPUs.
### 3.1 Interactive Configuration ### 3.1 Interactive Configuration
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your AMD GPU setup. **Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your AMD GPU, Intel Arc Pro B-Series GPUs or Intel Xeon CPUs setup.
import { Llama33Deployment } from "/src/snippets/autoregressive/llama33-70b-deployment.jsx"; import { Llama33Deployment } from "/src/snippets/autoregressive/llama33-70b-deployment.jsx";
@@ -129,6 +129,7 @@ The NVIDIA Blackwell recipes are validated single-node: **B200 at `--tp 8`** and
- **Memory**: `--mem-fraction-static` reserves GPU memory for weights + KV pool; the rest is prefill **activation headroom**. The value scales with *free* memory per GPU (card capacity minus per-GPU weight), so it tracks the card more than the TP degree: **`0.65` on B200** (180 GB — less headroom once weights are resident) and **`0.75` on the larger-memory B300 / GB300** (`0.80` on AMD). Lower TP packs more weight per GPU, so a tighter config needs a *lower* value — B200 needs `0.65` even at `--tp 4`. Raising it past the validated value is fine only for low-concurrency single-stream serving; it OOMs under high concurrency or long context. - **Memory**: `--mem-fraction-static` reserves GPU memory for weights + KV pool; the rest is prefill **activation headroom**. The value scales with *free* memory per GPU (card capacity minus per-GPU weight), so it tracks the card more than the TP degree: **`0.65` on B200** (180 GB — less headroom once weights are resident) and **`0.75` on the larger-memory B300 / GB300** (`0.80` on AMD). Lower TP packs more weight per GPU, so a tighter config needs a *lower* value — B200 needs `0.65` even at `--tp 4`. Raising it past the validated value is fine only for low-concurrency single-stream serving; it OOMs under high concurrency or long context.
- **Long context (32K+)**: keep `--mem-fraction-static` at the platform default and raise `--chunked-prefill-size` to `16384`. Decode TPOT stays roughly flat in context length thanks to sparse attention; 1K128K prompts are validated. - **Long context (32K+)**: keep `--mem-fraction-static` at the platform default and raise `--chunked-prefill-size` to `16384`. Decode TPOT stays roughly flat in context length thanks to sparse attention; 1K128K prompts are validated.
- **HiSparse for decode capacity**: on NVIDIA CUDA, HiSparse keeps the three dense layers on GPU, moves the 57 sparse-layer K/V caches to pinned host memory, and feeds selected block IDs directly to the swap-in kernel. For the released four-KV-head model, use `--tp 4` or greater, `--disable-radix-cache`, and `device_buffer_size >= 2048`. Enable it with `--enable-hisparse --hisparse-config='{"device_buffer_size":4096,"host_to_device_ratio":2}'` on the Triton launch command.
- **Scaling TP**: B200 is documented at `--tp 8`; B300 / GB200 / GB300 at `--tp 4` (the single-node cross-family common denominator). On an 8-GPU B300 host you can also raise to `--tp 8` for more throughput / KV headroom. - **Scaling TP**: B200 is documented at `--tp 8`; B300 / GB200 / GB300 at `--tp 4` (the single-node cross-family common denominator). On an 8-GPU B300 host you can also raise to `--tp 8` for more throughput / KV headroom.
- **Expert parallelism**: to trade latency for throughput add `--ep` (see [Expert Parallelism Deployment](../../../docs/advanced_features/expert_parallelism)). On AMD, set `--ep` equal to `--tp`. Shared-experts fusion is automatically disabled when EP > 1; on AMD standard EP the server also disables `--enable-aiter-allreduce-fusion` automatically to preserve accuracy. - **Expert parallelism**: to trade latency for throughput add `--ep` (see [Expert Parallelism Deployment](../../../docs/advanced_features/expert_parallelism)). On AMD, set `--ep` equal to `--tp`. Shared-experts fusion is automatically disabled when EP > 1; on AMD standard EP the server also disables `--enable-aiter-allreduce-fusion` automatically to preserve accuracy.
- `--trust-remote-code` is required to load the MiniMax config / processor classes. - `--trust-remote-code` is required to load the MiniMax config / processor classes.
@@ -30,7 +30,7 @@ Then run the **Python** output of the command panel below in that environment.
```bash Command ```bash Command
docker pull lmsysorg/sglang:latest # NVIDIA (CUDA) docker pull lmsysorg/sglang:latest # NVIDIA (CUDA)
docker pull lmsysorg/sglang-rocm:v0.5.19-rocm720-mi35x-20260910 # AMD MI350X / MI355X (ROCm) docker pull lmsysorg/sglang-rocm:v0.5.19-rocm720-mi35x-20260916 # AMD MI350X / MI355X (ROCm)
``` ```
For how to launch the image, see [Install → Method 3: Using Docker](../../../docs/get-started/install#method-3-using-docker). Substitute the inner `sglang serve ...` with what the command generator below produces. For how to launch the image, see [Install → Method 3: Using Docker](../../../docs/get-started/install#method-3-using-docker). Substitute the inner `sglang serve ...` with what the command generator below produces.
@@ -40,14 +40,16 @@ For how to launch the image, see [Install → Method 3: Using Docker](../../../d
<Tab title="NPU"> <Tab title="NPU">
```bash Command ```bash Command
docker pull quay.io/ascend/sglang:main-cann9.0.0-a3 docker pull quay.io/ascend/sglang:main-cann9.0.0-a3 # Ascend A3 Series
docker pull swr.cn-southwest-2.myhuaweicloud.com/base_image/dockerhub/lmsysorg/sglang:cann9.1.0-950-B070 # Ascend 950PR/DT Series
``` ```
For host and platform setup, see the For host and platform setup, see the
[NPU installation guide](../../../docs/hardware-platforms/ascend-npus/getting-started/installation) and the [NPU installation guide](../../../docs/hardware-platforms/ascend-npus/getting-started/installation) and the
[quick start guide](../../../docs/hardware-platforms/ascend-npus/getting-started/quick_start). [quick start guide](../../../docs/hardware-platforms/ascend-npus/getting-started/quick_start).
**Weights (NPU):** [Kimi-K3-W4A8](https://www.modelscope.cn/models/sgl-npu/Kimi-K3-W4A8) (W4A8, 1.49 TB) · **Weights (NPU):** [Kimi-K3](https://modelscope.cn/models/moonshotai/Kimi-K3) (950PR/DT Series, MXFP4) ·
[Kimi-K3-W4A8](https://www.modelscope.cn/models/sgl-npu/Kimi-K3-W4A8) (A3 Series, W4A8, 1.49 TB) ·
[Kimi-K3-DSpark](https://www.modelscope.cn/models/RadixArk/Kimi-K3-DSpark) (DSPARK draft, 4.5 GB) [Kimi-K3-DSpark](https://www.modelscope.cn/models/RadixArk/Kimi-K3-DSpark) (DSPARK draft, 4.5 GB)
</Tab> </Tab>
@@ -56,14 +58,14 @@ For host and platform setup, see the
</Accordion> </Accordion>
Pick your hardware, then the deployment shape and operating point. Node count follows the hardware recipe (B200 2×8, GB200 4×4, H100 4×8, B300 1×8, H200 2×8 — 4×8 on Unified High-Throughput, GB300 2×4, MI350X/MI355X 1×8, Ascend A3 Series 4×8 — 32 cards / 64 dies), so it is not a separate choice. If you serve the NVFP4 checkpoint (`nvidia/Kimi-K3-NVFP4`, the **Quantization** row in the panel below), use the `lmsysorg/sglang:dev-dev-kimi-k3-nvfp4` image. Pick your hardware, then the deployment shape and operating point. Node count follows the hardware recipe (B200 2×8, GB200 4×4, H100 4×8, B300 1×8, H200 2×8 — 4×8 on Unified High-Throughput, GB300 2×4, MI350X/MI355X 1×8, Ascend A3 Series 4×8 — 32 cards / 64 dies, Ascend 950PR/DT Series 4×8), so it is not a separate choice. If you serve the NVFP4 checkpoint (`nvidia/Kimi-K3-NVFP4`, the **Quantization** row in the panel below), use the `lmsysorg/sglang:dev-dev-kimi-k3-nvfp4` image.
**PD Mode** — `Unified` serves prefill and decode together. `Prefill` / `Decode` split them into dedicated pools (see [PD disaggregation](#3-4-pd-disaggregation)); `Prefill` ships two strategies, both chunked at 16k. On the 8-GPU platforms (B300 1×8, GB300 2×4), `Default` is TP8 and `Long-Context` is `--pp-size 8 --tp-size 1`. On the 16-GPU platforms (B200 2×8, GB200 4×4), both are `--pp-size 16 --tp-size 1` and differ only in `--mem-fraction-static` (0.85 vs 0.90) — deep PP is the throughput shape there, not just the long-context one (see [Deep PP](#deep-pp-for-prefill)). **PD Mode** — `Unified` serves prefill and decode together. `Prefill` / `Decode` split them into dedicated pools (see [PD disaggregation](#3-4-pd-disaggregation)); `Prefill` ships two strategies, both chunked at 16k. On the 8-GPU platforms (B300 1×8, GB300 2×4), `Default` is TP8 and `Long-Context` is `--pp-size 8 --tp-size 1`. On the 16-GPU platforms (B200 2×8, GB200 4×4), both are `--pp-size 16 --tp-size 1` and differ only in `--mem-fraction-static` (0.85 vs 0.90) — deep PP is the throughput shape there, not just the long-context one (see [Deep PP](#deep-pp-for-prefill)).
**Strategy** — the operating point within that shape: **Strategy** — the operating point within that shape:
- **Low-Latency** — no DCP, so the MLA KV stays TP-replicated. For chat. B200 splits its two nodes into PP2 × TP8; every other platform is flat TP. - **Low-Latency** — no DCP, so the MLA KV stays TP-replicated. For chat. B200 splits its two nodes into PP2 × TP8; every other platform is flat TP.
- **Balanced** — the accuracy-preserving default: PP2 × DCPEP8 on B200 (the two pipeline stages and DCP8 split KV and KDA state), TP16/DCP16 on GB200, TP8/DCP8 on B300/GB300, TP8 ROCm/AITER on MI35x. - **Balanced** — the accuracy-preserving default: PP2 × DCPEP8 on B200 (the two pipeline stages and DCP8 split KV and KDA state), TP16/DCP16 on GB200, TP8/DCP8 on B300/GB300, TP8/DCP8 ROCm/AITER on MI35x.
- **High-Throughput** — the large-scale lane: pick a **Cluster Size** and **Large-Scale Preset** in the Playground ([details](#large-scale-presets)). The cell itself is Balanced, except on H100 (plus `extra_buffer_lazy`) and H200 (widens to 4×8 TP32/EP32 at `--mem-fraction-static 0.90`). - **High-Throughput** — the large-scale lane: pick a **Cluster Size** and **Large-Scale Preset** in the Playground ([details](#large-scale-presets)). The cell itself is Balanced, except on H100 (plus `extra_buffer_lazy`) and H200 (widens to 4×8 TP32/EP32 at `--mem-fraction-static 0.90`).
`Long-Context` appears only under the `Prefill` PD mode; for long-context unified serving on B200, start from High-Throughput and raise `--context-length`. `Long-Context` appears only under the `Prefill` PD mode; for long-context unified serving on B200, start from High-Throughput and raise `--context-length`.
@@ -71,7 +73,7 @@ Pick your hardware, then the deployment shape and operating point. Node count fo
**Spec Decode** — layers onto the strategy without changing it, on every platform except B200. DSPARK proposes 7 draft tokens per step (tune in the Playground) and requires `pp_size == 1`, so on B200 it also drops the pipeline and re-lays the same 16 GPUs flat: PP2 × TP8 → TP16, PP2 × DCPEP8 → DCPEP16. DFLASH has no published draft checkpoint. The win is largest on short interactive traffic and fades as the prompt grows. **Spec Decode** — layers onto the strategy without changing it, on every platform except B200. DSPARK proposes 7 draft tokens per step (tune in the Playground) and requires `pp_size == 1`, so on B200 it also drops the pipeline and re-lays the same 16 GPUs flat: PP2 × TP8 → TP16, PP2 × DCPEP8 → DCPEP16. DFLASH has no published draft checkpoint. The win is largest on short interactive traffic and fades as the prompt grows.
<Note> <Note>
`--mamba-full-memory-ratio` is the one sizing flag, computed live: set your average request length in the [Mamba ratio calculator](#mamba-ratio-calculator); everything else follows the panels, and the result is pinned into the command. (The Ascend A3 Series uses `--max-mamba-cache-size` instead.) `--mamba-full-memory-ratio` is the one sizing flag, computed live: set your average request length in the [Mamba ratio calculator](#mamba-ratio-calculator); everything else follows the panels, and the result is pinned into the command. (The Ascend NPU recipes don't use the calculator and set no ratio at all — the Ascend path sizes both pools itself: the A3 Series pins `--max-mamba-cache-size` instead, and the A5 recipe serves with the radix cache off.)
</Note> </Note>
import { Deployment } from "/src/snippets/_deployment.jsx"; import { Deployment } from "/src/snippets/_deployment.jsx";
@@ -91,6 +93,22 @@ import { KimiK3MambaRatioCalculator } from "/src/snippets/_kimi_k3_mamba_ratio_c
NVFP4 NOSPEC / NVFP4 DSPARK), which is why no point past concurrency 64 is published for Balanced. NVFP4 NOSPEC / NVFP4 DSPARK), which is why no point past concurrency 64 is published for Balanced.
</Note> </Note>
### AMD AITER with DCP8
The MI350X/MI355X unified Balanced recipe uses TP8/DCP8 with AITER prefill and
decode attention. DCP shards the target MLA KV cache; RadixArk DSPARK's draft KV
remains replicated. The pinned `v0.5.19-rocm720-mi35x-20260916` image records
SGLang revision `e7f7447333`, which includes
[AITER DCP support (#34432)](https://github.com/sgl-project/sglang/pull/34432) and
the [DCP KV-free fix (#38941)](https://github.com/sgl-project/sglang/pull/38941).
No source overlay is required for DCP.
Keep `SGLANG_K3_KDA_FUSED_BACKEND` unset with this image. The separate fused-KDA
opt-in requires the [deferred-gate fix (#39066)](https://github.com/sgl-project/sglang/pull/39066),
which is not included in this image. This updated recipe remains **Final
Verification In Progress**; the recorded speed numbers use their original
configurations and do not validate the new image or DCP8 recipe.
### Mamba ratio calculator ### Mamba ratio calculator
<KimiK3MambaRatioCalculator /> <KimiK3MambaRatioCalculator />
@@ -146,7 +164,7 @@ not been re-measured on any cell — re-measure before you rely on one.
## 2. Configuration Tips ## 2. Configuration Tips
**Memory: two pools, one flag.** K3 splits static memory into a worst-case-reserved **KDA state pool** (it sets the concurrency ceiling) and a paged **MLA KV pool**, divided by `--mamba-full-memory-ratio`. The command panel pins that flag to the [calculator](#mamba-ratio-calculator)'s output — set your average request length there; every other calculator input follows the panels. (On the Ascend A3 Series: `--max-mamba-cache-size`, no calculator.) After boot, read back `max_total_num_tokens` (the KV side) and the admitted-request cap (the state side). **Memory: two pools, one flag.** K3 splits static memory into a worst-case-reserved **KDA state pool** (it sets the concurrency ceiling) and a paged **MLA KV pool**, divided by `--mamba-full-memory-ratio`. The command panel pins that flag to the [calculator](#mamba-ratio-calculator)'s output — set your average request length there; every other calculator input follows the panels. (On the Ascend NPU recipes there is no calculator and no ratio flag: the Ascend path sizes both pools itself — the A3 Series pins `--max-mamba-cache-size`, and the A5 recipe runs the radix cache off.) After boot, read back `max_total_num_tokens` (the KV side) and the admitted-request cap (the state side).
Capacity levers, all in the Playground. Each trades precision or cache behavior for capacity — re-verify accuracy on your workload: Capacity levers, all in the Playground. Each trades precision or cache behavior for capacity — re-verify accuracy on your workload:
@@ -178,10 +196,11 @@ Speculation: DSPARK holds block size + 1 (= 8) intermediate states per request
| GB200 4×4 | TP16/DCP16 | MNNVL auto-detected | | GB200 4×4 | TP16/DCP16 | MNNVL auto-detected |
| H200 2×8 (4×8 on Unified High-Throughput) | TP16/EP16 + symm-mem, Marlin + FlashMLA; High-Throughput widens to TP32/EP32 over 4 nodes at mem-frac 0.90 with `extra_buffer_lazy` | same block on every node; export the cross-node NIC (`GLOO_SOCKET_IFNAME` / `NCCL_SOCKET_IFNAME`, `SGLANG_HOST_IP`); keep `NCCL_MNNVL_ENABLE=1 NCCL_CUMEM_ENABLE=1` | | H200 2×8 (4×8 on Unified High-Throughput) | TP16/EP16 + symm-mem, Marlin + FlashMLA; High-Throughput widens to TP32/EP32 over 4 nodes at mem-frac 0.90 with `extra_buffer_lazy` | same block on every node; export the cross-node NIC (`GLOO_SOCKET_IFNAME` / `NCCL_SOCKET_IFNAME`, `SGLANG_HOST_IP`); keep `NCCL_MNNVL_ENABLE=1 NCCL_CUMEM_ENABLE=1` |
| H100 4×8 | TP32/EP32, Marlin + FlashMLA | SM90a build of the K3 image; pin NCCL/Gloo to the same NIC on all nodes; least post-weight headroom (80 GB) | | H100 4×8 | TP32/EP32, Marlin + FlashMLA | SM90a build of the K3 image; pin NCCL/Gloo to the same NIC on all nodes; least post-weight headroom (80 GB) |
| MI350X/MI355X 1×8 | TP8 ROCm/AITER | AITER A8W4 FlyDSL MoE, Triton attention (`SGLANG_MLA_DECODE_TUNE=1` for gfx950 MLA decode geometry), graph bs up to 256, fp8 kvcache; DSPARK supported. Activation-quant and fused-KDA-decode knobs: [AMD ROCm/AITER environment](#amd-env) | | MI350X/MI355X 1×8 | TP8/DCP8 ROCm/AITER (Unified Balanced) | AITER A8W4 FlyDSL MoE, AITER prefill/decode attention with sharded target MLA KV, graph bs up to 256, fp8 kvcache; DSPARK supported. Activation-quant and fused-KDA-decode knobs: [AMD ROCm/AITER environment](#amd-env) |
| Ascend A3 Series 4×8 (32 cards / 64 dies) | TP64/DP4 + DeepEP | PD-mixed `Unified` only; DSPARK baked in; pin `GLOO`/`HCCL_SOCKET_IFNAME` on every node | | Ascend A3 Series 4×8 (32 cards / 64 dies) | TP64/DP4 + DeepEP | PD-mixed `Unified` only; DSPARK baked in; pin `GLOO`/`HCCL_SOCKET_IFNAME` on every node |
| Ascend 950PR/DT Series 4×8 | TP32/dp1 + DeepEP | PD-mixed `Unified` only; DSPARK baked in; shared experts / dense MLP shard over attention-TP (`--shared-experts-tp-size 4`); radix cache off; pin `GLOO`/`HCCL_SOCKET_IFNAME` on every node |
**DCP notes** — the DCP cells are Balanced and High-Throughput on every Blackwell platform, in both the `Unified` and `Decode` roles: **Blackwell DCP notes** — the DCP cells are Balanced and High-Throughput on every Blackwell platform, in both the `Unified` and `Decode` roles:
- DCP is the only axis that shards the TP-replicated MLA KV; Low-Latency skips it. - DCP is the only axis that shards the TP-replicated MLA KV; Low-Latency skips it.
- Leave `--dcp-comm-backend` unset (fabric-resolved: `fi_a2a` on GB200/GB300, `a2a` on B200/B300). - Leave `--dcp-comm-backend` unset (fabric-resolved: `fi_a2a` on GB200/GB300, `a2a` on B200/B300).
@@ -239,7 +258,7 @@ Pending update...
### 3.2 Tool Calling ### 3.2 Tool Calling
Enable the `kimi_k3` tool-call parser (toggle **Tool Call Parser** in the **Parsers** card of the [Playground above](#playground)) to surface structured tool calls via `message.tool_calls`. Because K3 is a thinking model, the follow-up turn may put text in `reasoning_content` as well as `content` — print both. (Not yet supported on the Ascend A3 Series.) Enable the `kimi_k3` tool-call parser (toggle **Tool Call Parser** in the **Parsers** card of the [Playground above](#playground)) to surface structured tool calls via `message.tool_calls`. Because K3 is a thinking model, the follow-up turn may put text in `reasoning_content` as well as `content` — print both. (Not yet supported on the Ascend NPU recipes — A3 and A5.)
<Accordion title="Tool Calling Example (Python)"> <Accordion title="Tool Calling Example (Python)">
@@ -31,6 +31,8 @@ This section provides a progressive guide from quick deployment to performance t
### 3.1 Basic Configuration ### 3.1 Basic Configuration
The Nemotron3-Nano series offers models in various sizes and architectures, optimized for different hardware platforms including NVIDIA GPUs and Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)).
**Interactive Command Generator**: select hardware, model variant, and common knobs to generate a launch command. **Interactive Command Generator**: select hardware, model variant, and common knobs to generate a launch command.
<Nemotron3NanoDeployment /> <Nemotron3NanoDeployment />
@@ -111,11 +111,9 @@ docker pull lmsysorg/sglang-rocm:v0.5.19-rocm720-mi35x-20260911
For the full Docker setup and other installation methods, please refer to the [official SGLang installation guide](../../../docs/get-started/install). For the full Docker setup and other installation methods, please refer to the [official SGLang installation guide](../../../docs/get-started/install).
For SGLang CPU installation, please refer to the [CPU version installation guide](../../../docs/hardware-platforms/cpu_server#installation).
## 3. Model Deployment ## 3. Model Deployment
This section provides deployment configurations optimized for different hardware platforms and use cases. This section provides deployment configurations optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)), and Intel Xeon CPUs.
### 3.1 Basic Configuration ### 3.1 Basic Configuration
+1 -3
View File
@@ -26,15 +26,13 @@ SGLang offers multiple installation methods. You can choose the most suitable in
Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions. Please refer to the [official SGLang installation guide](../../../docs/get-started/install) for installation instructions.
For SGLang CPU installation, please refer to the [CPU version installation guide](../../../docs/hardware-platforms/cpu_server#installation).
## 3. Model Deployment ## 3. Model Deployment
This section provides deployment configurations optimized for different hardware platforms and use cases. This section provides deployment configurations optimized for different hardware platforms and use cases.
### 3.1 Basic Configuration ### 3.1 Basic Configuration
The Qwen3 series offers models in various sizes and architectures, optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, and Intel Xeon CPUs. The recommended launch configurations vary by hardware and model size. The Qwen3 series offers models in various sizes and architectures, optimized for different hardware platforms including NVIDIA GPUs, AMD GPUs, Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)), and Intel Xeon CPUs. The recommended launch configurations vary by hardware and model size.
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform, model size, quantization method, and thinking capabilities. **Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform, model size, quantization method, and thinking capabilities.
@@ -2,7 +2,6 @@
title: MiMo-V2.5 title: MiMo-V2.5
metatags: metatags:
description: "Deploy XiaomiMiMo MiMo-V2.5-Pro (1.02T MoE, text) and MiMo-V2.5 (310B MoE, multimodal) with SGLang — EAGLE speculative decoding, hybrid attention, and 1M-token context." description: "Deploy XiaomiMiMo MiMo-V2.5-Pro (1.02T MoE, text) and MiMo-V2.5 (310B MoE, multimodal) with SGLang — EAGLE speculative decoding, hybrid attention, and 1M-token context."
tag: NEW
--- ---
## 1. Model Introduction ## 1. Model Introduction
@@ -0,0 +1,124 @@
---
title: MiMo-V2.6
description: "Deploy MiMo-V2.6-Flash and Pro with SGLang on NVIDIA B300 GPUs, using MXFP4 MoE weights, BF16 routing, DFlash decoding, and a 1M-token context window."
tag: NEW
---
## Deployment
The recipes below target **MiMo-V2.6-Flash on 4× B300** and **MiMo-V2.6-Pro on 8× B300**, each on a single node. Both use MXFP4 MoE weights, a BF16 MoE router, and DFlash speculative decoding. B300 validation was reported by the model team; the launch settings come from [SGLang PR #40448](https://github.com/sgl-project/sglang/pull/40448).
<a id="install" />
<Accordion title="Install SGLang with MiMo-V2.6 support">
BF16 MoE routing and MXFP4 expert loading for MiMo V2.6 landed in [SGLang PR #40448](https://github.com/sgl-project/sglang/pull/40448) and are on `main`, so a recent nightly already carries them. Prepare an NVIDIA CUDA environment with FlashAttention 4, DeepGEMM, and DeepEP available. See the [installation guide](/docs/get-started/install) for platform prerequisites.
<Tabs>
<Tab title="Python (source)">
Install from source. The commit below is the revision these recipes were captured at; build from `main` instead if you want later fixes.
```bash Command
git clone https://github.com/sgl-project/sglang.git sglang-mimo-v2.6
cd sglang-mimo-v2.6
git checkout 983e643854f15cf9ef4370a49dfd74b6af54c3e3
python3 -m pip install -e ./python
```
Then run the **Python** command from the panel below. Checkpoint paths refer to directories on this host.
</Tab>
<Tab title="Docker (official image)">
Pull the official nightly image, which already includes the MiMo-V2.6 support:
```bash Command
docker pull lmsysorg/sglang:dev
```
</Tab>
</Tabs>
</Accordion>
Select **Flash** or **Pro** to generate its command. Each variant has one base recipe; **Balanced** identifies that recipe and does not imply a measured throughput or latency optimum. The `/model/...` checkpoint and DFlash paths in the generated command are editable defaults, not download locations — set them to your own paths under **⚙ Env** before launching. Use the panel's cURL example to send a request to port **30000** after the server is ready.
import { Deployment } from "/src/snippets/_deployment.jsx";
import { config } from "/src/snippets/configs/XiaomiMiMo/mimo-v2.6.jsx";
<Deployment config={config} />
## Playground
Experiment with reasoning and tool-call parsers, or disable speculative decoding for comparison. These overrides inherit the selected Flash or Pro recipe; they require separate validation. Parallelism and attention backends remain at the PR settings.
import { Playground } from "/src/snippets/_playground.jsx";
<Playground config={config} />
## 1. Model introduction
**MiMo-V2.6-Flash** is an open-source foundation model developed by Xiaomi. Its Mixture-of-Experts architecture has **309B total parameters and 15B activated per token**, with hybrid attention for computational efficiency. It supports a **1M-token context window** and **native multimodal capabilities**, and is designed for coding, visual understanding, general-purpose assistance, and research in agentic workflows, including complex tasks that require many steps.
**MiMo-V2.6-Pro** is Xiaomi's flagship foundation model, with **1.02T total parameters and 42B activated per token**, designed for demanding workloads. It also supports a **1M-token context window** and **native multimodal capabilities**, with an emphasis on coding, visual understanding, general-purpose assistance, research, and long-horizon agentic tasks.
<table style={{width: "100%", borderCollapse: "collapse", tableLayout: "fixed"}}>
<thead>
<tr style={{borderBottom: "2px solid #d55816"}}>
<th style={{textAlign: "left", padding: "10px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>Variant</th>
<th style={{textAlign: "left", padding: "10px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Total parameters</th>
<th style={{textAlign: "left", padding: "10px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>Context window</th>
<th style={{textAlign: "left", padding: "10px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>B300 recipe</th>
</tr>
</thead>
<tbody>
<tr>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>MiMo-V2.6-Flash</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>309B (15B active)</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>1M tokens</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>4 GPUs, TP=EP=4</td>
</tr>
<tr>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>MiMo-V2.6-Pro</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>1.02T (42B active)</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>1M tokens</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>8 GPUs, TP=EP=8</td>
</tr>
</tbody>
</table>
**License:** MIT, continuing the MiMo-V2.5 licensing as confirmed by the model team.
**Architecture references:** The [MiMo-V2.6-Flash-RL](https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Flash-RL) and [MiMo-V2.6-Pro-RL](https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Pro-RL) model cards document this generation directly — the hybrid sliding-window/global attention backbone, the omnimodal encoders, and the published evaluation scores — and each repository also ships the MiMo-V2.6 technical report. Both checkpoints declare `store_dtype: mxfp4` alongside `moe_router_dtype: bfloat16` in `config.json`, which is the expert-only MXFP4 quantization and BF16 routing these recipes depend on, and both carry the paired DFlash drafter in a `dflash/` subdirectory.
## 2. Configuration tips
### Parallelism and precision
- **Flash:** `--tp 4 --ep 4`; **Pro:** `--tp 8 --ep 8`. Both keep `--dp 1 --pp-size 1`. TP and EP use the same GPU ranks; do not multiply them to determine the GPU count.
- **MoE compute and communication:** Keep `--moe-runner-backend deep_gemm --moe-a2a-backend deepep --deepep-mode auto`, together with `--moe-dense-tp-size 1` and `--enable-dp-lm-head`, as in the PR.
- **Precision:** MXFP4 describes the MoE expert weights, not the entire model. BF16 router weights are selected by the checkpoint's `moe_router_dtype`; the support commit computes router output logits in FP32. The PR relies on checkpoint quantization metadata and does not add a `--quantization` override.
- **Attention:** Keep `--attention-backend fa4` and `--mm-attention-backend fa4` on B300. The recipe also enables `--mm-enable-dp-encoder` for multimodal encoding.
### Context length and memory
Both commands set `--context-length 1048576`. This is the configured per-request context limit; it does not establish that 64 simultaneous 1M-token requests fit in memory.
Flash uses `--mem-fraction-static 0.6 --swa-full-tokens-ratio 0.03`; Pro uses `--mem-fraction-static 0.7 --swa-full-tokens-ratio 0.08`. The memory fraction budgets weights and KV cache, while the SWA ratio controls sliding-window versus full-attention KV allocation. Preserve these model-specific values for the base recipe.
The shared prefill settings are `--chunked-prefill-size 49152 --max-prefill-tokens 65536`. Both commands cap running requests and decode CUDA-graph batch size at **64**, disable prefill CUDA graphs, and use page size **1**. `--enable-cache-report` and `--log-level-http warning` retain the PR's reporting settings.
### DFlash and caching
The base recipe uses `--speculative-algorithm DFLASH --speculative-num-draft-tokens 8`. Supply the DFlash checkpoint paired with the selected target model; the draft path is independently editable and does not have to be a subdirectory of the target checkpoint.
At the pinned commit, DFlash on NVIDIA GPUs requires PP=1 and rejects DP-attention. Do not add `--enable-dp-attention` or copy EAGLE-specific settings from the V2.5 cookbook.
## 3. Reasoning and tool calling
Both launch commands enable `--reasoning-parser mimo --tool-call-parser mimo`. The MiMo reasoning parser uses the request's `chat_template_kwargs.enable_thinking` setting: set it to `true` for reasoning or `false` for a direct answer. The command panel's cURL example enables reasoning explicitly.
For OpenAI-compatible clients, read reasoning from `choices[0].message.reasoning_content`, the final answer from `choices[0].message.content`, and structured tool requests from `choices[0].message.tool_calls`. Preserve both reasoning and answer fields when inspecting responses. Use the checkpoint's generation defaults unless your application needs explicit sampling settings.
Runtime throughput, latency, accuracy, and example responses for V2.6 will be added when measurements from these checkpoints are available.
+1 -1
View File
@@ -154,7 +154,7 @@ metatags:
<Card <Card
title="Xiaomi" title="Xiaomi"
mode="card" mode="card"
href="/cookbook/autoregressive/Xiaomi/MiMo-V2.5" href="/cookbook/autoregressive/Xiaomi/MiMo-V2.6"
img="/cards/logos/xiaomi.png" img="/cards/logos/xiaomi.png"
/> />
<Card <Card
@@ -400,3 +400,9 @@ remains accepted for compatibility, but new clients should use `extra_body`:
- `use_resolution_template`: accepted for vLLM-Omni request compatibility. - `use_resolution_template`: accepted for vLLM-Omni request compatibility.
- `use_system_prompt`: whether to add the Cosmos3 system prompt to the chat template. - `use_system_prompt`: whether to add the Cosmos3 system prompt to the chat template.
- `guardrails` or `use_guardrails`: per-request guardrail toggle when the server started with guardrails enabled. - `guardrails` or `use_guardrails`: per-request guardrail toggle when the server started with guardrails enabled.
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -81,3 +81,9 @@ with open("ernie_image.png", "wb") as f:
- `--performance-mode auto` keeps conservative defaults while preserving explicit user flags. - `--performance-mode auto` keeps conservative defaults while preserving explicit user flags.
- If the checkpoint includes a PE component, SGLang loads it automatically with the native Ministral3 runtime. Use `--layerwise-offload-components pe` when the local PE decoder needs to trade latency for lower GPU memory usage. - If the checkpoint includes a PE component, SGLang loads it automatically with the native Ministral3 runtime. Use `--layerwise-offload-components pe` when the local PE decoder needs to trade latency for lower GPU memory usage.
- Treat FSDP, SP/Ulysses/Ring, and TP as explicit benchmark knobs. Measure the target resolution, step count, and GPU type before making them production defaults. - Treat FSDP, SP/Ulysses/Ring, and TP as explicit benchmark knobs. Measure the target resolution, step count, and GPU type before making them production defaults.
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="image" />
+7 -1
View File
@@ -34,7 +34,7 @@ This section provides deployment configurations optimized for different hardware
FLUX models are optimized for high-quality image generation. The recommended launch configurations vary by hardware and model version. FLUX models are optimized for high-quality image generation. The recommended launch configurations vary by hardware and model version.
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform and model version. SGLang supports serving FLUX on NVIDIA B200, H200, H100, and AMD MI355X, MI325X, MI300X GPUs and Ascend A2/A3 Series NPUs. **Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform and model version. SGLang supports serving FLUX on NVIDIA B200, H200, H100, and AMD MI355X, MI325X, MI300X GPUs, Ascend A2, A3 NPUs Series NPUs and Intel Arc B-series graphics(codename: BMG (Battlemage)).
<FluxDeployment /> <FluxDeployment />
@@ -407,3 +407,9 @@ Test Environment:
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="flux" />
@@ -187,3 +187,9 @@ response = client.images.generate(
``` ```
Base Ideogram 4 presets are `V4_DEFAULT_20`, `V4_QUALITY_48`, and `V4_TURBO_12`. The fal variants automatically select `V4_FAST_20` and `V4_INSTANT_8`, respectively. A preset controls both `num_inference_steps` and guidance, so do not set those fields directly. Base Ideogram 4 presets are `V4_DEFAULT_20`, `V4_QUALITY_48`, and `V4_TURBO_12`. The fal variants automatically select `V4_FAST_20` and `V4_INSTANT_8`, respectively. A preset controls both `num_inference_steps` and guidance, so do not set those fields directly.
## 5. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="image" />
@@ -238,3 +238,9 @@ Use `quality=lossless` for this recipe. High-mode output did not pass the separa
- For **2-GPU latency**, try **Ulysses SP** (`--num-gpus 2 --ulysses-degree 2`) on both single-shot and multi-shot runs. Use **TP** when you need a different sharding strategy or more than two GPUs. - For **2-GPU latency**, try **Ulysses SP** (`--num-gpus 2 --ulysses-degree 2`) on both single-shot and multi-shot runs. Use **TP** when you need a different sharding strategy or more than two GPUs.
- Set `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` for long multi-shot SP sessions. - Set `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` for long multi-shot SP sessions.
- JoyEcho outputs per-shot mp4 files with synchronized audio. There is no built-in two-stage HQ upscaling path like LTX-2.3 HQ. - JoyEcho outputs per-shot mp4 files with synchronized audio. There is no built-in two-stage HQ upscaling path like LTX-2.3 HQ.
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
+6
View File
@@ -334,3 +334,9 @@ Peak Memory Mean (MB): 37466.40
Peak Memory Median (MB): 37466.00 Peak Memory Median (MB): 37466.00
============================================================ ============================================================
``` ```
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="image" />
@@ -248,3 +248,9 @@ Some community LoRAs only include weights for transformer blocks. In that case,
- Use `--ltx2-two-stage-device-mode resident` on high-VRAM GPUs if latency matters more than memory usage. - Use `--ltx2-two-stage-device-mode resident` on high-VRAM GPUs if latency matters more than memory usage.
- Use `--ltx2-two-stage-device-mode original` when comparing against official two-stage behavior. - Use `--ltx2-two-stage-device-mode original` when comparing against official two-stage behavior.
- Keep `--width` and `--height` aligned with the target model resolution; for LTX models, these are output video dimensions. - Keep `--width` and `--height` aligned with the target model resolution; for LTX models, these are output video dimensions.
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -184,3 +184,9 @@ LingBot World 2.0 uses raw-frame websocket GT plus per-chunk latency guards for
- Use the realtime endpoint for interactive sessions: `/v1/realtime_video/generate`. - Use the realtime endpoint for interactive sessions: `/v1/realtime_video/generate`.
- Prefer WebP preview transport for interactive testing; use raw-frame transport for consistency checks. - Prefer WebP preview transport for interactive testing; use raw-frame transport for consistency checks.
- Long-running sessions should be validated with raw-frame consistency before changing causal cache, condition sampling, or VAE decode behavior. - Long-running sessions should be validated with raw-frame consistency before changing causal cache, condition sampling, or VAE decode behavior.
## 7. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -159,3 +159,9 @@ LingBot World uses raw-frame websocket GT plus per-chunk latency guards for cons
- Use the realtime endpoint for interactive sessions: `/v1/realtime_video/generate`. - Use the realtime endpoint for interactive sessions: `/v1/realtime_video/generate`.
- Prefer WebP preview transport for interactive testing; use raw-frame transport for consistency checks. - Prefer WebP preview transport for interactive testing; use raw-frame transport for consistency checks.
- Long-running sessions should be validated with raw-frame consistency before changing causal cache, condition sampling, or VAE decode behavior. - Long-running sessions should be validated with raw-frame consistency before changing causal cache, condition sampling, or VAE decode behavior.
## 7. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -117,3 +117,9 @@ The image is used as the first-frame condition.
- SGLang supports T2V sizes 1280x704, 704x1280, 832x480, and 480x832. - SGLang supports T2V sizes 1280x704, 704x1280, 832x480, and 480x832.
- I2V request images follow the Wan TI2V preprocessing path in SGLang. This is different from the original LongLive dataset resize path. - I2V request images follow the Wan TI2V preprocessing path in SGLang. This is different from the original LongLive dataset resize path.
- For multi-shot runs, set `num_frames` to match `len(shot_prompts) * chunks_per_shot * 8` latent frames, that is `num_frames = (len(shot_prompts) * chunks_per_shot * 8 - 1) * 4 + 1`. - For multi-shot runs, set `num_frames` to match `len(shot_prompts) * chunks_per_shot * 8` latent frames, that is `num_frames = (len(shot_prompts) * chunks_per_shot * 8 - 1) * 4 + 1`.
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
+6
View File
@@ -266,3 +266,9 @@ python3 -m sglang.multimodal_gen.benchmarks.bench_serving \
--task image-to-video --dataset vbench --num-prompts 20 --max-concurrency 20 \ --task image-to-video --dataset vbench --num-prompts 20 --max-concurrency 20 \
--port 30002 --port 30002
``` ```
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -2055,3 +2055,9 @@ For a measured lower-count AMD deployment, set both `--num-gpus` and
`--ulysses-degree` to 4, 2, or 1. AITER packed attention matched segment-wise `--ulysses-degree` to 4, 2, or 1. AITER packed attention matched segment-wise
BF16 SDPA at cosine similarity `0.9999991655` on MI355X and `0.9999991059` on BF16 SDPA at cosine similarity `0.9999991655` on MI355X and `0.9999991059` on
MI300X. MI300X.
## 10. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="minimax-h3" />
@@ -14,9 +14,10 @@ import { config } from '/src/snippets/configs/Qwen/qwen-image-2.1.jsx';
Install the runtime dependencies with `uv pip install "sglang[diffusion]" --prerelease=allow`, Install the runtime dependencies with `uv pip install "sglang[diffusion]" --prerelease=allow`,
then install this integration from its source checkout with then install this integration from its source checkout with
`uv pip install -e "python[diffusion]"`. Use an authorized checkpoint directory in `uv pip install -e "python[diffusion]"`. The picker uses `Qwen/Qwen-Image-2.1`;
place of `/models/qwen-image-2.1`. The recipes below target NVIDIA CUDA on Linux; you can also set a local checkpoint directory under **Variables**. The recipes
the hardware picker selects a tested single-GPU recipe for the full checkpoint. target NVIDIA CUDA on Linux; the picker marks which single-GPU workloads have
been verified with the full checkpoint.
<Deployment config={config} /> <Deployment config={config} />
@@ -27,7 +28,7 @@ steps, and output count. Set reference PNG paths under **Variables**; edits
upload files from the machine running cURL, so they need not exist on the server. upload files from the machine running cURL, so they need not exist on the server.
Hardware selection applies the recommended placement for that GPU. H200, Hardware selection applies the recommended placement for that GPU. H200,
B200, and RTX PRO 6000 96GB keep weights resident; RTX 5090 and RTX 4090 B200, RTX PRO 6000 96GB, and DGX Spark keep weights resident; RTX 5090 and RTX 4090
offload selected components to fit the full pipeline. offload selected components to fit the full pipeline.
Untested topologies and feature combinations remain selectable and are labeled Untested topologies and feature combinations remain selectable and are labeled
**Unverified**. Invalid topology combinations disable Copy. This integration **Unverified**. Invalid topology combinations disable Copy. This integration
@@ -51,6 +52,8 @@ PY
The picker defaults to native BF16/FP32 precision, exact attention, eager The picker defaults to native BF16/FP32 precision, exact attention, eager
execution, and full-image VAE decoding. execution, and full-image VAE decoding.
Commands omit default values, including one GPU, encoder auto scheduling, and
batch size one. Explicit placement and attention overrides preserve each recipe.
| GPU | Placement / attention | Generation | Edit | Peak VRAM | | GPU | Placement / attention | Generation | Edit | Peak VRAM |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
@@ -58,6 +61,7 @@ execution, and full-image VAE decoding.
| B200 192GB | Resident / FlashAttention | 2.46 s | 3.02 s | 38.5 GiB | | B200 192GB | Resident / FlashAttention | 2.46 s | 3.02 s | 38.5 GiB |
| RTX PRO 6000 96GB | Resident / Torch SDPA | 8.03 s | 9.63 s | 38.4 GiB | | RTX PRO 6000 96GB | Resident / Torch SDPA | 8.03 s | 9.63 s | 38.4 GiB |
| RTX 4090 24GB | DiT and VAE resident, encoder layerwise offload / FlashAttention | 18.68 s | 21.68 s | 22.7 GiB | | RTX 4090 24GB | DiT and VAE resident, encoder layerwise offload / FlashAttention | 18.68 s | 21.68 s | 22.7 GiB |
| DGX Spark 128GB unified | Resident / Torch SDPA | 35.36 s | 42.23 s | — (unified) |
Measured on 2026-09-20 at 1024×1024, 40 steps, CFG 1, and one RGBA PNG per Measured on 2026-09-20 at 1024×1024, 40 steps, CFG 1, and one RGBA PNG per
request. Times are median HTTP latency after warmup, including PNG serialization request. Times are median HTTP latency after warmup, including PNG serialization
@@ -68,6 +72,25 @@ RTX 5090 uses DiT layerwise offload and Torch SDPA; its recipe has not been
retested with the updated checkpoint. Both RTX 5090 and RTX PRO 6000 use SDPA retested with the updated checkpoint. Both RTX 5090 and RTX PRO 6000 use SDPA
when FlashAttention is selected in this runtime. CPU offload requires host RAM. when FlashAttention is selected in this runtime. CPU offload requires host RAM.
### DGX Spark
Select **DGX Spark** for one GB10 GPU on Linux ARM64 with CUDA 13. Use the
source installation above. The recommended configuration keeps all components
resident, uses native BF16/FP32 precision, and lets the runtime select Torch SDPA:
```bash Command
sglang serve \
--model-path Qwen/Qwen-Image-2.1 \
--performance-mode speed
```
The [128 GB unified memory](https://docs.nvidia.com/dgx/dgx-spark/hardware.html)
is shared by the CPU and GPU. CPU offload is unnecessary for the verified
single-image 1024×1024 workload. Keep full-image VAE decoding and eager execution.
Generation, editing, transparent generation, and transparent editing were
verified with PyTorch 2.13.0+cu130. Spark reports no separate VRAM usage in `nvidia-smi`.
This recipe covers one Spark; multi-node deployment and batching remain unverified.
### Batching ### Batching
Keep **Request batching → Off** and **Outputs → 1** for interactive use. Keep **Request batching → Off** and **Outputs → 1** for interactive use.
@@ -119,29 +142,24 @@ including partly transparent edges, without thresholding or background removal.
## 4. Offline requests ## 4. Offline requests
Defaults are 1024×1024, 40 steps, CFG 1, and seed 42; output saving is enabled.
For GPUs that need offload, also pass the placement flags from the picker.
### Text-to-image ### Text-to-image
```bash Command ```bash Command
sglang generate \ sglang generate \
--model-path /models/qwen-image-2.1 \ --model-path Qwen/Qwen-Image-2.1 \
--model-id Qwen-Image-2.1 \ --prompt "A capybara reading a book by candlelight"
--prompt "A capybara reading a book by candlelight" \
--width 1024 --height 1024 \
--num-inference-steps 40 --guidance-scale 1 \
--seed 0 --save-output
``` ```
### Image-conditioned editing ### Image-conditioned editing
```bash Command ```bash Command
sglang generate \ sglang generate \
--model-path /models/qwen-image-2.1 \ --model-path Qwen/Qwen-Image-2.1 \
--model-id Qwen-Image-2.1 \
--image-path /path/to/input.png \ --image-path /path/to/input.png \
--prompt "Move the scene to a snowy mountain at sunrise" \ --prompt "Move the scene to a snowy mountain at sunrise"
--width 1024 --height 1024 \
--num-inference-steps 40 --guidance-scale 1 \
--seed 0 --save-output
``` ```
Height and width must be positive multiples of 32. Reference images preserve Height and width must be positive multiples of 32. Reference images preserve
@@ -203,7 +221,7 @@ See the [GGUF guide](/docs/sglang-diffusion/quantization#gguf).
NVFP4 requires Blackwell and compatible ModelOpt exports. Select the component NVFP4 requires Blackwell and compatible ModelOpt exports. Select the component
directories using `--component-paths.transformer` and/or directories using `--component-paths.transformer` and/or
`--component-paths.text_encoder`. Keep the FlashInfer backend at `auto` on `--component-paths.text_encoder`. Keep the FlashInfer backend at `auto` on
RTX 5090 and RTX PRO 6000: TensorRT-LLM FP4 GEMM does not support SM120. RTX 5090, RTX PRO 6000, and DGX Spark: TensorRT-LLM FP4 GEMM does not support SM12.x.
These GPUs remain unverified for this model's NVFP4 exports. See the These GPUs remain unverified for this model's NVFP4 exports. See the
[NVFP4 guide](/docs/sglang-diffusion/quantization#modelopt-nvfp4). [NVFP4 guide](/docs/sglang-diffusion/quantization#modelopt-nvfp4).
@@ -216,3 +234,11 @@ Keep eager execution as the default. Breakable CUDA Graph replay requires
matching resolution and condition-prefix length; unseen shapes run eagerly. matching resolution and condition-prefix length; unseen shapes run eagerly.
Text buckets alone do not guarantee replay. SageAttention and Cache-DiT can Text buckets alone do not guarantee replay. SageAttention and Cache-DiT can
change numerical results and require quality checks for your workload. change numerical results and require quality checks for your workload.
### Cache-DiT
Enable `--enable-cache-dit true` or `SGLANG_CACHE_DIT_ENABLED=true`. 2.1 prefix
KV is per layer: each block slices caches by `_layer_id`. Cache-DiT wraps
`transformer_blocks` and forwards the same extras to every layer; without that
slice, later layers reuse layer 0 and the image collapses to color noise.
See the [Cache-DiT guide](/docs/sglang-diffusion/cache_dit).
@@ -315,3 +315,9 @@ Peak Memory Mean (MB): 47971.49
Peak Memory Median (MB): 47971.29 Peak Memory Median (MB): 47971.29
============================================================ ============================================================
``` ```
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="qwen-image-edit" />
@@ -428,3 +428,9 @@ Test Environment:
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="qwen-image" />
@@ -487,3 +487,9 @@ At WebSocket `init` the realtime adapter fills SANA-WM defaults that differ from
<Note> <Note>
`guidance_scale` applies to the dense path (§4) only; the distilled streaming path uses `streaming_cfg_scale` (default `1.0`, i.e. no CFG) so a `guidance_scale` override never accidentally enables CFG on the streaming stage. `denoising_step_list = (1000, 960, 889, 727, 0)` is the official 4-step streaming schedule (it must end in 0). `guidance_scale` applies to the dense path (§4) only; the distilled streaming path uses `streaming_cfg_scale` (default `1.0`, i.e. no CFG) so a `guidance_scale` override never accidentally enables CFG on the streaming stage. `denoising_step_list = (1000, 960, 889, 727, 0)` is the official 4-step streaming schedule (it must end in 0).
</Note> </Note>
## 10. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
+6
View File
@@ -381,3 +381,9 @@ You can use the built-in SGLang diffusion benchmark script to evaluate Wan2.1 pe
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
+6
View File
@@ -459,3 +459,9 @@ Test Environment:
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="video" />
@@ -29,7 +29,7 @@ This section provides deployment configurations optimized for different hardware
Z-Image-Turbo is optimized for high-quality image generation with only 8 inference steps. The recommended launch configurations vary by hardware. Z-Image-Turbo is optimized for high-quality image generation with only 8 inference steps. The recommended launch configurations vary by hardware.
**Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform. **Interactive Command Generator**: Use the configuration selector below to automatically generate the appropriate deployment command for your hardware platform and model version. SGLang supports serving Z-Image-Turbo on NVIDIA B200, H200, H100, and AMD MI355X, MI325X, MI300X GPUs, Ascend A2, A3 NPUs and Intel Arc Pro B-Series GPUs(codename: BMG (Battlemage)).
<ZImageTurboDeployment /> <ZImageTurboDeployment />
@@ -366,3 +366,9 @@ Test Environment:
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
## 6. Run in ComfyUI
import { ComfyUISupport } from '/src/snippets/diffusion/comfyui-support.jsx';
<ComfyUISupport model="z-image" />
+6
View File
@@ -649,6 +649,10 @@
"source": "/docs/hardware-platforms/ascend-npus/best_practice/mimo_v2_flash", "source": "/docs/hardware-platforms/ascend-npus/best_practice/mimo_v2_flash",
"destination": "/docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_flash" "destination": "/docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_flash"
}, },
{
"source": "/docs/hardware-platforms/ascend-npus/best_practice/mimo_v2_5_pro",
"destination": "/docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_5_pro"
},
{ {
"source": "/docs/hardware-platforms/ascend-npus/best_practice/qwen3-8b", "source": "/docs/hardware-platforms/ascend-npus/best_practice/qwen3-8b",
"destination": "/docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_8b" "destination": "/docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_8b"
@@ -1142,6 +1146,7 @@
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/kimi_k2_6", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/kimi_k2_6",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/minimax_m2_5", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/minimax_m2_5",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_flash", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_flash",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/mimo_v2_5_pro",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_8b", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_8b",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_32b", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_32b",
"docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_30b_a3b", "docs/hardware-platforms/ascend-npus/model-deployment/best-practices/qwen3_30b_a3b",
@@ -1428,6 +1433,7 @@
{ {
"group": "Xiaomi", "group": "Xiaomi",
"pages": [ "pages": [
"cookbook/autoregressive/Xiaomi/MiMo-V2.6",
"cookbook/autoregressive/Xiaomi/MiMo-V2.5", "cookbook/autoregressive/Xiaomi/MiMo-V2.5",
"cookbook/autoregressive/Xiaomi/MiMo-V2-Flash" "cookbook/autoregressive/Xiaomi/MiMo-V2-Flash"
] ]
+10 -1
View File
@@ -6,7 +6,7 @@ metatags:
HiSparse reduces per-request GPU memory consumption during the decode phase by maintaining only a small "hot" KV buffer on GPU while keeping complete KV data in CPU pinned memory. Combined with PD disaggregation, it enables significantly higher decode concurrency. HiSparse reduces per-request GPU memory consumption during the decode phase by maintaining only a small "hot" KV buffer on GPU while keeping complete KV data in CPU pinned memory. Combined with PD disaggregation, it enables significantly higher decode concurrency.
> **Prerequisites**: HiSparse works with models that use **DeepSeek Sparse Attention (DSA)** architectures (e.g., DeepSeek-V3.2, GLM-5.1) and **DeepSeek V4**. These models natively select a subset of tokens for attention, making it possible to keep only the top-k KV on GPU while storing the full KV in host memory — without accuracy loss. Additionally, HiSparse currently requires **PD disaggregation mode** and is enabled on the **decode instance** only. > **Prerequisites**: HiSparse works with models that use **DeepSeek Sparse Attention (DSA)** architectures (e.g., DeepSeek-V3.2, GLM-5.1), **DeepSeek V4**, and **MiniMax M3**. These models natively select a subset of tokens for attention, making it possible to keep only the top-k KV on GPU while storing the full KV in host memory — without accuracy loss. Additionally, HiSparse currently requires **PD disaggregation mode** and is enabled on the **decode instance** only.
## Why HiSparse? ## Why HiSparse?
@@ -165,6 +165,15 @@ python3 -m sglang.launch_server \
> **Note**: For DSA models, `--kv-cache-dtype` defaults to `auto`, which resolves to `fp8_e4m3` on SM100+ (Blackwell) and `bfloat16` on older architectures. The DSA decode backend is automatically selected based on KV dtype (`bfloat16` → `flashmla_sparse`, `fp8_e4m3` → `flashmla_kv`), except for GLM DSA models on SM120/SM121 with `fp8_e4m3`, which use `flashinfer_sparse_mla`. DSA backend flags apply only to DSA models; DeepSeek V4 uses its own `dsv4` attention backend. > **Note**: For DSA models, `--kv-cache-dtype` defaults to `auto`, which resolves to `fp8_e4m3` on SM100+ (Blackwell) and `bfloat16` on older architectures. The DSA decode backend is automatically selected based on KV dtype (`bfloat16` → `flashmla_sparse`, `fp8_e4m3` → `flashmla_kv`), except for GLM DSA models on SM120/SM121 with `fp8_e4m3`, which use `flashinfer_sparse_mla`. DSA backend flags apply only to DSA models; DeepSeek V4 uses its own `dsv4` attention backend.
### MiniMax M3
Dense-layer K/V and index K stay on GPU; sparse-layer K/V use host memory plus a GPU working set.
- Use TP 4 or greater, with the same TP size and PP 1 on both PD instances.
- Use `--attention-backend triton`, `--mm-attention-backend triton_attn`, `--disable-prefill-cuda-graph`, and `--disable-radix-cache`.
- Set `device_buffer_size` to at least 2048 in `--hisparse-config`; `top_k` does not override the model's selection width.
- PD retraction backup is not supported. Use `--num-reserved-decode-tokens` to reserve capacity for the expected output length.
### Benchmark ### Benchmark
```bash Command ```bash Command
@@ -1154,13 +1154,13 @@ Combining `--enable-response-store` with `--disaggregation-mode=prefill` or `dec
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--reasoning-parser`</td> <td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--reasoning-parser`</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Specify the parser for reasoning models. Use `auto` to detect the parser from the model's chat template.</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Specify the parser for reasoning models. Use `auto` to detect the parser from the model's chat template.</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>`None`</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>`None`</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>auto</code>, <code>apertus2509</code>, <code>deepseek-r1</code>, <code>deepseek-v3</code>, <code>deepseek-v4</code>, <code>dots</code>, <code>glm45</code>, <code>ling3</code>, <code>hunyuan</code>, <code>gpt-oss</code>, <code>k2_horizon</code>, <code>kimi</code>, <code>kimi_k2</code>, <code>kimi_k3</code>, <code>mimo</code>, <code>muse</code>, <code>poolside_v1</code>, <code>qwen3</code>, <code>qwen3-thinking</code>, <code>minimax</code>, <code>minimax-append-think</code>, <code>minimax-m3</code>, <code>step3</code>, <code>step3p5</code>, <code>mistral</code>, <code>nemotron_3</code>, <code>interns1</code>, <code>gemma4</code>, <code>inkling</code>, <code>cohere_command4</code></td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>auto</code>, <code>apertus2509</code>, <code>deepseek-r1</code>, <code>deepseek-v3</code>, <code>deepseek-v4</code>, <code>dots</code>, <code>glm45</code>, <code>ling3</code>, <code>hunyuan</code>, <code>gpt-oss</code>, <code>k2_horizon</code>, <code>kimi</code>, <code>kimi_k2</code>, <code>kimi_k3</code>, <code>mimo</code>, <code>muse</code>, <code>poolside_v1</code>, <code>qwen3</code>, <code>qwen3-thinking</code>, <code>minimax</code>, <code>minimax-append-think</code>, <code>minimax-m3</code>, <code>step3</code>, <code>step3p5</code>, <code>mistral</code>, <code>nemotron_3</code>, <code>interns1</code>, <code>gemma4</code>, <code>gigachat35</code>, <code>inkling</code>, <code>cohere_command4</code></td>
</tr> </tr>
<tr> <tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--tool-call-parser`</td> <td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--tool-call-parser`</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Specify the parser for handling tool-call interactions. Use `auto` to detect the parser from the model's chat template.</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Specify the parser for handling tool-call interactions. Use `auto` to detect the parser from the model's chat template.</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>`None`</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>`None`</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>auto</code>, <code>apertus2509</code>, <code>cohere_command4</code>, <code>deepseekv3</code>, <code>deepseekv31</code>, <code>deepseekv32</code>, <code>deepseekv4</code>, <code>dots</code>, <code>glm</code>, <code>glm45</code>, <code>glm47</code>, <code>gpt-oss</code>, <code>k2_horizon</code>, <code>kimi_k2</code>, <code>kimi_k3</code>, <code>lfm2</code>, <code>ling3</code>, <code>llama3</code>, <code>mimo</code>, <code>minicpm5</code>, <code>mistral</code>, <code>muse</code>, <code>poolside_v1</code>, <code>pythonic</code>, <code>qwen</code>, <code>qwen25</code>, <code>qwen3_coder</code>, <code>spark25</code>, <code>step3</code>, <code>step3p5</code>, <code>minimax-m2</code>, <code>minimax-m3</code>, <code>trinity</code>, <code>interns1</code>, <code>hermes</code>, <code>hunyuan</code>, <code>gigachat3</code>, <code>gemma4</code>, <code>inkling</code></td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>auto</code>, <code>apertus2509</code>, <code>cohere_command4</code>, <code>deepseekv3</code>, <code>deepseekv31</code>, <code>deepseekv32</code>, <code>deepseekv4</code>, <code>dots</code>, <code>glm</code>, <code>glm45</code>, <code>glm47</code>, <code>gpt-oss</code>, <code>k2_horizon</code>, <code>kimi_k2</code>, <code>kimi_k3</code>, <code>lfm2</code>, <code>ling3</code>, <code>llama3</code>, <code>mimo</code>, <code>minicpm5</code>, <code>mistral</code>, <code>muse</code>, <code>poolside_v1</code>, <code>pythonic</code>, <code>qwen</code>, <code>qwen25</code>, <code>qwen3_coder</code>, <code>spark25</code>, <code>step3</code>, <code>step3p5</code>, <code>minimax-m2</code>, <code>minimax-m3</code>, <code>trinity</code>, <code>interns1</code>, <code>hermes</code>, <code>hunyuan</code>, <code>gigachat3</code>, <code>gigachat35</code>, <code>gemma4</code>, <code>inkling</code></td>
</tr> </tr>
<tr> <tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--tool-server`</td> <td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>`--tool-server`</td>
@@ -147,6 +147,21 @@ To avoid spamming a PR with too many `/rerun-failed-ci` comments, you can also t
If you dont have permission and youre not the PR author, please ask maintainers to trigger CI for you. If you dont have permission and youre not the PR author, please ask maintainers to trigger CI for you.
### CI control labels
Four labels change how a PR's CI runs. Label before you trigger:
`max-concurrency` fixes the shard fan-out when the run is created.
| Label | Effect |
| --- | --- |
| `bypass-fail-fast` | A job failing no longer aborts its siblings; the run continues instead of stopping at the first root-cause failure. A failing `lint` still stops everything. |
| `parallel-stages` | Stages stop waiting on each other. `base-a`, `base-b` and `base-c` dispatch together, the way a scheduled run does. |
| `max-concurrency` | A suite fans out to all of its shards at once rather than a third of them. |
| `highest-priority` | All three of the above, and the PR is also skipped by the batch-cancel workflow, sorted first in the active-runs report, and never closed as stale. |
Each spends extra runner capacity, so they unblock a specific PR rather than
serve as a default.
### CI rate limits ### CI rate limits
Due to CI scheduling and limited resources, higher-priority PRs may preempt running jobs. In such cases, you may need to rerun the tests. Due to CI scheduling and limited resources, higher-priority PRs may preempt running jobs. In such cases, you may need to rerun the tests.
@@ -0,0 +1,170 @@
---
title: "MiMo-V2.5-Pro"
metatags:
description: "Best Practice for MiMo-V2.5-Pro on Ascend NPU"
---
<Note>
This page focuses on the deployment of MiMo-V2.5-Pro (FP4) with DFlash speculative decoding in PD disaggregation mode on the Ascend NPU.
On the A3 Series, each card has 2 dies, so `--tp-size` is twice the card count; see [Ascend NPU Reference](/docs/hardware-platforms/ascend-npus/reference/glossary#hardware) for details.
</Note>
### Model Deployment
MiMo-V2.5-Pro-FP4-DFlash is an MXFP4-quantized checkpoint with a built-in DFlash draft model (located in the `dflash/` subdirectory of the weights). The following example deploys it in 1P1D mode (1 prefill node + 1 decode node, TP8 + DP2 per node).
#### Common environment setup (both nodes)
```bash Command
# ============================================================
# Before running, update the following variables:
# ASCEND_MF_STORE_URL: prefill node IP with port
# HCCL_SOCKET_IFNAME / GLOO_SOCKET_IFNAME: network interface name
# ============================================================
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
sysctl -w vm.swappiness=0
sysctl -w kernel.numa_balancing=0
sysctl -w kernel.sched_migration_cost_ns=50000
export SGLANG_SET_CPU_AFFINITY=1
unset https_proxy
unset http_proxy
unset HTTPS_PROXY
unset HTTP_PROXY
unset ASCEND_LAUNCH_BLOCKING
source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh
export HCCL_BUFFSIZE=300
export HCCL_OP_EXPANSION_MODE=AIV
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export STREAMS_PER_DEVICE=32
export SGLANG_DISAGGREGATION_BOOTSTRAP_TIMEOUT=600
# Use the AscendC flash attention
export ASCEND_USE_FIA=1
# PD disaggregation transfer config
export ASCEND_MF_STORE_URL="tcp://<your prefill ip>:24669"
export ASCEND_MF_TRANSFER_PROTOCOL="device_urma"
# DeepEP
export SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK=32
export HCCL_SOCKET_IFNAME=<network-interface>
export GLOO_SOCKET_IFNAME=<network-interface>
export HCCL_HOST_SOCKET_PORT_RANGE=auto
MODEL_PATH=/path/to/MiMo-V2.5-Pro-FP4-DFlash
```
#### Prefill node
```bash Command
export DEEPEP_HCCL_BUFFSIZE=2500
# Enable chunked dispatch for long sequences
export DEEPEP_NORMAL_LONG_SEQ_ROUND=10
export DEEPEP_NORMAL_LONG_SEQ_PER_ROUND_TOKENS=4096
export DEEPEP_NORMAL_COMBINE_ENABLE_LONG_SEQ=0
python3 -m sglang.launch_server \
--model-path $MODEL_PATH \
--attention-backend ascend \
--device npu \
--tp-size 8 --nnodes 1 --node-rank 0 \
--chunked-prefill-size 8192 \
--trust-remote-code --port 10001 \
--host <your prefill ip> --max-running-requests 32 \
--mem-fraction-static 0.90 \
--swa-full-tokens-ratio 0.3 \
--disaggregation-mode prefill --disaggregation-transfer-backend ascend \
--disaggregation-bootstrap-port 8996 \
--disable-piecewise-cuda-graph \
--dp-size 2 --enable-dp-attention --enable-dp-lm-head \
--moe-a2a-backend deepep --deepep-mode normal
```
#### Decode node
```bash Command
# Use eagle_worker_v2 and overlap plan stream to hide the draft/target preparation
export SGLANG_ENABLE_SPEC_V2=1
export SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1
# DFlash draft model has a longer context length than the derived value
export SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1
export DEEPEP_HCCL_BUFFSIZE=1200
python3 -m sglang.launch_server \
--model-path $MODEL_PATH \
--speculative-draft-model-path $MODEL_PATH/dflash \
--attention-backend ascend \
--device npu \
--tp-size 8 --nnodes 1 --node-rank 0 \
--trust-remote-code --port 20001 \
--host <your decode ip> --max-running-requests 32 \
--mem-fraction-static 0.88 \
--swa-full-tokens-ratio 0.3 \
--cuda-graph-bs 1 2 4 8 12 16 \
--disaggregation-mode decode --disaggregation-transfer-backend ascend \
--disaggregation-bootstrap-port 8996 \
--moe-a2a-backend deepep --deepep-mode low_latency \
--dp-size 2 --enable-dp-attention --enable-dp-lm-head \
--speculative-algorithm DFLASH \
--speculative-num-draft-tokens 8
```
#### Router
```bash Command
python -m sglang_router.launch_router \
--pd-disaggregation \
--policy cache_aware \
--prefill http://<your prefill ip>:10001 \
--decode http://<your decode ip>:20001 \
--host 127.0.0.1 \
--port 6688 \
--health-check-interval-secs 3600 --mini-lb
```
### Benchmark
We tested it based on the `RANDOM` dataset.
#### Benchmark Prefill Node (TTFT)
```bash Command
python3 -m sglang.bench_serving \
--backend sglang \
--host 127.0.0.1 \
--port 6688 \
--model /path/to/MiMo-V2.5-Pro-FP4-DFlash \
--dataset-name random \
--tokenize-prompt \
--random-input-len 16000 \
--random-output-len 1 \
--request-rate 0.4 \
--random-range-ratio 1 \
--num-prompts 128 \
--max-concurrency 32
```
#### Benchmark Decode Node (TPOT)
```bash Command
python3 -m sglang.bench_serving \
--backend sglang \
--host 127.0.0.1 \
--port 6688 \
--model /path/to/MiMo-V2.5-Pro-FP4-DFlash \
--dataset-name random \
--tokenize-prompt \
--random-input-len 16000 \
--random-output-len 1000 \
--request-rate inf \
--random-range-ratio 1 \
--num-prompts 128 \
--max-concurrency 32
```
+16 -34
View File
@@ -7,39 +7,8 @@ The document addresses how to set up the [SGLang](https://github.com/sgl-project
Specifically, SGLang is optimized for [Intel® Arc™ Pro B-Series Graphics](https://www.intel.com/content/www/us/en/ark/products/series/242616/intel-arc-pro-b-series-graphics.html) and [ Specifically, SGLang is optimized for [Intel® Arc™ Pro B-Series Graphics](https://www.intel.com/content/www/us/en/ark/products/series/242616/intel-arc-pro-b-series-graphics.html) and [
Intel® Arc™ B-Series Graphics](https://www.intel.com/content/www/us/en/ark/products/series/240391/intel-arc-b-series-graphics.html). Intel® Arc™ B-Series Graphics](https://www.intel.com/content/www/us/en/ark/products/series/240391/intel-arc-b-series-graphics.html).
## Optimized Model List A number of popular LLMs are optimized and run efficiently on XPU, including the most notable open-source models like Llama series, Qwen series, and Diffusion model series like FLUX and Z-Image.
Please check the [SGLang Cookbook pages](https://docs.sglang.io/cookbook/intro) in which the support status and example commands can be found.
A list of LLMs have been optimized on Intel GPU, and more are on the way:
<table style={{width: "100%", borderCollapse: "collapse", tableLayout: "fixed"}}>
<colgroup>
<col style={{width: "50%"}} />
<col style={{width: "50%"}} />
</colgroup>
<thead>
<tr style={{borderBottom: "2px solid #d55816"}}>
<th style={{textAlign: "left", padding: "10px 12px", fontWeight: 700, whiteSpace: "nowrap", backgroundColor: "rgba(255,255,255,0.02)"}}>Model Name</th>
<th style={{textAlign: "left", padding: "10px 12px", fontWeight: 700, whiteSpace: "nowrap", backgroundColor: "rgba(255,255,255,0.05)"}}>BF16</th>
</tr>
</thead>
<tbody>
<tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Llama-3.2-3B</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>[meta-llama/Llama-3.2-3B-Instruct](https://huggingface.co/meta-llama/Llama-3.2-3B-Instruct)</td>
</tr>
<tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Llama-3.1-8B</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>[meta-llama/Llama-3.1-8B-Instruct](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct)</td>
</tr>
<tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Qwen2.5-1.5B</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>[Qwen/Qwen2.5-1.5B](https://huggingface.co/Qwen/Qwen2.5-1.5B)</td>
</tr>
</tbody>
</table>
**Note:** The model identifiers listed in the table above
have been verified on [Intel® Arc™ B580 Graphics](https://www.intel.com/content/www/us/en/products/sku/241598/intel-arc-b580-graphics/specifications.html).
Quantized MoE models are covered separately in Quantized MoE models are covered separately in
[MXFP4 MoE Quantization](#mxfp4-moe-quantization) below. [MXFP4 MoE Quantization](#mxfp4-moe-quantization) below.
@@ -85,6 +54,20 @@ pip install -v . --extra-index-url https://download.pytorch.org/whl/xpu
### Install Using Docker ### Install Using Docker
It is recommended to use Docker for setting up the SGLang environment.
#### Pull from Docker Hub
Pull the prebuilt docker image of SGLang package releases from `lmsysorg/sglang` repository.
The [XPU image tags](https://hub.docker.com/r/lmsysorg/sglang/tags?name=xpu) end with `xpu` suffix.
The image pulling command is like:
```bash Command
docker pull lmsysorg/sglang:v0.5.20-xpu
```
#### Build from Dockerfile
[The SGLang XPU Dockerfile](https://github.com/sgl-project/sglang/blob/main/docker/xpu.Dockerfile) is provided to facilitate the installation. [The SGLang XPU Dockerfile](https://github.com/sgl-project/sglang/blob/main/docker/xpu.Dockerfile) is provided to facilitate the installation.
Replace `<secret>` below with your [HuggingFace access token](https://huggingface.co/docs/hub/en/security-tokens). Replace `<secret>` below with your [HuggingFace access token](https://huggingface.co/docs/hub/en/security-tokens).
@@ -121,7 +104,6 @@ Example command to launch SGLang serving:
sglang serve \ sglang serve \
--model-path <MODEL_ID_OR_PATH> \ --model-path <MODEL_ID_OR_PATH> \
--trust-remote-code \ --trust-remote-code \
--disable-overlap-schedule \
--device xpu \ --device xpu \
--host 0.0.0.0 \ --host 0.0.0.0 \
--tp 2 \ # using multi GPUs --tp 2 \ # using multi GPUs
+1 -1
View File
@@ -783,7 +783,7 @@ SGLang Diffusion x Cache-DiT supports almost all models originally supported in
</tr> </tr>
<tr> <tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Qwen</td> <td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Qwen</td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Qwen-Image, Qwen-Image-Edit</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}>Qwen-Image, Qwen-Image-Edit, Qwen-Image 2.1</td>
</tr> </tr>
<tr> <tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Hunyuan</td> <td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}>Hunyuan</td>
@@ -105,12 +105,10 @@ replay only matching warmup signatures, including condition-prefix length;
other shapes run eagerly. VAE tiling is opt-in and can change numerical results. other shapes run eagerly. VAE tiling is opt-in and can change numerical results.
Do not infer compatibility from the older Qwen-Image row. Do not infer compatibility from the older Qwen-Image row.
Use an authorized local checkpoint with `--model-id Qwen-Image-2.1`. Use the public `Qwen/Qwen-Image-2.1` checkpoint; no Hugging Face token is required.
See the [Qwen-Image 2.1 cookbook](/cookbook/diffusion/Qwen-Image/Qwen-Image-2.1) See the [Qwen-Image 2.1 cookbook](/cookbook/diffusion/Qwen-Image/Qwen-Image-2.1)
for checkpoint layout and usage. This entry does not assert public weight for checkpoint layout and usage. The standard two-GPU E2E suite includes `qwen_image21_t2i_tp2`
availability. The standard two-GPU E2E suite includes `qwen_image21_t2i_tp2`
with TP2, 1024px/40-step generation, two requests, and image consistency checks. with TP2, 1024px/40-step generation, two requests, and image consistency checks.
Its runners need access to `Qwen/Qwen-Image-2.1`.
The additional opt-in HTTP cases check repeated generation, editing, and real The additional opt-in HTTP cases check repeated generation, editing, and real
RGBA alpha output from a local checkpoint: RGBA alpha output from a local checkpoint:
@@ -308,5 +308,10 @@ in the GitHub search bar.
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>JetBrains/Mellum2-12B-A2.5B-Base</code>, <code>JetBrains/Mellum2-12B-A2.5B-Thinking</code></td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>JetBrains/Mellum2-12B-A2.5B-Base</code>, <code>JetBrains/Mellum2-12B-A2.5B-Thinking</code></td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>JetBrains' Qwen3-MoE-based code generation model with interleaved sliding-window/full attention, per-layer-type RoPE, and per-layer dense/sparse MLP routing.</td> <td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>JetBrains' Qwen3-MoE-based code generation model with interleaved sliding-window/full attention, per-layer-type RoPE, and per-layer dense/sparse MLP routing.</td>
</tr> </tr>
<tr>
<td style={{padding: "9px 12px", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.02)"}}><strong>GigaChat 3.5</strong></td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.05)"}}><code>ai-sage/GigaChat3.5-432B-A28B</code></td>
<td style={{padding: "9px 12px", backgroundColor: "rgba(255,255,255,0.02)"}}>GigaChat's hybrid MoE model: per-layer mix of DeepSeek-style MLA full attention and Qwen3 Gated-Delta-Net (GDN) linear attention, DeepSeek-style routed + shared experts, and multiple multi-token-prediction (MTP) heads for speculative decoding. Emits tool calls in GCML format.</td>
</tr>
</tbody> </tbody>
</table> </table>
+31 -14
View File
@@ -147,10 +147,14 @@ export const Deployment = ({ config, benchmarks }) => {
{ id: "mi355x", label: "MI355X", vram: "288GB", { id: "mi355x", label: "MI355X", vram: "288GB",
multiNodeDockerFlags: [...AMD_RDMA_DOCKER_FLAGS] }, multiNodeDockerFlags: [...AMD_RDMA_DOCKER_FLAGS] },
], ],
// Ascend A3 Series: 1 card = 2 dies, so --tp-size is 2× the card // Ascend device layout: one /dev/davinciN per core. An A3 Series card is
// count (32 cards -> --tp-size 64). // the exception 2 dies per card, so an 8-card node exposes 16 devices
// and --tp-size is twice the card count. A 950PR/DT Series card is a
// single core, so the device count and --tp-size follow the cards. Both
// counts feed the docker `--device` list (`npuDevices`).
npu: [ npu: [
{ id: "a3", label: "Ascend A3 Series", vram: "64GB/die" }, { id: "a3", label: "A3 Series", vram: "64GB/die", npuDevices: 16 },
{ id: "a5", label: "950PR/DT Series", vram: "128GB", npuDevices: 8 },
], ],
}; };
@@ -819,14 +823,31 @@ export const Deployment = ({ config, benchmarks }) => {
return (extra && extra.vendor) || "nvidia"; return (extra && extra.vendor) || "nvidia";
}; };
// `config.hardware` overrides by id, as in buildHardwareGroups. // `config.hardware` overrides by id, as in buildHardwareGroups.
const fabricFlagsOf = (hwId) => { const catalogEntryOf = (hwId) => {
const extra = (config.hardware || []).find((h) => h.id === hwId); const extra = (config.hardware || []).find((h) => h.id === hwId);
if (extra) return extra.multiNodeDockerFlags || []; if (extra) return extra;
for (const list of Object.values(HARDWARE_CATALOG)) { for (const list of Object.values(HARDWARE_CATALOG)) {
const hit = list.find((h) => h.id === hwId); const hit = list.find((h) => h.id === hwId);
if (hit) return hit.multiNodeDockerFlags || []; if (hit) return hit;
} }
return []; return null;
};
const fabricFlagsOf = (hwId) =>
(catalogEntryOf(hwId) || {}).multiNodeDockerFlags || [];
// NPU cards are reached with --device, one per /dev/davinciN core;
// `npuDevices` carries the per-product-line count (16 on an A3 Series
// node, 8 on a 950PR/DT Series node), four devices per line as the host
// docs show.
const davinciLines = (devices) => {
const lines = [];
for (let i = 0; i < devices; i += 4) {
const group = [];
for (let k = i; k < Math.min(i + 4, devices); k++) {
group.push(`--device=/dev/davinci${k}`);
}
lines.push(" " + group.join(" "));
}
return lines;
}; };
const gpuAccessLines = vendorOf(sel.hw) === "amd" const gpuAccessLines = vendorOf(sel.hw) === "amd"
? [ ? [
@@ -838,14 +859,10 @@ export const Deployment = ({ config, benchmarks }) => {
] ]
: vendorOf(sel.hw) === "npu" : vendorOf(sel.hw) === "npu"
? [ ? [
// NPU: --privileged grants the davinci devices (16 dies on an // NPU: --privileged grants the davinci devices; the host CANN
// 8-card Ascend A3 Series node); the host CANN driver/firmware/state // driver/firmware/state must be mounted in.
// must be mounted in.
"docker run --privileged --shm-size=16g", "docker run --privileged --shm-size=16g",
" --device=/dev/davinci0 --device=/dev/davinci1 --device=/dev/davinci2 --device=/dev/davinci3", ...davinciLines((catalogEntryOf(sel.hw) || {}).npuDevices || 16),
" --device=/dev/davinci4 --device=/dev/davinci5 --device=/dev/davinci6 --device=/dev/davinci7",
" --device=/dev/davinci8 --device=/dev/davinci9 --device=/dev/davinci10 --device=/dev/davinci11",
" --device=/dev/davinci12 --device=/dev/davinci13 --device=/dev/davinci14 --device=/dev/davinci15",
" --device=/dev/davinci_manager", " --device=/dev/davinci_manager",
" --device=/dev/hisi_hdc", " --device=/dev/hisi_hdc",
" -v /usr/local/sbin:/usr/local/sbin", " -v /usr/local/sbin:/usr/local/sbin",
@@ -135,10 +135,18 @@ export const KimiK3MambaRatioCalculator = () => {
const eff = derive(cfg.flags, cfg.env); const eff = derive(cfg.flags, cfg.env);
const bs = derive(cfg.baseFlags.length ? cfg.baseFlags : cfg.flags, const bs = derive(cfg.baseFlags.length ? cfg.baseFlags : cfg.flags,
cfg.baseFlags.length ? cfg.baseEnv : cfg.env); cfg.baseFlags.length ? cfg.baseEnv : cfg.env);
// A --max-mamba-cache-size cell sizes the pool explicitly no ratio to // Recipes that size the dual pool without the ratio neither render one nor
// compute or broadcast. // broadcast one:
const explicitSizing = (cfg.baseFlags.length ? cfg.baseFlags : cfg.flags) // - a --max-mamba-cache-size cell pins the KDA slot count explicitly;
.some((f) => f.startsWith("--max-mamba-cache-size")); // - the Ascend NPU recipes size both pools internally and never set
// --mamba-full-memory-ratio (every NPU recipe carries --device npu).
const baseFlagList = cfg.baseFlags.length ? cfg.baseFlags : cfg.flags;
const explicitSizing = baseFlagList.some((f) => f.startsWith("--max-mamba-cache-size"));
const npuRecipe = baseFlagList.some((f) => {
const [head, ...rest] = f.trim().split(/[\s=]+/);
return head === "--device" && rest[0] === "npu";
});
const ratioNotApplicable = explicitSizing || npuRecipe;
const { ratio, tp, dp, attnTp, dcp, kvDtype, ssmDtype, radixOff, strategy, skipLock, slots, specOn, replaySpec, block, pdRole } = eff; const { ratio, tp, dp, attnTp, dcp, kvDtype, ssmDtype, radixOff, strategy, skipLock, slots, specOn, replaySpec, block, pdRole } = eff;
const valid = Number.isFinite(ratio) && ratio > 0 && length > 0 && 96 % attnTp === 0; const valid = Number.isFinite(ratio) && ratio > 0 && length > 0 && 96 % attnTp === 0;
const baseValid = Number.isFinite(bs.ratio) && bs.ratio > 0 && length > 0; const baseValid = Number.isFinite(bs.ratio) && bs.ratio > 0 && length > 0;
@@ -155,18 +163,22 @@ export const KimiK3MambaRatioCalculator = () => {
const cliFlag = valid ? `--mamba-full-memory-ratio ${result}` : ""; const cliFlag = valid ? `--mamba-full-memory-ratio ${result}` : "";
// Broadcast both results: the Deploy command takes the base-config value, // Broadcast both results: the Deploy command takes the base-config value,
// the Playground's composed command takes the effective one. // the Playground's composed command takes the effective one. On a recipe
// that does not use the ratio, broadcast nulls instead of skipping the
// dispatch the panels must drop a ratio pinned for a recipe the reader
// has since left rather than keep injecting it.
useEffect(() => { useEffect(() => {
if (explicitSizing) return;
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("sglang-k3-mamba-ratio", { new CustomEvent("sglang-k3-mamba-ratio", {
detail: { detail: ratioNotApplicable
ratio: valid ? result : null, ? { ratio: null, baseRatio: null }
baseRatio: baseValid ? baseResult : null, : {
}, ratio: valid ? result : null,
baseRatio: baseValid ? baseResult : null,
},
}) })
); );
}, [result, valid, baseResult, baseValid, explicitSizing]); }, [result, valid, baseResult, baseValid, ratioNotApplicable]);
const copyFlag = () => { const copyFlag = () => {
if (!cliFlag || typeof navigator === "undefined" || !navigator.clipboard) return; if (!cliFlag || typeof navigator === "undefined" || !navigator.clipboard) return;
@@ -235,10 +247,12 @@ export const KimiK3MambaRatioCalculator = () => {
specLabel, specLabel,
].filter(Boolean); ].filter(Boolean);
if (explicitSizing) { if (ratioNotApplicable) {
return ( return (
<div className="not-prose" style={{ padding: "14px", border: `1px solid ${colors.border}`, borderRadius: "8px", background: colors.panel, color: colors.muted, fontSize: "13px" }}> <div className="not-prose" style={{ padding: "14px", border: `1px solid ${colors.border}`, borderRadius: "8px", background: colors.panel, color: colors.muted, fontSize: "13px" }}>
This recipe sizes the KDA state pool explicitly with <code style={{ color: colors.text }}>--max-mamba-cache-size</code>, so the ratio calculator does not apply. {explicitSizing
? <>This recipe sizes the KDA state pool explicitly with <code style={{ color: colors.text }}>--max-mamba-cache-size</code>, so the ratio calculator does not apply.</>
: <>This recipe runs on Ascend NPUs, which size the KDA state pool and the MLA KV pool internally and never set <code style={{ color: colors.text }}>--mamba-full-memory-ratio</code>, so the ratio calculator does not apply.</>}
</div> </div>
); );
} }
+35 -3
View File
@@ -1723,9 +1723,25 @@ export const Playground = ({ config }) => {
mi355x: AMD_RDMA_DOCKER_FLAGS, mi355x: AMD_RDMA_DOCKER_FLAGS,
}; };
const fabricFlags = HW_MULTINODE_DOCKER_FLAGS[sel.hw] || []; const fabricFlags = HW_MULTINODE_DOCKER_FLAGS[sel.hw] || [];
// Mirrors the vendor branch in _deployment.jsx: ROCm reaches its GPUs // Mirrors the vendor branches in _deployment.jsx: ROCm reaches its GPUs
// through /dev/kfd + /dev/dri and the video group, not --gpus all. // through /dev/kfd + /dev/dri and the video group (not --gpus all), and
// Ascend NPUs are reached with --device, one per /dev/davinciN core (16
// on an A3 Series node, 8 on a 950PR/DT Series node the catalog's
// `npuDevices`).
const isAmdHw = /^mi\d/.test(sel.hw || ""); const isAmdHw = /^mi\d/.test(sel.hw || "");
const HW_NPU_DEVICES = { a3: 16, a5: 8 };
const npuDevices = HW_NPU_DEVICES[sel.hw];
const davinciLines = (devices) => {
const lines = [];
for (let i = 0; i < devices; i += 4) {
const group = [];
for (let k = i; k < Math.min(i + 4, devices); k++) {
group.push(`--device=/dev/davinci${k}`);
}
lines.push(" " + group.join(" "));
}
return lines;
};
const dockerLines = [ const dockerLines = [
...(isAmdHw ...(isAmdHw
? [ ? [
@@ -1735,6 +1751,21 @@ export const Playground = ({ config }) => {
" --cap-add=SYS_PTRACE --security-opt seccomp=unconfined", " --cap-add=SYS_PTRACE --security-opt seccomp=unconfined",
" --shm-size 32g", " --shm-size 32g",
] ]
: npuDevices
? [
// NPU: --privileged grants the davinci devices; the host CANN
// driver/firmware/state must be mounted in.
"docker run --privileged --shm-size=16g",
...davinciLines(npuDevices),
" --device=/dev/davinci_manager",
" --device=/dev/hisi_hdc",
" -v /usr/local/sbin:/usr/local/sbin",
" -v /usr/local/Ascend/driver:/usr/local/Ascend/driver",
" -v /usr/local/Ascend/firmware:/usr/local/Ascend/firmware",
" -v /etc/ascend_install.info:/etc/ascend_install.info",
" -v /var/queue_schedule:/var/queue_schedule",
" -v ~/.cache/:/root/.cache/",
]
: [ : [
"docker run --gpus all", "docker run --gpus all",
" --shm-size 32g", " --shm-size 32g",
@@ -1743,7 +1774,8 @@ export const Playground = ({ config }) => {
// A PD pair is cross-host even when each role is a single-node cell, so // A PD pair is cross-host even when each role is a single-node cell, so
// the RDMA fabric flags are needed for `pdMode` too, not just multinode. // the RDMA fabric flags are needed for `pdMode` too, not just multinode.
...((multinode || pdMode) ? fabricFlags.map((x) => " " + x) : []), ...((multinode || pdMode) ? fabricFlags.map((x) => " " + x) : []),
" -v ~/.cache/huggingface:/root/.cache/huggingface", // The NPU device block already mounts ~/.cache/.
...(npuDevices ? [] : [" -v ~/.cache/huggingface:/root/.cache/huggingface"]),
...(config.dockerMounts || []).map((mount) => ` -v ${mount}`), ...(config.dockerMounts || []).map((mount) => ` -v ${mount}`),
` --env "HF_TOKEN={{HF_TOKEN}}"`, ` --env "HF_TOKEN={{HF_TOKEN}}"`,
...cellEnv.map((e) => ` --env ${e}`), ...cellEnv.map((e) => ` --env ${e}`),
@@ -10,6 +10,7 @@ export const DeepSeekOCR2Deployment = () => {
{ id: 'mi325x', label: 'MI325X', default: false }, { id: 'mi325x', label: 'MI325X', default: false },
{ id: 'mi355x', label: 'MI355X', default: false }, { id: 'mi355x', label: 'MI355X', default: false },
{ id: 'xeon', label: 'XEON', default: false }, { id: 'xeon', label: 'XEON', default: false },
{ id: 'arc_b', label: 'BMG', default: false },
] ]
}, },
quantization: { quantization: {
@@ -25,8 +26,8 @@ export const DeepSeekOCR2Deployment = () => {
type: 'checkbox', type: 'checkbox',
items: [ items: [
{ id: 'tp', label: 'TP', subtitle: 'Tensor Parallel', default: true, required: true }, { id: 'tp', label: 'TP', subtitle: 'Tensor Parallel', default: true, required: true },
{ id: 'dp', label: 'DP', subtitle: 'Data Parallel', default: false, disabledWhen: (v) => v.hardware === 'xeon', disabledReason: 'Intel Xeon CPUs only support Tensor Parallel (TP)' }, { id: 'dp', label: 'DP', subtitle: 'Data Parallel', default: false, disabledWhen: (v) => v.hardware === 'xeon' || v.hardware === 'arc_b', disabledReason: 'Only Tensor Parallel (TP) is supported on this hardware' },
{ id: 'ep', label: 'EP', subtitle: 'Expert Parallel', default: false, disabledWhen: (v) => v.hardware === 'xeon', disabledReason: 'Intel Xeon CPUs only support Tensor Parallel (TP)' } { id: 'ep', label: 'EP', subtitle: 'Expert Parallel', default: false, disabledWhen: (v) => v.hardware === 'xeon' || v.hardware === 'arc_b', disabledReason: 'Only Tensor Parallel (TP) is supported on this hardware' }
] ]
}, },
}; };
@@ -42,6 +43,8 @@ export const DeepSeekOCR2Deployment = () => {
cmd += ` --model-path ${modelPath}`; cmd += ` --model-path ${modelPath}`;
if (hardware === 'xeon') { if (hardware === 'xeon') {
cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule \\\n --trust-remote-code`; cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule \\\n --trust-remote-code`;
} else if (hardware === 'arc_b') {
cmd += ` \\\n --device xpu`;
} }
cmd += ` \\\n --enable-multimodal`; cmd += ` \\\n --enable-multimodal`;
@@ -272,9 +275,8 @@ export const DeepSeekOCR2Deployment = () => {
) : option.type === 'checkbox' ? ( ) : option.type === 'checkbox' ? (
(option.items || []).map((item) => { (option.items || []).map((item) => {
const isChecked = (values[option.name] || []).includes(item.id); const isChecked = (values[option.name] || []).includes(item.id);
const isDisabled = const dynDisabled = typeof item.disabledWhen === 'function' && item.disabledWhen(values);
item.required || const isDisabled = item.required || dynDisabled;
(typeof item.disabledWhen === 'function' && item.disabledWhen(values));
return ( return (
<label <label
key={item.id} key={item.id}
@@ -289,9 +291,11 @@ export const DeepSeekOCR2Deployment = () => {
type="checkbox" type="checkbox"
checked={isChecked} checked={isChecked}
disabled={isDisabled} disabled={isDisabled}
onChange={(event) => onChange={(event) => {
handleCheckboxChange(option.name, item.id, event.target.checked) if (!dynDisabled) {
} handleCheckboxChange(option.name, item.id, event.target.checked);
}
}}
style={{ display: 'none' }} style={{ display: 'none' }}
/> />
{item.label} {item.label}
@@ -30,6 +30,7 @@ export const Gemma4Deployment = () => {
{ id: 'b200', label: 'B200', default: false }, { id: 'b200', label: 'B200', default: false },
{ id: 'b300', label: 'B300', default: false }, { id: 'b300', label: 'B300', default: false },
{ id: 'mi300x', label: 'MI300X', default: false, disabled: !showMI300X }, { id: 'mi300x', label: 'MI300X', default: false, disabled: !showMI300X },
{ id: 'arc_b', label: 'BMG', default: false },
]; ];
} }
}, },
@@ -88,6 +89,10 @@ export const Gemma4Deployment = () => {
'31b': { tp: 1, mem: 0.80 }, '31b': { tp: 1, mem: 0.80 },
'26b-a4b': { tp: 1, mem: 0.80 }, '26b-a4b': { tp: 1, mem: 0.80 },
}, },
arc_b: {
'31b': { tp: 4, mem: 0.80 },
'26b-a4b': { tp: 4, mem: 0.75 },
},
}; };
const generateCommand = (values) => { const generateCommand = (values) => {
@@ -141,6 +146,10 @@ export const Gemma4Deployment = () => {
cmd += ` \\\n --attention-backend triton`; cmd += ` \\\n --attention-backend triton`;
} }
if (hardware === 'arc_b') {
cmd += ` \\\n --device xpu`;
}
cmd += ` \\\n --mem-fraction-static ${mem}`; cmd += ` \\\n --mem-fraction-static ${mem}`;
cmd += ` \\\n --host 0.0.0.0 --port 30000`; cmd += ` \\\n --host 0.0.0.0 --port 30000`;
@@ -205,7 +214,27 @@ export const Gemma4Deployment = () => {
}, []); }, []);
const handleRadioChange = (optionName, value) => { const handleRadioChange = (optionName, value) => {
setValues((prev) => ({ ...prev, [optionName]: value })); setValues((prev) => {
if (prev.hardware === 'arc_b' && optionName === 'modelSize' && !['31b', '26b-a4b'].includes(value)) {
return prev;
}
if (prev.hardware === 'arc_b' && optionName === 'checkpoint' && value !== 'standard') {
return prev;
}
if (prev.hardware === 'arc_b' && optionName === 'speculative' && value !== 'disabled') {
return prev;
}
const next = { ...prev, [optionName]: value };
if (optionName === 'hardware' && value === 'arc_b') {
if (!['31b', '26b-a4b'].includes(next.modelSize)) {
next.modelSize = '31b';
}
next.checkpoint = 'standard';
next.speculative = 'disabled';
}
return next;
});
}; };
const handleCheckboxChange = (optionName, itemId, isChecked) => { const handleCheckboxChange = (optionName, itemId, isChecked) => {
@@ -379,7 +408,21 @@ export const Gemma4Deployment = () => {
) : ( ) : (
items.map((item) => { items.map((item) => {
const isChecked = values[option.name] === item.id; const isChecked = values[option.name] === item.id;
const isDisabled = Boolean(item.disabled); const isArcBModelLocked =
values.hardware === 'arc_b' &&
option.name === 'modelSize' &&
!['31b', '26b-a4b'].includes(item.id);
const isArcBCheckpointLocked =
values.hardware === 'arc_b' &&
option.name === 'checkpoint' &&
item.id !== 'standard';
const isArcBSpeculativeLocked =
values.hardware === 'arc_b' &&
option.name === 'speculative' &&
item.id !== 'disabled';
const isDisabled = Boolean(
item.disabled || isArcBModelLocked || isArcBCheckpointLocked || isArcBSpeculativeLocked
);
return ( return (
<label <label
key={item.id} key={item.id}
@@ -11,7 +11,8 @@ export const Llama31Deployment = () => {
{ id: 'mi300x', label: 'MI300X', default: false }, { id: 'mi300x', label: 'MI300X', default: false },
{ id: 'mi325x', label: 'MI325X', default: false }, { id: 'mi325x', label: 'MI325X', default: false },
{ id: 'mi355x', label: 'MI355X', default: false }, { id: 'mi355x', label: 'MI355X', default: false },
{ id: 'xeon', label: 'XEON', default: false } { id: 'xeon', label: 'XEON', default: false },
{ id: 'arc_b', label: 'BMG', default: false },
] ]
}, },
modelsize: { modelsize: {
@@ -65,21 +66,28 @@ export const Llama31Deployment = () => {
...options.modelsize, ...options.modelsize,
items: options.modelsize.items.map(item => ({ items: options.modelsize.items.map(item => ({
...item, ...item,
disabled: values.hardware === 'xeon' && item.id !== '8b' disabled: (values.hardware === 'xeon' || values.hardware === 'arc_b') && item.id !== '8b'
}))
},
category: {
...options.category,
items: options.category.items.map(item => ({
...item,
disabled: values.hardware === 'arc_b' && item.id !== 'instruct'
})) }))
}, },
quantization: { quantization: {
...options.quantization, ...options.quantization,
items: options.quantization.items.map(item => ({ items: options.quantization.items.map(item => ({
...item, ...item,
disabled: values.hardware === 'xeon' && item.id === 'fp8' disabled: (values.hardware === 'xeon' && item.id === 'fp8') || (values.hardware === 'arc_b' && item.id !== 'bf16')
})) }))
}, },
optimization: { optimization: {
...options.optimization, ...options.optimization,
items: options.optimization.items.map(item => ({ items: options.optimization.items.map(item => ({
...item, ...item,
disabled: values.hardware === 'xeon' && item.id !== 'basic' disabled: (values.hardware === 'xeon' || values.hardware === 'arc_b') && item.id !== 'basic'
})) }))
} }
}; };
@@ -122,6 +130,12 @@ export const Llama31Deployment = () => {
next.quantization = 'bf16'; next.quantization = 'bf16';
next.optimization = 'basic'; next.optimization = 'basic';
} }
if (optionName === 'hardware' && value === 'arc_b') {
next.modelsize = '8b';
next.category = 'instruct';
next.quantization = 'bf16';
next.optimization = 'basic';
}
return next; return next;
}); });
}; };
@@ -131,6 +145,7 @@ export const Llama31Deployment = () => {
const { hardware, optimization, modelsize, category, toolcall, quantization } = values; const { hardware, optimization, modelsize, category, toolcall, quantization } = values;
const isAMD = hardware === 'mi300x' || hardware === 'mi325x' || hardware === 'mi355x'; const isAMD = hardware === 'mi300x' || hardware === 'mi325x' || hardware === 'mi355x';
const isXeon = hardware === 'xeon'; const isXeon = hardware === 'xeon';
const effectiveModelSize = isXeon ? '8b' : modelsize; const effectiveModelSize = isXeon ? '8b' : modelsize;
@@ -145,7 +160,7 @@ export const Llama31Deployment = () => {
// Determine model path // Determine model path
let modelPath; let modelPath;
if (quantization === 'fp8' && category === 'instruct' && !isXeon) { if (quantization === 'fp8' && category === 'instruct' && !isXeon && hardware !== 'arc_b') {
if (effectiveModelSize === '405b') { if (effectiveModelSize === '405b') {
// Meta official FP8 for 405B // Meta official FP8 for 405B
modelPath = `meta-llama/Llama-3.1-${sizeToken}${categorySuffix}-FP8`; modelPath = `meta-llama/Llama-3.1-${sizeToken}${categorySuffix}-FP8`;
@@ -186,6 +201,8 @@ export const Llama31Deployment = () => {
} else if (isXeon) { } else if (isXeon) {
// Intel Xeon CPU TP configuration // Intel Xeon CPU TP configuration
tpSize = 3; tpSize = 3;
} else if (hardware === 'arc_b') {
tpSize = 1;
} else { } else {
// NVIDIA GPU TP configuration // NVIDIA GPU TP configuration
if (effectiveModelSize === '405b') { if (effectiveModelSize === '405b') {
@@ -202,6 +219,8 @@ export const Llama31Deployment = () => {
if (isXeon) { if (isXeon) {
args.push(`--device cpu`); args.push(`--device cpu`);
args.push(`--disable-overlap-schedule`); args.push(`--disable-overlap-schedule`);
} else if (hardware === 'arc_b') {
args.push(`--device xpu`);
} }
if (tpSize) { if (tpSize) {
@@ -214,7 +233,7 @@ export const Llama31Deployment = () => {
} }
// NVIDIA-specific optimizations // NVIDIA-specific optimizations
if (!isAMD && !isXeon) { if (!isAMD && !isXeon && hardware !== 'arc_b') {
if (optimization === 'throughput') { if (optimization === 'throughput') {
args.push(`--enable-dp-attention`); args.push(`--enable-dp-attention`);
args.push(`--mem-fraction-static 0.85`); args.push(`--mem-fraction-static 0.85`);
@@ -8,7 +8,8 @@ export const Llama33Deployment = () => {
{ id: 'mi300x', label: 'MI300X', default: true }, { id: 'mi300x', label: 'MI300X', default: true },
{ id: 'mi325x', label: 'MI325X', default: false }, { id: 'mi325x', label: 'MI325X', default: false },
{ id: 'mi355x', label: 'MI355X', default: false }, { id: 'mi355x', label: 'MI355X', default: false },
{ id: 'xeon', label: 'XEON', default: false } { id: 'xeon', label: 'XEON', default: false },
{ id: 'arc_b', label: 'BMG', default: false }
] ]
}, },
quantization: { quantization: {
@@ -35,7 +36,7 @@ export const Llama33Deployment = () => {
...options.quantization, ...options.quantization,
items: options.quantization.items.map(item => ({ items: options.quantization.items.map(item => ({
...item, ...item,
disabled: values.hardware === 'xeon' && item.id === 'fp8' disabled: (values.hardware === 'xeon' && item.id === 'fp8') || (values.hardware === 'arc_b' && item.id !== 'bf16')
})) }))
} }
}); });
@@ -74,6 +75,9 @@ export const Llama33Deployment = () => {
if (optionName === 'hardware' && value === 'xeon') { if (optionName === 'hardware' && value === 'xeon') {
next.quantization = 'bf16'; next.quantization = 'bf16';
} }
if (optionName === 'hardware' && value === 'arc_b') {
next.quantization = 'bf16';
}
return next; return next;
}); });
}; };
@@ -83,7 +87,7 @@ export const Llama33Deployment = () => {
const { hardware, quantization, toolcall } = values; const { hardware, quantization, toolcall } = values;
// Select model based on quantization // Select model based on quantization
const modelPath = quantization === 'fp8' && hardware !== 'xeon' const modelPath = quantization === 'fp8' && hardware !== 'xeon' && hardware !== 'arc_b'
? 'amd/Llama-3.3-70B-Instruct-FP8-KV' ? 'amd/Llama-3.3-70B-Instruct-FP8-KV'
: 'meta-llama/Llama-3.3-70B-Instruct'; : 'meta-llama/Llama-3.3-70B-Instruct';
@@ -94,6 +98,9 @@ export const Llama33Deployment = () => {
cmd += ` --device cpu \\\n`; cmd += ` --device cpu \\\n`;
cmd += ` --disable-overlap-schedule \\\n`; cmd += ` --disable-overlap-schedule \\\n`;
cmd += ` --tp 6`; cmd += ` --tp 6`;
} else if (hardware === 'arc_b') {
cmd += ` --device xpu \\\n`;
cmd += ` --tp 8`;
} else { } else {
cmd += ` --tp 1`; cmd += ` --tp 1`;
} }
@@ -8,7 +8,8 @@ export const Nemotron3NanoDeployment = () => {
items: [ items: [
{ id: 'h200', label: 'H200', default: false }, { id: 'h200', label: 'H200', default: false },
{ id: 'b200', label: 'B200', default: true }, { id: 'b200', label: 'B200', default: true },
{ id: 'b300', label: 'B300', default: false } { id: 'b300', label: 'B300', default: false },
{ id: 'arc_b', label: 'BMG', default: false }
] ]
}, },
modelVariant: { modelVariant: {
@@ -76,6 +77,10 @@ export const Nemotron3NanoDeployment = () => {
cmd += ` --attention-backend flashinfer \\\n`; cmd += ` --attention-backend flashinfer \\\n`;
} }
if (hardware === 'arc_b') {
cmd += ` --device xpu \\\n`;
}
// Add thinking parser and tool call parser if enabled // Add thinking parser and tool call parser if enabled
for (const [key, option] of Object.entries(options)) { for (const [key, option] of Object.entries(options)) {
if (option.commandRule) { if (option.commandRule) {
@@ -153,7 +158,18 @@ export const Nemotron3NanoDeployment = () => {
}, []); }, []);
const handleRadioChange = (optionName, value) => { const handleRadioChange = (optionName, value) => {
setValues((prev) => ({ ...prev, [optionName]: value })); setValues((prev) => {
if (prev.hardware === 'arc_b' && optionName === 'modelVariant' && value !== 'bf16') {
return prev;
}
const next = { ...prev, [optionName]: value };
if (optionName === 'hardware' && value === 'arc_b') {
next.modelVariant = 'bf16';
next.tp = '4';
}
return next;
});
}; };
const handleCheckboxChange = (optionName, itemId, isChecked) => { const handleCheckboxChange = (optionName, itemId, isChecked) => {
@@ -327,7 +343,11 @@ export const Nemotron3NanoDeployment = () => {
) : ( ) : (
items.map((item) => { items.map((item) => {
const isChecked = values[option.name] === item.id; const isChecked = values[option.name] === item.id;
const isDisabled = Boolean(item.disabled); const isArcBModelLocked =
values.hardware === 'arc_b' &&
option.name === 'modelVariant' &&
item.id !== 'bf16';
const isDisabled = Boolean(item.disabled || isArcBModelLocked);
return ( return (
<label <label
key={item.id} key={item.id}
@@ -23,7 +23,8 @@ export const Qwen3Deployment = () => {
mi300x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi300x: { tp: 1, ep: 0, bf16: true, fp8: true },
mi325x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi325x: { tp: 1, ep: 0, bf16: true, fp8: true },
mi355x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi355x: { tp: 1, ep: 0, bf16: true, fp8: true },
xeon: { tp: 3, ep: 0, bf16: true, fp8: true } xeon: { tp: 3, ep: 0, bf16: true, fp8: true },
arc_b: { tp: 4, ep: 0, bf16: true, fp8: true },
}, },
'32b': { '32b': {
baseName: '32B', baseName: '32B',
@@ -35,7 +36,8 @@ export const Qwen3Deployment = () => {
mi300x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi300x: { tp: 1, ep: 0, bf16: true, fp8: true },
mi325x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi325x: { tp: 1, ep: 0, bf16: true, fp8: true },
mi355x: { tp: 1, ep: 0, bf16: true, fp8: true }, mi355x: { tp: 1, ep: 0, bf16: true, fp8: true },
xeon: { tp: 6, ep: 0, bf16: true, fp8: true } xeon: { tp: 6, ep: 0, bf16: true, fp8: true },
arc_b: { tp: 4, ep: 0, bf16: true, fp8: true }
}, },
'14b': { '14b': {
baseName: '14B', baseName: '14B',
@@ -112,7 +114,8 @@ export const Qwen3Deployment = () => {
{ id: 'mi300x', label: 'MI300X', default: false }, { id: 'mi300x', label: 'MI300X', default: false },
{ id: 'mi325x', label: 'MI325X', default: false }, { id: 'mi325x', label: 'MI325X', default: false },
{ id: 'mi355x', label: 'MI355X', default: false }, { id: 'mi355x', label: 'MI355X', default: false },
{ id: 'xeon', label: 'XEON', default: false } { id: 'xeon', label: 'XEON', default: false },
{ id: 'arc_b', label: 'BMG', default: false },
] ]
}, },
modelsize: { modelsize: {
@@ -169,8 +172,26 @@ export const Qwen3Deployment = () => {
const options = { ...baseOptions }; const options = { ...baseOptions };
const currentModelConfig = modelConfigs[values.modelsize]; const currentModelConfig = modelConfigs[values.modelsize];
if (values.hardware === 'arc_b') {
options.quantization = {
...baseOptions.quantization,
items: baseOptions.quantization.items.map(item => ({
...item,
disabled: item.id !== 'bf16'
}))
};
options.modelsize = {
...baseOptions.modelsize,
items: baseOptions.modelsize.items.map(item => ({
...item,
disabled: item.id !== '30b' && item.id !== '32b'
}))
};
}
// If model doesn't have thinking variants, disable non-base category options // If model doesn't have thinking variants, disable non-base category options
if (currentModelConfig && !currentModelConfig.hasThinkingVariants) { if (values.hardware === 'arc_b' || (currentModelConfig && !currentModelConfig.hasThinkingVariants)) {
options.category = { options.category = {
...baseOptions.category, ...baseOptions.category,
items: baseOptions.category.items.map(item => ({ items: baseOptions.category.items.map(item => ({
@@ -220,6 +241,14 @@ export const Qwen3Deployment = () => {
setValues(prev => { setValues(prev => {
const newValues = { ...prev, [optionName]: value }; const newValues = { ...prev, [optionName]: value };
if (optionName === 'hardware' && value === 'arc_b') {
newValues.quantization = 'bf16';
if (newValues.modelsize !== '30b' && newValues.modelsize !== '32b') {
newValues.modelsize = '32b';
}
newValues.category = 'base';
}
// Auto-switch to 'base' category for models without thinking variants // Auto-switch to 'base' category for models without thinking variants
if (optionName === 'modelsize') { if (optionName === 'modelsize') {
const modelConfig = modelConfigs[value]; const modelConfig = modelConfigs[value];
@@ -242,10 +271,10 @@ export const Qwen3Deployment = () => {
// Generate command // Generate command
const generateCommand = () => { const generateCommand = () => {
const { hardware, modelsize, quantization, category, reasoningParser, toolcall } = values; const { hardware, modelsize, quantization, category, reasoningParser, toolcall } = values;
const displayOptions = getDisplayOptions(values); const effectiveQuantization = hardware === 'arc_b' ? 'bf16' : quantization;
// Special error handling // Special error handling
const commandKey = `${hardware}-${modelsize}-${quantization}-${category}`; const commandKey = `${hardware}-${modelsize}-${effectiveQuantization}-${category}`;
if (commandKey === 'h100-235b-bf16-instruct' || commandKey === 'h100-235b-bf16-thinking') { if (commandKey === 'h100-235b-bf16-instruct' || commandKey === 'h100-235b-bf16-thinking') {
return '# Error: Model is too large, cannot fit into 8*H100\n# Please use H200 (141GB) or select FP8 quantization'; return '# Error: Model is too large, cannot fit into 8*H100\n# Please use H200 (141GB) or select FP8 quantization';
} }
@@ -260,7 +289,7 @@ export const Qwen3Deployment = () => {
return `# Error: Unknown hardware platform: ${hardware}`; return `# Error: Unknown hardware platform: ${hardware}`;
} }
const quantSuffix = quantization === 'fp8' ? '-FP8' : ''; const quantSuffix = effectiveQuantization === 'fp8' ? '-FP8' : '';
// Build model name based on model category // Build model name based on model category
let modelName; let modelName;
@@ -281,6 +310,8 @@ export const Qwen3Deployment = () => {
if (hardware === 'xeon') { if (hardware === 'xeon') {
cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule`; cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule`;
} else if (hardware === 'arc_b') {
cmd += ` \\\n --device xpu`;
} }
if (hwConfig.tp > 1) { if (hwConfig.tp > 1) {
@@ -288,7 +319,7 @@ export const Qwen3Deployment = () => {
} }
let ep = hwConfig.ep; let ep = hwConfig.ep;
if (quantization === 'fp8' && hwConfig.tp === 8) { if (effectiveQuantization === 'fp8' && hwConfig.tp === 8) {
ep = 2; ep = 2;
} }
@@ -65,7 +65,8 @@ export const Qwen35Deployment = () => {
{ id: 'mi300x', label: 'MI300X', default: false, disabled: isNvfp4 }, { id: 'mi300x', label: 'MI300X', default: false, disabled: isNvfp4 },
{ id: 'mi325x', label: 'MI325X', default: false, disabled: isNvfp4 }, { id: 'mi325x', label: 'MI325X', default: false, disabled: isNvfp4 },
{ id: 'mi355x', label: 'MI355X', default: false, disabled: false }, { id: 'mi355x', label: 'MI355X', default: false, disabled: false },
{ id: 'xeon', label: 'XEON', default: false, disabled: isNvfp4 } { id: 'xeon', label: 'XEON', default: false, disabled: isNvfp4 },
{ id: 'arc_b', label: 'BMG', default: false, disabled: isNvfp4 }
]; ];
} }
}, },
@@ -76,11 +77,12 @@ export const Qwen35Deployment = () => {
const hasFp8 = FP8_MODELS.has(values.model); const hasFp8 = FP8_MODELS.has(values.model);
const hasFp4 = values.model === '397b'; const hasFp4 = values.model === '397b';
const isXeon = values.hardware === 'xeon'; const isXeon = values.hardware === 'xeon';
const isArcB = values.hardware === 'arc_b';
return [ return [
{ id: 'bf16', label: 'BF16', default: !hasFp8 || isXeon }, { id: 'bf16', label: 'BF16', default: !hasFp8 || isXeon || isArcB },
{ id: 'fp8', label: 'FP8', default: hasFp8 && !isXeon, disabled: !hasFp8, { id: 'fp8', label: 'FP8', default: hasFp8 && !isXeon && !isArcB, disabled: !hasFp8 || isArcB,
disabledReason: 'No FP8 variant available for this model' }, disabledReason: 'No FP8 variant available for this model' },
{ id: 'fp4', label: 'FP4', default: false, disabled: !hasFp4 || isXeon, { id: 'fp4', label: 'FP4', default: false, disabled: !hasFp4 || isXeon || isArcB,
disabledReason: isXeon ? 'FP4 is not supported on Xeon' : 'FP4 is only available for Qwen3.5-397B-A17B' } disabledReason: isXeon ? 'FP4 is not supported on Xeon' : 'FP4 is only available for Qwen3.5-397B-A17B' }
]; ];
} }
@@ -104,7 +106,7 @@ export const Qwen35Deployment = () => {
speculative: { speculative: {
name: 'speculative', name: 'speculative',
title: 'Speculative Decoding (MTP)', title: 'Speculative Decoding (MTP)',
condition: (values) => values.hardware !== 'xeon', condition: (values) => values.hardware !== 'xeon' && values.hardware !== 'arc_b',
items: [ items: [
{ id: 'disabled', label: 'Disabled', default: false }, { id: 'disabled', label: 'Disabled', default: false },
{ id: 'enabled', label: 'Enabled', default: true } { id: 'enabled', label: 'Enabled', default: true }
@@ -124,7 +126,7 @@ export const Qwen35Deployment = () => {
mambaCache: { mambaCache: {
name: 'mambaCache', name: 'mambaCache',
title: 'Mamba Radix Cache', title: 'Mamba Radix Cache',
condition: (values) => MOE_MODELS.has(values.model) && values.hardware !== 'xeon', condition: (values) => MOE_MODELS.has(values.model) && values.hardware !== 'xeon' && values.hardware !== 'arc_b',
getDynamicItems: (currentValues) => { getDynamicItems: (currentValues) => {
const amdGpus = ['mi300x', 'mi325x', 'mi355x']; const amdGpus = ['mi300x', 'mi325x', 'mi355x'];
const isAmdGpu = amdGpus.includes(currentValues.hardware); const isAmdGpu = amdGpus.includes(currentValues.hardware);
@@ -184,7 +186,8 @@ export const Qwen35Deployment = () => {
mi300x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } }, mi300x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } },
mi325x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } }, mi325x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } },
mi355x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } }, mi355x: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } },
xeon: { bf16: { tp: 3 }, fp8: { tp: 3 } } xeon: { bf16: { tp: 3 }, fp8: { tp: 3 } },
arc_b: { bf16: { tp: 4, mem: 0.8 } }
}, },
'27b': { '27b': {
h100: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } }, h100: { bf16: { tp: 1, mem: 0.8 }, fp8: { tp: 1, mem: 0.8 } },
@@ -204,7 +207,8 @@ export const Qwen35Deployment = () => {
mi300x: { bf16: { tp: 1, mem: 0.8 } }, mi300x: { bf16: { tp: 1, mem: 0.8 } },
mi325x: { bf16: { tp: 1, mem: 0.8 } }, mi325x: { bf16: { tp: 1, mem: 0.8 } },
mi355x: { bf16: { tp: 1, mem: 0.8 } }, mi355x: { bf16: { tp: 1, mem: 0.8 } },
xeon: { bf16: { tp: 3 } } xeon: { bf16: { tp: 3 } },
arc_b: { bf16: { tp: 1, mem: 0.8 } }
}, },
'4b': { '4b': {
h100: { bf16: { tp: 1, mem: 0.8 } }, h100: { bf16: { tp: 1, mem: 0.8 } },
@@ -214,7 +218,8 @@ export const Qwen35Deployment = () => {
mi300x: { bf16: { tp: 1, mem: 0.8 } }, mi300x: { bf16: { tp: 1, mem: 0.8 } },
mi325x: { bf16: { tp: 1, mem: 0.8 } }, mi325x: { bf16: { tp: 1, mem: 0.8 } },
mi355x: { bf16: { tp: 1, mem: 0.8 } }, mi355x: { bf16: { tp: 1, mem: 0.8 } },
xeon: { bf16: { tp: 3 } } xeon: { bf16: { tp: 3 } },
arc_b: { bf16: { tp: 1, mem: 0.8 } }
}, },
'2b': { '2b': {
h100: { bf16: { tp: 1, mem: 0.8 } }, h100: { bf16: { tp: 1, mem: 0.8 } },
@@ -286,7 +291,17 @@ export const Qwen35Deployment = () => {
}, [values.hardware, values.model]); }, [values.hardware, values.model]);
const handleRadioChange = (optionName, value) => { const handleRadioChange = (optionName, value) => {
setValues(prev => ({ ...prev, [optionName]: value })); setValues(prev => {
if (prev.hardware === 'arc_b' && optionName === 'model' && !['35b', '9b', '4b'].includes(value)) {
return prev;
}
const next = { ...prev, [optionName]: value };
if (optionName === 'hardware' && value === 'arc_b' && !['35b', '9b', '4b'].includes(next.model)) {
next.model = '35b';
}
return next;
});
}; };
// Multi-node flag template mirrors DeepSeek-V4 cookbook's multiNodeFlags. // Multi-node flag template mirrors DeepSeek-V4 cookbook's multiNodeFlags.
@@ -354,6 +369,9 @@ export const Qwen35Deployment = () => {
let cmd = `sglang serve --model-path ${modelName}`; let cmd = `sglang serve --model-path ${modelName}`;
if (hardware === 'xeon') { if (hardware === 'xeon') {
cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule`; cmd += ` \\\n --device cpu \\\n --disable-overlap-schedule`;
} else if (hardware === 'arc_b') {
cmd += ` \\\n --device xpu`;
cmd += ` \\\n --linear-attn-backend intel_xpu`;
} }
if (tpValue > 1) { if (tpValue > 1) {
cmd += ` \\\n --tp ${tpValue}`; cmd += ` \\\n --tp ${tpValue}`;
@@ -378,7 +396,7 @@ export const Qwen35Deployment = () => {
// would emit a spurious --mamba-radix-cache-strategy extra_buffer. The UI // would emit a spurious --mamba-radix-cache-strategy extra_buffer. The UI
// radio is hidden for dense models, so users can't manually correct it. // radio is hidden for dense models, so users can't manually correct it.
// MoE keeps the old behavior the UI radio is the recovery path there. // MoE keeps the old behavior the UI radio is the recovery path there.
const mamba_v1_dev = ['mi300x', 'mi325x', 'mi355x', 'xeon']; const mamba_v1_dev = ['mi300x', 'mi325x', 'mi355x', 'xeon', 'arc_b'];
const actualMambaCache = mamba_v1_dev.includes(hardware) const actualMambaCache = mamba_v1_dev.includes(hardware)
? 'v1' ? 'v1'
: (speculative === 'enabled' ? 'v2' : (MOE_MODELS.has(model) ? mambaCache : 'v1')); : (speculative === 'enabled' ? 'v2' : (MOE_MODELS.has(model) ? mambaCache : 'v1'));
@@ -427,7 +445,7 @@ export const Qwen35Deployment = () => {
// benchmark only enables this for TP>=8). AMD MI GPUs use the AITER allreduce // benchmark only enables this for TP>=8). AMD MI GPUs use the AITER allreduce
// fusion flag instead, handled in the AMD backend block below. // fusion flag instead, handled in the AMD backend block below.
const amdGpu = hardware === 'mi300x' || hardware === 'mi325x' || hardware === 'mi355x'; const amdGpu = hardware === 'mi300x' || hardware === 'mi325x' || hardware === 'mi355x';
if (quantization !== 'fp4' && hardware !== 'xeon' && !amdGpu) { if (quantization !== 'fp4' && hardware !== 'xeon' && hardware !== 'arc_b' && !amdGpu) {
cmd += ` \\\n --enable-flashinfer-allreduce-fusion`; cmd += ` \\\n --enable-flashinfer-allreduce-fusion`;
} }
@@ -580,7 +598,11 @@ export const Qwen35Deployment = () => {
<div style={itemsStyle}> <div style={itemsStyle}>
{items.map(item => { {items.map(item => {
const isChecked = values[option.name] === item.id; const isChecked = values[option.name] === item.id;
const isDisabled = !!item.disabled; const isArcBModelLocked =
values.hardware === 'arc_b' &&
option.name === 'model' &&
!['35b', '9b', '4b'].includes(item.id);
const isDisabled = !!item.disabled || isArcBModelLocked;
return ( return (
<label <label
key={item.id} key={item.id}

Some files were not shown because too many files have changed in this diff Show More