Files
vllm-omni/tests/entrypoints/test_omni_sleep_mode.py
T
2026-09-23 22:06:58 -07:00

677 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Entrypoint sleep-mode coverage on small models plus H100 BAGEL.
Layering (AR before tiny DiT on the same L4). Thinker lives in a child
``StageEngineCoreProc`` that shutdown can reap; tiny DiT is an inline
UniProc ``DiffusionWorker`` in pytest and leftover VRAM cannot be killed
(#8016). DiT-then-7B OOMs (6.78 GiB leftover + 16.78 GiB weights > 22 GiB).
1. AR protocol (#4473) — ``Qwen/Qwen2.5-Omni-7B`` thinker-only on L4
2. Diffusion sleep/wake/generate — ``riverclouds/qwen_image_random`` on L4
3. Light multistage orchestration — thinker-only AR + tiny DiT on L4×2
4. BAGEL BagelPipeline TP=2 / coordinated dual-engine — H100, ``full_model``
"""
from __future__ import annotations
import asyncio
import logging
from typing import Any, NamedTuple
import pytest
import pytest_asyncio
from vllm import SamplingParams
from tests.helpers.mark import hardware_test
from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config
from vllm_omni.entrypoints.async_omni import AsyncOmni
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
from vllm_omni.platforms import current_omni_platform
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("OmniTest")
MODEL_DIFF = "riverclouds/qwen_image_random"
MODEL_AR = "Qwen/Qwen2.5-Omni-7B"
MODEL_BAGEL = "ByteDance-Seed/BAGEL-7B-MoT"
# Thinker weights are 16.78 GiB. On L4 (22 GiB) util 0.45 only budgets 9.9 GiB
# so device-level KV profiling reports 0.0 GiB and StageEngineCoreProc dies.
# Keep abort-test util 0.90; cap len/batch at 2048 so the leftover ~3 GiB KV fits.
_AR_SLEEP_GPU_MEMORY_UTILIZATION = 0.90
_AR_SLEEP_MAX_MODEL_LEN = 2048
_AR_SLEEP_MAX_NUM_BATCHED_TOKENS = 2048
AR_STAGE_CONFIG = modify_stage_config(
get_deploy_config_path("ci/qwen2_5_omni_thinker_only.yaml"),
updates={
"stages": {
0: {
"enable_sleep_mode": True,
"gpu_memory_utilization": _AR_SLEEP_GPU_MEMORY_UTILIZATION,
"max_model_len": _AR_SLEEP_MAX_MODEL_LEN,
"max_num_batched_tokens": _AR_SLEEP_MAX_NUM_BATCHED_TOKENS,
}
}
},
)
_MULTISTAGE_SLEEP_PIPELINE = "qwen2_5_omni_thinker_tiny_dit"
def _tiny_dit_multistage_deploy_config() -> str:
"""Thinker-only AR + tiny DiT deploy for joint sleep/wake.
``AsyncOmni(stages=...)`` is ignored by ``resolve_omni_config``: a
``model=Qwen2.5-Omni-7B`` call otherwise loads the native 3-stage
thinker/talker/code2wav pipeline (``Expected 3 sampling params, got 2``).
"""
import atexit
import os
import tempfile
from pathlib import Path
import yaml
from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, register_pipeline
from vllm_omni.config.stage_config import PipelineConfig, StageExecutionType, StagePipelineConfig
if _MULTISTAGE_SLEEP_PIPELINE not in OMNI_PIPELINES:
register_pipeline(
PipelineConfig(
model_type=_MULTISTAGE_SLEEP_PIPELINE,
hf_architectures=(),
stages=(
StagePipelineConfig(
stage_id=0,
model_stage="thinker",
execution_type=StageExecutionType.LLM_AR,
input_sources=(),
owns_tokenizer=True,
requires_multimodal_data=True,
hf_config_name="thinker_config",
model_arch="Qwen2_5OmniForConditionalGeneration",
engine_output_type="latent",
sampling_constraints={"detokenize": True},
),
StagePipelineConfig(
stage_id=1,
model_stage="dit",
execution_type=StageExecutionType.DIFFUSION,
input_sources=(0,),
final_output=True,
final_output_type="image",
),
),
)
)
config = {
"pipeline": _MULTISTAGE_SLEEP_PIPELINE,
"async_chunk": False,
"dtype": "bfloat16",
"trust_remote_code": True,
"enable_prefix_caching": False,
"connectors": {
"shared_memory_connector": {"name": "SharedMemoryConnector"},
},
"stages": [
{
"stage_id": 0,
"devices": "0",
"enable_sleep_mode": True,
"gpu_memory_utilization": _AR_SLEEP_GPU_MEMORY_UTILIZATION,
"enforce_eager": True,
"max_model_len": _AR_SLEEP_MAX_MODEL_LEN,
"max_num_batched_tokens": _AR_SLEEP_MAX_NUM_BATCHED_TOKENS,
"skip_mm_profiling": True,
"mm_processor_cache_gb": 0,
"max_num_seqs": 1,
},
{
"stage_id": 1,
"devices": "1",
"model": MODEL_DIFF,
"enable_sleep_mode": True,
"gpu_memory_utilization": 0.4,
"enforce_eager": True,
"tensor_parallel_size": 1,
"max_num_seqs": 1,
"input_connectors": {"from_stage_0": "shared_memory_connector"},
},
],
}
fd, path = tempfile.mkstemp(prefix="sleep_thinker_tiny_dit_", suffix=".yaml")
atexit.register(Path(path).unlink, missing_ok=True)
with os.fdopen(fd, "w", encoding="utf-8") as f:
yaml.safe_dump(config, f, sort_keys=False)
return path
def get_ack_info(ack, key, default=None):
if hasattr(ack, key):
return getattr(ack, key)
if isinstance(ack, dict):
return ack.get(key, default)
return default
async def _ensure_awake(engine: AsyncOmni, stage_ids: list[int] | None = None) -> None:
try:
await engine.wake_up(stage_ids=stage_ids)
except Exception as e:
logger.warning("ensure_awake failed (stage_ids=%s): %s", stage_ids, e)
try:
await engine.resume_generation(stage_ids=stage_ids)
except Exception as e:
logger.warning("ensure_resume failed (stage_ids=%s): %s", stage_ids, e)
async def _tiny_dit_multistage_generate(engine: AsyncOmni, prompt: str, request_id: str):
"""2-step tiny-DiT generate through the joint AR + DiT pipeline.
Restores the old H100 ``test_multistage_llm_diffusion_sleep_wake`` contract
(generate before and after ``sleep(stage_ids=[0, 1])``) without BAGEL.
AR is capped so this stays a sleep/wake orchestration check, not a 7B
completion.
"""
params_list = [
SamplingParams(max_tokens=4),
OmniDiffusionSamplingParams(num_inference_steps=2, height=256, width=256),
]
output = None
async for item in engine.generate(prompt, request_id=request_id, sampling_params_list=params_list):
output = item
assert output is not None, f"generate({request_id!r}) produced no output"
return output
_ENGINE_CHILD_MARKERS = (
"stagediffusionproc",
"enginecore",
"diffusionworker",
"vllm::worker",
"vllm-omni:",
)
def _is_engine_worker_proc(proc) -> bool:
blob = " ".join([proc.name(), *proc.cmdline()]).lower()
return any(marker in blob for marker in _ENGINE_CHILD_MARKERS)
class _EngineWorkerSnap(NamedTuple):
"""Pre-shutdown worker handle. ``proc`` already caches identity."""
proc: Any
pid: int
create_time: float
def _snapshot_engine_workers() -> list[_EngineWorkerSnap]:
"""Engine workers under this pytest process (before shutdown).
``DiffusionWorker`` is started ``daemon=True``. After StagePool joins
``StageDiffusionProc``, that worker is reparented to PID 1 and a later
``children(recursive=True)`` scan misses it while it still holds VRAM.
Keep the original ``psutil.Process`` (and its cached ``create_time``)
across ``engine.shutdown()``. Rebuilding ``Process(pid)`` after exit
can bind to a reused PID and ``kill()`` an unrelated process.
"""
try:
import psutil
except ImportError:
return []
try:
children = psutil.Process().children(recursive=True)
except psutil.Error:
return []
snaps: list[_EngineWorkerSnap] = []
for proc in children:
try:
if _is_engine_worker_proc(proc):
snaps.append(_EngineWorkerSnap(proc, proc.pid, proc.create_time()))
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return snaps
def _same_engine_worker(snap: _EngineWorkerSnap) -> bool:
"""True only if ``snap.proc`` is still the original worker, not a reused PID."""
try:
import psutil
except ImportError:
return False
try:
if not snap.proc.is_running():
return False
return snap.proc.pid == snap.pid and snap.proc.create_time() == snap.create_time
except (psutil.NoSuchProcess, psutil.AccessDenied):
return False
def _reap_engine_workers(snaps: list[_EngineWorkerSnap]) -> None:
"""SIGKILL leftover snapshot workers even if they were reparented."""
try:
import psutil
except ImportError:
return
procs = [snap.proc for snap in snaps if _same_engine_worker(snap)]
print(
f"[sleep_mode] engine worker snapshot pids={[snap.pid for snap in snaps]} still_alive={[p.pid for p in procs]}"
)
if not procs:
return
# shutdown() already tried a graceful exit. SIGTERM + 5s here just
# delays SIGKILL on CUDA workers that ignore it.
for proc in procs:
try:
if not proc.is_running():
continue
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
psutil.wait_procs(procs, timeout=3)
async def _shutdown_engine_and_clear_gpu(engine: AsyncOmni) -> None:
"""Shut down the engine, reap leftover workers, then run shared cleanup."""
from tests.helpers.clean import cleanup_test_environment
leftover_workers = _snapshot_engine_workers()
engine.shutdown()
_reap_engine_workers(leftover_workers)
cleanup_test_environment()
@pytest.fixture(scope="module", autouse=True)
def _module_device_cleanup():
from tests.helpers.clean import cleanup_test_environment
print("\n=== PRE-MODULE DEVICE CLEANUP (sleep_mode) ===")
cleanup_test_environment()
yield
# Fixture / test teardown already ran cleanup_test_environment(); a second
# POST wait just repeats the same 5%×60s on leftover worker VRAM.
# ---------------------------------------------------------------------------
# 1) AR protocol — Omni thinker-only (L4)
# ---------------------------------------------------------------------------
@pytest_asyncio.fixture(scope="class", loop_scope="class")
async def ar_engine():
"""Shared thinker-only AR engine for #4473 protocol checks on L4."""
engine = AsyncOmni(
model=MODEL_AR,
deploy_config=AR_STAGE_CONFIG,
enable_sleep_mode=True,
stage_init_timeout=1200,
)
yield engine
await _shutdown_engine_and_clear_gpu(engine)
class TestOmniArSleepMode:
"""AR sleep protocol on Qwen2.5-Omni thinker-only."""
@pytest.mark.advanced_model
@pytest.mark.omni
@pytest.mark.asyncio(loop_scope="class")
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=1)
async def test_llm_sleep_ack(self, ar_engine: AsyncOmni):
"""AR sleep reports EngineCore SUCCESS; generate works after a full wake.
``AsyncOmni.sleep()`` synthesizes OmniACK with ``freed_bytes=0`` (no
worker handshake), so VRAM / ``freed_bytes`` is not a real contract.
"""
try:
acks = await ar_engine.sleep(stage_ids=[0], level=1)
assert acks
assert all(get_ack_info(ack, "status") == "SUCCESS" for ack in acks)
for ack in acks:
meta = get_ack_info(ack, "metadata") or {}
assert meta.get("path") == "engine_core", f"expected EngineCore ACK, got metadata={meta}"
await ar_engine.wake_up(stage_ids=[0])
await ar_engine.resume_generation(stage_ids=[0])
output = None
async for item in ar_engine.generate("test", sampling_params=SamplingParams(max_tokens=4)):
output = item
assert output is not None, "generate after full wake produced no output"
finally:
await _ensure_awake(ar_engine, [0])
@pytest.mark.advanced_model
@pytest.mark.omni
@pytest.mark.asyncio(loop_scope="class")
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=1)
async def test_partial_wake_blocks_generate(self, ar_engine: AsyncOmni):
"""#4473 Repro B: generate() rejected while kv_cache stays asleep."""
try:
await ar_engine.sleep(stage_ids=[0], level=1)
await ar_engine.wake_up(stage_ids=[0], tags=["weights"])
await ar_engine.resume_generation(stage_ids=[0])
with pytest.raises(RuntimeError, match="partially or fully asleep"):
async for _ in ar_engine.generate("test", sampling_params=SamplingParams(max_tokens=4)):
pass
finally:
await _ensure_awake(ar_engine, [0])
@pytest.mark.advanced_model
@pytest.mark.omni
@pytest.mark.asyncio(loop_scope="class")
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=1)
async def test_duplicate_wake_is_idempotent(self, ar_engine: AsyncOmni):
"""#4473 Repro C: duplicate wake_up(tags=None) is a safe no-op."""
try:
await ar_engine.sleep(stage_ids=[0], level=1)
first_acks = await ar_engine.wake_up(stage_ids=[0])
assert len(first_acks) > 0, "First wake_up() should return ACKs"
second_acks = await ar_engine.wake_up(stage_ids=[0])
assert second_acks == [], f"Duplicate wake_up() should return [] but got {second_acks}"
finally:
await _ensure_awake(ar_engine, [0])
# ---------------------------------------------------------------------------
# 2) Diffusion sleep/wake/generate — qwen_image_random (L4)
# ---------------------------------------------------------------------------
@pytest_asyncio.fixture(scope="class", loop_scope="class")
async def diffusion_engine():
"""Shared tiny diffusion engine on L4."""
engine = AsyncOmni(
model=MODEL_DIFF,
enable_sleep_mode=True,
tensor_parallel_size=1,
enforce_eager=True,
dtype="bfloat16",
gpu_memory_utilization=0.5,
stage_init_timeout=1200,
)
yield engine
await _shutdown_engine_and_clear_gpu(engine)
class TestOmniDiffusionSleepMode:
"""Diffusion worker sleep/wake on ``qwen_image_random`` (TP=1)."""
@pytest.mark.advanced_model
@pytest.mark.omni
@pytest.mark.asyncio(loop_scope="class")
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=1)
async def test_diffusion_sleep_handshake(self, diffusion_engine: AsyncOmni):
try:
acks = await diffusion_engine.sleep(level=1)
assert acks is not None
assert all(get_ack_info(ack, "status") == "SUCCESS" for ack in acks)
await diffusion_engine.wake_up()
finally:
await _ensure_awake(diffusion_engine)
@pytest.mark.omni
@pytest.mark.core_model
@pytest.mark.asyncio(loop_scope="class")
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=1)
async def test_diffusion_sleep_wake_generate(self, diffusion_engine: AsyncOmni):
try:
acks = await diffusion_engine.sleep(level=1)
assert acks is not None
assert all(get_ack_info(ack, "status") == "SUCCESS" for ack in acks)
await diffusion_engine.wake_up()
await diffusion_engine.resume_generation()
output = None
async for item in diffusion_engine.generate(
"test",
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=2, height=256, width=256),
):
output = item
assert output is not None, "generate after sleep/wake produced no output"
assert output.images and output.images[0] is not None, "generate after sleep/wake produced no image"
finally:
await _ensure_awake(diffusion_engine)
# ---------------------------------------------------------------------------
# 3) Light multistage — small AR + small DiT (L4×2)
# ---------------------------------------------------------------------------
@pytest.mark.advanced_model
@pytest.mark.omni
@hardware_test(res={"cuda": "L4", "rocm": "MI325"}, num_cards=2)
@pytest.mark.asyncio
async def test_multistage_ar_diffusion_sleep_wake():
"""Orchestration: generate → joint sleep/wake/resume → generate.
Covers the L4 path that ``TestBagelCoordinatedSleepMode`` (still skipped)
and BAGEL TP=2 (diffusion-only, ``full_model``) do not: a 2-step tiny-DiT
generate after ``sleep(stage_ids=[0, 1])`` + ``resume_generation``.
"""
engine = AsyncOmni(
model=MODEL_AR,
deploy_config=_tiny_dit_multistage_deploy_config(),
enable_sleep_mode=True,
stage_init_timeout=1200,
)
try:
await _tiny_dit_multistage_generate(engine, "warmup", "warmup")
acks = await engine.sleep(stage_ids=[0, 1], level=1)
assert len(acks) == 2
assert all(get_ack_info(ack, "status") == "SUCCESS" for ack in acks)
await engine.wake_up(stage_ids=[0, 1])
await engine.resume_generation(stage_ids=[0, 1])
await _tiny_dit_multistage_generate(engine, "verify", "verify")
logger.info("Light multistage joint sleep/wake/resume generate OK")
finally:
await _shutdown_engine_and_clear_gpu(engine)
# ---------------------------------------------------------------------------
# 4) BAGEL — H100/B200 ``full_model`` (nightly Entrypoints Test)
# ---------------------------------------------------------------------------
def _get_device_global_memory_used_gib(device_id: int) -> float:
"""GPU-wide memory in use (GiB), including all processes (driver view).
Fail closed: a swallowed query that returned 0.0 used to inflate
``drop_gib`` and false-pass VRAM assertions.
"""
with current_omni_platform.device(device_id):
current_omni_platform.synchronize()
free_b, total_b = current_omni_platform.get_device_memory()
return (total_b - free_b) / 1024**3
def _bagel_diffusion_deploy_config() -> str:
"""Official single-stage BAGEL DiT, TP=2 on cards 0,1.
``AsyncOmni(stages=...)`` is ignored and leaked as an unknown diffusion
field (``Unknown diffusion config field(s) for stage 1: 'stages'``).
"""
return modify_stage_config(
get_deploy_config_path("bagel_single_stage.yaml"),
updates={
"stages": {
0: {
"devices": "0,1",
"tensor_parallel_size": 2,
"enable_sleep_mode": True,
"enforce_eager": True,
"dtype": "bfloat16",
"gpu_memory_utilization": 0.4,
}
}
},
)
@pytest_asyncio.fixture(scope="class", loop_scope="class")
async def bagel_diffusion_engine():
"""Shared BAGEL single-stage DiT TP=2 engine for sleep/wake + generate."""
engine = AsyncOmni(
model=MODEL_BAGEL,
deploy_config=_bagel_diffusion_deploy_config(),
init_timeout=600,
enable_sleep_mode=True,
)
yield engine
await _shutdown_engine_and_clear_gpu(engine)
@pytest.mark.full_model
@pytest.mark.omni
@hardware_test(res={"cuda": ["H100", "B200"], "rocm": "MI325"}, num_cards=2)
class TestBagelDiffusionSleepMode:
"""BAGEL diffusion sleep/wake on a class-scoped TP=2 BagelPipeline."""
@pytest.mark.asyncio(loop_scope="class")
async def test_diffusion_sleep_handshake(self, bagel_diffusion_engine: AsyncOmni):
try:
acks = await bagel_diffusion_engine.sleep(stage_ids=[0], level=1)
assert len(acks) >= 1
assert all(get_ack_info(ack, "status") == "SUCCESS" for ack in acks)
await bagel_diffusion_engine.wake_up(stage_ids=[0])
finally:
await _ensure_awake(bagel_diffusion_engine, [0])
@pytest.mark.asyncio(loop_scope="class")
async def test_cross_device_cleanup(self, bagel_diffusion_engine: AsyncOmni):
try:
used_before = _get_device_global_memory_used_gib(0) + _get_device_global_memory_used_gib(1)
acks = await bagel_diffusion_engine.sleep(stage_ids=[0], level=1)
await asyncio.sleep(1.5)
used_after = _get_device_global_memory_used_gib(0) + _get_device_global_memory_used_gib(1)
drop_gib = used_before - used_after
freed_gb = sum(get_ack_info(ack, "freed_bytes", 0) for ack in acks) / 1024**3
assert freed_gb > 14.0 or drop_gib > 8.0, f"ACK={freed_gb:.2f} GiB, global_drop={drop_gib:.2f} GiB"
finally:
await _ensure_awake(bagel_diffusion_engine, [0])
@pytest.mark.asyncio(loop_scope="class")
async def test_diffusion_sleep_wake_generate(self, bagel_diffusion_engine: AsyncOmni):
import gc
device_id = 1
try:
prompt = "A huge swimming pool, with many people swimming."
sp = OmniDiffusionSamplingParams(num_inference_steps=4, height=512, width=512, seed=42)
base_output = None
async for output in bagel_diffusion_engine.generate(prompt, request_id="base", sampling_params=sp):
base_output = output
assert base_output is not None and len(base_output.images) > 0
current_omni_platform.empty_cache()
vram_initial = _get_device_global_memory_used_gib(device_id)
acks = await bagel_diffusion_engine.sleep(stage_ids=[0], level=1)
statuses = [get_ack_info(ack, "status") for ack in acks]
assert all(s == "SUCCESS" for s in statuses), f"Sleep failed. Statuses: {statuses}"
reported_freed_gib = sum(get_ack_info(ack, "freed_bytes", 0) for ack in acks) / 1024**3
await asyncio.sleep(2)
current_omni_platform.empty_cache()
vram_sleeping = _get_device_global_memory_used_gib(device_id)
assert reported_freed_gib > 14.0 or vram_sleeping < 5.0, (
f"Reported: {reported_freed_gib:.2f}G, Measured: {vram_sleeping:.2f}G"
)
await bagel_diffusion_engine.wake_up(stage_ids=[0])
await bagel_diffusion_engine.resume_generation(stage_ids=[0])
await asyncio.sleep(2.0)
gc.collect()
current_omni_platform.empty_cache()
vram_restored = _get_device_global_memory_used_gib(device_id)
assert abs(vram_restored - vram_initial) < 3.0, "VRAM failed to restore to initial levels"
post_output = None
async for output in bagel_diffusion_engine.generate(prompt, request_id="post", sampling_params=sp):
post_output = output
assert post_output is not None
assert len(base_output.images) == len(post_output.images)
assert post_output.images[0] is not None
finally:
await _ensure_awake(bagel_diffusion_engine, [0])
def _build_bagel_llm_stages() -> tuple[list[dict], list[dict]]:
common_args = {
"worker_type": "ar",
"enable_sleep_mode": True,
"dtype": "bfloat16",
"trust_remote_code": True,
"max_model_len": 2048,
"max_num_batched_tokens": 8192,
"enforce_eager": True,
}
stages = [
{
"stage_id": 0,
"stage_type": "llm",
"runtime": {"process": True, "devices": "0", "max_batch_size": 1},
"engine_args": {**common_args, "model_stage": "thinker", "gpu_memory_utilization": 0.1},
},
{
"stage_id": 1,
"stage_type": "llm",
"engine_input_source": [0],
"runtime": {"process": True, "devices": "1", "max_batch_size": 1, "connector_type": "queue"},
"engine_args": {**common_args, "model_stage": "talker", "gpu_memory_utilization": 0.1},
},
]
connectors = [{"src_stage_id": 0, "dst_stage_id": 1, "connector_type": "queue"}]
return stages, connectors
@pytest.mark.full_model
@pytest.mark.omni
@hardware_test(res={"cuda": ["H100", "B200"], "rocm": "MI325"}, num_cards=2)
class TestBagelCoordinatedSleepMode:
"""Dual-engine coordination (kept skipped; do not delete)."""
@pytest.mark.skip(
reason=(
"Flaky/CI: dual AsyncOmni can fail with "
"RuntimeError: Orchestrator init failed, StageDiffusionProc died during handshake. "
"Re-enable when stable (no OOM on coordinated talker+diffusion)."
)
)
@pytest.mark.asyncio(loop_scope="class")
async def test_coordinated_cross_device(self):
"""Heterogeneous coordinated cleanup (talker + diffusion on GPU 1)."""
llm_stages, llm_connectors = _build_bagel_llm_stages()
llm_engine = AsyncOmni(
model=MODEL_BAGEL, stages=llm_stages, connectors=llm_connectors, init_timeout=600, enable_sleep_mode=True
)
diffusion_engine = AsyncOmni(
model=MODEL_BAGEL,
deploy_config=_bagel_diffusion_deploy_config(),
init_timeout=600,
enable_sleep_mode=True,
)
device_id = 1
try:
await llm_engine.wake_up(stage_ids=[1])
await diffusion_engine.wake_up(stage_ids=[0])
current_omni_platform.empty_cache()
await asyncio.sleep(2)
initial_vram = _get_device_global_memory_used_gib(device_id)
await llm_engine.sleep(stage_ids=[1], level=2)
await asyncio.sleep(1.0)
await diffusion_engine.sleep(stage_ids=[0], level=2)
await asyncio.sleep(3.0)
current_omni_platform.empty_cache()
final_vram = _get_device_global_memory_used_gib(device_id)
assert initial_vram - final_vram > 15.0 or final_vram < 8.0
finally:
await _shutdown_engine_and_clear_gpu(llm_engine)
await _shutdown_engine_and_clear_gpu(diffusion_engine)