Files
vllm-omni/tests/entrypoints/openai_api/test_serving_chat_metrics.py
T
2026-09-10 00:41:52 +08:00

157 lines
5.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Unit tests for OmniChatCompletionResponse/StreamResponse metrics field."""
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
from tests.helpers.serving_chat import (
build_serving_chat,
collect_stream,
make_request,
make_text_omni_output,
parse_sse_chunks,
)
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
def test_omni_chat_completion_response_metrics():
"""Test OmniChatCompletionResponse metrics field works correctly."""
from vllm.entrypoints.serve.engine.protocol import UsageInfo
from vllm_omni.entrypoints.openai.protocol.chat_completion import (
OmniChatCompletionResponse,
)
usage = UsageInfo(prompt_tokens=0, completion_tokens=0, total_tokens=0)
# Default is None
response = OmniChatCompletionResponse(id="test-id", created=1234567890, model="test-model", choices=[], usage=usage)
assert response.metrics is None
# Can set metrics and serialize
test_metrics = {"thinker_ttft": 0.123, "talker_ttft": 0.456}
response = OmniChatCompletionResponse(
id="test-id",
created=1234567890,
model="test-model",
choices=[],
usage=usage,
metrics=test_metrics,
)
assert response.metrics == test_metrics
assert "thinker_ttft" in response.model_dump_json()
def test_omni_chat_completion_stream_response_metrics():
"""Test OmniChatCompletionStreamResponse metrics and modality fields."""
from vllm_omni.entrypoints.openai.protocol.chat_completion import (
OmniChatCompletionStreamResponse,
)
response = OmniChatCompletionStreamResponse(
id="test-id",
created=1234567890,
model="test-model",
choices=[],
modality="audio",
metrics={"stage_latency": 0.5},
)
assert response.modality == "audio"
assert response.metrics == {"stage_latency": 0.5}
@pytest.mark.asyncio
async def test_stream_prompt_token_details_include_zero_cached_and_multimodal_tokens():
serving_chat = build_serving_chat()
serving_chat.enable_prompt_tokens_details = True
request = make_request(modalities=["text"], include_usage=True)
async def result_generator():
yield make_text_omni_output(
token_ids=[10],
finish_reason="stop",
num_cached_tokens=0,
)
raw_lines = await collect_stream(
serving_chat.chat_completion_stream_generator(
request=request,
result_generator=result_generator(),
request_id="test-req",
model_name="test-model",
conversation=[],
tokenizer=MagicMock(),
request_metadata=MagicMock(),
mm_token_counts={"image": 7},
)
)
chunks = parse_sse_chunks(raw_lines)
usage = next(chunk["usage"] for chunk in chunks if chunk.get("usage"))
assert usage["prompt_tokens_details"] == {
"cached_tokens": 0,
"multimodal_tokens": {"image": 7},
}
def test_non_stream_prompt_token_details_include_zero_cached_and_multimodal_tokens():
serving_chat = build_serving_chat()
serving_chat.enable_prompt_tokens_details = True
request = make_request(modalities=["text"], stream=False)
omni_output = make_text_omni_output(
token_ids=[10],
finish_reason="stop",
num_cached_tokens=0,
)
_, usage, _, _, _ = serving_chat._create_text_choice(
request=request,
omni_outputs=omni_output,
tokenizer=MagicMock(),
conversation=[],
role="assistant",
mm_token_counts={"audio": 5},
)
assert usage.prompt_tokens_details is not None
assert usage.prompt_tokens_details.cached_tokens == 0
assert usage.prompt_tokens_details.multimodal_tokens == {"audio": 5}
def test_create_image_choice_exposes_diffusion_metrics():
"""Ensure image chat content exposes profiler metrics for clients."""
from PIL import Image
from vllm_omni.entrypoints.openai.serving_chat import OmniOpenAIServingChat
stage_durations = {"prefill": 0.12, "diffusion": 1.23}
peak_memory_mb = 3210.5
omni_outputs = SimpleNamespace(
request_output=None,
stage_durations=stage_durations,
peak_memory_mb=peak_memory_mb,
images=[Image.new("RGB", (2, 2), color=(255, 0, 0))],
)
choices = OmniOpenAIServingChat._create_image_choice( # type: ignore[misc]
None,
omni_outputs=omni_outputs,
role="assistant",
request=SimpleNamespace(return_token_ids=False),
)
assert len(choices) == 1
content = choices[0].message.content
assert isinstance(content, list)
assert len(content) == 1
first_item = content[0]
assert first_item["type"] == "image_url"
assert first_item["image_url"]["url"].startswith("data:image/png;base64,")
assert first_item["stage_durations"] == stage_durations
assert first_item["peak_memory_mb"] == peak_memory_mb