feat(voice): auto-disable realtime server-side turn detection#6495
feat(voice): auto-disable realtime server-side turn detection#6495longcw wants to merge 6 commits into
Conversation
9c2cc63 to
9caf4ba
Compare
a557e26 to
2926544
Compare
f9c7088 to
b525a7a
Compare
48359d0 to
9c988f3
Compare
82a241c to
2b70881
Compare
9c988f3 to
eca4e01
Compare
| # model to auto-generate a tool reply (auto_tool_reply_generation=True). | ||
| self._pending_auto_tool_reply_fut: asyncio.Future[None] | None = None | ||
|
|
||
| def _resolve_rt_turn_detection_enabled(self) -> bool: |
There was a problem hiding this comment.
should we handle update_options with turn_detection for realtime too?
There was a problem hiding this comment.
toggle turn detection at runtime will need a restart of rt_session if the server-side turn detection is disabled. I added a warning for now.
eca4e01 to
9e9feca
Compare
Disable a realtime model's server-side turn detection for the session when the user configured client-side turn-taking (a TurnDetector, "vad", "manual", or an explicit interruption mode) and the model supports it, so client-side EoT/barge-in works without also passing turn_detection=None to the RealtimeModel. - add RealtimeCapabilities.can_disable_turn_detection; OpenAI sets it from the ctor turn_detection given-ness - add session(turn_detection_disabled=...), applied on the initial session.update via the per-session opts copy so the model is never mutated (stays reusable) - AgentActivity resolves the effective state once (_rt_turn_detection_enabled) and routes every server-side turn-detection check through it Other realtime plugins accept-and-ignore the flag (can_disable_turn_detection stays False): xAI (client turn-taking not stable in testing) and Gemini/others (no commit_audio/clear_audio; turns are activity-signal based).
RealtimeModel.update_options force-synced turn_detection=self._opts.turn_detection to every session unconditionally, so changing an unrelated field (e.g. voice) re-enabled server VAD on a session that opted out of server-side turn detection. Only propagate turn_detection when the caller set it; an explicit value still propagates so callers can re-enable it.
…tection On an agent handoff the realtime session is reused as-is. If the new agent resolves server-side turn detection differently (it can override turn_detection, interruption, or vad), the reused session kept the previous agent's mode, causing duplicate or missing replies. Refuse reuse on a mismatch so a fresh session is created with the correct setting.
_resolve_interruption_detection and the on_end_of_turn backchannel-drop guard still read the model's static capabilities.turn_detection instead of the session-resolved _rt_turn_detection_enabled. When a realtime model with built-in turn detection has it auto-disabled for the session (client-side turn-taking configured), can_gatekeep stayed False so the adaptive detector was never created, and the backchannel-drop path was skipped — silently disabling the client-side barge-in this stack is meant to enable.
with_azure applied the Azure default turn detection before constructing the model, so can_disable_turn_detection was always False for Azure even when the caller passed nothing. Capture the caller's intent before defaulting and set the capability accordingly, so the framework can still auto-disable server-side turn detection.
A realtime model's server-side turn detection is resolved once at session start and isn't re-synced at runtime yet. Warn when an explicit update_options(turn_detection=...) would flip that resolved state, so the mismatch isn't silent. Reverting to None (automatic re-selection) is left unwarned. Also move the auto-disable info log to the resolver's call site so the resolver stays a pure query, and mark the session turn detection explicit when set via update_options.
9e9feca to
24154ca
Compare
| self._rt_session.update_options(tool_choice=self._tool_choice) | ||
|
|
||
| if utils.is_given(turn_detection): | ||
| # a realtime model's server-side turn detection is resolved once at session start; |
There was a problem hiding this comment.
we should have a similar warning for RealtimeModel.update_options(turn_detection=...) too
|
Another thing I noticed in testing: if the interruption is detected by the barge-in model, it leads to the false interruption path, and for realtime model, transcript isn't always available immediately for that interruption to take effect. Should we consider disable false interruption when no STT is configured with realtime model? |
chenghao-mou
left a comment
There was a problem hiding this comment.
LGTM overall. Tested locally with OAI Realtime and VAD or turn detector. Two nits and two issues from previous comments.
| """Whether the model can produce audio output directly""" | ||
| manual_function_calls: bool | ||
| """Whether function call items already in the chat context can be resumed""" | ||
| can_disable_turn_detection: bool = False |
There was a problem hiding this comment.
nit: maybe optional_turn_detection so it matches other adj_noun patterns
|
|
||
| @abstractmethod | ||
| def session(self) -> RealtimeSession: ... | ||
| def session(self, *, turn_detection_disabled: bool = False) -> RealtimeSession: |
There was a problem hiding this comment.
nit: should we rename it enable_turn_detection: bool = False instead?
Auto-disables a realtime model's server-side turn detection for the session when the user configured client-side turn-taking (a TurnDetector,
vad,manual, or an explicit interruption mode) and the model supports it, so client-side EoT/barge-in works without also passingturn_detection=Noneto the RealtimeModel.Adds
RealtimeCapabilities.can_disable_turn_detection(OpenAI sets it from the ctorturn_detectiongiven-ness) and asession(turn_detection_disabled=...)seam applied on the initialsession.updatevia the per-session opts copy, so the model is never mutated and stays reusable.AgentActivityresolves the effective state once (_rt_turn_detection_enabled) and routes every server-side turn-detection check through it.Other realtime plugins accept-and-ignore the flag (
can_disable_turn_detectionstays False): xAI (client turn-taking not stable in testing) and Gemini/others (nocommit_audio/clear_audio; turns are activity-signal based).Stacked on
longc/realtime-barge-in; this PR targets that branch, not main.