Add speech token usage headers (#4499)

Signed-off-by: JackyLiu <lzwgre@126.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
This commit is contained in:
JackyLiuandHongsheng Liu authored and GitHub committed 2026-08-18 06:53:06 +08:00
1 parent 2fdbf2234a
commit d7216ef514
3 files changed
+86 -5

No files matched your search

@@ -33,6 +33,8 @@ from vllm_omni.entrypoints.openai.protocol.audio import (
OpenAICreateAudioGenerateRequest,
OpenAICreateSpeechRequest,
SpeechBatchItem,
SpeechInputTokenDetails,
SpeechTokenUsage,
StreamingSpeechSessionConfig,
)
from vllm_omni.entrypoints.openai.serving_speech import (
@@ -155,6 +157,7 @@ class TestAudioMixin:
# Helper to create mock model output for endpoint tests
def create_mock_audio_output_for_test(
request_id: str = "speech-mock-123",
metrics: dict | None = None,
) -> OmniRequestOutput:
class MockCompletionOutput:
def __init__(self, index: int = 0):
@@ -181,11 +184,16 @@ def create_mock_audio_output_for_test(
audio_tensor = torch.sin(torch.linspace(0, 440 * 2 * torch.pi, num_samples))
mock_request_output = MockRequestOutput(request_id=request_id, audio_tensor=audio_tensor)
return OmniRequestOutput.from_stage_output(
if metrics is None:
metrics = {"stage_metrics": {"0": {"num_tokens_in": 0, "num_tokens_out": 5}}}
output = OmniRequestOutput.from_stage_output(
mock_request_output,
stage_id=0,
final_output_type="audio",
)
output.metrics = metrics
return output
def _write_custom_voice_manifest(root: Path, *, model_type: str, voices: dict) -> None:
@@ -233,6 +241,7 @@ def test_app(mocker: MockerFixture):
models=mock_models,
request_logger=mock_request_logger,
)
speech_server._tts_tokenizer = lambda text, padding=False: {"input_ids": text.split()}
# Skip TTS validation in tests (mock doesn't set up supported_speakers)
speech_server._validate_tts_request = mocker.MagicMock(return_value=None)
@@ -370,6 +379,37 @@ class TestSpeechAPI:
assert response.headers["content-type"] == "audio/wav"
assert len(response.content) > 0
def test_create_speech_includes_token_usage_and_detail_headers(self, client):
payload = {
"input": "Hello world",
"model": "tts-model",
"voice": "alloy",
"response_format": "wav",
}
response = client.post("/v1/audio/speech", json=payload)
assert response.status_code == 200
assert response.headers["x-vllm-omni-input-tokens"] == "2"
assert response.headers["x-vllm-omni-output-tokens"] == "5"
assert response.headers["x-vllm-omni-total-tokens"] == "7"
assert response.headers["x-vllm-omni-input-text-tokens"] == "2"
assert response.headers["x-vllm-omni-input-audio-tokens"] == "0"
def test_build_speech_usage_headers_uses_usage_and_detail_field_names(self):
usage = SpeechTokenUsage(
input_tokens=7,
output_tokens=11,
total_tokens=18,
input_token_details=SpeechInputTokenDetails(text_tokens=3, audio_tokens=4),
)
headers = OmniOpenAIServingSpeech._build_speech_usage_headers(usage)
assert headers == {
"X-VLLM-OMNI-INPUT-TOKENS": "7",
"X-VLLM-OMNI-OUTPUT-TOKENS": "11",
"X-VLLM-OMNI-TOTAL-TOKENS": "18",
"X-VLLM-OMNI-INPUT-TEXT-TOKENS": "3",
"X-VLLM-OMNI-INPUT-AUDIO-TOKENS": "4",
}
def test_create_speech_mp3_format(self, client):
payload = {
"input": "Hello world",