Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

510 lines
18 KiB
Python

from __future__ import annotations
import logging
from typing import Callable, Iterable, Optional, Tuple
import torch
from torch import nn
from sglang.srt.distributed.communication_op import tensor_model_parallel_all_gather
from sglang.srt.model_loader.weight_utils import default_weight_loader
from sglang.srt.models.dflash import DFlashDraftModel
from sglang.srt.speculative.dspark_components.dspark_config import (
parse_dspark_draft_config,
)
from sglang.srt.speculative.ragged_verify import (
RaggedVerifyMode,
read_ragged_verify_mode,
)
logger = logging.getLogger(__name__)
StepSampler = Callable[[torch.Tensor, int], torch.Tensor]
def gather_and_crop_vocab(
local_logits: torch.Tensor, lm_head: nn.Module
) -> torch.Tensor:
full_logits = tensor_model_parallel_all_gather(local_logits, dim=-1)
return full_logits[..., : int(lm_head.org_vocab_size)]
def run_markov_block(
head: nn.Module,
base_logits: torch.Tensor,
*,
first_prev_tokens: torch.Tensor,
hidden_states: Optional[torch.Tensor],
sampler: StepSampler,
) -> Tuple[torch.Tensor, torch.Tensor]:
batch_size, proposal_len = base_logits.shape[:2]
if proposal_len == 0:
empty = torch.empty(batch_size, 0, dtype=torch.long, device=base_logits.device)
return empty, base_logits
sampled_tokens = []
corrected_logits = []
prev_tokens = first_prev_tokens.long()
for step_idx in range(proposal_len):
step_hidden = None if hidden_states is None else hidden_states[:, step_idx, ...]
step_logits = head.apply_step_logits(
base_logits[:, step_idx, :],
token_ids=prev_tokens,
hidden_states=step_hidden,
)
next_tokens = sampler(step_logits, step_idx)
sampled_tokens.append(next_tokens)
corrected_logits.append(step_logits.unsqueeze(1))
prev_tokens = next_tokens
return (
torch.stack(sampled_tokens, dim=1),
torch.cat(corrected_logits, dim=1),
)
class VanillaMarkov(nn.Module):
markov_head_type = "vanilla"
def __init__(self, *, vocab_size: int, markov_rank: int) -> None:
super().__init__()
self.vocab_size = int(vocab_size)
self.markov_rank = int(markov_rank)
if self.markov_rank <= 0:
raise ValueError(
f"VanillaMarkov requires markov_rank > 0, got {self.markov_rank}."
)
self.markov_w1 = nn.Embedding(self.vocab_size, self.markov_rank)
self.markov_w2 = nn.Linear(self.markov_rank, self.vocab_size, bias=False)
def get_prev_embeddings(self, token_ids: torch.Tensor) -> torch.Tensor:
return self.markov_w1(token_ids.long())
def project_bias(self, latent_states: torch.Tensor) -> torch.Tensor:
return self.markov_w2(latent_states)
def compute_step_bias(
self,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
del hidden_states
return self.project_bias(self.get_prev_embeddings(token_ids))
def apply_step_logits(
self,
logits: torch.Tensor,
*,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
return logits + self.compute_step_bias(token_ids, hidden_states)
def apply_block_logits(
self,
base_logits: torch.Tensor,
*,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
if base_logits.size(-2) == 0:
return base_logits
return base_logits + self.compute_step_bias(token_ids, hidden_states)
def sample_block(
self,
base_logits: torch.Tensor,
*,
first_prev_tokens: torch.Tensor,
hidden_states: Optional[torch.Tensor],
sampler: StepSampler,
) -> Tuple[torch.Tensor, torch.Tensor]:
return run_markov_block(
self,
base_logits,
first_prev_tokens=first_prev_tokens,
hidden_states=hidden_states,
sampler=sampler,
)
class GatedMarkovHead(VanillaMarkov):
markov_head_type = "gated"
def __init__(self, *, vocab_size: int, markov_rank: int, hidden_size: int) -> None:
super().__init__(vocab_size=vocab_size, markov_rank=markov_rank)
self.gate_proj = nn.Linear(int(hidden_size) + markov_rank, markov_rank)
def compute_gate(
self,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
if hidden_states is None:
raise ValueError("GatedMarkovHead requires hidden_states.")
prev_embeddings = self.get_prev_embeddings(token_ids)
gate_inputs = torch.cat([hidden_states, prev_embeddings], dim=-1)
return torch.sigmoid(self.gate_proj(gate_inputs))
def compute_step_bias(
self,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
prev_embeddings = self.get_prev_embeddings(token_ids)
gate = self.compute_gate(token_ids, hidden_states).to(
dtype=prev_embeddings.dtype
)
return self.project_bias(gate * prev_embeddings)
class RNNHead(VanillaMarkov):
markov_head_type = "rnn"
def __init__(self, *, vocab_size: int, markov_rank: int, hidden_size: int) -> None:
super().__init__(vocab_size=vocab_size, markov_rank=markov_rank)
self.hidden_size = int(hidden_size)
self.state_size = markov_rank
self.joint_proj = nn.Linear(2 * markov_rank + self.hidden_size, 3 * markov_rank)
def _rnn_step(
self,
state: torch.Tensor,
prev_embeddings: torch.Tensor,
hidden_states: torch.Tensor,
) -> Tuple[torch.Tensor, torch.Tensor]:
z = torch.cat([state, prev_embeddings, hidden_states], dim=-1)
gate_raw, candidate_raw, output_raw = self.joint_proj(z).chunk(3, dim=-1)
gate = torch.sigmoid(gate_raw)
candidate = torch.tanh(candidate_raw)
new_state = gate * state + (1.0 - gate) * candidate
bias = self.project_bias(torch.tanh(output_raw))
return new_state, bias
def compute_step_bias(
self,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
if hidden_states is None:
raise ValueError("RNNHead requires hidden_states.")
prev_embeddings = self.get_prev_embeddings(token_ids)
state = torch.zeros_like(prev_embeddings)
_, bias = self._rnn_step(state, prev_embeddings, hidden_states)
return bias
def apply_block_logits(
self,
base_logits: torch.Tensor,
*,
token_ids: torch.Tensor,
hidden_states: Optional[torch.Tensor],
) -> torch.Tensor:
if hidden_states is None:
raise ValueError("RNNHead requires hidden_states.")
block_size = base_logits.size(-2)
if block_size == 0:
return base_logits
leading_shape = base_logits.shape[:-2]
state = torch.zeros(
*leading_shape,
self.markov_rank,
device=base_logits.device,
dtype=hidden_states.dtype,
)
output_logits = []
for k in range(block_size):
prev_emb = self.get_prev_embeddings(token_ids[..., k])
state, bias = self._rnn_step(state, prev_emb, hidden_states[..., k, :])
output_logits.append(base_logits[..., k, :] + bias)
return torch.stack(output_logits, dim=-2)
def sample_block(
self,
base_logits: torch.Tensor,
*,
first_prev_tokens: torch.Tensor,
hidden_states: Optional[torch.Tensor],
sampler: StepSampler,
) -> Tuple[torch.Tensor, torch.Tensor]:
if hidden_states is None:
raise ValueError("RNNHead requires hidden_states.")
batch_size, proposal_len = base_logits.shape[:2]
if proposal_len == 0:
empty = torch.empty(
batch_size, 0, dtype=torch.long, device=base_logits.device
)
return empty, base_logits
state = torch.zeros(
batch_size,
self.markov_rank,
device=base_logits.device,
dtype=hidden_states.dtype,
)
sampled_tokens = []
corrected_logits = []
prev_tokens = first_prev_tokens.long()
for step_idx in range(proposal_len):
prev_emb = self.get_prev_embeddings(prev_tokens)
state, bias = self._rnn_step(state, prev_emb, hidden_states[:, step_idx, :])
step_logits = base_logits[:, step_idx, :] + bias
next_tokens = sampler(step_logits, step_idx)
sampled_tokens.append(next_tokens)
corrected_logits.append(step_logits.unsqueeze(1))
prev_tokens = next_tokens
return (
torch.stack(sampled_tokens, dim=1),
torch.cat(corrected_logits, dim=1),
)
def build_markov_head(config) -> Optional[nn.Module]:
markov_rank = int(getattr(config, "markov_rank", 0))
if markov_rank <= 0:
raise ValueError(
"DSpark requires markov_rank > 0 (the Markov head is the core of the "
f"semi-AR draft); got markov_rank={markov_rank}."
)
markov_head_type = str(getattr(config, "markov_head_type", "vanilla")).lower()
vocab_size = int(config.vocab_size)
hidden_size = int(config.hidden_size)
if markov_head_type == "vanilla":
return VanillaMarkov(vocab_size=vocab_size, markov_rank=markov_rank)
if markov_head_type == "gated":
return GatedMarkovHead(
vocab_size=vocab_size, markov_rank=markov_rank, hidden_size=hidden_size
)
if markov_head_type == "rnn":
return RNNHead(
vocab_size=vocab_size, markov_rank=markov_rank, hidden_size=hidden_size
)
raise ValueError(f"Unsupported DSpark markov_head_type={markov_head_type!r}.")
class DSparkConfidenceHead(nn.Module):
def __init__(
self,
*,
hidden_size: int,
markov_rank: int,
with_markov: bool = True,
bias: bool = True,
dtype: torch.dtype = torch.float32,
) -> None:
super().__init__()
self.with_markov = bool(with_markov)
input_dim = int(hidden_size) + (int(markov_rank) if self.with_markov else 0)
self.proj = nn.Linear(input_dim, 1, bias=bias, dtype=dtype)
self.register_buffer(
"sts_temperatures", torch.ones((), dtype=torch.float32), persistent=False
)
self._last_confidence_raw: Optional[torch.Tensor] = None
def forward(
self,
hidden_states: torch.Tensor,
markov_embed_stack: Optional[torch.Tensor] = None,
) -> torch.Tensor:
if self.with_markov:
if markov_embed_stack is None:
raise ValueError(
"DSparkConfidenceHead(with_markov=True) requires markov_embed_stack."
)
features = torch.cat(
[hidden_states, markov_embed_stack.to(dtype=hidden_states.dtype)],
dim=-1,
)
else:
features = hidden_states
features = features.to(dtype=self.proj.weight.dtype)
return self.proj(features).squeeze(-1)
def apply_sts(self, confidence_raw: torch.Tensor) -> torch.Tensor:
self._last_confidence_raw = confidence_raw
return torch.sigmoid(confidence_raw.float() / self.sts_temperatures)
def build_confidence_head(config) -> Optional[nn.Module]:
if read_ragged_verify_mode() is RaggedVerifyMode.STATIC:
return None
if not hasattr(config, "enable_confidence_head"):
logger.warning(
"DSpark draft config has no enable_confidence_head field; treating the "
"confidence head as enabled."
)
hidden_size = int(config.hidden_size)
markov_rank = int(getattr(config, "markov_rank", 0))
with_markov = bool(getattr(config, "confidence_head_with_markov", markov_rank > 0))
if with_markov and markov_rank <= 0:
raise ValueError(
"DSpark confidence_head_with_markov requires markov_rank > 0, "
f"got markov_rank={markov_rank}."
)
return DSparkConfidenceHead(
hidden_size=hidden_size,
markov_rank=markov_rank,
with_markov=with_markov,
)
_DSPARK_SKIPPED_WEIGHT_PREFIXES = (
"embed_tokens.",
"lm_head.",
"rotary_emb.",
)
class DSparkDraftMixin:
def __init__(self, config, quant_config=None, prefix: str = "") -> None:
super().__init__(config=config, quant_config=quant_config, prefix=prefix)
dspark_config = parse_dspark_draft_config(draft_hf_config=config)
if not dspark_config.require_markov():
raise ValueError(
"DSpark draft requires markov_rank > 0, "
f"got markov_rank={dspark_config.markov_rank}."
)
self.gamma = int(dspark_config.resolve_gamma(default=self.block_size))
self.markov_head = build_markov_head(config)
self.confidence_head = build_confidence_head(config)
self.lm_head: Optional[nn.Module] = None
def attach_shared_modules(
self, *, embed_tokens: nn.Module, lm_head: nn.Module
) -> None:
del embed_tokens
self.lm_head = lm_head
def compute_base_logits(
self, hidden: torch.Tensor
) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
if self.lm_head is None:
raise ValueError(
"DSpark dense draft requires the target lm_head "
"(call attach_shared_modules first)."
)
weight = self.lm_head.weight
if hidden.dtype != weight.dtype:
hidden = hidden.to(weight.dtype)
local_logits = torch.matmul(hidden, weight.T)
base_logits = gather_and_crop_vocab(local_logits, self.lm_head)
return base_logits, None
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]):
markov_weights = []
confidence_weights = []
backbone_weights = []
params_dict = dict(self.named_parameters())
for name, loaded_weight in weights:
if any(name.startswith(p) for p in _DSPARK_SKIPPED_WEIGHT_PREFIXES):
continue
if name.startswith("confidence_head."):
if self.confidence_head is None:
continue
confidence_weights.append((name, loaded_weight))
elif name.startswith("markov_head."):
markov_weights.append((name, loaded_weight))
else:
backbone_weights.append((name, loaded_weight))
super().load_weights(backbone_weights)
for name, loaded_weight in markov_weights:
if name not in params_dict:
raise ValueError(
f"DSpark unexpected markov weight {name!r} not found in model "
f"parameters (known markov params require a {type(self.markov_head).__name__} head)."
)
param = params_dict[name]
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight)
self._load_confidence_weights(
confidence_weights=confidence_weights, params_dict=params_dict
)
def _load_confidence_weights(
self,
*,
confidence_weights: list,
params_dict: dict,
) -> None:
if self.confidence_head is None:
return
loaded_names = set()
for name, loaded_weight in confidence_weights:
if name not in params_dict:
raise ValueError(
f"DSpark unexpected confidence weight {name!r} not found in "
"model parameters."
)
param = params_dict[name]
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight)
loaded_names.add(name)
confidence_param_names = {
name for name in params_dict if name.startswith("confidence_head.")
}
missing = confidence_param_names - loaded_names
if missing:
raise ValueError(
f"DSpark confidence head is enabled but the checkpoint is missing "
f"{sorted(missing)}. Provide a checkpoint with trained confidence weights, "
f"or disable the confidence head (enable_confidence_head=False)."
)
def write_target_hidden_kv(
self,
*,
target_hidden: torch.Tensor,
pool,
positions: torch.Tensor,
cache_loc: torch.Tensor,
cache_loc_2d: Optional[torch.Tensor] = None,
commit_lens: Optional[torch.Tensor] = None,
) -> None:
ctx_hidden = self.project_target_hidden(target_hidden)
for layer in self.layers:
attn = layer.self_attn
k, v = attn.kv_proj_only(ctx_hidden)
k = attn.apply_k_norm(k)
k = attn.apply_k_rope(positions, k)
k = k.view(-1, attn.num_kv_heads, attn.head_dim)
v = v.view(-1, attn.num_kv_heads, attn.head_dim)
if cache_loc_2d is not None and commit_lens is not None:
pool.set_kv_buffer_prefix_valid(
attn.attn,
cache_loc_2d,
commit_lens,
k,
v,
attn.attn.k_scale,
attn.attn.v_scale,
)
else:
pool.set_kv_buffer(
attn.attn,
cache_loc,
k,
v,
attn.attn.k_scale,
attn.attn.v_scale,
)
class DSparkDraftModel(DSparkDraftMixin, DFlashDraftModel):
pass
class Qwen3DSparkModel(DSparkDraftModel):
pass
EntryClass = [Qwen3DSparkModel]