Files
enginex-ascend-910-vllm/tests/ut/worker/a2/test_worker_v1.py
Sun Ruoxi 7f8a1b1f7a init v0.23.0
Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
2026-08-27 15:11:51 +08:00

1512 lines
64 KiB
Python

import unittest
from unittest.mock import MagicMock, patch
import torch
from vllm.config import CacheConfig, ModelConfig, ParallelConfig, ProfilerConfig, VllmConfig
from tests.ut.base import TestBase
init_cached_hf_modules_path = "vllm.utils.import_utils.init_cached_hf_modules"
class TestNPUWorker(TestBase):
def setUp(self):
"""Setup test environment"""
# Create configuration mocks
self.cache_config_mock = MagicMock(spec=CacheConfig)
self.cache_config_mock.cache_dtype = "auto"
self.model_config_mock = MagicMock(spec=ModelConfig)
self.model_config_mock.dtype = torch.float16
self.model_config_mock.trust_remote_code = False
self.hf_config_mock = MagicMock()
self.hf_config_mock.model_type = "test_model"
if hasattr(self.hf_config_mock, "index_topk"):
delattr(self.hf_config_mock, "index_topk")
self.model_config_mock.hf_config = self.hf_config_mock
self.parallel_config_mock = MagicMock(spec=ParallelConfig)
self.vllm_config_mock = MagicMock(spec=VllmConfig)
self.vllm_config_mock.cache_config = self.cache_config_mock
self.vllm_config_mock.model_config = self.model_config_mock
self.vllm_config_mock.parallel_config = self.parallel_config_mock
self.vllm_config_mock.additional_config = None
self.vllm_config_mock.load_config = None
self.vllm_config_mock.scheduler_config = None
self.vllm_config_mock.device_config = None
self.vllm_config_mock.compilation_config = MagicMock()
self.vllm_config_mock.compilation_config.ir_enable_torch_wrap = False
self.vllm_config_mock.kernel_config = MagicMock()
self.vllm_config_mock.kernel_config.ir_op_priority = MagicMock()
self.vllm_config_mock.kernel_config.ir_op_priority.set_default = MagicMock()
self.vllm_config_mock.profiler_config = MagicMock()
self.vllm_config_mock.quant_config = MagicMock()
self.vllm_config_mock.speculative_config = None
self.vllm_config_mock.observability_config = None
self.vllm_config_mock.weight_transfer_config = None
self.local_rank = 0
self.rank = 0
self.distributed_init_method = "tcp://localhost:12345"
self.is_driver_worker = False
@patch("vllm_ascend.utils.adapt_patch")
@patch("vllm_ascend.ops")
@patch("vllm_ascend.worker.worker._register_atb_extensions")
@patch("vllm_ascend.worker.worker.register_ascend_customop")
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.init_ascend_config")
@patch("vllm_ascend.worker.worker.check_ascend_device_type")
@patch(init_cached_hf_modules_path, create=True)
@patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper")
def test_init_npu_worker_normal_case(
self,
mock_profiler_wrapper,
mock_init_cached_hf_modules,
mock_check_ascend_device_type,
mock_init_ascend_config,
mock_get_ascend_config,
mock_register_ascend_customop,
mock_register_atb_extensions,
mock_ops,
mock_adapt_patch,
):
"""Test NPUWorker normal initialization"""
# Setup mock behavior
mock_ops.register_dummy_fusion_op.return_value = None
mock_ascend_config = MagicMock()
mock_ascend_config.enable_cpu_binding = True
mock_get_ascend_config.return_value = mock_ascend_config
# Import and create NPUWorker instance
from vllm_ascend.worker.worker import NPUWorker
worker = NPUWorker(
vllm_config=self.vllm_config_mock,
local_rank=self.local_rank,
rank=self.rank,
distributed_init_method=self.distributed_init_method,
is_driver_worker=self.is_driver_worker,
)
# Verify initialization call order
mock_adapt_patch.assert_called_once()
mock_ops.register_dummy_fusion_op.assert_called_once()
mock_register_atb_extensions.assert_called_once()
mock_register_ascend_customop.assert_called_once()
mock_init_ascend_config.assert_called_once_with(self.vllm_config_mock)
mock_check_ascend_device_type.assert_called_once()
# Verify cache_dtype setting
self.assertEqual(worker.cache_dtype, torch.float16)
# Profiler is lazily initialized - not created during __init__ (RFC #6954)
mock_profiler_wrapper.assert_not_called()
# Verify init_cached_hf_modules is not called (trust_remote_code=False)
mock_init_cached_hf_modules.assert_not_called()
@patch("vllm_ascend.utils.adapt_patch")
@patch("vllm_ascend.ops")
@patch("vllm_ascend.worker.worker._register_atb_extensions")
@patch("vllm_ascend.worker.worker.register_ascend_customop")
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.init_ascend_config")
@patch("vllm_ascend.worker.worker.check_ascend_device_type")
@patch(init_cached_hf_modules_path, create=True)
@patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper")
def test_init_npu_worker_with_trust_remote_code(
self,
mock_profiler_wrapper,
mock_init_cached_hf_modules,
mock_check_ascend_device_type,
mock_init_ascend_config,
mock_get_ascend_config,
mock_register_ascend_customop,
mock_register_atb_extensions,
mock_ops,
mock_adapt_patch,
):
"""Test NPUWorker initialization with trust_remote_code=True"""
# Set trust_remote_code=True
self.model_config_mock.trust_remote_code = True
mock_ops.register_dummy_fusion_op.return_value = None
mock_ascend_config = MagicMock()
mock_ascend_config.enable_cpu_binding = True
mock_get_ascend_config.return_value = mock_ascend_config
# Create NPUWorker instance
from vllm_ascend.worker.worker import NPUWorker
_ = NPUWorker(
vllm_config=self.vllm_config_mock,
local_rank=self.local_rank,
rank=self.rank,
distributed_init_method=self.distributed_init_method,
is_driver_worker=self.is_driver_worker,
)
# Verify init_cached_hf_modules is called (trust_remote_code=True)
mock_init_cached_hf_modules.assert_not_called()
@patch("vllm_ascend.utils.adapt_patch")
@patch("vllm_ascend.ops")
@patch("vllm_ascend.worker.worker._register_atb_extensions")
@patch("vllm_ascend.worker.worker.register_ascend_customop")
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.init_ascend_config")
@patch("vllm_ascend.worker.worker.check_ascend_device_type")
@patch(init_cached_hf_modules_path, create=True)
@patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper")
def test_init_npu_worker_with_custom_cache_dtype(
self,
mock_profiler_wrapper,
mock_init_cached_hf_modules,
mock_check_ascend_device_type,
mock_init_ascend_config,
mock_get_ascend_config,
mock_register_ascend_customop,
mock_register_atb_extensions,
mock_ops,
mock_adapt_patch,
):
"""Test NPUWorker initialization with custom cache_dtype"""
# Set custom cache_dtype
self.cache_config_mock.cache_dtype = "float32"
mock_ops.register_dummy_fusion_op.return_value = None
mock_ascend_config = MagicMock()
mock_ascend_config.enable_cpu_binding = True
mock_get_ascend_config.return_value = mock_ascend_config
# Create NPUWorker instance
from vllm_ascend.worker.worker import NPUWorker
with patch("vllm.utils.torch_utils.STR_DTYPE_TO_TORCH_DTYPE", {"float32": torch.float32}):
worker = NPUWorker(
vllm_config=self.vllm_config_mock,
local_rank=self.local_rank,
rank=self.rank,
distributed_init_method=self.distributed_init_method,
is_driver_worker=self.is_driver_worker,
)
# Verify cache_dtype is set to custom value
self.assertEqual(worker.cache_dtype, torch.float32)
def test_initialize_cache(self):
"""Test initialize_cache method"""
from vllm_ascend.worker.worker import NPUWorker
# Create a simple worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.cache_config = MagicMock()
# Test initialize_cache
worker.initialize_cache(100, 50)
# Verify parameter setting
self.assertEqual(worker.cache_config.num_gpu_blocks, 100)
self.assertEqual(worker.cache_config.num_cpu_blocks, 50)
@patch("vllm_ascend.worker.worker.CaMemAllocator")
@patch("vllm_ascend.worker.worker.get_ascend_config")
def test_wake_up_mode_enabled(self, mock_get_config, mock_allocator_class):
mock_config = MagicMock()
mock_config.weight_nz_mode = 0
mock_config.enable_sleep_mode_extra_cleanup = True
mock_get_config.return_value = mock_config
"""Test wake_up method when sleep mode is enabled"""
from vllm_ascend.worker.worker import NPUWorker
# Setup mock
mock_allocator = MagicMock()
mock_allocator_class.get_instance.return_value = mock_allocator
mock_hidden_size = MagicMock()
mock_hf_config = MagicMock()
mock_hf_config.hidden_size = mock_hidden_size
mock_model_config = MagicMock()
mock_model_config.hf_config = mock_hf_config
mock_vllm_config = MagicMock()
mock_vllm_config.model_config = mock_model_config
mock_model_runner = MagicMock()
mock_model_runner.model = MagicMock()
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = mock_model_runner
worker.vllm_config = mock_vllm_config
worker._sleep_saved_buffers = {}
worker.sleep_wakeup_manager = MagicMock()
# Test wake_up method
worker.wake_up(tags=["test_tag"])
mock_allocator.wake_up.assert_called_once_with(tags=["test_tag"])
worker.sleep_wakeup_manager.wakeup.assert_called_once_with(["test_tag"])
@patch("vllm_ascend.worker.worker.current_platform")
@patch("vllm_ascend.worker.worker.MemorySnapshot")
@patch("vllm_ascend.worker.worker.NPUWorker._init_worker_distributed_environment")
@patch("vllm_ascend.worker.worker.init_device_properties_triton")
@patch("vllm_ascend.worker.worker.get_ascend_device_type")
@patch("torch.npu.set_device")
@patch("torch.npu.empty_cache")
@patch("torch.npu.mem_get_info")
def test_init_device(
self,
mock_mem_get_info,
mock_empty_cache,
mock_set_device,
mock_get_device_type,
mock_init_triton,
mock_init_dist_env,
mock_snapshot_cls,
mock_current_platform,
):
"""Test _init_device method"""
from vllm_ascend.worker.worker import AscendDeviceType, NPUWorker
# Setup mock
mock_mem_get_info.return_value = (1000, 2000)
mock_get_device_type.return_value = AscendDeviceType.A2
# Mock MemorySnapshot
mock_snapshot = MagicMock()
mock_snapshot.free_memory = 1000
mock_snapshot.total_memory = 2000
mock_snapshot_cls.return_value = mock_snapshot
# Mock current_platform for v0.24.0 init_device path
mock_current_platform.logical_device_id_to_visible_device_id.return_value = 0
mock_current_platform.device_type = "npu"
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.local_rank = 0
worker.model_config = MagicMock()
worker.model_config.seed = 42
worker.parallel_config = MagicMock()
worker.parallel_config.local_world_size = 0
worker.parallel_config.data_parallel_size = 1
worker.parallel_config.assigned_physical_gpu_ids = None
worker.parallel_config.distributed_executor_backend = "ray"
worker.vllm_config = MagicMock()
worker.vllm_config.kv_transfer_config = None
worker.cache_config = MagicMock()
worker.cache_config.gpu_memory_utilization = 0.5
# Test _init_device
result = worker._init_device()
mock_init_dist_env.assert_called_once()
self.assertEqual(str(result), "npu:0")
self.assertEqual(worker.init_snapshot, mock_snapshot)
self.assertEqual(worker.requested_memory, 2000 * 0.5)
def test_profile_start_stop(self):
"""Test profile method start and stop"""
from vllm_ascend.worker.worker import NPUWorker
profiler_config = ProfilerConfig(
profiler="torch",
torch_profiler_dir="/path/to/traces",
)
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler_config = profiler_config
worker.rank = 0
mock_profiler = MagicMock()
worker.profiler = mock_profiler
with patch("vllm.distributed.utils.get_worker_rank_suffix", return_value="dp0_pp0_tp0_dcp0_ep0_rank0"):
worker.profile(is_start=True)
mock_profiler.start.assert_called_once()
worker.profile(is_start=False)
mock_profiler.stop.assert_called_once()
def test_profile_no_profiler_raises_error(self):
"""Test profile method raises exception when profiler is not available"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock - profiler_config indicates profiling disabled
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler = None
worker.profiler_config = ProfilerConfig(profiler=None, torch_profiler_dir="")
# Test should raise exception
with self.assertRaises(RuntimeError) as cm:
worker.profile()
self.assertIn("Profiling is not enabled", str(cm.exception))
def test_profile_with_prefix_uses_trace_name(self):
"""[RFC #6954] profile() accepts profile_prefix and passes trace_name to TorchNPUProfilerWrapper"""
from vllm_ascend.worker.worker import NPUWorker
profiler_config = ProfilerConfig(
profiler="torch",
torch_profiler_dir="/path/to/traces",
)
vllm_config_mock = MagicMock()
vllm_config_mock.profiler_config = profiler_config
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler_config = profiler_config
worker.profiler = None
worker.rank = 0
with (
patch("vllm.distributed.utils.get_worker_rank_suffix", return_value="dp0_pp0_tp0_dcp0_ep0_rank0"),
patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper") as mock_profiler_wrapper,
):
worker.profile(is_start=True, profile_prefix="warmup")
mock_profiler_wrapper.assert_called_once_with(
profiler_config,
"warmup_dp0_pp0_tp0_dcp0_ep0_rank0",
)
mock_profiler_wrapper.return_value.start.assert_called_once()
def test_profile_lazy_init(self):
"""[RFC #6954] Profiler is lazily created on first profile(is_start=True) call"""
from vllm_ascend.worker.worker import NPUWorker
profiler_config = ProfilerConfig(
profiler="torch",
torch_profiler_dir="/path/to/traces",
)
vllm_config_mock = MagicMock()
vllm_config_mock.profiler_config = profiler_config
with patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper") as mock_profiler_wrapper:
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler_config = profiler_config
worker.profiler = None
worker.rank = 0
self.assertIsNone(worker.profiler)
mock_profiler_wrapper.assert_not_called()
with patch("vllm.distributed.utils.get_worker_rank_suffix", return_value="dp0_pp0_tp0_dcp0_ep0_rank0"):
worker.profile(is_start=True)
mock_profiler_wrapper.assert_called_once_with(
profiler_config,
"dp0_pp0_tp0_dcp0_ep0_rank0",
)
self.assertIs(worker.profiler, mock_profiler_wrapper.return_value)
mock_profiler_wrapper.return_value.start.assert_called_once()
def test_profile_restart_reuses_existing_profiler(self):
"""[RFC #6954] Restarting profile reuses existing profiler."""
from vllm_ascend.worker.worker import NPUWorker
profiler_config = ProfilerConfig(
profiler="torch",
torch_profiler_dir="/path/to/traces",
)
mock_profiler = MagicMock()
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler_config = profiler_config
worker.profiler = None
worker.rank = 0
with (
patch("vllm.distributed.utils.get_worker_rank_suffix", return_value="dp0_pp0_tp0_dcp0_ep0_rank0"),
patch("vllm_ascend.worker.worker.TorchNPUProfilerWrapper", return_value=mock_profiler) as mock_wrapper,
):
worker.profile(is_start=True, profile_prefix="session1")
mock_wrapper.assert_called_once_with(
profiler_config,
"session1_dp0_pp0_tp0_dcp0_ep0_rank0",
)
worker.profile(is_start=False)
worker.profile(is_start=True) # Restart without new prefix
# Should NOT create new profiler, just restart existing
mock_wrapper.assert_called_once()
self.assertEqual(mock_profiler.start.call_count, 2)
mock_profiler.stop.assert_called_once()
@patch("vllm_ascend.worker.worker.logger")
def test_profile_stop_without_start_logs_warning(self, mock_logger):
"""Test stopping profiling before start logs a warning and returns."""
from vllm_ascend.worker.worker import NPUWorker
profiler_config = ProfilerConfig(
profiler="torch",
torch_profiler_dir="/path/to/traces",
)
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.profiler_config = profiler_config
worker.profiler = None
worker.profile(is_start=False)
mock_logger.warning.assert_called_once_with("Profiler was not started, nothing to stop.")
def test_lora_methods(self):
"""Test LoRA related methods"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
mock_model_runner = MagicMock()
worker.model_runner = mock_model_runner
# Set return values
mock_model_runner.add_lora.return_value = True
mock_model_runner.remove_lora.return_value = True
mock_model_runner.list_loras.return_value = {1, 2, 3}
mock_model_runner.pin_lora.return_value = True
# Test each method
mock_request = MagicMock()
self.assertTrue(worker.add_lora(mock_request))
mock_model_runner.add_lora.assert_called_once_with(mock_request)
self.assertTrue(worker.remove_lora(1))
mock_model_runner.remove_lora.assert_called_once_with(1)
self.assertEqual(worker.list_loras(), {1, 2, 3})
mock_model_runner.list_loras.assert_called_once()
self.assertTrue(worker.pin_lora(2))
mock_model_runner.pin_lora.assert_called_once_with(2)
def test_get_methods(self):
"""Test various get methods"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
mock_model_runner = MagicMock()
worker.model_runner = mock_model_runner
# Set return values
mock_model = MagicMock()
mock_kv_cache_spec = {"test": "spec"}
mock_pooling_tasks = ["task1", "task2"]
mock_supported_tasks = ("task1", "task2")
mock_model_runner.get_model.return_value = mock_model
mock_model_runner.get_kv_cache_spec.return_value = mock_kv_cache_spec
mock_model_runner.get_supported_pooling_tasks.return_value = mock_pooling_tasks
mock_model_runner.get_supported_tasks.return_value = mock_supported_tasks
# Test each get method
self.assertEqual(worker.get_model(), mock_model)
self.assertEqual(worker.get_kv_cache_spec(), mock_kv_cache_spec)
self.assertEqual(worker.get_supported_pooling_tasks(), mock_pooling_tasks)
self.assertEqual(worker.get_supported_tasks(), mock_supported_tasks)
def test_execute_dummy_batch(self):
"""Test execute_dummy_batch method"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.compilation_config = MagicMock()
worker.compilation_config.cudagraph_mode = MagicMock()
mock_model_runner = MagicMock()
mock_decode_token_per_req = mock_model_runner.decode_token_per_req
worker.model_runner = mock_model_runner
# Test execute_dummy_batch
worker.execute_dummy_batch()
# Verify call
mock_model_runner._dummy_run.assert_called_once_with(
num_tokens=mock_decode_token_per_req, uniform_decode=True
)
@patch("vllm_ascend.worker.worker.memory_profiling")
@patch("torch.npu.reset_peak_memory_stats")
@patch("torch.npu.empty_cache")
@patch("torch_npu.npu.memory_stats")
@patch("torch_npu.npu.mem_get_info")
@patch("vllm_ascend.worker.worker.logger")
def test_determine_available_memory_normal_case(
self,
mock_logger,
mock_torch_mem_get_info,
mock_torch_memory_stats,
mock_torch_empty_cache,
mock_torch_reset_peak_memory_stats,
mock_memory_profiling,
):
"""Test determine_available_memory normal case (no non-torch memory allocation)"""
from vllm_ascend.worker.worker import NPUWorker
# Mock memory_profiling context manager
mock_profile_result = MagicMock()
mock_profile_result.non_torch_increase = 1000
mock_profile_result.torch_peak_increase = 2000
mock_profile_result.weights_memory = 500
mock_profile_result.before_profile = MagicMock()
mock_profile_result.before_profile.torch_peak = 0
mock_profile_result.after_profile = MagicMock()
mock_profile_result.after_profile.free_memory = 6500
mock_context = MagicMock()
mock_context.__enter__ = MagicMock(return_value=mock_profile_result)
mock_context.__exit__ = MagicMock(return_value=False)
mock_memory_profiling.return_value = mock_context
# Mock init_snapshot
mock_init_snapshot = MagicMock()
mock_init_snapshot.free_memory = 8000
mock_init_snapshot.total_memory = 10000
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.init_snapshot = mock_init_snapshot
worker.requested_memory = 10000 * 0.8
worker.model_runner = MagicMock()
worker.model_runner.model_memory_usage = 500
worker.cache_config = MagicMock()
worker.cache_config.gpu_memory_utilization = 0.8
worker.cache_config.kv_cache_memory_bytes = None
worker.device = torch.device("npu:0")
# Mock torch.npu.memory_stats for profile_torch_peak
# profile_torch_peak = memory_stats()["allocated_bytes.all.peak"] = 2000
mock_torch_memory_stats.return_value = {"allocated_bytes.all.peak": 2000}
result = worker.determine_available_memory()
worker.model_runner.profile_run.assert_called_once()
# non_kv_cache_memory = non_torch_increase(1000) + torch_peak_increase(2000-0) + weights_memory(500) = 3500
# result = requested_memory(8000) - non_kv_cache_memory(3500) = 4500
expected_result = int(10000 * 0.8 - 3500)
self.assertEqual(result, expected_result)
@patch("vllm_ascend.worker.worker.memory_profiling")
@patch("torch.npu.reset_peak_memory_stats")
@patch("torch.npu.empty_cache")
@patch("torch_npu.npu.memory_stats")
@patch("torch_npu.npu.mem_get_info")
def test_determine_available_memory_with_non_torch_allocations(
self,
mock_torch_mem_get_info,
mock_torch_memory_stats,
mock_torch_empty_cache,
mock_torch_reset_peak_memory_stats,
mock_memory_profiling,
):
"""Test determine_available_memory with significant non-torch memory allocation"""
from vllm_ascend.worker.worker import NPUWorker
# Mock memory_profiling context manager with large non-torch allocation
mock_profile_result = MagicMock()
mock_profile_result.non_torch_increase = 4000
mock_profile_result.torch_peak_increase = 1500
mock_profile_result.weights_memory = 500
mock_profile_result.before_profile = MagicMock()
mock_profile_result.before_profile.torch_peak = 0
mock_profile_result.after_profile = MagicMock()
mock_profile_result.after_profile.free_memory = 4000
mock_context = MagicMock()
mock_context.__enter__ = MagicMock(return_value=mock_profile_result)
mock_context.__exit__ = MagicMock(return_value=False)
mock_memory_profiling.return_value = mock_context
# Mock init_snapshot
mock_init_snapshot = MagicMock()
mock_init_snapshot.free_memory = 8500
mock_init_snapshot.total_memory = 10000
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.init_snapshot = mock_init_snapshot
worker.requested_memory = 10000 * 0.9
worker.model_runner = MagicMock()
worker.model_runner.model_memory_usage = 500
worker.cache_config = MagicMock()
worker.cache_config.gpu_memory_utilization = 0.9
worker.cache_config.kv_cache_memory_bytes = None
worker.device = torch.device("npu:0")
mock_torch_memory_stats.return_value = {"allocated_bytes.all.peak": 1500}
result = worker.determine_available_memory()
# non_kv_cache_memory = non_torch_increase(4000) + torch_peak_increase(1500-0) + weights_memory(500) = 6000
# result = requested_memory(9000) - non_kv_cache_memory(6000) = 3000
expected_result = int(10000 * 0.9 - 6000)
self.assertEqual(result, expected_result)
@patch("vllm_ascend.worker.worker.memory_profiling")
@patch("torch.npu.mem_get_info")
@patch("torch.npu.reset_peak_memory_stats")
@patch("torch.npu.empty_cache")
def test_determine_available_memory_memory_profiling_error(
self, mock_torch_empty_cache, mock_torch_reset_peak_memory_stats, mock_torch_mem_get_info, mock_memory_profiling
):
"""Test determine_available_memory throws exception on memory profiling error"""
from vllm_ascend.worker.worker import NPUWorker
# Mock memory_profiling where free memory after profile > init free memory (error case)
mock_profile_result = MagicMock()
mock_profile_result.non_kv_cache_memory = 2000
mock_profile_result.after_profile = MagicMock()
mock_profile_result.after_profile.free_memory = 9000 # More free than init!
mock_profile_result.non_torch_increase = 0
mock_profile_result.torch_peak_increase = 0
mock_profile_result.weights_memory = 0
mock_context = MagicMock()
mock_context.__enter__ = MagicMock(return_value=mock_profile_result)
mock_context.__exit__ = MagicMock(return_value=False)
mock_memory_profiling.return_value = mock_context
mock_init_snapshot = MagicMock()
mock_init_snapshot.free_memory = 8500 # Less than after_profile free (9000)
mock_init_snapshot.total_memory = 10000
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.init_snapshot = mock_init_snapshot
worker.requested_memory = 10000 * 0.8
worker.model_runner = MagicMock()
worker.cache_config = MagicMock()
worker.cache_config.gpu_memory_utilization = 0.8
worker.cache_config.kv_cache_memory_bytes = None
worker.device = torch.device("npu:0")
# Test should throw assertion error
with self.assertRaises(AssertionError) as cm:
worker.determine_available_memory()
self.assertIn("Error in memory profiling", str(cm.exception))
@patch("vllm_ascend.worker.worker.memory_profiling")
@patch("torch.npu.reset_peak_memory_stats")
@patch("torch.npu.empty_cache")
@patch("torch_npu.npu.memory_stats")
@patch("torch_npu.npu.mem_get_info")
def test_determine_available_memory_negative_result(
self,
mock_torch_mem_get_info,
mock_torch_memory_stats,
mock_torch_empty_cache,
mock_torch_reset_peak_memory_stats,
mock_memory_profiling,
):
"""Test determine_available_memory returns 0 when result is negative"""
from vllm_ascend.worker.worker import NPUWorker
# Mock memory_profiling where non_kv_cache_memory > requested_memory
mock_profile_result = MagicMock()
mock_profile_result.non_torch_increase = 1000
mock_profile_result.torch_peak_increase = 9000
mock_profile_result.weights_memory = 500
mock_profile_result.before_profile = MagicMock()
mock_profile_result.before_profile.torch_peak = 0
mock_profile_result.after_profile = MagicMock()
mock_profile_result.after_profile.free_memory = 2000
mock_context = MagicMock()
mock_context.__enter__ = MagicMock(return_value=mock_profile_result)
mock_context.__exit__ = MagicMock(return_value=False)
mock_memory_profiling.return_value = mock_context
# Mock init_snapshot
mock_init_snapshot = MagicMock()
mock_init_snapshot.free_memory = 8500
mock_init_snapshot.total_memory = 10000
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.init_snapshot = mock_init_snapshot
worker.requested_memory = 10000 * 0.8
worker.model_runner = MagicMock()
worker.model_runner.model_memory_usage = 500
worker.cache_config = MagicMock()
worker.cache_config.gpu_memory_utilization = 0.8
worker.cache_config.kv_cache_memory_bytes = None
worker.device = torch.device("npu:0")
mock_torch_memory_stats.return_value = {"allocated_bytes.all.peak": 9000}
result = worker.determine_available_memory()
# non_kv_cache_memory = 1000 + 9000 + 500 = 10500
# available = requested(8000) - non_kv_cache(10500) = -2500
# upstream no longer clamps to 0, returns int(negative)
self.assertEqual(result, int(8000 - 10500))
def test_execute_model_first_rank(self):
"""Test execute_model method - first rank case"""
from vllm.v1.outputs import ModelRunnerOutput
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with (
patch.object(NPUWorker, "__init__", lambda x, **kwargs: None),
patch("vllm_ascend.worker.worker.get_pp_group") as mock_get_pp_group,
patch("vllm_ascend.worker.worker.get_ascend_config") as mock_get_ascend_config,
):
mock_ascend_config = MagicMock()
mock_ascend_config.msmonitor_use_daemon = False
mock_get_ascend_config.return_value = mock_ascend_config
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.parallel_config = MagicMock()
worker.vllm_config.parallel_config.distributed_executor_backend = "ray"
worker.profiler = None
worker._pp_send_work = []
# Set as first rank
mock_pp_group = MagicMock()
mock_pp_group.is_first_rank = True
mock_pp_group.is_last_rank = True
mock_get_pp_group.return_value = mock_pp_group
# Mock scheduler_output and return result
mock_scheduler_output = MagicMock()
mock_scheduler_output.total_num_scheduled_tokens = 1
mock_model_output = MagicMock(spec=ModelRunnerOutput)
worker.model_runner.execute_model.return_value = mock_model_output
# Test execute_model
result = worker.execute_model(mock_scheduler_output)
# Verify call
worker.model_runner.execute_model.assert_called_once_with(mock_scheduler_output, None)
self.assertEqual(result, mock_model_output)
def test_execute_model_calls_profiler_step_when_enabled(self):
"""Test execute_model steps the profiler before model execution."""
from vllm.v1.outputs import ModelRunnerOutput
from vllm_ascend.worker.worker import NPUWorker
call_order = []
# Create worker mock
with (
patch.object(NPUWorker, "__init__", lambda x, **kwargs: None),
patch("vllm_ascend.worker.worker.get_pp_group") as mock_get_pp_group,
patch("vllm_ascend.worker.worker.get_ascend_config") as mock_get_ascend_config,
):
mock_ascend_config = MagicMock()
mock_ascend_config.msmonitor_use_daemon = False
mock_get_ascend_config.return_value = mock_ascend_config
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.parallel_config = MagicMock()
worker.vllm_config.parallel_config.distributed_executor_backend = "ray"
worker.profiler = MagicMock()
worker.profiler.step.side_effect = lambda: call_order.append("step")
worker._pp_send_work = []
mock_pp_group = MagicMock()
mock_pp_group.is_first_rank = True
mock_pp_group.is_last_rank = True
mock_get_pp_group.return_value = mock_pp_group
mock_scheduler_output = MagicMock()
mock_scheduler_output.total_num_scheduled_tokens = 1
mock_model_output = MagicMock(spec=ModelRunnerOutput)
def execute_model(*args):
call_order.append("execute")
return mock_model_output
worker.model_runner.execute_model.side_effect = execute_model
result = worker.execute_model(mock_scheduler_output)
worker.profiler.step.assert_called_once()
worker.model_runner.execute_model.assert_called_once_with(mock_scheduler_output, None)
self.assertEqual(call_order, ["step", "execute"])
self.assertEqual(result, mock_model_output)
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.enable_sp", return_value=False)
@patch("vllm_ascend.worker.worker.get_pp_group")
@patch("vllm_ascend.worker.worker.get_tp_group")
def test_execute_model_middle_rank(
self, mock_get_tp_group, mock_get_pp_group, mock_enable_sp, mock_get_ascend_config
):
"""Test execute_model method - middle rank case"""
from vllm.sequence import IntermediateTensors
mock_ascend_config = MagicMock()
mock_ascend_config.msmonitor_use_daemon = False
mock_get_ascend_config.return_value = mock_ascend_config
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.parallel_config = MagicMock()
worker.vllm_config.parallel_config.distributed_executor_backend = "ray"
worker.profiler = None
worker._pp_send_work = []
# Set as middle rank (not first, not last)
mock_pp_group = MagicMock()
mock_pp_group.is_first_rank = False
mock_pp_group.is_last_rank = False
mock_get_pp_group.return_value = mock_pp_group
# Setup tensor reception data
mock_pp_group.irecv_tensor_dict.return_value = ({"tensor": "data"}, None, None)
mock_pp_group.isend_tensor_dict.return_value = []
# Mock return IntermediateTensors - use real type
mock_intermediate_output = MagicMock(spec=IntermediateTensors)
mock_intermediate_output.tensors = {"output_tensor": "data"}
mock_intermediate_output.kv_connector_output = None # Set to None to trigger return None
worker.model_runner.execute_model.return_value = mock_intermediate_output
mock_scheduler_output = MagicMock()
mock_scheduler_output.total_num_scheduled_tokens = 1
# Test execute_model
result = worker.execute_model(mock_scheduler_output)
# Verify tensor reception
mock_pp_group.irecv_tensor_dict.assert_called_once()
# Verify model execution with intermediate_tensors
# Second parameter should be AsyncIntermediateTensors instance
worker.model_runner.execute_model.assert_called_once()
args, kwargs = worker.model_runner.execute_model.call_args
self.assertEqual(args[0], mock_scheduler_output)
# Verify tensor sending
mock_pp_group.isend_tensor_dict.assert_called_once()
# Middle rank without kv_transfer_group should return None
self.assertIsNone(result)
def test_execute_model_external_launcher(self):
"""Test execute_model method - external_launcher mode"""
from vllm.v1.outputs import ModelRunnerOutput
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with (
patch.object(NPUWorker, "__init__", lambda x, **kwargs: None),
patch("vllm_ascend.worker.worker.get_pp_group") as mock_get_pp_group,
patch("vllm_ascend.worker.worker.get_ascend_config") as mock_get_ascend_config,
):
mock_ascend_config = MagicMock()
mock_ascend_config.msmonitor_use_daemon = False
mock_get_ascend_config.return_value = mock_ascend_config
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.parallel_config = MagicMock()
worker.vllm_config.parallel_config.distributed_executor_backend = "external_launcher"
worker.profiler = None
worker._pp_send_work = []
# Set as non-last rank
mock_pp_group = MagicMock()
mock_pp_group.is_first_rank = True
mock_pp_group.is_last_rank = False
mock_get_pp_group.return_value = mock_pp_group
# Mock return result
mock_scheduler_output = MagicMock()
mock_scheduler_output.total_num_scheduled_tokens = 1
mock_model_output = MagicMock(spec=ModelRunnerOutput)
worker.model_runner.execute_model.return_value = mock_model_output
# Test execute_model
result = worker.execute_model(mock_scheduler_output)
# In external_launcher mode, it doesn't enter middle processing logic, returns result directly
self.assertEqual(result, mock_model_output)
@patch("vllm_ascend.worker.worker.CaMemAllocator")
def test_load_model_with_sleep_mode(self, mock_allocator_class):
"""Test load_model method - with sleep mode enabled"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.model_config = MagicMock()
worker.vllm_config.model_config.enable_sleep_mode = True
worker.vllm_config.weight_transfer_config = None
worker.vllm_config.kv_transfer_config = None
# Setup allocator mock
mock_allocator = MagicMock()
mock_allocator.get_current_usage.return_value = 0
mock_context = MagicMock()
mock_allocator.use_memory_pool.return_value = mock_context
mock_allocator_class.get_instance.return_value = mock_allocator
# Test load_model
worker.load_model()
# Verify calls
mock_allocator_class.get_instance.assert_called_once()
mock_allocator.get_current_usage.assert_called_once()
mock_allocator.use_memory_pool.assert_called_once_with(tag="weights")
worker.model_runner.load_model.assert_called_once()
def test_load_model_without_sleep_mode(self):
"""Test load_model method - without sleep mode enabled"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.model_config = MagicMock()
worker.vllm_config.model_config.enable_sleep_mode = False
worker.vllm_config.weight_transfer_config = None
# Test load_model
worker.load_model()
# Verify calls
worker.model_runner.load_model.assert_called_once()
@patch("vllm_ascend.worker.worker.CaMemAllocator")
def test_load_model_sleep_mode_assertion_error(self, mock_allocator_class):
"""Test load_model method - assertion error in sleep mode"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.model_config = MagicMock()
worker.vllm_config.model_config.enable_sleep_mode = True
# Setup allocator mock - current usage is not 0
mock_allocator = MagicMock()
mock_allocator.get_current_usage.return_value = 100 # Non-zero value
mock_allocator_class.get_instance.return_value = mock_allocator
# Test should throw assertion error
with self.assertRaises(AssertionError) as cm:
worker.load_model()
self.assertIn("Sleep mode can only be", str(cm.exception))
@patch("vllm_ascend.worker.worker.set_random_seed")
@patch("vllm_ascend.worker.worker.get_ascend_device_type")
@patch("vllm_ascend.worker.worker.AscendDeviceType")
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.logger")
@patch("vllm_ascend.worker.worker.NPUWorker._warm_up_atb")
def test_compile_or_warm_up_model_with_eager_mode(
self,
mock_warm_up_atb,
mock_logger,
mock_get_ascend_config,
mock_ascend_device_type,
mock_get_ascend_device_type,
mock_set_random_seed,
):
"""Test compile_or_warm_up_model method - eager mode"""
mock_ascend_config = MagicMock()
mock_ascend_config.ascend_compilation_config = MagicMock()
mock_ascend_config.ascend_compilation_config.enable_npugraph_ex = False
mock_ascend_config.enable_cpu_binding = False
mock_get_ascend_config.return_value = mock_ascend_config
mock_get_ascend_device_type.return_value = mock_ascend_device_type.A9B
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.model_config = MagicMock()
worker.model_config.enforce_eager = True
worker.model_config.seed = 12345
worker.cache_config = MagicMock()
worker.cache_config.kv_cache_memory_bytes = 1024
# Setup compilation config
worker.vllm_config.compilation_config = MagicMock()
worker.vllm_config.compilation_config.compile_sizes = [1, 4, 8, 16]
worker.vllm_config.compilation_config.cudagraph_capture_sizes = [4, 8]
# Test compile_or_warm_up_model
worker.compile_or_warm_up_model()
# Verify _dummy_run call count and order (by size descending)
expected_calls = [
unittest.mock.call(16),
unittest.mock.call(8),
unittest.mock.call(4),
unittest.mock.call(1),
]
worker.model_runner._dummy_run.assert_has_calls(expected_calls)
# Should not call capture_model in eager mode
worker.model_runner.capture_model.assert_not_called()
# Verify log output
self.assertEqual(mock_logger.info.call_count, 4)
# Verify atb warm up
mock_warm_up_atb.assert_called_once()
@patch("vllm_ascend.worker.worker.set_random_seed")
@patch("vllm_ascend.worker.worker.get_ascend_device_type")
@patch("vllm_ascend.worker.worker.AscendDeviceType")
@patch("vllm_ascend.worker.worker.CUDAGraphMode")
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.logger")
@patch("vllm_ascend.worker.worker.NPUWorker._warm_up_atb")
def test_compile_or_warm_up_model_with_graph_capture(
self,
mock_warm_up_atb,
mock_logger,
mock_get_ascend_config,
mock_cudagraph_mode,
mock_ascend_device_type,
mock_get_ascend_device_type,
mock_set_random_seed,
):
"""Test compile_or_warm_up_model method - with graph capture enabled"""
mock_ascend_config = MagicMock()
mock_ascend_config.ascend_compilation_config = MagicMock()
mock_ascend_config.ascend_compilation_config.enable_npugraph_ex = False
mock_ascend_config.enable_cpu_binding = False
mock_get_ascend_config.return_value = mock_ascend_config
mock_get_ascend_device_type.return_value = mock_ascend_device_type.A9B
mock_cudagraph_mode.NONE = mock_cudagraph_mode.NONE
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.model_config = MagicMock()
worker.model_config.enforce_eager = False # Enable graph capture
worker.model_config.seed = 67890
worker.cache_config = MagicMock()
worker.cache_config.kv_cache_memory_bytes = 1024
# Setup compilation config
worker.vllm_config.compilation_config = MagicMock()
worker.vllm_config.compilation_config.compile_sizes = [1, 4, 8, 16]
worker.vllm_config.compilation_config.cudagraph_capture_sizes = [4, 8]
worker.vllm_config.compilation_config.cudagraph_mode = mock_cudagraph_mode.FULL
worker.vllm_config.compilation_config.get_compile_ranges.return_value = []
# Test compile_or_warm_up_model
worker.compile_or_warm_up_model()
# Verify only call _dummy_run for sizes not in cudagraph_capture_sizes
expected_calls = [unittest.mock.call(16), unittest.mock.call(1)]
worker.model_runner._dummy_run.assert_has_calls(expected_calls)
# Should call capture_model in non-eager mode
worker.model_runner.capture_model.assert_called_once()
# Verify atb warm up
mock_warm_up_atb.assert_called_once()
@patch("vllm_ascend.worker.worker.ensure_kv_transfer_initialized")
@patch("vllm_ascend.worker.worker.CaMemAllocator")
def test_initialize_from_config_with_sleep_mode(self, mock_allocator_class, mock_ensure_kv_transfer):
"""Test initialize_from_config method - with sleep mode enabled"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with (
patch.object(NPUWorker, "__init__", lambda x, **kwargs: None),
patch("vllm_ascend.worker.worker.ensure_kv_transfer_initialized"),
):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.speculative_config = None
worker.vllm_config.model_config = MagicMock()
worker.vllm_config.model_config.enable_sleep_mode = True
worker.vllm_config.kv_transfer_config = None
# Setup allocator mock
mock_allocator = MagicMock()
mock_context = MagicMock()
mock_allocator.use_memory_pool.return_value = mock_context
mock_allocator_class.get_instance.return_value = mock_allocator
# Create mock kv_cache_config
mock_kv_cache_config = MagicMock()
# Test initialize_from_config
worker.initialize_from_config(mock_kv_cache_config)
# Verify calls
mock_allocator_class.get_instance.assert_called_once()
mock_allocator.use_memory_pool.assert_called_once_with(tag="kv_cache")
worker.model_runner.initialize_kv_cache.assert_called_once_with(mock_kv_cache_config)
def test_acl_graph_sleep_wakeup_manager_sleep_resets_acl_graph_state(self):
from vllm_ascend.device_allocator.sleep_mem_optimized import AclGraphSleepWakeupManager
model_runner = MagicMock()
model_runner.use_aclgraph = True
graph_manager = MagicMock()
graph_manager.graphs = MagicMock()
graph_manager.pool = None
model_runner.cudagraph_manager = graph_manager
saver = AclGraphSleepWakeupManager(MagicMock(), lambda: model_runner)
with (
patch(
"vllm_ascend.device_allocator.sleep_mem_optimized.AclGraphSleepWakeupManager"
".clear_all_attention_workspaces"
) as mock_clear,
patch(
"vllm_ascend.device_allocator.sleep_mem_optimized.AclGraphSleepWakeupManager.reset_all_graph_params"
) as mock_reset,
):
saver.sleep()
mock_clear.assert_called_once()
mock_reset.assert_called_once()
graph_manager.graphs.clear.assert_called_once()
def test_hccl_sleep_wakeup_manager_sleep_waits_and_destroys(self):
from vllm_ascend.device_allocator.sleep_mem_optimized import HcclSleepWakeupManager
worker = MagicMock()
handle = MagicMock()
worker._pp_send_work = [handle]
saver = HcclSleepWakeupManager(MagicMock(), worker)
saver._destroyed = False
with (
patch("vllm_ascend.device_allocator.sleep_mem_optimized.torch.distributed.is_available", return_value=True),
patch(
"vllm_ascend.device_allocator.sleep_mem_optimized.torch.distributed.is_initialized",
return_value=True,
),
patch("vllm_ascend.device_allocator.sleep_mem_optimized.torch.npu.synchronize") as mock_synchronize,
patch(
"vllm_ascend.device_allocator.sleep_mem_optimized.HcclSleepWakeupManager.destroy_hccl",
return_value=2,
) as mock_destroy,
):
saver.sleep()
handle.wait.assert_called_once()
self.assertEqual(worker._pp_send_work, [])
mock_synchronize.assert_called_once()
mock_destroy.assert_called_once()
@patch("vllm_ascend.worker.worker.ensure_kv_transfer_initialized")
def test_initialize_from_config_without_sleep_mode(self, mock_ensure_kv_transfer):
"""Test initialize_from_config method - without sleep mode enabled"""
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with (
patch.object(NPUWorker, "__init__", lambda x, **kwargs: None),
patch("vllm_ascend.worker.worker.ensure_kv_transfer_initialized"),
):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.speculative_config = None
worker.vllm_config.model_config = MagicMock()
worker.vllm_config.model_config.enable_sleep_mode = False
worker.vllm_config.kv_transfer_config = None
# Create mock kv_cache_config
mock_kv_cache_config = MagicMock()
# Test initialize_from_config
worker.initialize_from_config(mock_kv_cache_config)
# Verify calls
worker.model_runner.initialize_kv_cache.assert_called_once_with(mock_kv_cache_config)
@patch("vllm_ascend.worker.worker.get_ascend_config")
@patch("vllm_ascend.worker.worker.enable_sp", return_value=False)
@patch("vllm_ascend.worker.worker.get_pp_group")
@patch("vllm_ascend.worker.worker.get_tp_group")
@patch("vllm_ascend.worker.worker.EMPTY_MODEL_RUNNER_OUTPUT")
def test_execute_model_kv_connector_not_finished(
self, mock_empty_output, mock_get_tp_group, mock_get_pp_group, mock_enable_sp, mock_get_ascend_config
):
"""Test execute_model method - kv_connector_output not finished sending/recving case"""
from vllm.sequence import IntermediateTensors
mock_ascend_config = MagicMock()
mock_ascend_config.msmonitor_use_daemon = False
mock_get_ascend_config.return_value = mock_ascend_config
from vllm_ascend.worker.worker import NPUWorker
# Create worker mock
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.model_runner = MagicMock()
worker.vllm_config = MagicMock()
worker.vllm_config.parallel_config = MagicMock()
worker.vllm_config.parallel_config.distributed_executor_backend = "ray"
worker.profiler = None
worker._pp_send_work = []
# Set as middle rank (not first, not last)
mock_pp_group = MagicMock()
mock_pp_group.is_first_rank = False
mock_pp_group.is_last_rank = False
mock_get_pp_group.return_value = mock_pp_group
# Setup tensor reception data
mock_pp_group.irecv_tensor_dict.return_value = ({"tensor": "data"}, None, None)
mock_pp_group.isend_tensor_dict.return_value = []
# Create mock kv_connector_output - both finished_sending and finished_recving are False
mock_kv_connector_output = MagicMock()
mock_kv_connector_output.finished_sending = False
mock_kv_connector_output.finished_recving = False
# Mock return IntermediateTensors with kv_connector_output
mock_intermediate_output = MagicMock(spec=IntermediateTensors)
mock_intermediate_output.tensors = {"output_tensor": "data"}
mock_intermediate_output.kv_connector_output = mock_kv_connector_output
worker.model_runner.execute_model.return_value = mock_intermediate_output
mock_scheduler_output = MagicMock()
mock_scheduler_output.total_num_scheduled_tokens = 1
# Test execute_model
result = worker.execute_model(mock_scheduler_output)
# Verify tensor reception and sending
mock_pp_group.irecv_tensor_dict.assert_called_once()
mock_pp_group.isend_tensor_dict.assert_called_once()
# When both flags are False, return EMPTY_MODEL_RUNNER_OUTPUT directly.
self.assertEqual(result, mock_empty_output)
class TestNPUWorkerWeightUpdate(TestBase):
def _make_worker(self, engine=None):
from vllm_ascend.worker.worker import NPUWorker
with patch.object(NPUWorker, "__init__", lambda x, **kwargs: None):
worker = NPUWorker()
worker.weight_transfer_engine = engine
worker._weight_update_active = False
worker._is_checkpoint_format = True
worker.device = torch.device("cpu")
worker.model_runner = MagicMock()
worker.model_runner.model = MagicMock()
worker.model_config = MagicMock()
return worker
def test_check_engine_raises_when_unconfigured(self):
worker = self._make_worker(engine=None)
with self.assertRaises(RuntimeError):
worker.init_weight_transfer_engine({})
with self.assertRaises(RuntimeError):
worker.start_weight_update()
with self.assertRaises(RuntimeError):
worker.update_weights({})
with self.assertRaises(RuntimeError):
worker.finish_weight_update()
def test_init_weight_transfer_engine_dispatches_to_engine(self):
engine = MagicMock()
engine.parse_init_info.return_value = "typed_init"
worker = self._make_worker(engine=engine)
init_info = {"master_address": "127.0.0.1", "master_port": 12345}
worker.init_weight_transfer_engine(init_info)
engine.parse_init_info.assert_called_once_with(init_info)
engine.init_transfer_engine.assert_called_once_with("typed_init")
@patch("vllm.model_executor.model_loader.reload.initialize_layerwise_reload")
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "0"})
def test_start_weight_update_checkpoint_format(self, mock_init_reload):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker.start_weight_update(is_checkpoint_format=True)
mock_init_reload.assert_called_once_with(worker.model_runner.model)
self.assertTrue(worker._weight_update_active)
self.assertTrue(worker._is_checkpoint_format)
@patch("vllm.model_executor.model_loader.reload.initialize_layerwise_reload")
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "0"})
def test_start_weight_update_kernel_format(self, mock_init_reload):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker.start_weight_update(is_checkpoint_format=False)
mock_init_reload.assert_not_called()
self.assertTrue(worker._weight_update_active)
self.assertFalse(worker._is_checkpoint_format)
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "0"})
def test_start_weight_update_rejects_reentry(self):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker._weight_update_active = True
with self.assertRaises(RuntimeError):
worker.start_weight_update()
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "1"})
def test_start_weight_update_rejects_nz(self):
engine = MagicMock()
worker = self._make_worker(engine=engine)
with self.assertRaises(ValueError):
worker.start_weight_update()
def test_update_weights_requires_start(self):
engine = MagicMock()
worker = self._make_worker(engine=engine)
with self.assertRaises(RuntimeError):
worker.update_weights({"names": [], "dtype_names": [], "shapes": []})
@patch("torch.npu.synchronize", create=True)
@patch("vllm.model_executor.model_loader.reload.finalize_layerwise_reload")
@patch("vllm.model_executor.model_loader.reload.initialize_layerwise_reload")
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "0"})
def test_update_weights_checkpoint_format(self, mock_init_reload, mock_finalize_reload, mock_sync):
engine = MagicMock()
worker = self._make_worker(engine=engine)
engine.parse_update_info.return_value = "typed_update"
worker._weight_update_active = True
worker._is_checkpoint_format = True
worker.update_weights({"foo": "bar"})
engine.parse_update_info.assert_called_once_with({"foo": "bar"})
engine.receive_weights.assert_called_once()
_, kwargs = engine.receive_weights.call_args
self.assertIs(kwargs["load_weights"], worker.model_runner.model.load_weights)
mock_sync.assert_called_once()
# reload lifecycle is split across start_weight_update / finish_weight_update
mock_init_reload.assert_not_called()
mock_finalize_reload.assert_not_called()
@patch("torch.npu.synchronize", create=True)
@patch.dict("os.environ", {"VLLM_ASCEND_ENABLE_NZ": "0"})
def test_update_weights_kernel_format(self, mock_sync):
engine = MagicMock()
def fake_receive(update_info, load_weights):
load_weights([("layer.weight", torch.zeros(2))])
engine.receive_weights.side_effect = fake_receive
worker = self._make_worker(engine=engine)
param = torch.nn.Parameter(torch.ones(2), requires_grad=True)
worker.model_runner.model.get_parameter.return_value = param
engine.parse_update_info.return_value = "typed_update"
worker._weight_update_active = True
worker._is_checkpoint_format = False
worker.update_weights({"foo": "bar"})
worker.model_runner.model.get_parameter.assert_called_once_with("layer.weight")
torch.testing.assert_close(param.detach(), torch.zeros(2))
self.assertTrue(param.requires_grad)
@patch("vllm.model_executor.model_loader.reload.finalize_layerwise_reload")
def test_finish_weight_update_resets_state(self, mock_finalize_reload):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker._weight_update_active = True
worker._is_checkpoint_format = True
worker.finish_weight_update()
mock_finalize_reload.assert_called_once_with(worker.model_runner.model, worker.model_config)
self.assertFalse(worker._weight_update_active)
self.assertTrue(worker._is_checkpoint_format)
def test_finish_without_start_raises(self):
engine = MagicMock()
worker = self._make_worker(engine=engine)
with self.assertRaises(RuntimeError):
worker.finish_weight_update()
def test_double_finish_raises(self):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker._weight_update_active = True
worker._is_checkpoint_format = False
worker.finish_weight_update()
with self.assertRaises(RuntimeError):
worker.finish_weight_update()
@patch("torch.npu.synchronize", create=True)
def test_update_after_finish_requires_restart(self, _mock_sync):
engine = MagicMock()
engine.parse_update_info.return_value = "typed"
worker = self._make_worker(engine=engine)
worker._weight_update_active = True
worker._is_checkpoint_format = False
worker.finish_weight_update()
with self.assertRaises(RuntimeError):
worker.update_weights({"names": [], "dtype_names": [], "shapes": []})
@patch("vllm.distributed.kv_transfer.ensure_kv_transfer_shutdown", create=True)
def test_shutdown_releases_engine(self, _mock_kv_shutdown):
engine = MagicMock()
worker = self._make_worker(engine=engine)
worker.profiler = None
worker.shutdown()
engine.shutdown.assert_called_once()