Prhub

#2035 tests: mirror sglang_speculative_algorithm into session-server test namespaces

原始 PR 作者 guapisolo 合并时间 2026-08-01 03:46 文件变更 5 提交数 2 评论 1 代码增减 +5 / -0

执行摘要

补齐测试夹具缺失的 spec 解码参数

PR body 明确指出:Root cause #2028 使 session-side sample assembly 在 miles/rollout/session/samples/merge.py 中无条件读取 args.sglang_speculative_algorithm,并更新了 test_samples.py 的命名空间,但漏掉了两个手工构造 session server args 的测试夹具(generation_fixtures.py::with_session_serverrollout_fixtures.py::_with_session_server)。它们的 SimpleNamespace 缺少该字段,导致 POST /sessions/{id}/samples 抛出 AttributeError → HTTP 500;生产环境因 start_session_server 转发完整解析后的 args 而不受影响。

建议快速合并。值得关注的点是:本 PR 暴露了手工构造 session server 命名空间的脆弱性,后续可以考虑抽一个公共构造器(例如 make_session_server_args)统一维护字段列表;另外在新增 server 侧读取的 args 时,应同步搜索所有 SimpleNamespace 构造点。

讨论亮点

PR 没有实质 review 评论,Zhichenzzz 直接批准。有价值的讨论集中在 PR body 中:作者用本地复现证明根因(与 CI 相同的 10 个 agentic_tool_call 500)并验证修复效果(stage-b 85 passed,integration 19s 完成),同时解释为何 session_pretokenized_test_utils.py::make_router_env 不需要修改(它不镜像 save_debug_trajectory_data,也不会进入样本合并路径)。

实现拆解

  1. 定位全部手工构造 session server args 的测试命名空间,共 5 处:tests/fast/fixtures/generation_fixtures.pytests/fast/fixtures/rollout_fixtures.pytests/fast/router/test_session_samples_op.pytests/fast/router/session_pretokenized_test_utils.pytests/fast/router/test_session_race_conditions.py
  2. 对两个从 driver args 派生 server 参数的夹具,把 sglang_speculative_algorithm 透传进去:generation_fixtures.py::with_session_server 直接写 args.sglang_speculative_algorithmrollout_fixtures.py::_with_session_servergetattr(args, "sglang_speculative_algorithm", None) 保持向后兼容。
  3. 对三个 router 级测试命名空间(不涉及真实 spec 解码路径),直接写死 sglang_speculative_algorithm=None,避免 AttributeError 且不改变测试语义。
  4. 验证:本地按 CI 环境复现,修复前 tests/fast/rollout/generate_hub/test_multi_turn.py 复现相同的 10 个 500 失败、integration 文件 300 秒无输出;修复后 integration 19 秒 4 项通过,stage-b 全套 85 passed、17 skipped、0 failed。

配套说明:本次无产品代码、无 schema 或部署变更,全部为测试命名空间配置补强。

文件 模块 状态 重要度
tests/fast/fixtures/generation_fixtures.py 测试夹具 modified 3.68
tests/fast/fixtures/rollout_fixtures.py 测试夹具 modified 3.68
tests/fast/router/test_session_samples_op.py 样本测试 modified 3.49
tests/fast/router/session_pretokenized_test_utils.py 测试工具 modified 3.49
tests/fast/router/test_session_race_conditions.py 竞态测试 modified 3.49

关键符号

with_session_server _with_session_server make_router_env _router_env

关键源码片段

tests/fast/fixtures/generation_fixtures.py test-coverage

generate_hub agentic 测试的核心夹具,手工构造 session server 命名空间;缺少字段导致样本收集 HTTP 500,是正文点明的两个被遗漏命名空间之一。

@contextmanager
def with_session_server(
    backend_url: str,
    args: Namespace,
    *,
    port: int,
):
    # 镜像 start_session_server(router_manager.py)的行为:
    # 每个端口生成一个实例 ID,OpenAIEndpointTracer.create 需要从 per-port map 读取。
    instance_id = uuid.uuid4().hex
    args.session_server_instance_ids = {port: instance_id}
​
    # 手工构造 session server 命名空间。
    # 样本组装在服务器内部执行,因此 decode 形状参数必须在服务器启动前
    # 传入 server_args,之后再修改 driver args 不会生效。
    server_args = SimpleNamespace(
        miles_router_timeout=30,
        hf_checkpoint=args.hf_checkpoint,
        chat_template_path=args.chat_template_path,
        tito_model=args.tito_model,
        use_rollout_routing_replay=args.use_rollout_routing_replay,
        # 修复点:merge.py 会无条件读取该字段,
        # 缺失会导致样本收集路径抛 AttributeError 并返回 HTTP 500。
        sglang_speculative_algorithm=args.sglang_speculative_algorithm,
        num_layers=getattr(args, "num_layers", None),
        moe_router_topk=getattr(args, "moe_router_topk", None),
        save_debug_trajectory_data=getattr(args, "save_debug_trajectory_data", None),
        session_server_instance_id=instance_id,
    )
    session_server = SessionServer(server_args, backend_url=backend_url)
​
    server = UvicornThreadServer(session_server.app, host="127.0.0.1", port=port)
    server.start()
​
    try:
        yield port
    finally:
        server.stop()
tests/fast/fixtures/rollout_fixtures.py test-coverage

inference_rollout 集成测试的 session server 启动器,同样缺少字段导致套件停滞,是正文点明的另一个被遗漏命名空间。

@contextmanager
def _with_session_server(args: Namespace, backend_url: str) -> Iterator[UvicornThreadServer]:
    """为 agentic 变体启动 SessionServer(需要 TITO 会话跟踪)。"""
    from types import SimpleNamespace
​
    # 修复点:与 generation fixtures 相同的字段透传,
    # 使用 getattr 加默认值,保证不传该参数的老调用方仍可工作。
    session_args = SimpleNamespace(
        miles_router_timeout=30,
        hf_checkpoint=args.hf_checkpoint,
        chat_template_path=getattr(args, "chat_template_path", None),
        tito_model=getattr(args, "tito_model", "default"),
        use_rollout_routing_replay=getattr(args, "use_rollout_routing_replay", False),
        save_debug_trajectory_data=getattr(args, "save_debug_trajectory_data", None),
        sglang_speculative_algorithm=getattr(args, "sglang_speculative_algorithm", None),
    )
    session_server = SessionServer(session_args, backend_url=backend_url)
    port = find_available_port(31000)
    server = UvicornThreadServer(session_server.app, host="127.0.0.1", port=port)
    try:
        server.start()
        args.session_server_ip = "127.0.0.1"
        args.session_server_ports = [port]
        yield server
    finally:
        server.stop()

评论区精华

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

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

风险与影响

  1. 这 5 处命名空间均为手写,未来新增 session server 参数时仍可能遗漏同类字段,属于反复出现的维护风险。
  2. rollout_fixtures.py 使用 getattr 默认 None,可能掩盖“参数未定义”的问题,让测试在不经意间偏离生产行为。
  3. router 级命名空间写死 None,如果未来这些测试要覆盖 spec 解码路径,需要显式改写。
  4. 修复范围限于测试,生产路径不受影响。

影响面集中在 CI 与本地测试:修复 stage-b CPU 的 10 个 [agentic_tool_call] 失败,以及 stage-a 两个分区因套件停滞导致的约 56 分钟静默与任务取消;开发者本地运行 generate_hub 与 inference_rollout 的 agentic 测试不再遇到 500。对生产系统无影响,因为生产使用完整解析后的 args。

测试夹具字段同步风险 参数默认值掩盖缺参

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论