Prhub

#26423 [RL] Fix crash when the reqs in a batch have a mix of `return_routed_experts` = True and False.

原始 PR 作者 ByronHsu 合并时间 2026-05-30 11:46 文件变更 3 提交数 5 评论 7 代码增减 +141 / -30

执行摘要

修复混合 return_routed_experts 标志导致服务器崩溃

当一批请求中同时包含 return_routed_experts=True 和 False 时,下游 dispatch(tokenizer_manager 和 multi_tokenizer_mixin)索引 recv_obj.routed_experts[i] 时会因为列表长度少于批次号而抛出 IndexError。该问题同样存在于 output_hidden_states 和 indexer_topk,因为它们的收集逻辑仅在请求选择了对应标志时才 append 数据,导致列表长度与批次号不对齐。

值得精读,尤其是 _GenerationStreamAccumulator 中对可选输出字段的 '全部填充 None' 策略。该模式可以推广到其他需要按批次位置对齐输出字段的场景,保持 batch_position 不变性。

讨论亮点

无(该 PR 没有 review 评论,仅由 ByronHsu 反复触发 CI 重跑)。

实现拆解

  1. output_streamer.py_stream_output_generation 方法中,计算三个批处理级别标志 return_hidden_statesreturn_routed_expertsreturn_indexer_topk(均为 any(req.return_xxx for req in reqs if req is not skip_req)),并将它们传入 _GenerationStreamAccumulator 构造器。
  2. _GenerationStreamAccumulator 类中添加三个布尔字段,将三个可选输出列表字段类型改为 Optional[list],默认 None;在 __post_init__ 中按标志初始化列表;在 accept 中当批处理标志为 True 时,为每个请求 append 其对应值(若请求未选择则为 None)。
  3. tokenizer_manager.py_handle_batch_output 中,对 output_hidden_statesrouted_experts 加上 if val is not None 保护,避免写入 None 到 meta_info。
  4. 测试文件 test_return_routed_experts.py 延迟加载 baseline 数据(_ensure_comparison_results),新增 test_mixed_return_routed_experts_batch_alignment 方法,启动两个服务(默认和 multi-tokenizer),发送混合标志请求,验证响应。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler_components/output_streamer.py 输出流 modified 7.25
python/sglang/srt/managers/tokenizer_manager.py 分词器管理 modified 5.23
test/registered/rl/test_return_routed_experts.py 测试 modified 6.97

关键符号

_GenerationStreamAccumulator.__post_init__ _GenerationStreamAccumulator.accept _stream_output_generation _handle_batch_output

关键源码片段

test/registered/rl/test_return_routed_experts.py test-coverage

新增混合标志端到端测试,延迟加载 baseline 数据以支持单方法快速运行,验证默认和 multi-tokenizer 路径。

def test_mixed_return_routed_experts_batch_alignment(self):
    # 覆盖默认 tokenizer manager 和 multi-tokenizer 路径
    self._run_mixed_batch_alignment_case([])
    self._run_mixed_batch_alignment_case(['--tokenizer-worker-num', 2])@classmethod
def _run_mixed_batch_alignment_case(cls, other_args):
    process = popen_launch_server(
        MODEL_PATH, DEFAULT_URL_FOR_TEST,
        other_args=['--tp', 2, '--enable-return-routed-experts',
                    '--disable-cuda-graph', '--disable-piecewise-cuda-graph'] + other_args)
    try:
        responses = asyncio.run(cls._send_mixed_batch())
        cls._assert_mixed_batch_result(responses)
    finally:
        kill_process_tree(process.pid)@classmethod
async def _send_mixed_batch(cls):
    # 两个请求:一个 return_routed_experts=False,另一个 True
    payload_no_rr = {'text': 'The quick brown fox jumps over the lazy dog.',
                     'sampling_params': {'temperature': 0, 'max_new_tokens': 16, 'ignore_eos': True},
                     'return_routed_experts': False}
    payload_with_rr = {'text': 'The quick brown fox jumps over the lazy dog.',
                       'sampling_params': {'temperature': 0, 'max_new_tokens': 16, 'ignore_eos': True},
                       'return_routed_experts': True}
    return await asyncio.gather(
        cls._post_generate(payload_no_rr),
        cls._post_generate(payload_with_rr),
    )@classmethod
def _assert_mixed_batch_result(cls, responses):
    resp_no_rr, resp_with_rr = responses
    # 第一个请求的 routed_experts 应为 None(响应中不包含该字段)
    assert 'routed_experts' not in resp_no_rr['meta_info']
    assert 'routed_experts' in resp_with_rr['meta_info']
    assert resp_with_rr['meta_info']['routed_experts'] is not None

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

主要风险在于其他消费者直接访问 recv_obj.routed_experts[i] 而期望非 None 值。由于此前在混合批次下会崩溃,改为 None 后至少不会崩溃,但可能向下游传递 None 导致其他错误。不过 tokenizer_manager 已经跳过 None,且 openai 响应中不包含该字段。此外,新增的 any 扫描引入少量 CPU 开销,但批次大小通常不大,影响可忽略。

用户:修复了启用 return_routed_expertsoutput_hidden_statesindexer_topk 且请求混合标志时的服务器崩溃,保证这些功能在混合场景下正常工作。系统:没有 API 变更,向下兼容。团队:核心逻辑集中在 output_streamer.py,新增测试覆盖关键场景。

索引越界修复 None 值处理 批处理级别标志

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论