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,2 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
@@ -0,0 +1,63 @@
|
||||
# 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.audio.pcm import (
|
||||
PCM_F32LE_BYTES_PER_SAMPLE,
|
||||
decode_pcm_f32le_base64,
|
||||
pcm_f32le_sample_count,
|
||||
pcm_f32le_samples,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
def _b64(samples: np.ndarray) -> str:
|
||||
return base64.b64encode(np.ascontiguousarray(samples, dtype="<f4").tobytes()).decode("ascii")
|
||||
|
||||
|
||||
def test_decode_returns_the_raw_bytes_of_whole_finite_samples() -> None:
|
||||
samples = np.arange(4, dtype=np.float32)
|
||||
|
||||
raw = decode_pcm_f32le_base64(_b64(samples))
|
||||
|
||||
assert raw == samples.tobytes()
|
||||
assert pcm_f32le_sample_count(raw) == 4
|
||||
assert PCM_F32LE_BYTES_PER_SAMPLE == 4
|
||||
|
||||
|
||||
def test_decode_accepts_an_empty_chunk() -> None:
|
||||
assert decode_pcm_f32le_base64("") == b""
|
||||
assert pcm_f32le_sample_count(b"") == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("encoded", "match"),
|
||||
[
|
||||
(None, "must be base64 pcm_f32le"),
|
||||
(b"bytes", "must be base64 pcm_f32le"),
|
||||
("not*base64", "not valid base64"),
|
||||
(base64.b64encode(b"\x00\x00\x00").decode("ascii"), "divisible by four"),
|
||||
(_b64(np.array([0.0, np.nan], dtype=np.float32)), "must be finite"),
|
||||
(_b64(np.array([np.inf], dtype=np.float32)), "must be finite"),
|
||||
],
|
||||
)
|
||||
def test_decode_rejects_malformed_audio_naming_the_model(encoded: object, match: str) -> None:
|
||||
with pytest.raises(ValueError, match=match) as excinfo:
|
||||
decode_pcm_f32le_base64(encoded, model="TestModel")
|
||||
|
||||
assert str(excinfo.value).startswith("TestModel ")
|
||||
|
||||
|
||||
def test_samples_are_a_writable_contiguous_float32_copy() -> None:
|
||||
raw = np.arange(3, dtype="<f4").tobytes()
|
||||
|
||||
samples = pcm_f32le_samples(raw)
|
||||
|
||||
assert samples.dtype == np.float32
|
||||
assert samples.flags.writeable and samples.flags.c_contiguous
|
||||
assert samples.tolist() == [0.0, 1.0, 2.0]
|
||||
@@ -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)
|
||||
@@ -0,0 +1,139 @@
|
||||
# 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
|
||||
import torch
|
||||
|
||||
from vllm_omni.model_executor.common.request_outputs import (
|
||||
audio_sample_count,
|
||||
audio_value,
|
||||
coerce_int,
|
||||
coerce_int_list,
|
||||
first_completion,
|
||||
multimodal_output,
|
||||
sample_rate_hz,
|
||||
slice_audio_delta,
|
||||
text_delta,
|
||||
text_value,
|
||||
unwrap_request_output,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
def test_unwrap_prefers_the_wrapped_request_output_and_its_first_completion() -> None:
|
||||
completion = SimpleNamespace(text="hi", multimodal_output={})
|
||||
inner = SimpleNamespace(request_id="req", outputs=[completion, SimpleNamespace(text="second")])
|
||||
wrapper = SimpleNamespace(request_output=inner)
|
||||
|
||||
output, first = unwrap_request_output(wrapper)
|
||||
|
||||
assert output is inner
|
||||
assert first is completion
|
||||
assert first_completion(SimpleNamespace(outputs=[])) is None
|
||||
assert first_completion(SimpleNamespace()) is None
|
||||
|
||||
|
||||
def test_multimodal_output_takes_the_first_non_empty_mapping_as_a_copy() -> None:
|
||||
completion = SimpleNamespace(multimodal_output={"audio": [1.0]})
|
||||
output = SimpleNamespace(multimodal_output={})
|
||||
|
||||
result = multimodal_output(output, completion)
|
||||
|
||||
assert result == {"audio": [1.0]}
|
||||
assert result is not completion.multimodal_output
|
||||
assert multimodal_output(SimpleNamespace(multimodal_output=None), None) == {}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
(4, 4),
|
||||
("11", 11),
|
||||
(2.9, 2),
|
||||
(True, 1),
|
||||
(None, None),
|
||||
("x", None),
|
||||
(object(), None),
|
||||
([1], None),
|
||||
(torch.tensor([7]), 7),
|
||||
(torch.tensor(9), 9),
|
||||
(torch.tensor([], dtype=torch.long), None),
|
||||
(np.array([5], dtype=np.int32), 5),
|
||||
(np.array([], dtype=np.int32), None),
|
||||
],
|
||||
)
|
||||
def test_coerce_int_handles_scalars_strings_tensors_and_arrays(value: object, expected: int | None) -> None:
|
||||
assert coerce_int(value) == expected
|
||||
|
||||
|
||||
def test_coerce_int_list_flattens_tensors_and_drops_non_integers() -> None:
|
||||
assert coerce_int_list(torch.tensor([[1, 2], [3, 4]])) == [1, 2, 3, 4]
|
||||
assert coerce_int_list(np.array([5, 6])) == [5, 6]
|
||||
assert coerce_int_list([1, "2", "x", None, torch.tensor(3)]) == [1, 2, 3]
|
||||
assert coerce_int_list(None) == []
|
||||
assert coerce_int_list("12") == []
|
||||
|
||||
|
||||
def test_audio_value_reads_the_known_keys_and_unwraps_a_single_item_list() -> None:
|
||||
assert audio_value({"model_outputs": [np.zeros(2)]}).shape == (2,)
|
||||
assert audio_value({"latent": "x"}) == "x"
|
||||
assert audio_value({"audio": [1, 2]}) == [1, 2]
|
||||
assert audio_value({}) is None
|
||||
|
||||
|
||||
def test_text_value_prefers_multimodal_text_then_llm_text_then_the_completion() -> None:
|
||||
completion = SimpleNamespace(text="from completion")
|
||||
assert text_value({"text": "mm"}, completion) == "mm"
|
||||
assert text_value({"llm_output_text": "llm"}, completion) == "llm"
|
||||
assert text_value({}, completion) == "from completion"
|
||||
assert text_value({"text": ""}, None) == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("text", "previous", "expected"),
|
||||
[("", "he", ""), ("he", "he", ""), ("hello", "he", "llo"), ("new", "old", "new"), ("abc", "", "abc")],
|
||||
)
|
||||
def test_text_delta(text: str, previous: str, expected: str) -> None:
|
||||
assert text_delta(text, previous) == expected
|
||||
|
||||
|
||||
def test_slice_audio_delta_returns_only_new_samples_for_cumulative_audio() -> None:
|
||||
cumulative = np.arange(6, dtype=np.float32)
|
||||
|
||||
assert slice_audio_delta(cumulative, 4).tolist() == [4.0, 5.0]
|
||||
assert slice_audio_delta(cumulative, 6) is None
|
||||
assert slice_audio_delta(cumulative, 0).tolist() == cumulative.tolist()
|
||||
# A shorter waveform than the cursor means the stream restarted: send it whole.
|
||||
assert slice_audio_delta(np.arange(3, dtype=np.float32), 4).tolist() == [0.0, 1.0, 2.0]
|
||||
assert slice_audio_delta(None, 2) is None
|
||||
assert slice_audio_delta(np.zeros(0, dtype=np.float32), 0) is None
|
||||
|
||||
|
||||
def test_slice_audio_delta_keeps_tensors_as_contiguous_tensors() -> None:
|
||||
delta = slice_audio_delta(torch.arange(6, dtype=torch.float32).reshape(2, 3), 4)
|
||||
|
||||
assert isinstance(delta, torch.Tensor)
|
||||
assert delta.is_contiguous()
|
||||
assert delta.tolist() == [4.0, 5.0]
|
||||
assert audio_sample_count(torch.zeros(2, 3)) == 6
|
||||
assert audio_sample_count("not audio") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("multimodal", "expected"),
|
||||
[
|
||||
({"sr": 16000}, 16000),
|
||||
({"sample_rate_hz": 22050}, 22050),
|
||||
({"sr": [48000]}, 48000),
|
||||
({"sr": np.array([8000])}, 8000),
|
||||
({"sr": "bad"}, 24000),
|
||||
({"sr": 0}, 24000),
|
||||
({}, 24000),
|
||||
],
|
||||
)
|
||||
def test_sample_rate_hz_falls_back_to_the_default(multimodal: dict, expected: int) -> None:
|
||||
assert sample_rate_hz(multimodal, default=24000) == expected
|
||||
Reference in new issue
Block a user