humaticai-speech-server / tests /test_lm_output_processor.py
s2s-deploy
Speech-to-speech realtime demo for HF Spaces GPU (parakeet + qwen3, port 7860)
79ec2b5
Raw
History Blame Contribute Delete
6.1 kB
from queue import Queue
from threading import Event, Thread
from openai.types.realtime.realtime_response_create_params import RealtimeResponseCreateParams
from speech_to_speech.LLM.lm_output_processor import LMOutputProcessor
from speech_to_speech.pipeline.messages import EndOfResponse, LLMResponseChunk, TTSInput
from speech_to_speech.pipeline.speculative_turns import SpeculativeTurnTracker
def _processor(tracker: SpeculativeTurnTracker) -> LMOutputProcessor:
processor = LMOutputProcessor.__new__(LMOutputProcessor)
processor.setup(text_output_queue=Queue(), speculative_turns=tracker)
return processor
def test_stale_end_of_response_is_not_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 1)
processor = _processor(tracker)
outputs = list(processor.process(EndOfResponse(turn_id="turn_1", turn_revision=0)))
assert outputs == []
def test_latest_end_of_response_is_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 1)
processor = _processor(tracker)
outputs = list(processor.process(EndOfResponse(turn_id="turn_1", turn_revision=1)))
assert len(outputs) == 1
assert outputs[0].turn_id == "turn_1"
assert outputs[0].turn_revision == 1
def test_cancel_generation_is_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
processor = _processor(tracker)
outputs = list(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
cancel_generation=7,
)
)
)
assert len(outputs) == 1
assert outputs[0].cancel_generation == 7
def test_text_only_chunk_is_not_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
processor = _processor(tracker)
outputs = list(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
response=RealtimeResponseCreateParams(output_modalities=["text"]),
)
)
)
assert outputs == []
# The assistant text still reaches clients even when TTS is skipped.
event = processor.text_output_queue.get_nowait()
assert event.text == "hello"
def test_audio_chunk_is_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
processor = _processor(tracker)
outputs = list(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
response=RealtimeResponseCreateParams(output_modalities=["audio"]),
)
)
)
assert len(outputs) == 1
assert isinstance(outputs[0], TTSInput)
assert outputs[0].text == "hello"
def test_empty_modalities_is_forwarded_to_tts():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
processor = _processor(tracker)
outputs = list(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
response=RealtimeResponseCreateParams(output_modalities=[]),
)
)
)
assert len(outputs) == 1
assert isinstance(outputs[0], TTSInput)
def test_pending_reopen_holds_assistant_chunk_until_cancelled():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
candidate_revision = tracker.begin_reopen_candidate("turn_1", 0)
processor = _processor(tracker)
done = Event()
outputs = []
def run_processor():
outputs.extend(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
)
)
)
done.set()
thread = Thread(target=run_processor)
thread.start()
assert not done.wait(0.05)
tracker.cancel_reopen_candidate("turn_1", candidate_revision)
assert done.wait(1.0)
thread.join(timeout=1.0)
assert len(outputs) == 1
assert outputs[0].text == "hello"
event = processor.text_output_queue.get_nowait()
assert event.text == "hello"
def test_reopen_grace_holds_assistant_chunk_until_elapsed():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
tracker.start_reopen_grace("turn_1", 0, grace_s=0.08)
processor = _processor(tracker)
done = Event()
outputs = []
def run_processor():
outputs.extend(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
)
)
)
done.set()
thread = Thread(target=run_processor)
thread.start()
assert not done.wait(0.02)
assert done.wait(1.0)
thread.join(timeout=1.0)
assert len(outputs) == 1
assert outputs[0].text == "hello"
event = processor.text_output_queue.get_nowait()
assert event.text == "hello"
def test_confirmed_reopen_drops_held_assistant_chunk():
tracker = SpeculativeTurnTracker()
tracker.observe("turn_1", 0)
candidate_revision = tracker.begin_reopen_candidate("turn_1", 0)
processor = _processor(tracker)
done = Event()
outputs = []
def run_processor():
outputs.extend(
processor.process(
LLMResponseChunk(
text="hello",
turn_id="turn_1",
turn_revision=0,
)
)
)
done.set()
thread = Thread(target=run_processor)
thread.start()
assert not done.wait(0.05)
assert tracker.confirm_reopen_candidate("turn_1", 0, candidate_revision)
assert done.wait(1.0)
thread.join(timeout=1.0)
assert outputs == []
assert processor.text_output_queue.empty()