The previous run showed session_started, then total silence from the
relay for the entire conversation — no events forwarded, no errors,
nothing. Two changes to disambiguate:
1. Drop the explicit audio.input.format / audio.output.format objects.
24 kHz PCM16 mono is the documented GA default for both directions
and the only thing OpenAI ever advertised; an unrecognised value
inside the format object is the most likely reason OpenAI silently
ignores incoming audio.
2. Forward 'upstream silent NxN250ms' debug envelopes every ~2 s when
ReceiveJsonAsync keeps returning null, and add a 'server_version'
field to hello_ack so the Pi journal proves which build is running.
GA Realtime API requires create_response:true and interrupt_response:true
inside turn_detection — without them the model never auto-generates a
reply after VAD detects speech_stopped, so the relay idles out at 30s
with no assistant audio. Add both.
Client side: when the /device WS drops (Coolify rolling deploy, etc.),
Session.run_forever reconnects but the StateMachine kept whatever state
it was in — usually LISTENING, which permanently disables the wakeword.
Add StateMachine.force_idle() and a Callbacks.on_disconnected() hook;
main.py wires it to force the state back to IDLE on every disconnect.
Also harden Session's fire-and-forget send_* helpers to swallow
ConnectionClosed instead of bubbling up as unhandled task exceptions.
Upstream returned 'beta_api_shape_disabled' on every wake. Changes:
- Drop OpenAI-Beta: realtime=v1 header from the upgrade.
- session.update payload nests audio under session.audio.{input,output}
and adds session.type='realtime'; audio.{input,output}.format is now
an object {type:audio/pcm, rate:24000} instead of 'pcm16' string.
- Default model bumped to gpt-realtime (the GA name); the relay also
promotes any stored 'gpt-4o-realtime-preview*' to gpt-realtime so
pre-Plan-3 DeviceConfig rows keep working without a manual DB edit.