Skip to content

[Added] Azure OpenAI realtime provider - #2

Open
isnine wants to merge 11 commits into
himomohi:masterfrom
isnine:master
Open

[Added] Azure OpenAI realtime provider#2
isnine wants to merge 11 commits into
himomohi:masterfrom
isnine:master

Conversation

@isnine

@isnine isnine commented May 21, 2026

Copy link
Copy Markdown

Azure OpenAI Realtime Provider

  • New provider switch (OpenAIProvider: openAI / azure) in Settings.
  • AzureOpenAIConfigStore securely stores endpoint + API key (Keychain + UserDefaults), with endpoint normalization (AzureOpenAIEndpoint).
  • OpenAIRealtimeTranscriber now takes an OpenAIRealtimeProviderConfig and routes requests per provider:
    • Auth: OpenAI uses Authorization: Bearer …, Azure uses api-key: ….
    • Custom Azure deployment name supported (defaults to gpt-realtime-1.5).
  • OpenAIRealtimeModelPickers lets users pick transcription / translation models or override the model ID per provider.
image image

API Preview

image

Floating Caption Controls

  • New FloatingCaptionTextAlignment (leading / center) — caption window aligns text, frame, and stack accordingly.
  • New FloatingCaptionPresentationPolicy — governs immediate vs. dwell-based caption updates:
  • SidebarView exposes the alignment toggle and immediate-display switch alongside existing caption controls.
image

Related Document

Summary by CodeRabbit

  • New Features
    • Added Azure OpenAI support, including provider selection and secure endpoint/API key configuration.
    • Added customizable transcription and translation model names for Azure.
    • Added floating caption alignment options and an immediate-display setting.
    • Added clearer GPT mode guidance when realtime translation manages transcription.
  • Bug Fixes
    • Improved floating caption timing and update behavior.
    • Improved realtime transcript handling and provider-specific connection support.
    • Added validation and clearer error messaging for Azure configuration.

@himomohi

Copy link
Copy Markdown
Owner

@isnine Thank you so much for this thoughtful contribution! 🙏

This is a very meaningful direction for AirTranslate, and I really appreciate the care you put into the provider abstraction, settings UI, localization updates, and tests.

I reviewed the implementation more closely. The overall direction looks good, but I’d like to clarify a few Azure-specific details before merging.

  1. Azure OpenAI Realtime seems to require a deployment name in addition to the endpoint and API key. Currently the UI/config only stores the endpoint and API key, and the code appears to use the model ID directly. Could you add support for an Azure deployment name, or confirm that this works with the current Azure Realtime API?

  2. The PR description says Azure is translation-only and transcription is unsupported, but the implementation and tests include an Azure transcription path. Could you clarify the intended behavior here? If transcription is unsupported, I think we should disable that path explicitly.

  3. Could you confirm whether /openai/v1/realtime/translations is supported by Azure OpenAI? I want to make sure this path works with Azure deployments, not only with the direct OpenAI provider.

  4. Some realtime logs include the full WebSocket URL / Azure host as public logs. Since Azure resource names can sometimes be sensitive, could you mark those values as private or reduce the logged details?

  5. Could you also share the local results for:

    • swift build
    • swift test

Again, thank you very much for the thoughtful work here. I really like the direction, and I’d love to merge this once these points are confirmed.

@isnine

isnine commented May 21, 2026

Copy link
Copy Markdown
Author

Sure, I’ll update the PR and let you know once it’s ready.

@isnine

isnine commented May 21, 2026

Copy link
Copy Markdown
Author

@himomohi The PR is ready; #1, #2, and #4 have been fixed/updated in the latest code.

For #3, please check the api preview in PR description, let me know if you have any questions

For #5
image

@isnine

isnine commented May 24, 2026

Copy link
Copy Markdown
Author

@himomohi Can you take a look

@himomohi

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c24a8e6ea5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

