Skip to content

fix(python): gate Twilio inbound silence so turns can end - #1028

Draft
ningyixi wants to merge 1 commit into
langwatch:mainfrom
ningyixi:fix/python-twilio-speech-gate
Draft

ningyixi wants to merge 1 commit into
langwatch:mainfrom
ningyixi:fix/python-twilio-speech-gate

Conversation

@ningyixi

Copy link
Copy Markdown

Why

Twilio sends media continuously, including silence. The Python adapter queues every batch, so the shared response drain never sees the arrival gap that ends a spoken turn. This ports the inbound speech gate already used by the JavaScript adapter in #990.

Closes #991.

What changed

  • Match the existing PCM16 RMS threshold of 800, 300 ms pre-roll and 400 ms hangover. Keep pre-roll bounded and replay it in order at a speech onset.
  • Add speech_gate=TwilioSpeechGateOptions(...) and speech_gate=False. Gate decoded inbound audio before queue delivery; terminal chunks always reach the consumer.
  • Clear held audio on session adoption, rearm and termination, retain connection-lifetime counters, and report gate enablement, dropped audio and onsets on disconnect.
  • Use audible µ-law samples in existing speech-delivery fixtures; 0x7F encodes silence. Document the Python controls and update the JS counterpart pointer.

Test plan

From python/:

  • Regression: test_media_loop_gates_quiet_batches_and_preserves_terminal fails on unchanged media handling (15 batches admitted instead of 8).
  • uv run --frozen --no-sync pytest tests/voice/test_twilio*.py tests/voice/test_voice_spans_twilio.py -m 'not integration' -q: 165 passed, 3 live tests deselected.
  • uv run --frozen --no-sync pytest tests/ -v --tb=short -m 'not integration': 1,588 passed, 10 skipped, 62 deselected. Two intentional reruns exercise test_arun_survives_pytest_reruns.
  • uv run --frozen --no-sync pyright .: zero errors or warnings.
  • Full pre-commit hooks, plus Black/isort on the new Python modules.

How I can prove I was successful

The runnable local transport demonstration is test_real_route_ends_turn_while_quiet_media_continues. It starts the real uvicorn route, sends µ-law speech followed by continuing silence over a real WebSocket, and asserts that adapter.call() returns while the stream is still connected and sending. Run it with uv run --frozen --no-sync pytest tests/voice/test_twilio_speech_gate.py -k real_route -q from python/.

No playable artifact: this is a library transport regression using synthetic PCM, not a recorded telephone conversation. Live PSTN/provider testing has not been performed.

Implementation and local validation by OpenAI Codex.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

voice: Python Twilio adapter needs the same inbound speech gate as JS (#990)

1 participant