Summary
RealtimeModel.update_options() mutates the model-level _opts before forwarding to active sessions, causing RealtimeSession.update_options() to see no diff and skip sending session.update to OpenAI.
Environment
livekit-agents: 1.5.x
livekit-plugins-openai: 1.5.x
- Model:
gpt-realtime-1.5
Steps to Reproduce
- Start an OpenAI Realtime session via LiveKit
- Call
agentSession._llm.update_options(speed=1.5) (or any other option like voice, turn_detection, etc.)
- Observe that no
session.update event is sent to OpenAI
- The change has no audible/observable effect
Root Cause
In realtime_model.py:
RealtimeModel.update_options() (around line 649):
# Mutates _opts FIRST
if is_given(speed):
self._opts.speed = speed
# THEN forwards to sessions
for sess in self._sessions:
sess.update_options(speed=speed, ...)
RealtimeSession.update_options() (around line 1207):
if is_given(speed):
if self._realtime_model._opts.speed != speed: # Always False! Already mutated above
audio_output.speed = speed
has_audio_config = True
self._realtime_model._opts.speed = speed
The session compares against the model's _opts, which was already mutated by the model-level call. The diff always sees "no change" and skips sending the update.
Affected Options
All options using this pattern:
speed
voice
turn_detection
input_audio_transcription
input_audio_noise_reduction
tool_choice
tracing
max_response_output_tokens
Workaround
Call RealtimeSession.update_options() directly on active sessions:
for sess in llm._sessions:
sess.update_options(speed=1.5)
This works because the session-level call hasn't had its comparison poisoned by prior mutation.
Suggested Fix
In RealtimeModel.update_options(), forward to sessions before mutating _opts:
def update_options(self, *, speed=NOT_GIVEN, ...):
# Forward to sessions FIRST (while _opts still has old values)
for sess in self._sessions:
sess.update_options(speed=speed, ...)
# THEN mutate model opts
if is_given(speed):
self._opts.speed = speed
...
Additional Context
- The workaround requires accessing
llm._sessions, which is a private attribute
- There's no public API to enumerate active sessions from
AgentSession
- Initial session setup works correctly (only live updates are broken)
Related Issues
Summary
RealtimeModel.update_options()mutates the model-level_optsbefore forwarding to active sessions, causingRealtimeSession.update_options()to see no diff and skip sendingsession.updateto OpenAI.Environment
livekit-agents: 1.5.xlivekit-plugins-openai: 1.5.xgpt-realtime-1.5Steps to Reproduce
agentSession._llm.update_options(speed=1.5)(or any other option likevoice,turn_detection, etc.)session.updateevent is sent to OpenAIRoot Cause
In
realtime_model.py:RealtimeModel.update_options()(around line 649):RealtimeSession.update_options()(around line 1207):The session compares against the model's
_opts, which was already mutated by the model-level call. The diff always sees "no change" and skips sending the update.Affected Options
All options using this pattern:
speedvoiceturn_detectioninput_audio_transcriptioninput_audio_noise_reductiontool_choicetracingmax_response_output_tokensWorkaround
Call
RealtimeSession.update_options()directly on active sessions:This works because the session-level call hasn't had its comparison poisoned by prior mutation.
Suggested Fix
In
RealtimeModel.update_options(), forward to sessions before mutating_opts:Additional Context
llm._sessions, which is a private attributeAgentSessionRelated Issues
update_options/session.updateissues withnoise_reduction)