# PR #28770 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[MLX] Fix Apple Silicon server startup; align MLX tests with upstream
- 合并时间：2026-06-24 13:58
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/28770

---

# 执行摘要

- 一句话：修复 MLX 后端启动崩溃并新增内存池覆盖
- 推荐动作：值得精读的修补 PR，展示了细粒度覆盖和契约测试的实践。建议关注 alloc_memory_pool 设计模式及签名验证方法。

# 功能与动机

PR #28660 修复了 initialize 参数问题，但服务器仍因 _init_pools 断言失败而崩溃。根本原因是存根在 initialize() 中自行构建 KV 缓存池，而基类 alloc_memory_pool 调用 _init_pools 并断言 is_draft_worker。通过添加无操作覆盖解决。

# 实现拆解

1. **源码修改**：在 `python/sglang/srt/hardware_backend/mlx/model_runner_stub.py` 的 `MlxModelRunnerStub` 类中新增 `alloc_memory_pool(self, memory_pool_config=None)` 方法，直接返回（无操作），避免基类 GPU 分配。
2. **新增测试**：在 `test/registered/unit/hardware_backend/mlx/test_mlx_runner_pool_contract.py` 中添加三个契约测试，验证覆盖存在性、无参数绑定和可选配置参数绑定。
3. **测试对齐**：修改 `test/registered/unit/hardware_backend/mlx/test_attention_patching.py`，为 `test_finished_request_snapshots_before_release` 等测试补充上游新增的属性和存根以通过验证。

关键文件：
- `python/sglang/srt/hardware_backend/mlx/model_runner_stub.py`（模块 MLX 后端；类别 source；类型 data-contract；符号 alloc_memory_pool）: 核心变更，新增 alloc_memory_pool 无操作覆盖。
- `test/registered/unit/hardware_backend/mlx/test_mlx_runner_pool_contract.py`（模块 MLX 契约测试；类别 test；类型 test-coverage；符号 TestMlxRunnerPoolContract, test_stub_overrides_base_alloc_memory_pool, test_stub_alloc_memory_pool_binds_with_no_args, test_stub_alloc_memory_pool_binds_with_optional_config）: 新增契约测试，防止 alloc_memory_pool 覆盖丢失。
- `test/registered/unit/hardware_backend/mlx/test_attention_patching.py`（模块 注意测试调整；类别 test；类型 test-coverage；符号 fake_release_kv_cache）: 对齐上游接口变化，确保 MLX 测试通过。

关键符号：alloc_memory_pool, test_stub_overrides_base_alloc_memory_pool, test_stub_alloc_memory_pool_binds_with_no_args, test_stub_alloc_memory_pool_binds_with_optional_config

## 关键源码片段

### `python/sglang/srt/hardware_backend/mlx/model_runner_stub.py`

核心变更，新增 alloc_memory_pool 无操作覆盖。

```python
# python/sglang/srt/hardware_backend/mlx/model_runner_stub.py
# 在 MlxModelRunnerStub 类中新增

def alloc_memory_pool(self, memory_pool_config=None):
    """No-op: MLX manages its own KV cache via MlxAttentionKVPool.

    The base ``ModelRunner.alloc_memory_pool`` runs ``_init_pools`` which asserts
    ``is_draft_worker`` (model_runner_kv_cache_mixin.py:409). Since the stub
    builds its pools eagerly in ``initialize()``, this method must short-circuit
    the GPU allocation path.
    """
    pass

```

### `test/registered/unit/hardware_backend/mlx/test_mlx_runner_pool_contract.py`

新增契约测试，防止 alloc_memory_pool 覆盖丢失。

```python
# test/registered/unit/hardware_backend/mlx/test_mlx_runner_pool_contract.py
# 新增契约测试类

import importlib.util
import inspect
import unittest

_HAS_MLX = importlib.util.find_spec("mlx") is not None

if _HAS_MLX:
    from sglang.srt.hardware_backend.mlx.model_runner_stub import MlxModelRunnerStub
    from sglang.srt.model_executor.model_runner import ModelRunner

@unittest.skipUnless(_HAS_MLX, "requires mlx")
class TestMlxRunnerPoolContract(unittest.TestCase):
    """``MlxModelRunnerStub.alloc_memory_pool`` must override the base."""

    def test_stub_overrides_base_alloc_memory_pool(self):
        self.assertIn("alloc_memory_pool", vars(MlxModelRunnerStub),
            msg="MlxModelRunnerStub lost its alloc_memory_pool override.")
        self.assertIsNot(MlxModelRunnerStub.alloc_memory_pool,
                         ModelRunner.alloc_memory_pool,
                         msg="Must be overridden, not inherited.")

    def test_stub_alloc_memory_pool_binds_with_no_args(self):
        sig = inspect.signature(MlxModelRunnerStub.alloc_memory_pool)
        try:
            sig.bind(object())
        except TypeError as exc:
            self.fail(f"Must accept no-arg call: {exc}")

    def test_stub_alloc_memory_pool_binds_with_optional_config(self):
        # _FakeConfig is a simple stub for MemoryPoolConfig
        class _FakeConfig:
            pass
        sig = inspect.signature(MlxModelRunnerStub.alloc_memory_pool)
        try:
            sig.bind(object(), _FakeConfig())
        except TypeError as exc:
            self.fail(f"Must accept optional config argument: {exc}")

```

# 评论区精华

yeahdongcn 指出与 #28660 部分重复，但 Toufupi 确认 alloc_memory_pool 路径仍需修复。jlee5814 建议仅保留 alloc_memory_pool 覆盖并添加测试。最终按此方向合并。

- initialize 参数修复重复与 alloc_memory_pool 必要性 (design): 保留 alloc_memory_pool 覆盖并添加契约测试，舍弃重复的 initialize 参数更改。
- 要求添加单元测试 (testing): Toufupi 完成 rebase，保留 alloc_memory_pool 覆盖，添加单元测试并修复 test_attention_patching.py。

# 风险与影响

- 风险：仅影响 MLX 后端，风险较低。主要风险是未来上游重构 alloc_memory_pool 签名时覆盖可能失效，但已通过契约测试降低该风险。
- 影响：对用户：MLX 后端可正常启动；对系统：仅 MLX 路径受影响；对团队：明确了 MLX 内存池管理策略。
- 风险标记：MLX 后端变更 , 回归风险低 , 契约测试防护

# 关联脉络

- PR #28660 Fix MLX server startup: 修复 initialize 参数问题，是当前 PR 的前置工作。当前 PR 在此基础上新增 alloc_memory_pool 覆盖。