"""Regression cases for silent, stalled, and unterminated model streams.""" import asyncio import base64 from contextlib import aclosing import json import numpy as np import pytest from test_gradio_ui import anyio_backend, studio, browser_request, body, EngineClient import server from accounts import Identity from gradio_ui import events def pcm(seconds, amplitude=0, rate=44100): return np.full(round(seconds * rate), amplitude, dtype=" server.token_budget(short, choice) for text in ['你好世界。' * 30, 'ภาษาไทย' * 40, 'The longer speech continues. ' * 20]: assert server.token_budget(short.model_copy(update={'text': text}), choice) == server.MAX_NEW_TOKENS directed = short.model_copy(update={'text': 'Direction: calm.\nLine: First sentence.\nSecond sentence.'}) assert server.spoken_text(directed.text) == 'First sentence. Second sentence.' assert server.token_budget(directed, choice) == server.token_budget(short.model_copy(update={'text': 'First sentence. Second sentence.'}), choice) @pytest.mark.anyio async def test_stop_during_idle_read_cancels_pending_read_and_closes_response(studio, monkeypatch): response = Response([pcm(3.5, 3000)], finish=None, hold=True) monkeypatch.setattr(server, "http", Client(response)) stream_response = await server.generate(body(studio), Identity(keys=("test",), name="test", signed_in=False)) stream = events(stream_response) async with aclosing(stream): assert (await anext(stream))['type'] == 'start' received = bytearray() while len(received) < len(pcm(3.5, 3000)): event = await anext(stream) assert event['type'] == 'audio' received.extend(base64.b64decode(event['pcm'])) waiting = asyncio.create_task(anext(stream)) await asyncio.sleep(.01) waiting.cancel() with pytest.raises(asyncio.CancelledError): await waiting assert_released(response) @pytest.mark.anyio async def test_wall_clock_limit_applies_even_while_audio_keeps_arriving(studio, monkeypatch): class EndlessResponse(Response): async def aiter_lines(self): try: while True: yield delta(pcm(.05, 3000)) await asyncio.sleep(.005) finally: self.cancelled = True response = EndlessResponse([]) monkeypatch.setattr(server, "http", Client(response)) monkeypatch.setattr(server, "TAKE_TIMEOUT_S", .04) result = await asyncio.wait_for(generate(studio), 1) assert result[-1]['finish_reason'] == 'timeout' and result[-1]['warning'] assert_released(response) @pytest.mark.anyio @pytest.mark.parametrize('fragment_s', [1.3, 2.0, 2.4]) async def test_early_stalled_fragment_is_retried_before_playback(studio, monkeypatch, fragment_s): failed = Response([pcm(fragment_s, 3000), pcm(5)], finish=None, hold=True) good = Response([pcm(3.5, 1000)]) client = Client(failed, good) monkeypatch.setattr(server, "http", client) result = await generate(studio) assert [e['type'] for e in result if e['type'] != 'audio'] == ['retrying', 'start', 'done'] output = b''.join(base64.b64decode(e['pcm']) for e in result if e['type'] == 'audio') assert output == pcm(3.5, 1000) assert result[-1]['retry_count'] == 1 assert_released(failed, good) @pytest.mark.anyio async def test_opening_plays_at_first_64_frame_window_without_waiting_for_next_chunk(studio, monkeypatch): response = Response([pcm(frames * 2048 / 44100, 3000) for frames in [10, 10, 12, 32]], finish=None, hold=True) monkeypatch.setattr(server, 'http', Client(response)) backend = await server.generate(body(studio), Identity(keys=("test",), name="test", signed_in=False)) async with aclosing(events(backend)) as stream: assert (await asyncio.wait_for(anext(stream), .5))['type'] == 'start' assert (await anext(stream))['type'] == 'audio' assert_released(response) @pytest.mark.anyio async def test_two_failed_openings_are_not_played(studio, monkeypatch): first = Response([pcm(1.3, 3000), pcm(5)]) second = Response([pcm(2, 3000)], finish='length') monkeypatch.setattr(server, 'http', Client(first, second)) result = await generate(studio) assert [e['type'] for e in result] == ['retrying', 'error'] assert not server.store.takes assert_released(first, second)