
vLLM-Omni Omni Control 操作控制路由解析/v1/omni/sleep 与 /v1/omni/wakeup 的实现与演进【免费下载链接】vllm-omniA framework for efficient model inference with omni-modality models项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omnivLLM-Omni 在多模态推理框架中引入了面向运维的Omni Control 操作控制路由统一承载/v1/omni/*下的服务级控制接口其核心场景是模型的休眠sleep与唤醒wakeup。本文以vllm_omni/entrypoints/serve/omni_control包的职责划分为骨架结合路由实现、Engine 客户端调用链与 Worker 底层内存回收机制完整梳理这套控制协议从 HTTP 入口到 GPU 显存释放的全链路帮助读者掌握如何在多阶段AR/LLM Diffusion部署中安全地按阶段休眠、快速唤醒与回收显存。一、Omni Control 包的职责边界vllm_omni/entrypoints/serve/omni_control包是 vLLM-Omni 中专门负责 Omni 服务端操作控制路由的模块。根据 README.md 的定义该包的定位非常明确应当放入此包的内容Put Here/v1/omni/*操作控制接口的请求/响应模型Request/Response models端点提取endpoint extraction之后的 Omni 控制端点路由主体Route bodies仅服务于这些操作控制的小型辅助工具Small helpers。明确禁止放入此包的内容Do Not Put HereOpenAI 模态端点OpenAI modality endpoints如/v1/chat/completions、/v1/audio/*等在 Omni control 之外共享的通用服务工具sleep / wakeup 的引擎实现细节Engine implementation details它们应留在 Engine 层。这种边界划分体现了清晰的分层原则控制面HTTP 协议与数据面/执行面Engine 内部实现解耦。控制路由只负责接收请求、校验参数、转发调用、汇总 ACK而休眠唤醒的具体显存操作则由 Engine 与 Worker 完成。二、协议模型protocol.py 中的请求体定义当前该包内已落地的核心产物是 protocol.py 中定义的两个 Pydantic 请求模型from pydantic import BaseModel class OmniSleepRequest(BaseModel): stage_ids: list[int] level: int 2 class OmniWakeupRequest(BaseModel): stage_ids: list[int]两个模型都基于stage_ids: list[int]精确定位目标阶段——这是 vLLM-Omni 多阶段流水线AR/LLM 阶段与 Diffusion 阶段可拆分部署的基础使得运维人员可以只休眠某一组 GPU 阶段而非整机。OmniSleepRequest额外携带level: int 2字段默认值为 2表示休眠深度。结合 docs/features/sleep_mode.md 与 worker/base.py 的实现两个 level 的语义是level行为内存效果恢复方式1将模型权重 offload 到 CPU 内存offload_tags(weights,)释放 GPU 显存权重保留在系统 RAM支持快速的 DMA 恢复2完全丢弃 GPU 上的权重Total Discard释放全部相关显存当前版本wake_up()尚未实现 level 2 的权重重载会抛出NotImplementedErrorlevel 语义在 worker/base.py 中有直接体现offload_tags (weights,) if level 1 else tuple()——level 2 时不保留任何 tag即彻底释放。而 async_omni.py 中的wake_up()对 level 2 明确拒绝if getattr(self, _level2_sleeping, False): raise NotImplementedError( wake_up() after sleep(level2) is not yet implemented: weights were discarded from GPU and reloading from disk is not yet supported. Use sleep(level1) instead, which offloads weights to CPU RAM and supports fast DMA restore. )因此在实际生产使用中若后续需要快速唤醒应优先使用level: 1level: 2更适用于休眠后准备彻底换模型/缩容的场景。三、HTTP 路由实现/v1/omni/sleep 与 /v1/omni/wakeup虽然 README 明确指出当前的 route bodies 仍位于openai/api_server.py应作为 #5227 的一部分迁移到此包但这两个端点当前已经在 api_server.py 中完整落地是理解该包未来内容的直接依据。3.1 /v1/omni/sleeprouter.post(/v1/omni/sleep) async def omni_sleep(request: OmniSleepRequest, raw_request: Request): engine_client raw_request.app.state.engine_client sleeping_set raw_request.app.state.sleeping_stages if not hasattr(engine_client, sleep): raise HTTPException(status_code501, detailEngine does not support sleep) acks await engine_client.sleep(stage_idsrequest.stage_ids, levelrequest.level) for sid in request.stage_ids: sleeping_set.add(sid) return {status: SUCCESS, acks: [dataclasses.asdict(a) if dataclasses.is_dataclass(a) else a for a in acks]}路由行为要点通过hasattr(engine_client, sleep)做能力探测引擎不支持休眠时返回501 Not Implemented对应测试 test_api_server_guards.py 中对该端点的守卫断言以及 test_omni_sleep_wakeup.py 中 501 响应的验证成功后把目标 stage_id 记入app.state.sleeping_stages集合作为后续 wakeup 的是否在休眠判断依据响应体统一返回{status: SUCCESS, acks: [...]}其中 acks 是Omni ACK 协议的确认列表dataclass 会被序列化为 dict。3.2 /v1/omni/wakeuprouter.post(/v1/omni/wakeup) async def omni_wakeup(request: OmniWakeupRequest, raw_request: Request): engine_client raw_request.app.state.engine_client sleeping_set raw_request.app.state.sleeping_stages if not any(sid in sleeping_set for sid in request.stage_ids): return {status: SKIPPED, reason: Target stages are not sleeping.} if not hasattr(engine_client, wake_up): raise HTTPException(status_code501, detailEngine does not support wake_up) acks await engine_client.wake_up(stage_idsrequest.stage_ids) for sid in request.stage_ids: if sid in sleeping_set: sleeping_set.remove(sid) return {status: SUCCESS, acks: [dataclasses.asdict(a) if dataclasses.is_dataclass(a) else a for a in acks]}wakeup 路由的差异化行为幂等跳过若目标 stage 均不在sleeping_stages中直接返回{status: SKIPPED, reason: Target stages are not sleeping.}而不调用引擎对应测试 test_wakeup_skipped_when_not_sleeping部分命中只要任一 stage 在休眠即继续执行唤醒仅从集合中移除确实在休眠的 stage对应 test_wakeup_partial_sleeping空列表any()对空可迭代对象为 False返回 SKIPPED引擎不会被调用test_wakeup_empty_stage_ids。四、Engine 客户端层async_omni.py 中的 sleep/wake_up路由只做转发真正的调度逻辑在engine_clientAsyncOmni见 async_omni.py中。sleep与wake_up的实现有以下关键设计4.1 准入控制Admission Controlasync with self._pause_cond: self._paused True await self._pause_cond.wait_for(lambda: getattr(self, _admitting, 0) 0)在发起任何 sleep RPC 之前先关闭前端准入_paused True并等待已进入 generate 的协程全部提交完毕避免流水线中正在进行的generate()与休眠操作竞争防止 EngineCore 在休眠期间收到 ADD frame。4.2 AR/LLM 与 Diffusion 阶段的差异化处理ar_stage_ids, diffusion_stage_ids self._split_stage_ids_by_type(stage_ids)AR/LLM 阶段走EngineCore.sleep暂停 scheduler → 等待空闲 → offload/discard 内存与 vLLM 的AsyncLLM.sleep语义一致Diffusion 阶段由于StageDiffusionProc不暴露EngineCore.pause_scheduler因此保留 Worker 级别的handle_sleep_taskRPCOmniSleepTask通过collective_rpc(methodhandle_sleep_task, ...)广播给所有相关 Worker。sleep 前还会先调用reset_mm_cache()丢弃 P0 sender 缓存中的 hash因为EngineCore.sleep会清空 P1 缓存顺序颠倒会导致一致性错误。4.3 唤醒的语义约束wake_up对 AR / 混合引擎不会清除_paused准入门——调用方需要显式调用resume_generation()才能恢复接收新请求。典型的训练循环顺序是pause → abort → sleep → train → wake → resume。而Diffusion-only引擎没有 EngineCore pause 需要维持wake 后会自动恢复准入sleep → wake → generate可以无缝衔接。五、Worker 底层CuMemAllocator 与显存回收链路的最底层是 worker/base.py 中 LLM Worker 的sleep/wake_up它直接操作 vLLM 的 CuMemCUDA Virtual Memory分配器def sleep(self, level: int 1) - bool: level: 1 (Offload weights to CPU), level: 2 (Total Discard). from vllm.device_allocator.cumem import CuMemAllocator # Drain in-flight kernels before CuMem unmap. current_omni_platform.synchronize() gc.collect() mem_before current_omni_platform.get_current_memory_usage(self.device) offload_tags (weights,) if level 1 else tuple() allocator CuMemAllocator.get_instance() allocator.sleep(offload_tagsoffload_tags) current_omni_platform.synchronize() mem_after current_omni_platform.get_current_memory_usage(self.device) freed max(0, mem_before - mem_after) ...实现中的两个工程细节值得注意先同步再回收在 CuMem unmap 前必须先 drain 在途 kernelsynchronize()gc.collect()否则 abort/decode 残留的 GPU 任务会触发CUDA_ERROR_INVALID_VALUE避免二次 empty_cacheallocator.sleep内部已执行gc.collect和empty_cache对可插拔分配器再做一次empty_cache会命中已知的 CUDA invalid argument 问题PyTorch issue 145168因此只做同步用于读显存读数。Worker 的wake_up则调用allocator.wake_up(tags)完成显存的物理重载Physical video memory reloading logic随后同步并记录日志。此外休眠模式需要在服务启动时开启。按 docs/features/sleep_mode.md 的说明启动命令需携带--enable-sleep-modeWorker 侧在 base.py 中根据v1_config_enabled或cache_config.enable_sleep_mode判断是否激活 CuMem poolallocator.use_memory_pool(tagtag)。同时该文档也提示了两个前提条件系统 RAM 必须足够容纳 offload 的权重level 1 场景以及Tensor Parallel 组会在所有 Worker 间同步 sleep/wake 切换。六、端到端实测curl 操作与测试佐证6.1 命令行操作以 docs/features/sleep_mode.md 中的示例为基础完整操作序列如下# 启动时开启 sleep mode示例命令参数依部署脚本为准 python -m vllm_omni.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-Omni-7B \ --enable-sleep-mode \ --port 8000 # 1) 休眠 stage 0level 2彻底释放显存 curl -X POST http://localhost:8000/v1/omni/sleep \ -H Content-Type: application/json \ -d {stage_ids: [0], level: 2} # 2) 休眠 stage 1level 1权重 offload 到 CPU可快速恢复 curl -X POST http://localhost:8000/v1/omni/sleep \ -H Content-Type: application/json \ -d {stage_ids: [1], level: 1} # 3) 并发多 stage 休眠stage 0 与 1 同时休眠 curl -X POST http://localhost:8000/v1/omni/sleep \ -H Content-Type: application/json \ -d {stage_ids: [0, 1], level: 2} # 4) 唤醒 stage curl -X POST http://localhost:8000/v1/omni/wakeup \ -H Content-Type: application/json \ -d {stage_ids: [0, 1]}注意level 2 休眠后的 wakeup 在当前版本会触发NotImplementedError见上文 4.1 节的代码引用与 test_wakeup_must_not_swallow_not_implemented 中wakeup 不得吞掉引擎抛出的 NotImplementedError的守卫测试需要快速恢复的场景请使用 level 1。6.2 行为验证矩阵test_omni_sleep_wakeup.py 对路由层的契约做了系统化验证可直接作为理解端点行为的参考场景请求预期响应sleep 成功默认 level{stage_ids: [0, 1]}SUCCESSACK 覆盖各 stagesleeping_stages更新sleep 空 stage_ids{stage_ids: [], level: 2}引擎被调用但集合不更新sleep 引擎不支持任意501detail 含 sleepwakeup 成功{stage_ids: [0, 1]}SUCCESSsleeping_stages清空wakeup 目标未在休眠任意SKIPPED reason引擎不被调用wakeup 部分在休眠{stage_ids: [0, 1]}仅 0 休眠SUCCESS仅移除 0wakeup 空列表{stage_ids: []}SKIPPED引擎不被调用七、当前状态与演进方向README 明确记录了该模块的演进状态The current route bodies still live inopenai/api_server.pyand should move here as part of #5227.也就是说当前/v1/omni/sleep、/v1/omni/wakeup的请求模型OmniSleepRequest/OmniWakeupRequest已经沉淀在omni_control/protocol.py但路由主体仍保留在 api_server.py尚未完成迁移后续演进方向对应 #5227是把路由主体抽取到omni_control包内与协议模型、专属 helper 聚合实现端点提取endpoint extraction后的统一归属。结合 sleep_mode.md 中列出的路线图该模块的未来扩展还包括在 ACK 指标中增加latency_ms休眠/唤醒切换开销与cuda_graph_recalledgraph 引擎恢复状态等性能观测字段以支撑高频 sleep/wake 场景下的精细化调优。结语vllm_omni/entrypoints/serve/omni_control虽是一个小而专的包却精准地定义了 vLLM-Omni 服务控制面的边界协议模型protocol.py已就位路由主体暂驻api_server.py并计划迁移#5227底层则依托AsyncOmni的准入控制与分阶段调度、Worker 的 CuMemAllocator 回收机制形成完整链路。理解这套HTTP 控制面 Engine 执行面的分层是在多阶段多模态部署中安全实施按阶段休眠、显存回收与快速唤醒的关键。【免费下载链接】vllm-omniA framework for efficient model inference with omni-modality models项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omni创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考