atlas-iac/testing/tests/test_hermes_voice_full_duplex.py

137 lines
5.5 KiB
Python

"""Contracts for low-latency, interruptible Hermes hands-free playback."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
VOICE = ROOT / "dockerfiles/hermes-webui-atlas-voice.js"
WORKLET = ROOT / "dockerfiles/hermes-webui-atlas-voice-worklet.js"
def test_barge_in_keeps_aec_capture_and_requires_sustained_speech():
source = VOICE.read_text(encoding="utf-8")
assert "echoCancellation:true" in source
assert "noiseSuppression:true" in source
assert "autoGainControl:true" in source
assert "const BARGE_DUCK_FRAMES=2" in source
assert "const BARGE_TRIGGER_FRAMES=4" in source
assert "let speechArmAt=Date.now()+100" in source
assert "speechArmAt=now+(state==='thinking'?150:450)" in source
assert "speechArmAt=now+100" in source
assert "playbackActive&&!playbackWasActive" in source
assert "const threshold=Math.max(0.05,(noiseFloor*3)+0.008)" in source
assert "setPlaybackDucked(true)" in source
assert "setPlaybackDucked(false)" in source
assert "settings.echoCancellation!==false" in source
assert "const automaticBargeAllowed=!playbackActive||monitor.aecUsable" in source
def test_barge_in_cancels_owned_turn_and_reuses_pcm_lookback():
source = VOICE.read_text(encoding="utf-8")
assert "api/chat/cancel?stream_id=" in source
assert "controller.abort();},1800" in source
assert "const deadline=Date.now()+10000" in source
assert "stopResponseObserver();" in source
assert "cancelSpeechTurn();" in source
assert "stopPlayback();" in source
assert "bargeCancelPromise=cancelActiveModelTurn()" in source
assert "lookback:monitor.lookback" in source
assert "heardSpeech:true" in source
assert "reusedCapture.lookback.forEach" in source
assert "const bytes=new Uint8Array(16)" in source
assert "window.crypto.getRandomValues(bytes)" in source
assert (
"captureTurnId=(voiceTabNonce?voiceTabNonce+'-':'')+String(token)+'-'+String(++turnSequence)"
in source
)
def test_streamed_chunks_share_one_audio_timeline_until_final_drain():
source = VOICE.read_text(encoding="utf-8")
worklet = WORKLET.read_text(encoding="utf-8")
assert "if(session.node)" in source
assert "return session;" in source
assert "session.node.port.postMessage({type:'push'" in source
assert "session.node.port.postMessage({type:'end'})" in source
assert "await drainPcmPlayback(session,turn.token)" in source
assert "node.connect(gain)" in source
assert "gain.connect(context.destination)" in source
assert "this.ended&&!this.queue.length" in worklet
assert "this.port.postMessage({type:'drained'})" in worklet
def test_barge_cancel_settles_before_new_turn_baseline_and_observer():
source = VOICE.read_text(encoding="utf-8")
region = source.split("async function sendTranscript", 1)[1].split(
"function audioExtension", 1
)[0]
assert region.index("await settleBargeCancellation(token)") < region.index(
"rememberAssistantBaseline()"
)
assert region.index("rememberAssistantBaseline()") < region.index("window.send()")
assert region.index("window.send()") < region.index("startResponseObserver(token)")
assert (
"suppressAutoRead||bargeCancelPromise||state==='listening'||state==='transcribing'"
in source
)
def test_barge_handoff_never_uses_a_truncated_container_fallback():
source = VOICE.read_text(encoding="utf-8")
assert "monitor.handoff=true" in source
assert "if(!monitor.handoff) trimBargeLookback(monitor)" in source
assert "handoffMonitor:monitor" in source
assert "reusedCapture.lookback.forEach" in source
assert "requireStreamingLookback:true" in source
assert "allowContainerFallback===false" in source
assert "Please repeat your interruption" in source
def test_rolling_partials_are_feedback_only_and_wav_fallback_is_owned():
source = VOICE.read_text(encoding="utf-8")
partial = source.split("payload.type==='partial'", 1)[1].split(
"payload.type==='final'", 1
)[0]
fallback = source.split("async function fetchSpeech", 1)[1].split(
"function cuePoolOffset", 1
)[0]
assert "payload.stable_transcript" in partial
assert "const provisional=String(payload.transcript||'').trim()" in partial
assert "const visible=stable||provisional" in partial
assert (
"label.textContent='Listening · '+preview+(stable?'':' · provisional')"
in partial
)
assert "if(stable){" in partial
assert "scheduleVoicePreflight(turnId,revision,stable)" in partial
assert "composer.value" not in partial
assert "window.send" not in partial
assert "signal:controller.signal" in fallback
assert "token!==generation" in fallback
assert "session.turnId!==turnId" in fallback
assert "closePcmBeforeBlob" in source
def test_stt_client_queue_and_canonical_archive_are_strictly_bounded():
source = VOICE.read_text(encoding="utf-8")
assert "const STT_MAX_QUEUED_BYTES=1048576" in source
assert "const STT_MAX_ARCHIVE_BYTES=2880000" in source
assert "queuedBytes+bytes.byteLength>STT_MAX_QUEUED_BYTES" in source
assert "Streaming transcription backpressure limit exceeded" in source
assert "if(flushTimer||settled||!queue.length" in source
assert (
"flushTimer=window.setTimeout(function(){flushTimer=null;flush();},20)"
in source
)
assert "function clearQueue(){queue=[];queuedBytes=0;clearFlushTimer();}" in source
assert "takeFallbackBlob:function()" in source
assert "pcm16WavBlob(archive,16000)" in source
assert "if(pcmFallback)" in source