case .openAI:
return URL(string: "wss://\(host)/v1/realtime/translations?model=\(encodedModel)")
case .azure:
return URL(string: "wss://\(host)/openai/v1/realtime/translations?model=\(encodedModel)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle Azure translation text events

When the new Azure provider connects to this translations endpoint, Azure realtime translation streams text as response.text.delta/response.text.done with the payload in text; the receive loop below only recognizes session.output_transcript.* and only decodes delta/transcript. In that Azure translation configuration the WebSocket can be healthy while didTranslate is never called, leaving live captions blank, so the Azure event names/fields need to be mapped before enabling this path.

Useful? React with 👍 / 👎.

Comment on lines +273 to +274
transcription: OpenAIRealtimeTranscriptionConfig(
model: modelID

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the Azure transcription override for input transcription

In Azure realtime translation mode, modelID here is the translation deployment used for the /translations?model=... connection, so the new customAzureTranscriptionModelName setting is ignored and the session asks Azure to use the translation deployment for audio.input.transcription. If input transcription is needed, pass the transcription override separately; otherwise omit this audio.input block so Azure translation sessions are not configured with the wrong deployment.

Useful? React with 👍 / 👎.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds Azure OpenAI configuration and realtime provider routing, persists provider-specific model settings, introduces reusable settings UI components, and makes floating caption alignment and presentation timing configurable.

Changes

Azure OpenAI and realtime provider support

Layer / File(s) Summary
Provider storage and realtime transport
Sources/AirTranslate/Models/OpenAIProvider.swift, Sources/AirTranslate/Services/AzureOpenAIConfigStore.swift, Sources/AirTranslate/Services/OpenAIRealtimeTranscriber.swift, Sources/AirTranslate/Services/OpenAITranslationService.swift
Adds OpenAI/Azure provider models, Keychain-backed Azure credentials, endpoint normalization, provider-specific WebSocket URLs and authentication, Azure session payloads, custom model overrides, and related errors.
Provider settings and session wiring
Sources/AirTranslate/Services/TranslationSessionStore.swift, Sources/AirTranslate/Views/OpenAIRealtimeModelPickers.swift
Persists provider and model selections, resolves credentials and overrides, filters Azure translation models, and passes provider configuration into realtime captioner startup.
Provider configuration interface
Sources/AirTranslate/Models/AppText.swift, Sources/AirTranslate/Views/SettingsComponents.swift, Sources/AirTranslate/Views/SettingsView.swift, Sources/AirTranslate/Views/SidebarView.swift
Adds provider-aware settings, Azure endpoint/API-key forms, save/remove status handling, GPT credential notices, model pickers, advanced overrides, and platform links.
Floating caption presentation
Sources/AirTranslate/Models/FloatingCaptionTextAlignment.swift, Sources/AirTranslate/Support/FloatingCaptionPresentationPolicy.swift, Sources/AirTranslate/Services/TranslationSessionStore.swift, Sources/AirTranslate/Views/FloatingCaptionWindowView.swift
Adds persisted caption alignment and immediate-display settings, configurable rendering alignment, and centralized revision, advancement, and dwell-time decisions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SettingsView
  participant TranslationSessionStore
  participant OpenAIRealtimeTranscriber
  participant AzureOpenAIConfigStore
  User->>SettingsView: Select Azure and enter endpoint/API key
  SettingsView->>TranslationSessionStore: Save Azure configuration
  TranslationSessionStore->>AzureOpenAIConfigStore: Persist normalized endpoint and key
  TranslationSessionStore->>OpenAIRealtimeTranscriber: Start with Azure provider configuration
  OpenAIRealtimeTranscriber->>OpenAIRealtimeTranscriber: Build Azure URL, headers, and session payload
Loading

Suggested reviewers: himomohi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding Azure OpenAI as a realtime provider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch master

Comment @coderabbitai help to get the list of available commands.

@himomohi

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Sources/AirTranslate/Support/FloatingCaptionPresentationPolicy.swift (1)

52-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add boundary tests for the presentation policy.

Cover normalized-equal text, the 0.45-second revision boundary, prefix/non-prefix candidates, immediate display, and the 1.4/3.6-second dwell clamps.

🤖 Prompt for 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.

In `@Sources/AirTranslate/Support/FloatingCaptionPresentationPolicy.swift` around
lines 52 - 58, Add boundary-focused tests for FloatingCaptionPresentationPolicy,
covering normalized-equal text, the 0.45-second revision threshold, prefix and
non-prefix candidate handling, immediate display, and dwell durations clamped at
1.4 and 3.6 seconds. Exercise the public policy methods and assert both sides of
each boundary without changing the policy implementation.
Sources/AirTranslate/Views/SidebarView.swift (1)

1076-1184: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consolidate the duplicated Azure/OpenAI credential rows. GPTAzureConfigRow in SidebarView.swift and SettingsAzureConfigRow in SettingsComponents.swift are near-identical (as are GPTAPIKeyRow / SettingsAPIKeyRow). Maintaining two copies already caused the save-on-failure behavior to drift between the two screens. Extracting a single shared component removes that drift risk.

  • Sources/AirTranslate/Views/SidebarView.swift#L1076-L1184: replace GPTAzureConfigRow usage with the shared SettingsAzureConfigRow component (and reuse the shared key row for the .openAI case).
  • Sources/AirTranslate/Views/SettingsComponents.swift#L308-L400: keep SettingsAzureConfigRow as the single source of truth reused by both SettingsView and ConfigurationSheetView.
🤖 Prompt for 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.

In `@Sources/AirTranslate/Views/SidebarView.swift` around lines 1076 - 1184,
Consolidate the duplicated Azure credential row by removing or replacing
GPTAzureConfigRow in Sources/AirTranslate/Views/SidebarView.swift lines
1076-1184 with the shared SettingsAzureConfigRow, and reuse the shared key-row
component for the .openAI case. Keep SettingsAzureConfigRow in
Sources/AirTranslate/Views/SettingsComponents.swift lines 308-400 as the single
implementation used by SettingsView and ConfigurationSheetView; no direct change
is required there unless needed to support both callers.
🤖 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 `@Sources/AirTranslate/Views/SidebarView.swift`:
- Around line 505-524: Move the azureAPIKey reset inside the successful `ok`
branch of the save closure, alongside the successful configuration updates.
Preserve the entered key when `saveAzureOpenAIConfig` fails while keeping the
existing failure alert and focus behavior unchanged.

---

Nitpick comments:
In `@Sources/AirTranslate/Support/FloatingCaptionPresentationPolicy.swift`:
- Around line 52-58: Add boundary-focused tests for
FloatingCaptionPresentationPolicy, covering normalized-equal text, the
0.45-second revision threshold, prefix and non-prefix candidate handling,
immediate display, and dwell durations clamped at 1.4 and 3.6 seconds. Exercise
the public policy methods and assert both sides of each boundary without
changing the policy implementation.

In `@Sources/AirTranslate/Views/SidebarView.swift`:
- Around line 1076-1184: Consolidate the duplicated Azure credential row by
removing or replacing GPTAzureConfigRow in
Sources/AirTranslate/Views/SidebarView.swift lines 1076-1184 with the shared
SettingsAzureConfigRow, and reuse the shared key-row component for the .openAI
case. Keep SettingsAzureConfigRow in
Sources/AirTranslate/Views/SettingsComponents.swift lines 308-400 as the single
implementation used by SettingsView and ConfigurationSheetView; no direct change
is required there unless needed to support both callers.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5654e59b-7fc5-4a76-b7d0-d2f817904ff5

📥 Commits

Reviewing files that changed from the base of the PR and between 9b49fc0 and c24a8e6.

📒 Files selected for processing (13)
  • Sources/AirTranslate/Models/AppText.swift
  • Sources/AirTranslate/Models/FloatingCaptionTextAlignment.swift
  • Sources/AirTranslate/Models/OpenAIProvider.swift
  • Sources/AirTranslate/Services/AzureOpenAIConfigStore.swift
  • Sources/AirTranslate/Services/OpenAIRealtimeTranscriber.swift
  • Sources/AirTranslate/Services/OpenAITranslationService.swift
  • Sources/AirTranslate/Services/TranslationSessionStore.swift
  • Sources/AirTranslate/Support/FloatingCaptionPresentationPolicy.swift
  • Sources/AirTranslate/Views/FloatingCaptionWindowView.swift
  • Sources/AirTranslate/Views/OpenAIRealtimeModelPickers.swift
  • Sources/AirTranslate/Views/SettingsComponents.swift
  • Sources/AirTranslate/Views/SettingsView.swift
  • Sources/AirTranslate/Views/SidebarView.swift

Comment on lines +505 to +524
save: {
let ok = session.saveAzureOpenAIConfig(endpoint: azureEndpoint, apiKey: azureAPIKey)
azureAPIKey = ""
if ok {
azureEndpoint = session.azureOpenAIEndpoint
configurationNotice = nil
shouldFocusOpenAIAPIKey = false
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSavedTitle,
message: session.statusMessage
)
} else {
configurationNotice = session.statusMessage
shouldFocusOpenAIAPIKey = true
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSaveFailedTitle,
message: session.statusMessage
)
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Azure API key is wiped even when the save fails.

azureAPIKey = "" runs before the ok check, so on a recoverable failure (e.g. invalid endpoint) the user's typed secret is cleared and must be re-entered. SettingsView.saveAzureOpenAIConfig only clears it in the success branch; align this closure with that behavior.

🔧 Proposed fix
                         save: {
                             let ok = session.saveAzureOpenAIConfig(endpoint: azureEndpoint, apiKey: azureAPIKey)
-                            azureAPIKey = ""
                             if ok {
+                                azureAPIKey = ""
                                 azureEndpoint = session.azureOpenAIEndpoint
                                 configurationNotice = nil
                                 shouldFocusOpenAIAPIKey = false
                                 saveAlert = SaveAlert(
                                     title: AppText.azureOpenAIConfigSavedTitle,
                                     message: session.statusMessage
                                 )
                             } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
save: {
let ok = session.saveAzureOpenAIConfig(endpoint: azureEndpoint, apiKey: azureAPIKey)
azureAPIKey = ""
if ok {
azureEndpoint = session.azureOpenAIEndpoint
configurationNotice = nil
shouldFocusOpenAIAPIKey = false
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSavedTitle,
message: session.statusMessage
)
} else {
configurationNotice = session.statusMessage
shouldFocusOpenAIAPIKey = true
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSaveFailedTitle,
message: session.statusMessage
)
}
},
save: {
let ok = session.saveAzureOpenAIConfig(endpoint: azureEndpoint, apiKey: azureAPIKey)
if ok {
azureAPIKey = ""
azureEndpoint = session.azureOpenAIEndpoint
configurationNotice = nil
shouldFocusOpenAIAPIKey = false
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSavedTitle,
message: session.statusMessage
)
} else {
configurationNotice = session.statusMessage
shouldFocusOpenAIAPIKey = true
saveAlert = SaveAlert(
title: AppText.azureOpenAIConfigSaveFailedTitle,
message: session.statusMessage
)
}
},
🤖 Prompt for 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.

In `@Sources/AirTranslate/Views/SidebarView.swift` around lines 505 - 524, Move
the azureAPIKey reset inside the successful `ok` branch of the save closure,
alongside the successful configuration updates. Preserve the entered key when
`saveAzureOpenAIConfig` fails while keeping the existing failure alert and focus
behavior unchanged.

@himomohi himomohi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isnine Thank you for the update. Before this can be merged, please address the current blocking items:

  1. Sync the branch with the latest master and resolve the merge conflicts.
  2. Handle Azure translation text events (response.text.delta / response.text.done) so translated captions are actually published.
  3. Use the configured Azure transcription deployment separately, or omit input transcription from translation-only sessions.
  4. Preserve the entered Azure API key when validation or saving fails, as noted by CodeRabbit.
  5. After updating, please provide fresh results for swift test, swift build -c release, and a real Azure Realtime translation smoke test.

Please also resolve the inline review conversations after addressing them. CodeRabbit will automatically review the new commits. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants