92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
|
|
|
"""``RealtimeProtocolCapabilities``: one consumer's answers, the codec's checking.
|
|
|
|
This is the seam that lets a non-duplex Realtime surface validate a
|
|
``session.update`` without a ``DuplexModelPlugin``. What it has to get right is
|
|
narrow: the order the checks run in (formats before turn detection, because
|
|
that is the order clients already see), the error codes, and that a consumer
|
|
which declares a narrower format set is actually narrowed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
|
|
import pytest
|
|
|
|
from vllm_omni.protocol.realtime.capabilities import (
|
|
RealtimeProtocolCapabilities,
|
|
RealtimeSessionRejection,
|
|
validate_session_payload,
|
|
)
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
|
|
|
_DEFAULT = RealtimeProtocolCapabilities()
|
|
|
|
|
|
def test_a_consumer_without_a_turn_detection_check_accepts_a_plain_session():
|
|
assert (
|
|
validate_session_payload({"model": "m", "audio": {"input": {"format": "pcm16"}}}, capabilities=_DEFAULT) is None
|
|
)
|
|
|
|
|
|
def test_a_consumer_without_a_turn_detection_check_ignores_turn_detection():
|
|
payload = {"turn_detection": {"type": "semantic_vad"}}
|
|
|
|
assert validate_session_payload(payload, capabilities=_DEFAULT) is None
|
|
|
|
|
|
def test_an_unsupported_input_format_is_rejected_with_the_realtime_code():
|
|
rejection = validate_session_payload({"input_audio_format": "opus"}, capabilities=_DEFAULT)
|
|
|
|
assert rejection == RealtimeSessionRejection(
|
|
code="unsupported_audio_format", message="Unsupported input_audio_format: opus"
|
|
)
|
|
|
|
|
|
def test_a_consumer_may_declare_a_narrower_format_set():
|
|
pcm_only = RealtimeProtocolCapabilities(input_audio_formats=frozenset({"pcm16"}))
|
|
|
|
assert validate_session_payload({"input_audio_format": "pcm16"}, capabilities=pcm_only) is None
|
|
rejection = validate_session_payload({"input_audio_format": "g711_ulaw"}, capabilities=pcm_only)
|
|
assert rejection is not None
|
|
assert rejection.code == "unsupported_audio_format"
|
|
# The default capabilities still accept it: the narrowing is per consumer.
|
|
assert validate_session_payload({"input_audio_format": "g711_ulaw"}, capabilities=_DEFAULT) is None
|
|
|
|
|
|
def test_the_turn_detection_check_runs_and_reports_its_own_code():
|
|
def reject_everything(session_payload: Mapping[str, object]) -> str | None:
|
|
del session_payload
|
|
return "server_vad is not available"
|
|
|
|
capabilities = RealtimeProtocolCapabilities(validate_turn_detection=reject_everything)
|
|
|
|
rejection = validate_session_payload({"turn_detection": {"type": "server_vad"}}, capabilities=capabilities)
|
|
|
|
assert rejection == RealtimeSessionRejection(
|
|
code="unsupported_turn_detection", message="server_vad is not available", param="turn_detection"
|
|
)
|
|
|
|
|
|
def test_a_bad_format_is_reported_before_a_bad_turn_detection():
|
|
calls: list[Mapping[str, object]] = []
|
|
|
|
def record(session_payload: Mapping[str, object]) -> str | None:
|
|
calls.append(session_payload)
|
|
return "turn detection is wrong too"
|
|
|
|
capabilities = RealtimeProtocolCapabilities(validate_turn_detection=record)
|
|
|
|
rejection = validate_session_payload(
|
|
{"input_audio_format": "opus", "turn_detection": {"type": "semantic_vad"}},
|
|
capabilities=capabilities,
|
|
)
|
|
|
|
assert rejection is not None
|
|
assert rejection.code == "unsupported_audio_format"
|
|
assert calls == []
|