fix: support GA transcription-type sessions in POST /v1/realtime/client_secrets#5092
Conversation
…crets POST /v1/realtime/client_secrets mints both full realtime sessions (session.model set) and transcription-only sessions (session.type=="transcription", RealtimeTranscriptionSessionCreateRequestGA) through the same endpoint. The transcription-session schema has no top-level session.model at all - the model lives only at session.audio.input.transcription.model - so every such request 400'd with "session.model or model is required", confirmed live against real OpenAI. - ExtractRealtimeClientSecretModel now falls back to session.audio.input.transcription.model when session.model is absent. - normalizeRealtimeClientSecretsRequest detects a transcription-shaped session (explicit type, or inferred from the audio.input.transcription shape with no model set) and writes the resolved model into the nested slot instead of adding a bogus top-level session.model that isn't part of this schema, and sets type="transcription" instead of defaulting to "realtime". - The handler's alias-resolution rewrite (fires when a virtual key alias changes the routing model after key selection) now also targets the nested slot for transcription-shaped sessions - it previously only rewrote session.model, so an alias change would silently not reach transcription sessions.
…review codex --yolo review of the previous commit found 4 real bugs, all verified end-to-end and against real OpenAI: - Critical: a full realtime session using the legacy top-level "model" field, with live input-audio transcription enabled as a completely standard sibling feature, had its model extraction hijacked by the nested transcription model and got silently reclassified as transcription-only - losing the real conversational model entirely. Root cause: the GA-nested extraction fallback ran before the legacy root-model fallback. - The transcription-type write branch never deleted a pre-existing stray session.model, leaking an un-stripped, schema-invalid field upstream. - The structural fallback classifier checked key presence instead of non-null, so an explicit audio.input.transcription: null (used to disable transcription on a full session) was misread as enabling it. - The handler's alias-resolution pass and the provider's normalization pass used two different, independently-written classification heuristics that could disagree on the same body. Fixes all four by consolidating classification into a single exported function, schemas.IsGATranscriptionSessionBody, used by extraction and both normalization layers so they cannot diverge. Priority: explicit session.type is authoritative when present (it's the schema's actual discriminator); only when absent do session.model / legacy root model take precedence over the audio.input.transcription structural fallback - a full session must never be reclassified just because it also enables transcription as a sibling feature. Added regression tests for all 4 findings at each layer (schema extraction, provider normalization, handler end-to-end), and re-verified live against real OpenAI that both the GA transcription path and the exact regression scenario now behave correctly.
📝 WalkthroughWalkthroughGA transcription-only realtime sessions now extract and rewrite models from ChangesGA transcription session handling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant HTTPHandler
participant RealtimeSchemas
participant OpenAIProvider
Client->>HTTPHandler: Submit realtime client-secret request
HTTPHandler->>RealtimeSchemas: Classify session and extract model
RealtimeSchemas-->>HTTPHandler: Resolved provider and model
HTTPHandler->>HTTPHandler: Rewrite nested transcription model
HTTPHandler->>OpenAIProvider: Normalize session request
OpenAIProvider-->>HTTPHandler: Normalized realtime session
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (1): Last reviewed commit: "fix: correct GA transcription-session cl..." | Re-trigger Greptile |
Summary
POST /v1/realtime/client_secretsmints both full realtime sessions and GA transcription-only sessions (session.type: "transcription"), but model extraction only checkedsession.model— the transcription schema has no such field, onlysession.audio.input.transcription.model. Every transcription-type request 400'd; confirmed live against real OpenAI.Changes
ExtractRealtimeClientSecretModelfalls back to the nested transcription model whensession.modelis absent.schemas.IsGATranscriptionSessionBody, a single classifier shared by extraction and both normalization layers (handler + provider), so they can't disagree on the same body. Explicitsession.typewins when present; otherwisesession.model/legacy rootmodelbeat the structural fallback, so a full session with transcription enabled as a sibling feature is never misclassified.session.model; the alias-resolution rewrite does the same.codex --yoloreview and fixed in a follow-up commit.Type of change
How to test
Also live-verified against real OpenAI's platform API using the
openaiPython SDK (and curl): minted a real GA transcription-sessionclient_secret, and confirmed the regression scenario (full session + transcription sibling) keepstype: "realtime"and the correct model.Breaking changes