Skip to content

[Bug] DeepSeek-V4 default prevents speculative decoding from resetting max-running-requests to 48 #33199

Description

@junliu-mde

Checklist

  • I searched related issues but found no solution.
  • The bug persists on current main (ae848116662ece923501131ce773a4602223237e).
  • This report includes a minimal source-level reproduction.
  • This report is in English.

Describe the bug

For DeepseekV4ForCausalLM, enabling speculative decoding without explicitly passing --max-running-requests leaves the effective value at 256.

This contradicts the DeepSeek-V4 cookbook:

Speculative decoding (MTP) is on — SGLang resets --max-running-requests to 48 when it is not set.

Cookbook: https://docs.sglang.io/cookbook/autoregressive/DeepSeek/DeepSeek-V4

It also contradicts the warning and intended default in the speculative decoding hook:

if server_args.max_running_requests is None:
server_args.max_running_requests = 48
logger.warning(
"Max running requests is reset to 48 for speculative decoding. You can override this by explicitly setting --max-running-requests."
)

The cause is default resolution order:

  1. The DeepSeek-V4 model hook runs first and changes max_running_requests from None to 256:
    if server_args.max_running_requests is None:
    server_args.max_running_requests = 256
    logger.warning(
    f"Setting max_running_requests to {server_args.max_running_requests} for {model_arch}."
    )
  2. The speculative hook runs later, but only sets 48 when the field is still None.
  3. The field is already 256, so the speculative default never applies.

The DeepSeek-V4 hook comment says that the speculative hook is a later writer of this field, but the is None guard means it cannot overwrite the model default:

def apply_deepseek_v4_defaults(server_args: ServerArgs, model_arch: str) -> None:
"""Residual imperative arm of the DeepSeek V4 defaults.
The attention/page/window/MoE-runner declarations moved to the override
registry (arg_groups/overrides.py: _deepseek_v4_overrides) and the
kv-cache dtype default to the resolution pipeline
(_deepseek_v4_kv_cache_dtype, invoked below at its legacy slot). This
keeps, at the legacy slot: the ROCm env fill (env-write policy), the
max_running_requests fill (the speculative hook is a later writer of
that field) and the validations.

Explicit user input must keep its current precedence. Only the model-provided default should yield to the speculative-decoding default.

Reproduction

Start DeepSeek-V4 with speculative decoding and omit --max-running-requests, as in a cookbook-generated low-latency command:

sglang serve \
  --trust-remote-code \
  --model-path deepseek-ai/DeepSeek-V4-Flash \
  --tp 4 \
  --moe-runner-backend flashinfer_mxfp4 \
  --speculative-algorithm EAGLE \
  --speculative-num-steps 3 \
  --speculative-eagle-topk 1 \
  --speculative-num-draft-tokens 4

The same issue applies to --speculative-algorithm DSPARK, whose handler has the same is None condition.

Expected effective value:

max_running_requests = 48

Actual effective value:

max_running_requests = 256

Passing --max-running-requests 48 explicitly works around the issue.

Environment

  • SGLang source: current main, commit ae848116662ece923501131ce773a4602223237e
  • Model architecture: DeepseekV4ForCausalLM
  • Reproduces through deterministic argument post-processing before GPU execution; it is not hardware-specific.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions