Skip to content

feat(soniox): select data-residency region (US/EU/JP) with auto-detect#262

Open
johngambleubind wants to merge 2 commits into
TypeWhisper:mainfrom
johngambleubind:feat/soniox-region-selection
Open

feat(soniox): select data-residency region (US/EU/JP) with auto-detect#262
johngambleubind wants to merge 2 commits into
TypeWhisper:mainfrom
johngambleubind:feat/soniox-region-selection

Conversation

@johngambleubind

@johngambleubind johngambleubind commented Jul 5, 2026

Copy link
Copy Markdown

Implements #261.

What

Adds a data-residency region selection to the Soniox plugin so it isn't locked to the US endpoint.

  • The plugin hardcoded BaseUrl = "https://api.soniox.com" (US). EU and Japan keys couldn't be used at all — Soniox binds each API key to its project's region and domain — and everyone paid US round-trip latency.
  • The base URL is now resolved from a persisted Region setting: US api.soniox.com, EU api.eu.soniox.com, JP api.jp.soniox.com. Default is US, so existing installs are unchanged.

UX

  • A Region dropdown in the Soniox settings, persisted via IPluginHostServices.GetSetting/SetSetting — the same mechanism the OpenAI-Compatible plugin already uses for its endpoint.
  • The Test button now auto-detects: it probes each regional /v1/models with the key and selects the region that authenticates. Because the region is tied to the key's project (not the user's geography), probing is the reliable way to detect it — one click sets it up correctly.

Testing

dotnet test --filter SonioxPluginTests → all pass (24/24), including 5 new tests:

  • region loaded from the persisted setting drives the endpoint,
  • an unknown region id falls back to US,
  • SetRegion persists the choice and switches the endpoint,
  • auto-detect returns the authenticating region, and returns null when no region accepts the key.

Notes

Summary by CodeRabbit

  • New Features

    • Added region selection for Soniox, letting users choose a data-residency region and have it remembered between sessions.
    • Improved API key testing to automatically detect the working region and show it in the result.
  • Bug Fixes

    • Updated Soniox requests to use the selected region’s endpoint instead of a single fixed address.
    • Added fallback behavior for unknown region values.
  • Chores

    • Updated the Soniox plugin version to 1.0.4.

The plugin hardcoded the US endpoint (api.soniox.com), so EU
(api.eu.soniox.com) and Japan (api.jp.soniox.com) keys could not be used at
all — Soniox binds each key to its project's region and domain — and everyone
paid US round-trip latency.

- Resolve the base URL from a persisted Region setting (default US, so
  existing installs are unchanged), via IPluginHostServices.GetSetting/SetSetting.
- Settings view gains a Region dropdown; the Test button now probes each
  regional /v1/models with the key and auto-selects the region it authenticates
  against. Region is bound to the key's project, not the user's location, so
  probing is the correct detection, not geo/IP.
- Bump plugin version to 1.0.4; add tests for region persistence, endpoint
  switching, unknown-region fallback, and auto-detect (hit + miss).

Implements TypeWhisper#261

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@johngambleubind, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 26eabefb-b473-4180-bef7-77f003dd1e70

📥 Commits

Reviewing files that changed from the base of the PR and between 5f35b40 and d251c54.

📒 Files selected for processing (2)
  • plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs
  • plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml.cs
📝 Walkthrough

Walkthrough

Adds Soniox data-residency region support: a region list (US/EU/JP) with base URLs, persisted region selection, region auto-detection via API-key probing, a computed base URL resolver, a settings UI region selector, plugin version bump to 1.0.4, manifest update, and corresponding tests.

Changes

Soniox Region Support

Layer / File(s) Summary
Region data model and base URL resolution
plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs
Adds DefaultRegionId/RegionSettingKey constants, a static Regions list (US, EU, JP), the SonioxRegion record, a _region field, a computed BaseUrl property, and NormalizeRegionId/ResolveRegion helpers.
Region activation, persistence, and detection APIs
plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs
Loads persisted region during ActivateAsync, exposes AvailableRegions and RegionId, implements SetRegion (persists to settings) and DetectRegionAsync (probes each region’s base URL), updates model-probing to use the passed base URL, and bumps PluginVersion to 1.0.4.
Settings UI region selector
plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml, SonioxSettingsView.xaml.cs
Adds RegionLabel/RegionBox controls, initializes and populates the combo box with a suppression flag, reworks OnTestClick to call DetectRegionAsync, adds OnRegionChanged/SelectRegionItem/ApplyDetectedRegion handlers, and extends fallback localization text.
Manifest version and test coverage
plugins/TypeWhisper.Plugin.Soniox/manifest.json, tests/TypeWhisper.PluginSystem.Tests/SonioxPluginTests.cs
Bumps manifest version to 1.0.4; adds tests for persisted-region activation, unknown-region fallback, SetRegion persistence/endpoint switching, and DetectRegionAsync success/failure paths; updates the version-assertion test.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SonioxSettingsView
  participant SonioxPlugin
  participant SonioxAPI

  User->>SonioxSettingsView: click Test (API key)
  SonioxSettingsView->>SonioxPlugin: DetectRegionAsync(key)
  loop each region base URL
    SonioxPlugin->>SonioxAPI: GET /v1/models
    SonioxAPI-->>SonioxPlugin: authorized/unauthorized
  end
  SonioxPlugin-->>SonioxSettingsView: detected region or null
  SonioxSettingsView->>SonioxSettingsView: ApplyDetectedRegion / show status
  SonioxSettingsView->>SonioxPlugin: SetRegion(regionId)
  SonioxPlugin->>SonioxPlugin: persist region setting
Loading

Possibly related issues

Possibly related PRs

  • TypeWhisper/typewhisper-win#182: Both PRs modify the SonioxPlugin API-key test logic against the models endpoint, with this PR extending that probing to detect and persist a region.
  • TypeWhisper/typewhisper-win#213: Both PRs touch SonioxPlugin.PluginVersion/manifest.json version and the matching test assertion.

Poem

A rabbit hops from US to JP,
Checking keys with a curious peep 🐰
EU, US, JP in a row,
Auto-detect tells me where to go!
Hop, persist, and settle in place—
Region found, with grace! 🌍✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: Soniox region selection with auto-detection.
Description check ✅ Passed The description covers summary, related issue, testing, and notes, and is mostly aligned with the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

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.

❤️ Share

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

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml.cs (1)

112-131: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stale test result can clobber a manual region change made during probing.

RegionBox stays interactive while DetectRegionAsync is awaited. If the user changes the region dropdown while Test is running, OnRegionChanged fires and calls SetRegion immediately; once the pending probe resolves, ApplyDetectedRegion overwrites that newer selection with the (now stale) detected region.

🔒 Suggested fix: disable RegionBox during probing
         TestButton.IsEnabled = false;
+        RegionBox.IsEnabled = false;
         StatusText.Text = L("Settings.Testing");
         StatusText.Foreground = Brushes.Gray;
 
         try
         {
             ...
         }
         finally
         {
             TestButton.IsEnabled = true;
+            RegionBox.IsEnabled = true;
         }
🤖 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 `@plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml.cs` around lines
112 - 131, The test flow in SonioxSettingsView.xaml.cs allows a stale async
region probe to overwrite a user’s newer manual selection because RegionBox
remains editable while DetectRegionAsync is running. Update the Test button
handler and/or OnRegionChanged flow in SonioxSettingsView to disable RegionBox
for the duration of the probe, then restore it afterward, so ApplyDetectedRegion
cannot clobber a region change made during testing.
🤖 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 `@plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs`:
- Around line 222-235: The DetectRegionAsync flow in SonioxPlugin should fail
fast per region instead of letting each ProbeModelsAsync call inherit the full
HttpClient timeout. Update DetectRegionAsync to create a short-lived linked
CancellationTokenSource for each region probe, pass that token into
ProbeModelsAsync, and keep the existing region loop/return behavior in place so
OnTestClick no longer blocks for the full timeout on every region.

---

Outside diff comments:
In `@plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml.cs`:
- Around line 112-131: The test flow in SonioxSettingsView.xaml.cs allows a
stale async region probe to overwrite a user’s newer manual selection because
RegionBox remains editable while DetectRegionAsync is running. Update the Test
button handler and/or OnRegionChanged flow in SonioxSettingsView to disable
RegionBox for the duration of the probe, then restore it afterward, so
ApplyDetectedRegion cannot clobber a region change made during testing.
🪄 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 Plus

Run ID: 2676dc1e-3b4d-4cd5-ac07-32560d03b2e8

📥 Commits

Reviewing files that changed from the base of the PR and between 64bbb15 and 5f35b40.

📒 Files selected for processing (5)
  • plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs
  • plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml
  • plugins/TypeWhisper.Plugin.Soniox/SonioxSettingsView.xaml.cs
  • plugins/TypeWhisper.Plugin.Soniox/manifest.json
  • tests/TypeWhisper.PluginSystem.Tests/SonioxPluginTests.cs

Comment thread plugins/TypeWhisper.Plugin.Soniox/SonioxPlugin.cs
…g test

Addresses the CodeRabbit review on TypeWhisper#262:
- DetectRegionAsync bounds each probe with a 10s linked-CTS timeout so an
  unreachable region fails fast instead of blocking the Test flow for the full
  HttpClient timeout. A probe timeout skips to the next region, while a genuine
  caller cancellation still propagates -- ProbeModelsAsync re-throws on cancel,
  so the timeout is caught only when the outer token is not cancelled.
- Disable the region dropdown while probing so a manual change mid-Test cannot be
  clobbered by ApplyDetectedRegion when the probe resolves.

Soniox tests still green (24/24).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@johngambleubind

Copy link
Copy Markdown
Author

Both addressed in d251c54:

  1. Bounded probes — each DetectRegionAsync probe now runs under a 10s linked CancellationTokenSource, so an unreachable region fails fast instead of blocking on the 5-min HttpClient timeout. I adjusted the suggested diff slightly: ProbeModelsAsync re-throws on cancellation (when (ct.IsCancellationRequested)), so I wrapped the probe in try/catch (OperationCanceledException) when (!ct.IsCancellationRequested) — a per-probe timeout skips to the next region, while a genuine caller cancellation still propagates.
  2. RegionBox lock — the region dropdown is disabled for the duration of the probe (in the Test button's try/finally), so a manual change mid-Test can't be clobbered by ApplyDetectedRegion.

Soniox suite green (24/24).

@SeoFood SeoFood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One stale-input case remains in the settings test flow: editing the API key while the region probe is in flight can persist the old key's detected region against the newly saved key.

var valid = await _plugin.ValidateApiKeyAsync(key);
StatusText.Text = valid ? L("Settings.ApiKeyValid") : L("Settings.ApiKeyInvalid");
StatusText.Foreground = valid ? Brushes.Green : Brushes.Red;
var detectedRegion = await _plugin.DetectRegionAsync(key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OnTestClick captures ApiKeyBox.Password once, then awaits DetectRegionAsync, but the password box stays editable and OnPasswordChanged can debounce-save a different key while the probe is in flight. When the old probe resolves, ApplyDetectedRegion persists the region detected for the old key against the newly saved key, leaving the plugin with a mismatched key/region pair. Please disable ApiKeyBox during the test as well, or re-check that ApiKeyBox.Password still matches the tested key before applying the detected region.

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