[V1][Metrics][Plugin] Add plugin support for custom StatLoggerBase implementations (#22456)
Signed-off-by: tovam <tovam@pliops.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
from vllm.v1.metrics.loggers import StatLoggerBase
|
||||
|
||||
|
||||
class DummyStatLogger(StatLoggerBase):
|
||||
"""
|
||||
A dummy stat logger for testing purposes.
|
||||
Implements the minimal interface expected by StatLoggerManager.
|
||||
"""
|
||||
|
||||
def __init__(self, vllm_config, engine_idx=0):
|
||||
self.vllm_config = vllm_config
|
||||
self.engine_idx = engine_idx
|
||||
self.recorded = []
|
||||
self.logged = False
|
||||
self.engine_initialized = False
|
||||
|
||||
def record(self, scheduler_stats, iteration_stats, mm_cache_stats, engine_idx):
|
||||
self.recorded.append(
|
||||
(scheduler_stats, iteration_stats, mm_cache_stats, engine_idx)
|
||||
)
|
||||
|
||||
def log(self):
|
||||
self.logged = True
|
||||
|
||||
def log_engine_initialized(self):
|
||||
self.engine_initialized = True
|
||||
15
tests/plugins/vllm_add_dummy_stat_logger/setup.py
Normal file
15
tests/plugins/vllm_add_dummy_stat_logger/setup.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="dummy_stat_logger",
|
||||
version="0.1",
|
||||
packages=["dummy_stat_logger"],
|
||||
entry_points={
|
||||
"vllm.stat_logger_plugins": [
|
||||
"dummy_stat_logger = dummy_stat_logger.dummy_stat_logger:DummyStatLogger" # noqa
|
||||
]
|
||||
},
|
||||
)
|
||||
76
tests/plugins_tests/test_stats_logger_plugins.py
Normal file
76
tests/plugins_tests/test_stats_logger_plugins.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
import pytest
|
||||
from dummy_stat_logger.dummy_stat_logger import DummyStatLogger
|
||||
|
||||
from vllm.config import VllmConfig
|
||||
from vllm.engine.arg_utils import AsyncEngineArgs
|
||||
from vllm.v1.engine.async_llm import AsyncLLM
|
||||
from vllm.v1.metrics.loggers import load_stat_logger_plugin_factories
|
||||
|
||||
|
||||
def test_stat_logger_plugin_is_discovered(monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setenv("VLLM_PLUGINS", "dummy_stat_logger")
|
||||
|
||||
factories = load_stat_logger_plugin_factories()
|
||||
assert len(factories) == 1, f"Expected 1 factory, got {len(factories)}"
|
||||
assert factories[0] is DummyStatLogger, (
|
||||
f"Expected DummyStatLogger class, got {factories[0]}"
|
||||
)
|
||||
|
||||
# instantiate and confirm the right type
|
||||
vllm_config = VllmConfig()
|
||||
instance = factories[0](vllm_config)
|
||||
assert isinstance(instance, DummyStatLogger)
|
||||
|
||||
|
||||
def test_no_plugins_loaded_if_env_empty(monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setenv("VLLM_PLUGINS", "")
|
||||
|
||||
factories = load_stat_logger_plugin_factories()
|
||||
assert factories == []
|
||||
|
||||
|
||||
def test_invalid_stat_logger_plugin_raises(monkeypatch: pytest.MonkeyPatch):
|
||||
def fake_plugin_loader(group: str):
|
||||
assert group == "vllm.stat_logger_plugins"
|
||||
return {"bad": object()}
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(
|
||||
"vllm.v1.metrics.loggers.load_plugins_by_group",
|
||||
fake_plugin_loader,
|
||||
)
|
||||
with pytest.raises(
|
||||
TypeError,
|
||||
match="Stat logger plugin 'bad' must be a subclass of StatLoggerBase",
|
||||
):
|
||||
load_stat_logger_plugin_factories()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_logger_plugin_integration_with_engine(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
with monkeypatch.context() as m:
|
||||
m.setenv("VLLM_PLUGINS", "dummy_stat_logger")
|
||||
|
||||
engine_args = AsyncEngineArgs(
|
||||
model="facebook/opt-125m",
|
||||
enforce_eager=True, # reduce test time
|
||||
disable_log_stats=True, # disable default loggers
|
||||
)
|
||||
|
||||
engine = AsyncLLM.from_engine_args(engine_args=engine_args)
|
||||
|
||||
assert len(engine.logger_manager.stat_loggers) == 2
|
||||
assert len(engine.logger_manager.stat_loggers[0].per_engine_stat_loggers) == 1
|
||||
assert isinstance(
|
||||
engine.logger_manager.stat_loggers[0].per_engine_stat_loggers[0],
|
||||
DummyStatLogger,
|
||||
)
|
||||
|
||||
engine.shutdown()
|
||||
@@ -4,33 +4,13 @@ import copy
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.plugins.vllm_add_dummy_stat_logger.dummy_stat_logger.dummy_stat_logger import ( # noqa E501
|
||||
DummyStatLogger,
|
||||
)
|
||||
from vllm.v1.engine.async_llm import AsyncEngineArgs, AsyncLLM
|
||||
from vllm.v1.metrics.ray_wrappers import RayPrometheusStatLogger
|
||||
|
||||
|
||||
class DummyStatLogger:
|
||||
"""
|
||||
A dummy stat logger for testing purposes.
|
||||
Implements the minimal interface expected by StatLoggerManager.
|
||||
"""
|
||||
|
||||
def __init__(self, vllm_config, engine_idx):
|
||||
self.vllm_config = vllm_config
|
||||
self.engine_idx = engine_idx
|
||||
self.recorded = []
|
||||
self.logged = False
|
||||
self.engine_initialized = False
|
||||
|
||||
def record(self, scheduler_stats, iteration_stats, engine_idx):
|
||||
self.recorded.append((scheduler_stats, iteration_stats, engine_idx))
|
||||
|
||||
def log(self):
|
||||
self.logged = True
|
||||
|
||||
def log_engine_initialized(self):
|
||||
self.engine_initialized = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def log_stats_enabled_engine_args():
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user