[Session R3] Add routed_experts_start_len for absolute routing slice control (#24851)
Co-authored-by: Byron Hsu <byron@periodiclabs.ai> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: zyzshishui <zyzshishui@gmail.com> Co-authored-by: Yuzhen Zhou <82826991+zyzshishui@users.noreply.github.com>
This commit is contained in:
co-authored by
Byron Hsu
Cursor
zyzshishui
Yuzhen Zhou
parent
9150e77399
commit
d82e339ce2
@@ -973,6 +973,7 @@ class MMReceiverBase(ABC):
|
||||
require_reasoning=recv_req.require_reasoning,
|
||||
return_hidden_states=recv_req.return_hidden_states,
|
||||
return_routed_experts=recv_req.return_routed_experts,
|
||||
routed_experts_start_len=recv_req.routed_experts_start_len,
|
||||
eos_token_ids=self.scheduler.model_config.hf_eos_token_id,
|
||||
bootstrap_host=recv_req.bootstrap_host,
|
||||
bootstrap_port=recv_req.bootstrap_port,
|
||||
|
||||
@@ -333,6 +333,7 @@ class Engine(EngineScoreMixin, EngineBase):
|
||||
custom_logit_processor: Optional[Union[List[str], str]] = None,
|
||||
return_hidden_states: bool = False,
|
||||
return_routed_experts: bool = False,
|
||||
routed_experts_start_len: int = 0,
|
||||
stream: bool = False,
|
||||
bootstrap_host: Optional[Union[List[str], str]] = None,
|
||||
bootstrap_port: Optional[Union[List[int], int]] = None,
|
||||
@@ -369,6 +370,7 @@ class Engine(EngineScoreMixin, EngineBase):
|
||||
custom_logit_processor=custom_logit_processor,
|
||||
return_hidden_states=return_hidden_states,
|
||||
return_routed_experts=return_routed_experts,
|
||||
routed_experts_start_len=routed_experts_start_len,
|
||||
stream=stream,
|
||||
bootstrap_host=bootstrap_host,
|
||||
bootstrap_port=bootstrap_port,
|
||||
@@ -423,6 +425,7 @@ class Engine(EngineScoreMixin, EngineBase):
|
||||
custom_logit_processor: Optional[Union[List[str], str]] = None,
|
||||
return_hidden_states: bool = False,
|
||||
return_routed_experts: bool = False,
|
||||
routed_experts_start_len: int = 0,
|
||||
stream: bool = False,
|
||||
bootstrap_host: Optional[Union[List[str], str]] = None,
|
||||
bootstrap_port: Optional[Union[List[int], int]] = None,
|
||||
@@ -458,6 +461,7 @@ class Engine(EngineScoreMixin, EngineBase):
|
||||
lora_path=lora_path,
|
||||
return_hidden_states=return_hidden_states,
|
||||
return_routed_experts=return_routed_experts,
|
||||
routed_experts_start_len=routed_experts_start_len,
|
||||
stream=stream,
|
||||
custom_logit_processor=custom_logit_processor,
|
||||
bootstrap_host=bootstrap_host,
|
||||
|
||||
@@ -285,6 +285,7 @@ class CompletionRequest(BaseModel):
|
||||
user: Optional[str] = None
|
||||
return_hidden_states: bool = False
|
||||
return_routed_experts: bool = False
|
||||
routed_experts_start_len: int = 0
|
||||
return_cached_tokens_details: bool = False
|
||||
|
||||
# Extra parameters for SRT backend only and will be ignored by OpenAI models.
|
||||
@@ -632,6 +633,7 @@ class ChatCompletionRequest(BaseModel):
|
||||
parallel_tool_calls: bool = True
|
||||
return_hidden_states: bool = False
|
||||
return_routed_experts: bool = False
|
||||
routed_experts_start_len: int = 0
|
||||
return_cached_tokens_details: bool = False
|
||||
reasoning_effort: Optional[Literal["none", "low", "medium", "high", "max"]] = Field(
|
||||
default=None,
|
||||
|
||||
@@ -437,6 +437,7 @@ class OpenAIServingChat(OpenAIServingBase):
|
||||
disagg_prefill_dp_rank=request.disagg_prefill_dp_rank,
|
||||
return_hidden_states=request.return_hidden_states,
|
||||
return_routed_experts=request.return_routed_experts,
|
||||
routed_experts_start_len=request.routed_experts_start_len,
|
||||
rid=request.rid,
|
||||
extra_key=self._compute_extra_key(request),
|
||||
require_reasoning=self._get_reasoning_from_request(request),
|
||||
|
||||
@@ -123,6 +123,7 @@ class OpenAIServingCompletion(OpenAIServingBase):
|
||||
disagg_prefill_dp_rank=request.disagg_prefill_dp_rank,
|
||||
return_hidden_states=request.return_hidden_states,
|
||||
return_routed_experts=request.return_routed_experts,
|
||||
routed_experts_start_len=request.routed_experts_start_len,
|
||||
rid=request.rid,
|
||||
extra_key=self._compute_extra_key(request),
|
||||
priority=request.priority,
|
||||
|
||||
@@ -174,7 +174,9 @@ class GenerateReqInput(BaseReq):
|
||||
# Whether to return captured routed experts
|
||||
return_routed_experts: bool = False
|
||||
return_indexer_topk: bool = False
|
||||
# The start location in the prompt for returning routed experts.
|
||||
# Absolute start position for returned routings; response covers
|
||||
# `[routed_experts_start_len, seqlen - 1)`. Must be in [0, prompt_tokens].
|
||||
# 0 = full sequence.
|
||||
routed_experts_start_len: int = 0
|
||||
|
||||
# The modalities of the image data [image, multi-images, video]
|
||||
@@ -654,6 +656,7 @@ class GenerateReqInput(BaseReq):
|
||||
else self.return_hidden_states
|
||||
),
|
||||
return_routed_experts=self.return_routed_experts,
|
||||
routed_experts_start_len=self.routed_experts_start_len,
|
||||
return_indexer_topk=self.return_indexer_topk,
|
||||
modalities=self.modalities[i] if self.modalities else None,
|
||||
session_params=self.session_params,
|
||||
@@ -730,7 +733,7 @@ class TokenizedGenerateReqInput(BaseReq):
|
||||
|
||||
# Whether to return captured routed experts
|
||||
return_routed_experts: bool = False
|
||||
# The start location in the prompt for returning routed experts.
|
||||
# See GenerateReqInput.routed_experts_start_len.
|
||||
routed_experts_start_len: int = 0
|
||||
|
||||
return_indexer_topk: bool = False
|
||||
|
||||
@@ -599,6 +599,7 @@ class Req(ReqDllmMixin):
|
||||
require_reasoning: bool = False,
|
||||
return_hidden_states: bool = False,
|
||||
return_routed_experts: bool = False,
|
||||
routed_experts_start_len: int = 0,
|
||||
return_indexer_topk: bool = False,
|
||||
eos_token_ids: Optional[Set[int]] = None,
|
||||
bootstrap_host: Optional[str] = None,
|
||||
@@ -818,6 +819,7 @@ class Req(ReqDllmMixin):
|
||||
|
||||
# capture routed experts
|
||||
self.return_routed_experts = return_routed_experts
|
||||
self.routed_experts_start_len = routed_experts_start_len
|
||||
self.routed_experts: Optional[torch.Tensor] = (
|
||||
None # cpu tensor: shape (seqlen, topk)
|
||||
)
|
||||
|
||||
@@ -2001,6 +2001,7 @@ class Scheduler(
|
||||
require_reasoning=recv_req.require_reasoning,
|
||||
return_hidden_states=recv_req.return_hidden_states,
|
||||
return_routed_experts=recv_req.return_routed_experts,
|
||||
routed_experts_start_len=recv_req.routed_experts_start_len,
|
||||
return_indexer_topk=recv_req.return_indexer_topk,
|
||||
eos_token_ids=self.model_config.hf_eos_token_id,
|
||||
bootstrap_host=recv_req.bootstrap_host,
|
||||
@@ -2158,6 +2159,27 @@ class Scheduler(
|
||||
self._add_request_to_queue(req)
|
||||
return
|
||||
|
||||
if recv_req.return_routed_experts:
|
||||
error_msg = None
|
||||
if recv_req.routed_experts_start_len < 0:
|
||||
error_msg = (
|
||||
f"{recv_req.routed_experts_start_len=} is lower than 0. "
|
||||
"Please use a non-negative routed_experts_start_len."
|
||||
)
|
||||
|
||||
if recv_req.routed_experts_start_len > len(req.origin_input_ids):
|
||||
error_msg = (
|
||||
f"{recv_req.routed_experts_start_len=} is higher than the "
|
||||
f"number of input tokens {len(req.origin_input_ids)=}. Please "
|
||||
f"use a smaller routed_experts_start_len."
|
||||
)
|
||||
|
||||
if error_msg is not None:
|
||||
req.routed_experts_start_len = 0
|
||||
req.set_finish_with_abort(error_msg)
|
||||
self._add_request_to_queue(req)
|
||||
return
|
||||
|
||||
added_to_grammar_queue = self.grammar_manager.process_req_with_grammar(req)
|
||||
if not added_to_grammar_queue:
|
||||
self._add_request_to_queue(req)
|
||||
|
||||
@@ -108,16 +108,48 @@ class SchedulerOutputProcessorMixin:
|
||||
self.token_to_kv_pool_allocator.free_group_end()
|
||||
|
||||
def maybe_collect_routed_experts(self: Scheduler, req: Req):
|
||||
"""Collect routed experts for a finished request."""
|
||||
"""Collect routed experts for a finished request.
|
||||
|
||||
Returns immediately if `return_routed_experts` was not set on the
|
||||
request, so non-opted-in reqs don't pay the host-gather cost.
|
||||
|
||||
Honors the caller's absolute start so the response covers
|
||||
`[start_len, seqlen - 1)`. The default start_len is 0, which returns
|
||||
the full sequence.
|
||||
|
||||
Logs a soft warning if the resulting tensor's row count differs from
|
||||
the expected `seqlen - 1 - start_len`, to catch silent regressions.
|
||||
"""
|
||||
if not req.return_routed_experts:
|
||||
return
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is None:
|
||||
return
|
||||
start_len = req.routed_experts_start_len
|
||||
req.routed_experts = capturer.get_topk(
|
||||
req_pool_idx=req.req_pool_idx,
|
||||
seqlen=req.seqlen,
|
||||
req_to_token_pool=self.req_to_token_pool,
|
||||
start_len=start_len,
|
||||
)
|
||||
|
||||
expected_rows = max(0, req.seqlen - 1 - start_len)
|
||||
if (
|
||||
req.routed_experts is not None
|
||||
and req.routed_experts.shape[0] != expected_rows
|
||||
):
|
||||
logger.warning(
|
||||
"routed_experts row-count mismatch for req %s: got %d, "
|
||||
"expected %d (seqlen=%d, cached_tokens=%d, start_len=%s). "
|
||||
"This indicates a silent bug.",
|
||||
req.rid,
|
||||
req.routed_experts.shape[0],
|
||||
expected_rows,
|
||||
req.seqlen,
|
||||
req.cached_tokens,
|
||||
req.routed_experts_start_len,
|
||||
)
|
||||
|
||||
def maybe_collect_indexer_topk(self: Scheduler, req: Req):
|
||||
capturer = get_global_indexer_capturer()
|
||||
if capturer is None:
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
|
||||
require_reasoning=obj.require_reasoning,
|
||||
return_hidden_states=obj.return_hidden_states,
|
||||
return_routed_experts=obj.return_routed_experts,
|
||||
routed_experts_start_len=obj.routed_experts_start_len,
|
||||
return_indexer_topk=obj.return_indexer_topk,
|
||||
routed_dp_rank=obj.routed_dp_rank,
|
||||
disagg_prefill_dp_rank=obj.disagg_prefill_dp_rank,
|
||||
|
||||
@@ -233,6 +233,7 @@ class Session:
|
||||
require_reasoning=req.require_reasoning,
|
||||
return_hidden_states=req.return_hidden_states,
|
||||
return_routed_experts=req.return_routed_experts,
|
||||
routed_experts_start_len=req.routed_experts_start_len,
|
||||
priority=req.priority,
|
||||
routing_key=req.routing_key,
|
||||
extra_key=req.extra_key,
|
||||
|
||||
@@ -147,10 +147,16 @@ class BaseTopkCapturer:
|
||||
req_pool_idx: int,
|
||||
seqlen: int,
|
||||
req_to_token_pool: ReqToTokenPool,
|
||||
start_len: int = 0,
|
||||
) -> torch.Tensor:
|
||||
cache_pool_idx = req_to_token_pool.req_to_token[req_pool_idx][
|
||||
: seqlen - 1
|
||||
].cpu()
|
||||
if start_len < 0:
|
||||
raise ValueError(f"{start_len=} must be non-negative")
|
||||
start_len = min(start_len, seqlen - 1)
|
||||
cache_pool_idx = (
|
||||
req_to_token_pool.req_to_token[req_pool_idx][start_len : seqlen - 1]
|
||||
.cpu()
|
||||
.clone()
|
||||
)
|
||||
return self.host_cache.buffer[cache_pool_idx]
|
||||
|
||||
def on_forward_end(
|
||||
|
||||
Reference in New Issue
Block a user