94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
296 lines
10 KiB
Python
296 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
from typing import TYPE_CHECKING, Any, List, Optional
|
|
|
|
import msgspec
|
|
|
|
from sglang.srt.speculative.dflash_utils import parse_dflash_draft_config
|
|
|
|
if TYPE_CHECKING:
|
|
from sglang.srt.server_args import ServerArgs
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DEFAULT_DSPARK_GAMMA = 7
|
|
SUPPORTED_DSPARK_MARKOV_HEAD_TYPES = ("vanilla", "gated", "rnn")
|
|
|
|
# The dsv4 self-drafting checkpoint runs its draft attention on the dedicated
|
|
# DeepSeek-V4 backend instead of the generic draft-backend fallback.
|
|
DSV4_DRAFT_ATTENTION_BACKEND = "dsv4"
|
|
|
|
|
|
def draft_is_deepseek_v4(*, server_args: ServerArgs) -> bool:
|
|
from sglang.srt.configs.model_config import is_deepseek_v4
|
|
from sglang.srt.utils.hf_transformers_utils import get_config
|
|
|
|
draft_model_path = server_args.speculative_draft_model_path
|
|
if not draft_model_path:
|
|
return False
|
|
draft_hf_config = get_config(
|
|
draft_model_path,
|
|
trust_remote_code=server_args.trust_remote_code,
|
|
revision=server_args.speculative_draft_model_revision,
|
|
model_override_args=json.loads(server_args.json_model_override_args),
|
|
model_config_parser=server_args.model_config_parser,
|
|
)
|
|
return draft_hf_config is not None and is_deepseek_v4(draft_hf_config)
|
|
|
|
|
|
def dspark_gamma_from_num_draft_tokens(num_draft_tokens: int) -> int:
|
|
gamma = int(num_draft_tokens) - 1
|
|
if gamma < 1:
|
|
raise ValueError(
|
|
"DSpark speculative_num_draft_tokens must be >= 2 (= gamma + 1), "
|
|
f"got {num_draft_tokens}."
|
|
)
|
|
return gamma
|
|
|
|
|
|
class DSparkDraftConfig(msgspec.Struct, frozen=True):
|
|
num_hidden_layers: Optional[int]
|
|
num_target_layers: Optional[int]
|
|
gamma: Optional[int]
|
|
target_layer_ids: Optional[List[int]]
|
|
mask_token: str
|
|
mask_token_id: Optional[int]
|
|
markov_rank: int
|
|
markov_head_type: Optional[str]
|
|
|
|
def resolve_gamma(self, *, default: Optional[int] = None) -> Optional[int]:
|
|
return self.gamma if self.gamma is not None else default
|
|
|
|
def require_markov(self) -> bool:
|
|
return int(self.markov_rank) > 0
|
|
|
|
|
|
class DSparkRuntimeConfig(msgspec.Struct, frozen=True):
|
|
gamma: int
|
|
verify_num_draft_tokens: int
|
|
mask_token_id: int
|
|
|
|
|
|
def resolve_runtime_config(
|
|
*,
|
|
draft_hf_config: Any,
|
|
speculative_num_draft_tokens: Optional[int],
|
|
target_vocab_size: int,
|
|
) -> DSparkRuntimeConfig:
|
|
"""Resolve and validate the worker-facing DSpark runtime knobs (gamma,
|
|
verify window, mask token) from the draft checkpoint config, with
|
|
server_args.speculative_num_draft_tokens taking precedence for gamma."""
|
|
draft_config = parse_dspark_draft_config(draft_hf_config=draft_hf_config)
|
|
if not draft_config.require_markov():
|
|
raise ValueError(
|
|
"DSpark draft requires markov_rank > 0; got "
|
|
f"markov_rank={draft_config.markov_rank}."
|
|
)
|
|
|
|
if speculative_num_draft_tokens is None:
|
|
gamma = int(draft_config.resolve_gamma(default=None) or 0)
|
|
if gamma < 1:
|
|
raise ValueError(
|
|
"DSpark could not resolve gamma from the draft config and "
|
|
"speculative_num_draft_tokens is unset."
|
|
)
|
|
else:
|
|
gamma = dspark_gamma_from_num_draft_tokens(int(speculative_num_draft_tokens))
|
|
config_gamma = draft_config.resolve_gamma(default=None)
|
|
if config_gamma is not None and int(config_gamma) != gamma:
|
|
logger.warning(
|
|
"DSpark gamma mismatch: using gamma=%s (from "
|
|
"speculative_num_draft_tokens=%s) but draft config block_size=%s.",
|
|
gamma,
|
|
speculative_num_draft_tokens,
|
|
config_gamma,
|
|
)
|
|
|
|
if draft_config.mask_token_id is None:
|
|
raise ValueError(
|
|
"DSpark requires mask_token_id to be set in the draft model config."
|
|
)
|
|
mask_token_id = int(draft_config.mask_token_id)
|
|
if mask_token_id >= target_vocab_size:
|
|
raise ValueError(
|
|
f"DSpark mask_token_id={mask_token_id} is outside the target "
|
|
f"vocab size {target_vocab_size}."
|
|
)
|
|
|
|
return DSparkRuntimeConfig(
|
|
gamma=gamma,
|
|
verify_num_draft_tokens=gamma + 1,
|
|
mask_token_id=mask_token_id,
|
|
)
|
|
|
|
|
|
def read_draft_checkpoint_gamma(*, server_args: ServerArgs) -> Optional[int]:
|
|
"""Load the draft checkpoint's hf config and read its DSpark gamma
|
|
(block_size). Raises on config-load failure; callers pick the fallback."""
|
|
from sglang.srt.utils.hf_transformers_utils import get_config
|
|
|
|
draft_hf_config = get_config(
|
|
server_args.speculative_draft_model_path,
|
|
trust_remote_code=server_args.trust_remote_code,
|
|
revision=server_args.speculative_draft_model_revision,
|
|
model_override_args=json.loads(server_args.json_model_override_args),
|
|
)
|
|
return parse_dspark_draft_config(draft_hf_config=draft_hf_config).resolve_gamma(
|
|
default=None
|
|
)
|
|
|
|
|
|
def checkpoint_bundles_dspark_draft(hf_config: Any) -> bool:
|
|
"""The checkpoint carries a bundled DSpark draft head, marked by the
|
|
prefixed dspark_* keys on the target hf config. Single source of truth
|
|
for the bundling convention (draft-path defaulting, draft-arch remap)."""
|
|
return any(
|
|
_cfg_get(hf_config, key, None) is not None
|
|
for key in (
|
|
"dspark_block_size",
|
|
"dspark_markov_rank",
|
|
"dspark_noise_token_id",
|
|
"dspark_target_layer_ids",
|
|
)
|
|
)
|
|
|
|
|
|
def _cfg_get(config: Any, key: str, default: Any = None) -> Any:
|
|
if isinstance(config, dict):
|
|
return config.get(key, default)
|
|
return getattr(config, key, default)
|
|
|
|
|
|
def _get_text_config(config: Any) -> Any:
|
|
if config is None:
|
|
return None
|
|
if isinstance(config, dict):
|
|
return config.get("text_config", config)
|
|
text_config = getattr(config, "text_config", None)
|
|
if text_config is not None:
|
|
return text_config
|
|
return config
|
|
|
|
|
|
def _get_dspark_config(config: Any) -> dict:
|
|
cfg = _cfg_get(config, "dspark_config", None)
|
|
if cfg is None:
|
|
return {}
|
|
if isinstance(cfg, dict):
|
|
return cfg
|
|
try:
|
|
return dict(cfg)
|
|
except Exception:
|
|
return {}
|
|
|
|
|
|
def parse_dspark_draft_config(*, draft_hf_config: Any) -> DSparkDraftConfig:
|
|
base = parse_dflash_draft_config(draft_hf_config=draft_hf_config)
|
|
|
|
dspark_cfg = _get_dspark_config(draft_hf_config)
|
|
text_config = _get_text_config(draft_hf_config)
|
|
|
|
prefixed_block_size = _cfg_get(draft_hf_config, "dspark_block_size", None)
|
|
prefixed_markov_rank = _cfg_get(draft_hf_config, "dspark_markov_rank", None)
|
|
prefixed_markov_head_type = _cfg_get(
|
|
draft_hf_config, "dspark_markov_head_type", None
|
|
)
|
|
prefixed_noise_token_id = _cfg_get(draft_hf_config, "dspark_noise_token_id", None)
|
|
prefixed_target_layer_ids = _cfg_get(
|
|
draft_hf_config, "dspark_target_layer_ids", None
|
|
)
|
|
uses_prefixed = any(
|
|
value is not None
|
|
for value in (
|
|
prefixed_block_size,
|
|
prefixed_markov_rank,
|
|
prefixed_noise_token_id,
|
|
prefixed_target_layer_ids,
|
|
)
|
|
)
|
|
|
|
raw_markov_rank = (
|
|
prefixed_markov_rank
|
|
if prefixed_markov_rank is not None
|
|
else dspark_cfg.get(
|
|
"markov_rank",
|
|
_cfg_get(
|
|
text_config, "markov_rank", _cfg_get(draft_hf_config, "markov_rank", 0)
|
|
),
|
|
)
|
|
)
|
|
markov_rank = int(raw_markov_rank) if raw_markov_rank is not None else 0
|
|
if markov_rank < 0:
|
|
raise ValueError(f"DSpark markov_rank must be >= 0, got {markov_rank}.")
|
|
|
|
markov_head_type = (
|
|
prefixed_markov_head_type
|
|
if prefixed_markov_head_type is not None
|
|
else dspark_cfg.get(
|
|
"markov_head_type",
|
|
_cfg_get(
|
|
text_config,
|
|
"markov_head_type",
|
|
_cfg_get(draft_hf_config, "markov_head_type", None),
|
|
),
|
|
)
|
|
)
|
|
if markov_rank > 0 and markov_head_type is None and not uses_prefixed:
|
|
raise ValueError(
|
|
"DSpark requires markov_head_type when markov_rank > 0, got None."
|
|
)
|
|
if markov_head_type is not None:
|
|
markov_head_type = str(markov_head_type).lower()
|
|
if markov_head_type not in SUPPORTED_DSPARK_MARKOV_HEAD_TYPES:
|
|
raise ValueError(
|
|
f"Unsupported DSpark markov_head_type={markov_head_type!r}. "
|
|
f"Supported: {SUPPORTED_DSPARK_MARKOV_HEAD_TYPES}."
|
|
)
|
|
|
|
raw_mask_token_id = (
|
|
prefixed_noise_token_id
|
|
if prefixed_noise_token_id is not None
|
|
else dspark_cfg.get(
|
|
"mask_token_id",
|
|
_cfg_get(
|
|
text_config,
|
|
"mask_token_id",
|
|
_cfg_get(draft_hf_config, "mask_token_id", base.mask_token_id),
|
|
),
|
|
)
|
|
)
|
|
mask_token_id = int(raw_mask_token_id) if raw_mask_token_id is not None else None
|
|
if mask_token_id is not None and mask_token_id < 0:
|
|
raise ValueError(
|
|
f"DSpark mask_token_id must be non-negative, got {mask_token_id}."
|
|
)
|
|
|
|
gamma = (
|
|
int(prefixed_block_size) if prefixed_block_size is not None else base.block_size
|
|
)
|
|
|
|
if prefixed_target_layer_ids is not None:
|
|
if not isinstance(prefixed_target_layer_ids, (list, tuple)) or not len(
|
|
prefixed_target_layer_ids
|
|
):
|
|
raise ValueError(
|
|
"DSpark dspark_target_layer_ids must be a non-empty list of ints, "
|
|
f"got {prefixed_target_layer_ids!r}."
|
|
)
|
|
target_layer_ids: Optional[List[int]] = [
|
|
int(x) for x in prefixed_target_layer_ids
|
|
]
|
|
else:
|
|
target_layer_ids = base.target_layer_ids
|
|
|
|
return DSparkDraftConfig(
|
|
num_hidden_layers=base.num_hidden_layers,
|
|
num_target_layers=base.num_target_layers,
|
|
gamma=gamma,
|
|
target_layer_ids=target_layer_ids,
|
|
mask_token=base.mask_token,
|
|
mask_token_id=mask_token_id,
|
|
markov_rank=markov_rank,
|
|
markov_head_type=markov_head_type,
|
|
)
|