Signed-off-by: chickeyton <ngton2014@gmail.com> Signed-off-by: Yueqian Lin <linyueqian@outlook.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Yueqian Lin <linyueqian@outlook.com>
This commit is contained in:
49 files changed
+3056
-1715
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
@@ -0,0 +1,104 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from vllm_omni.engine.duplex.contracts import DuplexFence, duplex_resource_request_id
|
||||
from vllm_omni.engine.duplex.plugin import DuplexDataPlane, DuplexDataPlaneContext
|
||||
from vllm_omni.model_executor.common.duplex.data_plane import CumulativeAudioTextDataPlane
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
def _encode_audio(audio, _sample_rate, _response_format, _speed):
|
||||
if audio is None:
|
||||
return None
|
||||
size = int(np.asarray(audio, dtype=np.float32).size)
|
||||
return f"audio-{size}" if size else None
|
||||
|
||||
|
||||
def _output(request_id: str, *, text: str, samples: int, sr: int = 24000) -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
request_id=request_id,
|
||||
outputs=[SimpleNamespace(text=text, multimodal_output={})],
|
||||
multimodal_output={"model_outputs": np.arange(samples, dtype=np.float32), "sr": sr},
|
||||
finished=False,
|
||||
)
|
||||
|
||||
|
||||
class _TestDataPlane(CumulativeAudioTextDataPlane):
|
||||
stage_role = "codec"
|
||||
default_sample_rate_hz = 16000
|
||||
|
||||
|
||||
def test_is_a_duplex_data_plane_and_projects_cumulative_output_as_deltas() -> None:
|
||||
plane = CumulativeAudioTextDataPlane(_encode_audio)
|
||||
plane.begin_request("req")
|
||||
context = DuplexDataPlaneContext(response_format="wav", modalities=("audio", "text"))
|
||||
|
||||
projected = list(
|
||||
plane.project(
|
||||
{"data_plane_outputs": [_output("req", text="he", samples=4), _output("req", text="hello", samples=6)]},
|
||||
context=context,
|
||||
)
|
||||
)
|
||||
|
||||
assert isinstance(plane, DuplexDataPlane)
|
||||
assert [item["text"] for item in projected] == ["he", "llo"]
|
||||
assert [item["audio_data"] for item in projected] == ["audio-4", "audio-2"]
|
||||
assert [item["audio_duration_ms"] for item in projected] == [0, 0]
|
||||
assert all(item["sample_rate_hz"] == 24000 for item in projected)
|
||||
assert all(item["data_plane_request_id"] == "req" for item in projected)
|
||||
assert all(item["is_listen"] is False and item["end_of_turn"] is False for item in projected)
|
||||
assert projected[0]["stage_role"] == "tts"
|
||||
assert projected[0]["runtime_impl"] == "scheduler_data_plane"
|
||||
assert projected[0]["audio_format"] == "wav"
|
||||
|
||||
|
||||
def test_subclass_constants_and_default_sample_rate_reach_the_projection() -> None:
|
||||
plane = _TestDataPlane(_encode_audio)
|
||||
output = SimpleNamespace(
|
||||
request_id="req",
|
||||
outputs=[SimpleNamespace(text="", multimodal_output={})],
|
||||
multimodal_output={"audio": np.zeros(160, dtype=np.float32)},
|
||||
finished=False,
|
||||
)
|
||||
|
||||
[item] = list(plane.project({"data_plane_outputs": [output]}))
|
||||
|
||||
assert item["stage_role"] == "codec"
|
||||
assert item["sample_rate_hz"] == 16000
|
||||
assert item["audio_duration_ms"] == 10
|
||||
|
||||
|
||||
def test_nothing_new_yields_nothing_and_non_results_are_ignored() -> None:
|
||||
plane = CumulativeAudioTextDataPlane(_encode_audio)
|
||||
same = _output("req", text="hi", samples=4)
|
||||
|
||||
assert len(list(plane.project({"data_plane_outputs": [same, same]}))) == 1
|
||||
assert list(plane.project("not a dict")) == []
|
||||
assert list(plane.project({"data_plane_outputs": None})) == []
|
||||
|
||||
|
||||
def test_terminal_and_close_bookkeeping() -> None:
|
||||
plane = CumulativeAudioTextDataPlane(_encode_audio)
|
||||
session_request = duplex_resource_request_id(DuplexFence("sid", epoch=0), "stage0")
|
||||
plane.begin_request(session_request)
|
||||
plane.begin_request("other-req")
|
||||
|
||||
assert plane.is_terminal(None) is False
|
||||
assert plane.is_terminal(session_request) is False
|
||||
plane.mark_terminal(session_request)
|
||||
assert plane.is_terminal(session_request) is True
|
||||
plane.begin_request(session_request)
|
||||
assert plane.is_terminal(session_request) is False
|
||||
|
||||
plane.close_session("sid", active_request_id="other-req")
|
||||
assert plane._requests == {}
|
||||
|
||||
plane.mark_terminal("late")
|
||||
plane.close_stream("late")
|
||||
assert plane.is_terminal("late") is False
|
||||
@@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
|
||||
import base64
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from vllm_omni.model_executor.common.duplex.payload import (
|
||||
decode_pcm_f32le_payload,
|
||||
payload_audio,
|
||||
payload_sample_count,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
def _payload(samples: int, *, sample_rate_hz: int = 24000, fmt: str = "pcm_f32le", key: str = "audio") -> dict:
|
||||
audio = base64.b64encode(np.zeros(samples, dtype="<f4").tobytes()).decode("ascii")
|
||||
return {"type": "audio", "format": fmt, "sample_rate_hz": sample_rate_hz, key: audio}
|
||||
|
||||
|
||||
def test_decode_returns_the_pcm_of_a_matching_payload() -> None:
|
||||
raw = decode_pcm_f32le_payload(_payload(1920), sample_rate_hz=24000, exact_samples=1920)
|
||||
|
||||
assert len(raw) == 1920 * 4
|
||||
|
||||
|
||||
def test_decode_accepts_the_older_data_key() -> None:
|
||||
assert len(decode_pcm_f32le_payload(_payload(8, key="data"), sample_rate_hz=24000)) == 32
|
||||
assert payload_audio({"data": "x"}) == "x"
|
||||
assert payload_audio("not a mapping") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("payload", "match"),
|
||||
[
|
||||
("frame", "must be a mapping"),
|
||||
(_payload(1920, fmt="pcm16"), "format must be pcm_f32le"),
|
||||
(_payload(1920, sample_rate_hz=16000), "sample_rate_hz must be 24000"),
|
||||
(_payload(1919), "exactly 1920 samples"),
|
||||
({"format": "pcm_f32le", "sample_rate_hz": 24000, "audio": 12}, "must be base64"),
|
||||
],
|
||||
)
|
||||
def test_decode_rejects_payloads_that_do_not_match_the_model_unit(payload: object, match: str) -> None:
|
||||
with pytest.raises(ValueError, match=match) as excinfo:
|
||||
decode_pcm_f32le_payload(payload, sample_rate_hz=24000, exact_samples=1920, model="TestModel")
|
||||
|
||||
assert str(excinfo.value).startswith("TestModel ")
|
||||
|
||||
|
||||
def test_decode_without_exact_samples_accepts_any_whole_number_of_samples() -> None:
|
||||
assert len(decode_pcm_f32le_payload(_payload(7), sample_rate_hz=24000)) == 28
|
||||
|
||||
|
||||
def test_payload_sample_count_is_lenient() -> None:
|
||||
assert payload_sample_count(_payload(16000, sample_rate_hz=16000)) == 16000
|
||||
assert payload_sample_count(_payload(16000, fmt="pcm16")) is None
|
||||
assert payload_sample_count({"format": "pcm_f32le", "audio": "not*base64"}) is None
|
||||
assert payload_sample_count(None) is None
|
||||
@@ -0,0 +1,185 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
|
||||
import base64
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from vllm_omni.engine.duplex.plugin import PcmAppendBuffer, PcmAppendReservation
|
||||
from vllm_omni.model_executor.common.duplex.pcm_buffer import (
|
||||
FixedFramePcmAppendBuffer,
|
||||
FixedFramePcmAppendReservation,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
FRAME = 1920
|
||||
RATE = 24000
|
||||
PERIOD = 80
|
||||
|
||||
|
||||
def _pcm_payload(samples: np.ndarray, *, sample_rate_hz: int = RATE, fmt: str = "pcm_f32le") -> dict[str, object]:
|
||||
samples = np.ascontiguousarray(samples, dtype="<f4")
|
||||
return {
|
||||
"type": "audio",
|
||||
"format": fmt,
|
||||
"sample_rate_hz": sample_rate_hz,
|
||||
"audio": base64.b64encode(samples.tobytes()).decode("ascii"),
|
||||
}
|
||||
|
||||
|
||||
def _buffer() -> FixedFramePcmAppendBuffer:
|
||||
return FixedFramePcmAppendBuffer(sample_rate_hz=RATE, frame_samples=FRAME, chunk_period_ms=PERIOD, model="Test")
|
||||
|
||||
|
||||
def _decode(payload: dict[str, object]) -> np.ndarray:
|
||||
return np.frombuffer(base64.b64decode(str(payload["audio"])), dtype="<f4")
|
||||
|
||||
|
||||
def test_buffer_implements_the_framework_contracts() -> None:
|
||||
buffer = _buffer()
|
||||
|
||||
assert isinstance(buffer, PcmAppendBuffer)
|
||||
assert buffer.frame_bytes == FRAME * 4
|
||||
reservation = buffer.prepare_append(
|
||||
_pcm_payload(np.arange(FRAME)), operation_id="op", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
assert isinstance(reservation, FixedFramePcmAppendReservation)
|
||||
assert isinstance(reservation, PcmAppendReservation)
|
||||
|
||||
|
||||
def test_one_whole_frame_is_reserved_transactionally() -> None:
|
||||
buffer = _buffer()
|
||||
|
||||
reservation = buffer.prepare_append(
|
||||
_pcm_payload(np.arange(FRAME)), operation_id="op-1", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
|
||||
assert reservation is not None
|
||||
assert reservation.active
|
||||
assert reservation.byte_count == FRAME * 4
|
||||
assert reservation.payload is not None and reservation.payload["sample_rate_hz"] == RATE
|
||||
assert _decode(reservation.payload).tolist() == list(range(FRAME))
|
||||
assert buffer.pending_byte_count == 0
|
||||
assert buffer.has_reserved() and not buffer.has_pending()
|
||||
|
||||
reservation.rollback()
|
||||
|
||||
assert not reservation.active
|
||||
assert buffer.pending_byte_count == FRAME * 4
|
||||
assert buffer.has_pending() and not buffer.has_reserved()
|
||||
|
||||
|
||||
def test_partial_chunks_accumulate_until_a_whole_frame_exists() -> None:
|
||||
buffer = _buffer()
|
||||
half = _pcm_payload(np.zeros(FRAME // 2))
|
||||
|
||||
assert buffer.prepare_append(half, operation_id="op-1", chunk_period_ms=PERIOD, allow_emit=True) is None
|
||||
assert buffer.pending_byte_count == FRAME * 2
|
||||
reservation = buffer.prepare_append(half, operation_id="op-2", chunk_period_ms=PERIOD, allow_emit=True)
|
||||
|
||||
assert reservation is not None
|
||||
assert reservation.byte_count == FRAME * 4
|
||||
assert buffer.pending_byte_count == 0
|
||||
|
||||
|
||||
def test_appends_are_not_emitted_while_emission_is_disallowed() -> None:
|
||||
buffer = _buffer()
|
||||
|
||||
assert (
|
||||
buffer.prepare_append(
|
||||
_pcm_payload(np.zeros(FRAME)), operation_id="op", chunk_period_ms=PERIOD, allow_emit=False
|
||||
)
|
||||
is None
|
||||
)
|
||||
assert buffer.pending_byte_count == FRAME * 4
|
||||
|
||||
|
||||
def test_commit_flushes_a_padded_final_frame_and_an_empty_buffer_reserves_nothing() -> None:
|
||||
buffer = _buffer()
|
||||
buffer.prepare_append(_pcm_payload(np.ones(10)), operation_id="op-1", chunk_period_ms=PERIOD, allow_emit=False)
|
||||
|
||||
reservation = buffer.prepare_commit(operation_id="commit", chunk_period_ms=PERIOD)
|
||||
|
||||
assert reservation.payload is not None
|
||||
assert reservation.payload["final"] is True
|
||||
decoded = _decode(reservation.payload)
|
||||
assert decoded.size == FRAME
|
||||
assert decoded[:10].tolist() == [1.0] * 10 and not decoded[10:].any()
|
||||
assert reservation.byte_count == 40
|
||||
reservation.commit()
|
||||
assert not buffer.has_reserved()
|
||||
|
||||
empty = buffer.prepare_commit(operation_id="commit-2", chunk_period_ms=PERIOD)
|
||||
assert empty.payload is None and empty.byte_count == 0
|
||||
empty.commit()
|
||||
assert buffer.flush(chunk_period_ms=PERIOD) is None
|
||||
|
||||
|
||||
def test_rolling_back_an_earlier_reservation_restores_later_ones_in_order() -> None:
|
||||
buffer = _buffer()
|
||||
first = buffer.prepare_append(
|
||||
_pcm_payload(np.full(FRAME, 1.0)), operation_id="op-1", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
second = buffer.prepare_append(
|
||||
_pcm_payload(np.full(FRAME, 2.0)), operation_id="op-2", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
assert first is not None and second is not None
|
||||
|
||||
first.rollback()
|
||||
|
||||
assert not first.active and not second.active
|
||||
assert buffer.pending_byte_count == 2 * FRAME * 4
|
||||
replay = buffer.prepare_append(
|
||||
_pcm_payload(np.zeros(0)), operation_id="op-3", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
assert replay is not None and _decode(replay.payload)[0] == 1.0
|
||||
|
||||
|
||||
def test_clear_drops_pending_audio_and_deactivates_reservations() -> None:
|
||||
buffer = _buffer()
|
||||
reservation = buffer.prepare_append(
|
||||
_pcm_payload(np.zeros(FRAME + 8)), operation_id="op-1", chunk_period_ms=PERIOD, allow_emit=True
|
||||
)
|
||||
assert reservation is not None and buffer.pending_byte_count == 32
|
||||
|
||||
buffer.clear()
|
||||
buffer.clear_force_listen()
|
||||
|
||||
assert not reservation.active
|
||||
assert buffer.pending_byte_count == 0
|
||||
reservation.rollback() # no-op after clear
|
||||
assert buffer.pending_byte_count == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("payload", "match"),
|
||||
[
|
||||
(_pcm_payload(np.zeros(8), sample_rate_hz=16000), "sample_rate_hz must be 24000"),
|
||||
(_pcm_payload(np.zeros(8), fmt="pcm16"), "format must be pcm_f32le"),
|
||||
(_pcm_payload(np.array([0.0, np.nan])), "finite"),
|
||||
],
|
||||
)
|
||||
def test_malformed_audio_is_rejected_and_nothing_is_buffered(payload: dict[str, object], match: str) -> None:
|
||||
buffer = _buffer()
|
||||
|
||||
with pytest.raises(ValueError, match=match):
|
||||
buffer.prepare_append(payload, operation_id="op", chunk_period_ms=PERIOD, allow_emit=True)
|
||||
|
||||
assert buffer.pending_byte_count == 0
|
||||
|
||||
|
||||
def test_chunk_period_and_duplicate_operations_are_refused() -> None:
|
||||
buffer = _buffer()
|
||||
|
||||
with pytest.raises(ValueError, match="chunk_period_ms must be 80"):
|
||||
buffer.prepare_append(_pcm_payload(np.zeros(8)), operation_id="op", chunk_period_ms=1000, allow_emit=True)
|
||||
buffer.prepare_append(_pcm_payload(np.zeros(FRAME)), operation_id="op", chunk_period_ms=PERIOD, allow_emit=True)
|
||||
with pytest.raises(ValueError, match="duplicate active operation_id"):
|
||||
buffer.prepare_append(_pcm_payload(np.zeros(8)), operation_id="op", chunk_period_ms=PERIOD, allow_emit=True)
|
||||
|
||||
|
||||
def test_constructor_validates_its_parameters() -> None:
|
||||
with pytest.raises(ValueError, match="positive"):
|
||||
FixedFramePcmAppendBuffer(sample_rate_hz=0, frame_samples=FRAME, chunk_period_ms=PERIOD)
|
||||
Reference in new issue
Block a user