feat: add Azure realtime provider and nested model normalization - #3334
Conversation
|
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughExports OpenAI realtime normalization helpers; updates RealtimeHeaders signature across providers; implements AzureProvider realtime (WebSocket URL/headers, WebRTC SDP exchange, client_secrets proxy), delegating event conversion and extraction to OpenAI helpers. ChangesAzure Realtime and OpenAI Utility Export
Sequence DiagramsequenceDiagram
participant Client
participant AzureProvider
participant AzureUpstream
Client->>AzureProvider: POST /v1/realtime/client_secrets or SDP multipart (sdp + optional session)
AzureProvider->>AzureUpstream: POST /openai/v1/realtime?model&api-version (JSON or multipart + auth/extra headers)
AzureUpstream-->>AzureProvider: response + headers (2xx or error)
AzureProvider-->>Client: passthrough body (2xx) or shaped BifrostError (non-2xx)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Comment |
Confidence Score: 4/5Safe to merge with one open issue in the WebRTC session path that was flagged in a prior review and is unaddressed here. The core normalization logic is correct for both the WebSocket and client-secrets flows. The previously flagged omission — resolveRealtimeSDPTarget in webrtc_realtime.go normalizing the top-level model but not calling StripNestedModelPrefixes on the remaining session fields — is confirmed still present and means a WebRTC session sent with input_audio_transcription.model of openai/whisper-1 will reach Azure with the bare openai/ prefix intact and be rejected. The rest of the new Azure provider and the normalization additions look correct and well-structured. transports/bifrost-http/handlers/webrtc_realtime.go — resolveRealtimeSDPTarget normalizes the top-level model but leaves nested model fields in the forwarded session without stripping provider prefixes. Important Files Changed
Reviews (7): Last reviewed commit: "feat: add Azure realtime provider and ne..." | Re-trigger Greptile |
cab5a58 to
befa6a2
Compare
ddc3e56 to
a1a3812
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/providers/azure/realtime.go`:
- Around line 253-254: The client_secrets failure path currently returns
parseAzureRealtimeError(resp) which cannot access ctx/sendBackRawResponse so
RawResponse isn’t attached; change the error-return path in realtime.go (the
spot calling parseAzureRealtimeError(resp) around the client_secrets branch and
the similar block at lines ~343-366) to use the same helper that preserves raw
payloads (e.g., call realtimeWebRTCUpstreamError(resp, ctx) or extend
parseAzureRealtimeError to accept ctx/sendBackRawResponse) so that when
sendBackRawResponse is enabled the returned error includes RawResponse; ensure
the modified call site passes the request context (ctx) and any flags required
to attach resp.Body to the returned error object.
- Around line 337-339: The current code returns
key.AzureKeyConfig.APIVersion.GetValue() without checking for an empty string,
which yields "api-version=" and breaks realtime calls; update the logic in the
block that references key.AzureKeyConfig and APIVersion.GetValue() to treat an
empty resolved value as unset and instead return AzureAPIVersionPreview as the
fallback (i.e., get the value from APIVersion.GetValue(), and if it's empty,
return AzureAPIVersionPreview).
- Around line 197-208: The BifrostError returned for the legacy /sessions
endpoint uses a hardcoded provider key (schemas.Azure) which can mislabel errors
when an Azure provider alias is configured; update the ExtraFields.Provider to
use provider.GetProviderKey() instead so the error metadata reflects the
configured provider key (change the provider field in the BifrostError
construction in the branch where endpointType ==
schemas.RealtimeSessionEndpointSessions to call provider.GetProviderKey()).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d2eff3dd-c4f1-42ce-8637-0f63a47ae4c1
📒 Files selected for processing (3)
core/providers/azure/realtime.gocore/providers/openai/realtime.gocore/providers/openai/realtime_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
- core/providers/openai/realtime_test.go
- core/providers/openai/realtime.go
a1a3812 to
b5b4f19
Compare
befa6a2 to
4e05e6f
Compare
b5b4f19 to
d256aa3
Compare
4e05e6f to
49f495f
Compare
d256aa3 to
34a893c
Compare
49f495f to
4b8c081
Compare
34a893c to
de6e290
Compare
Merge activity
|
The base branch was changed.
## Summary
Adds Azure as a realtime provider (WebSocket, WebRTC, client secrets) and
introduces nested model prefix stripping so Bifrost-style `provider/model`
strings in session configs (e.g. `openai/whisper-1` in
`input_audio_transcription.model`) are normalized to bare model names before
forwarding upstream.
## Changes
- **`core/providers/azure/realtime.go` (new)**: Full Azure realtime
implementation covering `RealtimeProvider` (WebSocket),
`RealtimeWebRTCProvider` (SDP exchange), and `RealtimeSessionProvider` (client
secrets only — legacy `/sessions` returns a clear error). Reuses OpenAI event
converters since Azure uses the same wire protocol. Key Azure-specific
behavior:
- URLs use `/openai/v1/realtime` prefix with preview `api-version` query param
- Auth uses `api-key` header for API keys, `Authorization: Bearer` for
ephemeral tokens (`ek_*`)
- Model value maps to the Azure deployment name (resolved via key aliases
upstream)
- **`core/providers/openai/realtime.go`**: Exported `StripNestedModelPrefixes`
and `ExtractNestedVoice` for reuse by Azure and the transport handlers. Added
`StripNestedModelPrefixes` calls in `normalizeRealtimeClientSecretsRequest`
and `normalizeRealtimeSessionsRequest` to strip provider prefixes from nested
model fields in both old format (`input_audio_transcription.model`) and new
format (`audio.input.transcription.model`)
- **`core/providers/openai/realtime_test.go`**: Updated test expectations for
exported helpers
## Type of change
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs
## How to test
```sh
# Provider tests
go test ./core/providers/azure/ -count=1 -v
go test ./core/providers/openai/ -count=1 -v
# Full build
make build LOCAL=1
```
Manual verification:
1. Configure an Azure key with a realtime deployment alias
2. Connect via WebSocket to `/v1/realtime?model=<azure-deployment>`
3. Connect via WebRTC with ephemeral token from `/v1/realtime/client_secrets`
4. Send a session config with `openai/whisper-1` as transcription model — verify
it's normalized to `whisper-1` upstream
5. POST to `/v1/realtime/sessions` routed to Azure — verify clear error message
about using `/client_secrets` instead
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
Ephemeral token detection uses `ek_` prefix check to switch from `api-key` to
`Authorization: Bearer` header. This is consistent with how OpenAI ephemeral
tokens work and doesn't expose any additional auth surface.
## Checklist
- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary
Adds Azure as a realtime provider (WebSocket, WebRTC, client secrets) and
introduces nested model prefix stripping so Bifrost-style `provider/model`
strings in session configs (e.g. `openai/whisper-1` in
`input_audio_transcription.model`) are normalized to bare model names before
forwarding upstream.
## Changes
- **`core/providers/azure/realtime.go` (new)**: Full Azure realtime
implementation covering `RealtimeProvider` (WebSocket),
`RealtimeWebRTCProvider` (SDP exchange), and `RealtimeSessionProvider` (client
secrets only — legacy `/sessions` returns a clear error). Reuses OpenAI event
converters since Azure uses the same wire protocol. Key Azure-specific
behavior:
- URLs use `/openai/v1/realtime` prefix with preview `api-version` query param
- Auth uses `api-key` header for API keys, `Authorization: Bearer` for
ephemeral tokens (`ek_*`)
- Model value maps to the Azure deployment name (resolved via key aliases
upstream)
- **`core/providers/openai/realtime.go`**: Exported `StripNestedModelPrefixes`
and `ExtractNestedVoice` for reuse by Azure and the transport handlers. Added
`StripNestedModelPrefixes` calls in `normalizeRealtimeClientSecretsRequest`
and `normalizeRealtimeSessionsRequest` to strip provider prefixes from nested
model fields in both old format (`input_audio_transcription.model`) and new
format (`audio.input.transcription.model`)
- **`core/providers/openai/realtime_test.go`**: Updated test expectations for
exported helpers
## Type of change
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs
## How to test
```sh
# Provider tests
go test ./core/providers/azure/ -count=1 -v
go test ./core/providers/openai/ -count=1 -v
# Full build
make build LOCAL=1
```
Manual verification:
1. Configure an Azure key with a realtime deployment alias
2. Connect via WebSocket to `/v1/realtime?model=<azure-deployment>`
3. Connect via WebRTC with ephemeral token from `/v1/realtime/client_secrets`
4. Send a session config with `openai/whisper-1` as transcription model — verify
it's normalized to `whisper-1` upstream
5. POST to `/v1/realtime/sessions` routed to Azure — verify clear error message
about using `/client_secrets` instead
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
Ephemeral token detection uses `ek_` prefix check to switch from `api-key` to
`Authorization: Bearer` header. This is consistent with how OpenAI ephemeral
tokens work and doesn't expose any additional auth surface.
## Checklist
- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary
Adds Azure as a realtime provider (WebSocket, WebRTC, client secrets) and
introduces nested model prefix stripping so Bifrost-style `provider/model`
strings in session configs (e.g. `openai/whisper-1` in
`input_audio_transcription.model`) are normalized to bare model names before
forwarding upstream.
## Changes
- **`core/providers/azure/realtime.go` (new)**: Full Azure realtime
implementation covering `RealtimeProvider` (WebSocket),
`RealtimeWebRTCProvider` (SDP exchange), and `RealtimeSessionProvider` (client
secrets only — legacy `/sessions` returns a clear error). Reuses OpenAI event
converters since Azure uses the same wire protocol. Key Azure-specific
behavior:
- URLs use `/openai/v1/realtime` prefix with preview `api-version` query param
- Auth uses `api-key` header for API keys, `Authorization: Bearer` for
ephemeral tokens (`ek_*`)
- Model value maps to the Azure deployment name (resolved via key aliases
upstream)
- **`core/providers/openai/realtime.go`**: Exported `StripNestedModelPrefixes`
and `ExtractNestedVoice` for reuse by Azure and the transport handlers. Added
`StripNestedModelPrefixes` calls in `normalizeRealtimeClientSecretsRequest`
and `normalizeRealtimeSessionsRequest` to strip provider prefixes from nested
model fields in both old format (`input_audio_transcription.model`) and new
format (`audio.input.transcription.model`)
- **`core/providers/openai/realtime_test.go`**: Updated test expectations for
exported helpers
## Type of change
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs
## How to test
```sh
# Provider tests
go test ./core/providers/azure/ -count=1 -v
go test ./core/providers/openai/ -count=1 -v
# Full build
make build LOCAL=1
```
Manual verification:
1. Configure an Azure key with a realtime deployment alias
2. Connect via WebSocket to `/v1/realtime?model=<azure-deployment>`
3. Connect via WebRTC with ephemeral token from `/v1/realtime/client_secrets`
4. Send a session config with `openai/whisper-1` as transcription model — verify
it's normalized to `whisper-1` upstream
5. POST to `/v1/realtime/sessions` routed to Azure — verify clear error message
about using `/client_secrets` instead
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
Ephemeral token detection uses `ek_` prefix check to switch from `api-key` to
`Authorization: Bearer` header. This is consistent with how OpenAI ephemeral
tokens work and doesn't expose any additional auth surface.
## Checklist
- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary This PR cuts the `v1.5.11` / `v1.3.11` release across core, framework, and all plugins, and introduces a new Claude skill (`release-checklist`) for pre-release migration safety auditing. ## Changes - **`release-checklist` skill** — Adds `.claude/skills/release-checklist/SKILL.md`, a read-only pre-release audit tool that scans Go-defined database migrations changed in a release for high-scale deadlock/lock-contention risks and boot-time-blocking operations. It produces a structured `PASS`/`WARN`/`FAIL` report with a concrete remediation plan per finding. The skill is designed to grow via an extensible Checks Registry. - **Version bumps** — `core` → `1.5.11`, `framework` → `1.3.11`, `transports` → `1.5.3`, `plugins/governance` → `1.5.11`, `plugins/logging` → `1.5.11`, `plugins/semanticcache` → `1.5.11`, `plugins/otel` → `1.2.11`, `plugins/maxim` → `1.6.11`, `plugins/prompts` → `1.0.11`, and remaining plugins bumped accordingly. - **Changelogs populated** — All per-package changelogs updated with the full set of features and fixes shipping in this release. Key highlights in this release: - Temporary access tokens for scoped, time-limited API access - MCP per-user OAuth flow refactor - Bedrock Mantle inference engine support - Azure Realtime provider with enriched session tracking - Direct access control (DAC) and virtual key rotation - Cluster-aware log metadata and per-node usage aggregation - Feature flag framework - Config-hash-based file value override of DB on restart - Semantic cache plugin rewrite - Numerous streaming stability, Bedrock, Anthropic, and Gemini fixes - AWS SDK and dependency security updates ## Type of change - [ ] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [x] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [x] Plugins - [x] UI (React) - [ ] Docs ## How to test ```sh # Verify version files reflect the new release cat core/version # expect 1.5.11 cat framework/version # expect 1.3.11 cat transports/version # expect 1.5.3 # Core/Transports go test ./... ``` To exercise the new `release-checklist` skill, invoke it via Claude with: ``` /release-checklist origin/dev...HEAD ``` Expected output: a structured report with `PASS`/`WARN`/`FAIL` per check and a Remediation Plan table for any findings. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues #3603, #3565, #3489, #3334, #3335, #3435, #3554, #3590, #3444, #3198, #3581, #3610, #3599, #3567, #3382, #3461 and others listed in the changelogs. ## Security considerations - AWS SDK and dependency security updates are included (#3461). - `FullyRedacted()` for proxy passwords and `MarshalForStorage()` for `ProxyConfig` prevent partial secret leakage in API responses (#3445). - The `release-checklist` skill is strictly read-only and never modifies files. ## Checklist - [x] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [x] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable

