diff --git a/src/google/adk/evaluation/_audio_utils.py b/src/google/adk/evaluation/_audio_utils.py index ecc92d9517..2d6e37ac89 100644 --- a/src/google/adk/evaluation/_audio_utils.py +++ b/src/google/adk/evaluation/_audio_utils.py @@ -31,7 +31,7 @@ LIVE_OUTPUT_RATE_HZ = 24000 LIVE_INPUT_MIME_TYPE = "audio/pcm;rate=16000" -_RATE_RE = re.compile(r"rate=(\d+)") +_RATE_RE = re.compile(r"(?:^|;)\s*rate\s*=\s*(\d+)\s*(?=;|$)", re.IGNORECASE) def parse_sample_rate(mime_type: str | None, default: int) -> int: @@ -49,6 +49,8 @@ def resample_pcm16(pcm: bytes, src_rate: int, dst_rate: int) -> bytes: interpolate, avoiding a heavy DSP dependency for speech relayed to a transcribing model. """ + if src_rate <= 0 or dst_rate <= 0: + raise ValueError("Sample rates must be positive") if not pcm or src_rate == dst_rate: return pcm diff --git a/tests/unittests/evaluation/test_audio_utils.py b/tests/unittests/evaluation/test_audio_utils.py index 0d56dd5e63..a4a7874d52 100644 --- a/tests/unittests/evaluation/test_audio_utils.py +++ b/tests/unittests/evaluation/test_audio_utils.py @@ -24,6 +24,7 @@ import logging from google.adk.evaluation import _audio_utils as audio_utils +import pytest def _pcm(samples: list[int]) -> bytes: @@ -58,6 +59,18 @@ def test_parse_sample_rate_none_returns_default(): assert audio_utils.parse_sample_rate(None, 16000) == 16000 +def test_parse_sample_rate_ignores_rate_substrings(): + """A parameter containing rate as a substring is not a sample rate.""" + assert ( + audio_utils.parse_sample_rate("audio/pcm;bitrate=128000", 24000) == 24000 + ) + + +def test_parse_sample_rate_is_case_insensitive(): + """The rate parameter name is parsed case-insensitively.""" + assert audio_utils.parse_sample_rate("audio/pcm;RATE=16000", 24000) == 16000 + + # --------------------------------------------------------------------------- # resample_pcm16 # --------------------------------------------------------------------------- @@ -75,6 +88,18 @@ def test_resample_empty_input_returns_empty(): assert audio_utils.resample_pcm16(b"", 24000, 16000) == b"" +def test_resample_zero_source_rate_raises(): + """A zero source sample rate is rejected before resampling.""" + with pytest.raises(ValueError, match="Sample rates must be positive"): + audio_utils.resample_pcm16(_pcm([1, 2]), 0, 16000) + + +def test_resample_zero_target_rate_raises(): + """A zero target sample rate is rejected before resampling.""" + with pytest.raises(ValueError, match="Sample rates must be positive"): + audio_utils.resample_pcm16(_pcm([1, 2]), 24000, 0) + + def test_resample_single_sample_returns_input_unchanged(): """Audio too short to interpolate is returned unchanged.""" pcm = _pcm([42])