feat(server): single-session lifetime and relay-only ICE for serverle… - #107
feat(server): single-session lifetime and relay-only ICE for serverle…#107berges99 wants to merge 5 commits into
Conversation
…ss voice boxes TIMBAL_VOICE_SINGLE_SESSION=1 makes the server serve exactly one voice session (WS or RTC) and exit 0 — after draining the platform recording upload, since the process is the only thing holding that data. Boxes nobody connects to exit after TIMBAL_VOICE_IDLE_EXIT_SECS (default 60, boot → media established); a second session gets 409 (RTC) / 1008 (WS). Needed on serverless workforce deployments where the platform can't see the call (media flows browser ↔ TURN ↔ box) and reaps on process exit. TIMBAL_VOICE_RTC_FORCE_RELAY=1 restricts ICE to the TURN relay: skip STUN, allocate on TIMBAL_TURN_URL, and strip host/srflx candidates from the answer SDP (aiortc has no iceTransportPolicy) — private-subnet boxes advertise only reachable candidates. Degrades loudly to all candidates when TURN is unconfigured or the allocation yields no relay candidate.
PR SummaryMedium Risk Overview Single-session lifetime ( Relay-only ICE ( Supporting fix: Smart Turn and Namo ONNX loaders switch from Extensive tests and README updates document the new env vars and HTTP status codes. Reviewed by Cursor Bugbot for commit 333f453. Bugbot is set up for automated code reviews on this repo. Configure here. |
… RTC ICE Guarantee finish() after WS claim so handshake/session-build failures still exit; route idle exit through finish() so in-flight recording uploads drain; stop treating WebRTC disconnected as terminal (transient ICE blips).
A claim() that fails before the driver task starts (e.g. build_voice_session) left the slot claimed with no finish()/release, so retries got 409 until idle exit. Mirror the 400 paths: release on setup exceptions and re-raise the 500.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9a14b46. Configure here.
_idle_exit cleared the timer task and always finish()'d without checking _connected, so a WebRTC connect at the idle deadline could still exit 0. Yield once for queued ICE handlers, re-check through the upload drain, and only then commit the exit.
asyncio.Lock on the process-wide EOU singletons breaks under Starlette TestClient on Windows/py3.13: lifespan warmup binds the lock (often while stuck on HF 429), then the WS handler runs on another loop. Load is already executor work, so a thread lock is the right primitive.

…ss voice boxes
TIMBAL_VOICE_SINGLE_SESSION=1 makes the server serve exactly one voice session (WS or RTC) and exit 0 — after draining the platform recording upload, since the process is the only thing holding that data. Boxes nobody connects to exit after TIMBAL_VOICE_IDLE_EXIT_SECS (default 60, boot → media established); a second session gets 409 (RTC) / 1008 (WS). Needed on serverless workforce deployments where the platform can't see the call (media flows browser ↔ TURN ↔ box) and reaps on process exit.
TIMBAL_VOICE_RTC_FORCE_RELAY=1 restricts ICE to the TURN relay: skip STUN, allocate on TIMBAL_TURN_URL, and strip host/srflx candidates from the answer SDP (aiortc has no iceTransportPolicy) — private-subnet boxes advertise only reachable candidates. Degrades loudly to all candidates when TURN is unconfigured or the allocation yields no relay candidate.