2025-09-01 07:07:12 +01:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
2026-03-04 16:01:03 +00:00
|
|
|
from collections.abc import Sequence
|
|
|
|
|
from unittest.mock import MagicMock, patch
|
2025-09-01 07:07:12 +01:00
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from vllm.config import VllmConfig
|
2026-03-26 01:22:54 +08:00
|
|
|
from vllm.inputs import PromptType
|
2026-03-04 16:01:03 +00:00
|
|
|
from vllm.outputs import PoolingRequestOutput
|
2025-09-01 07:07:12 +01:00
|
|
|
from vllm.plugins.io_processors import get_io_processor
|
2026-03-04 16:01:03 +00:00
|
|
|
from vllm.plugins.io_processors.interface import IOProcessor
|
|
|
|
|
from vllm.renderers import BaseRenderer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DummyIOProcessor(IOProcessor):
|
|
|
|
|
"""Minimal IOProcessor used as the target of the mocked plugin entry point."""
|
|
|
|
|
|
|
|
|
|
def pre_process(
|
|
|
|
|
self,
|
|
|
|
|
prompt: object,
|
|
|
|
|
request_id: str | None = None,
|
|
|
|
|
**kwargs,
|
|
|
|
|
) -> PromptType | Sequence[PromptType]:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
def post_process(
|
|
|
|
|
self,
|
|
|
|
|
model_output: Sequence[PoolingRequestOutput],
|
|
|
|
|
request_id: str | None = None,
|
|
|
|
|
**kwargs,
|
|
|
|
|
) -> object:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def my_plugin_entry_points():
|
|
|
|
|
"""Patch importlib.metadata.entry_points to expose a single 'my_plugin'
|
|
|
|
|
entry point backed by DummyIOProcessor, exercising the full plugin-loading
|
|
|
|
|
code path: entry_points → plugin.load() → func() →
|
|
|
|
|
resolve_obj_by_qualname → IOProcessor.__init__."""
|
|
|
|
|
qualname = f"{DummyIOProcessor.__module__}.{DummyIOProcessor.__qualname__}"
|
|
|
|
|
ep = MagicMock()
|
|
|
|
|
ep.name = "my_plugin"
|
|
|
|
|
ep.value = qualname
|
|
|
|
|
ep.load.return_value = lambda: qualname
|
|
|
|
|
with patch("importlib.metadata.entry_points", return_value=[ep]):
|
|
|
|
|
yield
|
2025-09-01 07:07:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_loading_missing_plugin():
|
|
|
|
|
vllm_config = VllmConfig()
|
2026-03-04 16:01:03 +00:00
|
|
|
renderer = MagicMock(spec=BaseRenderer)
|
2025-09-01 07:07:12 +01:00
|
|
|
with pytest.raises(ValueError):
|
2026-03-04 16:01:03 +00:00
|
|
|
get_io_processor(
|
|
|
|
|
vllm_config, renderer=renderer, plugin_from_init="wrong_plugin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_loading_plugin(my_plugin_entry_points):
|
|
|
|
|
# Plugin name supplied via plugin_from_init.
|
|
|
|
|
vllm_config = MagicMock(spec=VllmConfig)
|
|
|
|
|
renderer = MagicMock(spec=BaseRenderer)
|
|
|
|
|
|
|
|
|
|
result = get_io_processor(
|
|
|
|
|
vllm_config, renderer=renderer, plugin_from_init="my_plugin"
|
2025-09-01 07:07:12 +01:00
|
|
|
)
|
|
|
|
|
|
2026-03-04 16:01:03 +00:00
|
|
|
assert isinstance(result, DummyIOProcessor)
|
2025-09-01 07:07:12 +01:00
|
|
|
|
|
|
|
|
|
2026-03-04 16:01:03 +00:00
|
|
|
def test_loading_missing_plugin_from_model_config():
|
|
|
|
|
# Build a mock VllmConfig whose hf_config advertises a plugin name,
|
|
|
|
|
# exercising the model-config code path without loading a real model.
|
|
|
|
|
mock_hf_config = MagicMock()
|
|
|
|
|
mock_hf_config.to_dict.return_value = {"io_processor_plugin": "wrong_plugin"}
|
2025-09-01 07:07:12 +01:00
|
|
|
|
2026-03-04 16:01:03 +00:00
|
|
|
vllm_config = MagicMock(spec=VllmConfig)
|
|
|
|
|
vllm_config.model_config.hf_config = mock_hf_config
|
2025-09-04 08:22:41 +01:00
|
|
|
|
2026-03-04 16:01:03 +00:00
|
|
|
renderer = MagicMock(spec=BaseRenderer)
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
get_io_processor(vllm_config, renderer=renderer)
|
2025-09-04 08:22:41 +01:00
|
|
|
|
|
|
|
|
|
2026-03-04 16:01:03 +00:00
|
|
|
def test_loading_plugin_from_model_config(my_plugin_entry_points):
|
|
|
|
|
# Plugin name supplied via the model's hf_config.
|
|
|
|
|
mock_hf_config = MagicMock()
|
|
|
|
|
mock_hf_config.to_dict.return_value = {"io_processor_plugin": "my_plugin"}
|
|
|
|
|
|
|
|
|
|
vllm_config = MagicMock(spec=VllmConfig)
|
|
|
|
|
vllm_config.model_config.hf_config = mock_hf_config
|
|
|
|
|
|
|
|
|
|
renderer = MagicMock(spec=BaseRenderer)
|
|
|
|
|
|
|
|
|
|
result = get_io_processor(vllm_config, renderer=renderer)
|
|
|
|
|
|
|
|
|
|
assert isinstance(result, DummyIOProcessor)
|