Summary
Adds Azure as a realtime provider (WebSocket, WebRTC, client secrets) and
introduces nested model prefix stripping so Bifrost-style
provider/modelstrings in session configs (e.g.
openai/whisper-1ininput_audio_transcription.model) are normalized to bare model names beforeforwarding upstream.
Changes
core/providers/azure/realtime.go(new): Full Azure realtimeimplementation covering
RealtimeProvider(WebSocket),RealtimeWebRTCProvider(SDP exchange), andRealtimeSessionProvider(clientsecrets only — legacy
/sessionsreturns a clear error). Reuses OpenAI eventconverters since Azure uses the same wire protocol. Key Azure-specific
behavior:
/openai/v1/realtimeprefix with previewapi-versionquery paramapi-keyheader for API keys,Authorization: Bearerforephemeral tokens (
ek_*)upstream)
core/providers/openai/realtime.go: ExportedStripNestedModelPrefixesand
ExtractNestedVoicefor reuse by Azure and the transport handlers. AddedStripNestedModelPrefixescalls innormalizeRealtimeClientSecretsRequestand
normalizeRealtimeSessionsRequestto strip provider prefixes from nestedmodel fields in both old format (
input_audio_transcription.model) and newformat (
audio.input.transcription.model)core/providers/openai/realtime_test.go: Updated test expectations forexported helpers
Type of change
Affected areas
How to test
Manual verification:
/v1/realtime?model=<azure-deployment>/v1/realtime/client_secretsopenai/whisper-1as transcription model — verifyit's normalized to
whisper-1upstream/v1/realtime/sessionsrouted to Azure — verify clear error messageabout using
/client_secretsinsteadScreenshots/Recordings
N/A
Breaking changes
Related issues
N/A
Security considerations
Ephemeral token detection uses
ek_prefix check to switch fromapi-keytoAuthorization: Bearerheader. This is consistent with how OpenAI ephemeraltokens work and doesn't expose any additional auth surface.
Checklist
docs/contributing/README.mdand followed the guidelines