diff --git a/tests/metrics/test_prometheus.py b/tests/metrics/test_prometheus.py index 1f79b186b..f01933dbc 100644 --- a/tests/metrics/test_prometheus.py +++ b/tests/metrics/test_prometheus.py @@ -12,10 +12,10 @@ pytestmark = [pytest.mark.core_model, pytest.mark.cpu] _MODEL = "test-model" _PIPELINE_METRICS = [ - "vllm:omni_num_requests_running", - "vllm:omni_num_requests_waiting", - "vllm:omni_requests_success", - "vllm:omni_e2e_request_latency_s", + "vllm_omni:num_requests_running", + "vllm_omni:num_requests_waiting", + "vllm_omni:requests_success", + "vllm_omni:e2e_request_latency_s", ] @@ -58,32 +58,32 @@ class TestMetricObservation: # Per-reason buckets sourced from the merged completion Counter. stop = _sample_value( scrape_output, - f'vllm:omni_requests_success_total{{finished_reason="stop",model_name="{_MODEL}"}}', + f'vllm_omni:requests_success_total{{finished_reason="stop",model_name="{_MODEL}"}}', ) assert stop == 2.0 length = _sample_value( scrape_output, - f'vllm:omni_requests_success_total{{finished_reason="length",model_name="{_MODEL}"}}', + f'vllm_omni:requests_success_total{{finished_reason="length",model_name="{_MODEL}"}}', ) assert length == 1.0 abort = _sample_value( scrape_output, - f'vllm:omni_requests_success_total{{finished_reason="abort",model_name="{_MODEL}"}}', + f'vllm_omni:requests_success_total{{finished_reason="abort",model_name="{_MODEL}"}}', ) assert abort == 1.0 def test_gauge_values(self, scrape_output: str) -> None: running = _sample_value( scrape_output, - f'vllm:omni_num_requests_running{{model_name="{_MODEL}"}}', + f'vllm_omni:num_requests_running{{model_name="{_MODEL}"}}', ) assert running == 5.0 waiting = _sample_value( scrape_output, - f'vllm:omni_num_requests_waiting{{model_name="{_MODEL}"}}', + f'vllm_omni:num_requests_waiting{{model_name="{_MODEL}"}}', ) assert waiting == 2.0 @@ -93,7 +93,7 @@ class TestMetricObservation: # observing the latency histogram, so the count stays at 3. e2e_count = _sample_value( scrape_output, - f'vllm:omni_e2e_request_latency_s_count{{model_name="{_MODEL}"}}', + f'vllm_omni:e2e_request_latency_s_count{{model_name="{_MODEL}"}}', ) assert e2e_count == 3.0 @@ -110,8 +110,8 @@ class TestLabelCorrectness: def test_no_legacy_seconds_or_ms_families(self, scrape_output: str) -> None: # Renamed: *_time_ms → *_s; *_time_seconds dropped. for legacy in ( - "vllm:omni_request_queue_time_seconds", - "vllm:omni_e2e_request_latency_seconds", + "vllm_omni:request_queue_time_seconds", + "vllm_omni:e2e_request_latency_seconds", ): assert legacy not in scrape_output, f"legacy family {legacy} still registered" @@ -170,8 +170,8 @@ class TestRequestLifecycleGauges: assert obj.engine._running_counter.value == 0 assert len(obj.request_states) == 0 out = generate_latest(registry).decode() - assert _sample_value(out, 'vllm:omni_num_requests_running{model_name="lifecycle-test"}') == 0.0 - assert _sample_value(out, 'vllm:omni_num_requests_waiting{model_name="lifecycle-test"}') == 0.0 + assert _sample_value(out, 'vllm_omni:num_requests_running{model_name="lifecycle-test"}') == 0.0 + assert _sample_value(out, 'vllm_omni:num_requests_waiting{model_name="lifecycle-test"}') == 0.0 def test_gauges_reflect_remaining_requests_after_one_completes(self, registry: CollectorRegistry) -> None: from types import SimpleNamespace @@ -196,5 +196,5 @@ class TestRequestLifecycleGauges: obj._log_summary_and_cleanup("req-1") out = generate_latest(registry).decode() - assert _sample_value(out, 'vllm:omni_num_requests_running{model_name="lifecycle-test-2"}') == 1.0 - assert _sample_value(out, 'vllm:omni_num_requests_waiting{model_name="lifecycle-test-2"}') == 0.0 + assert _sample_value(out, 'vllm_omni:num_requests_running{model_name="lifecycle-test-2"}') == 1.0 + assert _sample_value(out, 'vllm_omni:num_requests_waiting{model_name="lifecycle-test-2"}') == 0.0 diff --git a/vllm_omni/metrics/definitions.py b/vllm_omni/metrics/definitions.py index 00b6ff1cb..4411e5097 100644 --- a/vllm_omni/metrics/definitions.py +++ b/vllm_omni/metrics/definitions.py @@ -13,9 +13,9 @@ time-bearing metrics use the ``_s`` suffix (values in seconds), counters use from typing import Any -# vllm:omni_ avoids upstream's unregister_vllm_metrics() stripping, which -# removes every collector whose ``_name`` does not start with ``vllm``. -METRIC_PREFIX = "vllm:omni_" +# vllm_omni: namespace for omni-specific Prometheus families, distinct from +# the upstream vllm:* families. +METRIC_PREFIX = "vllm_omni:" # ============================================================================ diff --git a/vllm_omni/metrics/prometheus.py b/vllm_omni/metrics/prometheus.py index 225a7c8db..d372c3d6b 100644 --- a/vllm_omni/metrics/prometheus.py +++ b/vllm_omni/metrics/prometheus.py @@ -42,9 +42,8 @@ _generation_tokens_family = Counter( class OmniPrometheusMetrics: """Label-bound wrapper around the raw Prometheus metrics. - Metric collectors use the ``vllm:omni_`` prefix to avoid being - removed by upstream vLLM's ``unregister_vllm_metrics()``, which - strips every collector whose ``_name`` contains ``"vllm"``. + Metric collectors use the ``vllm_omni:`` prefix, distinct from the + upstream ``vllm:*`` families. """ def __init__(self, model_name: str, log_stats: bool = True) -> None: diff --git a/vllm_omni/patch.py b/vllm_omni/patch.py index 8bccb324f..3b71e6056 100644 --- a/vllm_omni/patch.py +++ b/vllm_omni/patch.py @@ -313,43 +313,6 @@ for module_name, module in list(sys.modules.items()): if hasattr(module, "EngineCoreRequest") and module.EngineCoreRequest == _OriginalEngineCoreRequest: module.EngineCoreRequest = OmniEngineCoreRequest -# ============================================================================= -# Patch unregister_vllm_metrics to skip vllm:omni_* collectors -# ============================================================================= -# WHY: Upstream unregister_vllm_metrics() uses `"vllm" in collector._name` to -# strip collectors before each new PrometheusStatLogger registers, which is -# how it avoids "Duplicated timeseries" when the same process spawns multiple -# engines / orchestrators. But its substring match also wipes our vllm:omni_* -# families, so we must replace it with a scoped version that keeps ours. -# -# REMOVAL: Remove this patch once upstream vLLM adds -# _STAT_LOGGER_METRIC_NAMES to vllm.v1.metrics.prometheus and scopes -# unregister_vllm_metrics() to that set. Track: -# https://github.com/vllm-project/vllm/pull/42331 -import vllm.v1.metrics.prometheus as _vllm_prometheus # noqa: E402 - -_logger = logging.getLogger(__name__) - - -def _scoped_unregister_vllm_metrics(): - """Drop upstream vllm:* collectors but preserve vllm:omni_* (ours).""" - from prometheus_client import REGISTRY - - for collector in list(REGISTRY._collector_to_names): - name = getattr(collector, "_name", "") - if "vllm" not in name: - continue - if name.startswith("vllm:omni_") or name.startswith("vllm_omni"): - continue - REGISTRY.unregister(collector) - - -_vllm_prometheus.unregister_vllm_metrics = _scoped_unregister_vllm_metrics -_logger.warning( - "Monkey-patched unregister_vllm_metrics() to scope drops to non-omni vllm:* collectors. " - "Remove this patch once vLLM adds _STAT_LOGGER_METRIC_NAMES." -) - # Patch: add qwen3_omni_moe to vllm's chat template fallback registry. # Qwen/Qwen3-Omni-30B-A3B-Instruct stores its chat_template in a standalone