fix(ios/talk): use Gateway talk.speak for non-ElevenLabs speech providers#98214
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 12:47 PM ET / 16:47 UTC. Summary PR surface: Other +162. Total +162 across 2 files. Reproducibility: yes. source-level reproduction is high-confidence: current main classifies non-ElevenLabs Gateway speech providers as realtime and lacks a Gateway Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land one canonical iOS fix branch that routes native speech through a complete Gateway Do we have a high-confidence way to reproduce the issue? Yes, source-level reproduction is high-confidence: current main classifies non-ElevenLabs Gateway speech providers as realtime and lacks a Gateway Is this the best way to solve the issue? No, not yet. The direction is the right owner-boundary fix, but the iOS helper should forward the full Gateway Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 6cb82eaab865. Label changesLabel justifications:
Evidence reviewedPR surface: Other +162. Total +162 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…ders - Fix realtime misclassification: derive usesRealtimeConfig from executionMode only, not from activeProvider == Self.defaultTalkProvider - Add Gateway talk.speak playback path in playAssistant() for non-ElevenLabs providers (xiaomi, microsoft, etc.) - Add playGatewayTalkSpeak() helper that calls talk.speak RPC, decodes audio, and plays via the existing MP3 player - Add test coverage for speech-only provider parsing as .native mode and regression test preserving realtime config as .realtimeRelay Fixes openclaw#98153
ae467f3 to
a00fd39
Compare
- Route incremental playback through Gateway TTS for non-ElevenLabs providers in speakIncrementalSegment - Add language parameter to talk.speak RPC payload - Play returned audio in the format returned by the provider instead of always using MP3 player Ref. openclaw#98214 Co-Authored-By: Claude <[email protected]>
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Summary
Problem: iOS Talk Mode misclassifies any Gateway speech/TTS provider that is not "elevenlabs" — such as Xiaomi MiMo, Microsoft Azure, or any other non-ElevenLabs TTS provider — as a realtime voice configuration. When users set Talk Provider to "Gateway Default", the app shows "Voice Mode: Realtime Voice" and "Transport: Native WebRTC", attempts a WebRTC realtime session through talk.client.create (which fails because only a speech provider is configured), and then falls back to iOS system speech. The user hears a generic system voice instead of their carefully configured Gateway TTS provider.
Solution: Two root causes are fixed in TalkModeManager.swift. First, the realtime classification formula
usesRealtimeConfig = activeProvider != Self.defaultTalkProvider || executionMode != .nativeis corrected tousesRealtimeConfig = executionMode != .native— removing the provider name comparison that incorrectly flagged all non-ElevenLabs providers as realtime. The executionMode is already correctly determined byresolvedExecutionMode()based on the config'stalk.realtime.modefield. Second, a Gatewaytalk.speakplayback path is added toplayAssistant(text:), inserting a new branch between the existing ElevenLabs and system voice fallback that calls the Gateway RPC to synthesize speech for non-ElevenLabs providers.What changed:
activeProvider != Self.defaultTalkProvidercondition)else ifbranch inplayAssistant()for non-ElevenLabs providers that callsplayGatewayTalkSpeak()playGatewayTalkSpeak()method that builds talk.speak RPC params, calls the Gateway, decodes base64 audio, and plays it via the existing MP3 streaming playerWhat did NOT change:
Fixes #98153
What Problem This Solves
Problem: Users who configure a non-ElevenLabs TTS provider (e.g. Xiaomi MiMo, Microsoft Azure) in their Gateway configuration cannot use it with iOS Talk Mode. When they set Talk Provider to "Gateway Default", iOS incorrectly treats the speech provider as a realtime voice configuration. The app shows "Voice Mode: Realtime Voice" and "Transport: Native WebRTC", attempts a WebRTC session that fails, and then falls back to iOS system speech. The user hears a generic system voice instead of their configured TTS provider. This breaks a core Talk workflow for anyone who does not use ElevenLabs as their TTS provider.
Why This Change Was Made: The root cause analysis identified two independent but compounding issues. First, the realtime classification logic used a provider name comparison (
activeProvider != "elevenlabs") as a condition, which is semantically incorrect — realtime mode should be determined by the config's talk.realtime.mode field, not by comparing provider names. The executionMode was already being computed correctly byresolvedExecutionMode(), so removing the provider name check is both safe and sufficient. Second, the playback fallback path had no knowledge of Gateway's talk.speak RPC, which is the correct way to synthesize speech for any non-ElevenLabs provider. macOS and Android both already implement this correctly — macOS viaplaybackPlan(provider:)returning.gatewayTalkSpeakThenSystemVoicefor non-default providers, and Android viaTalkSpeakClient.synthesize()callingtalk.speakdirectly. This fix aligns iOS behavior with both sibling platforms.User Impact: Positive — users of Xiaomi, Microsoft, and other Gateway speech providers can now use them with iOS Talk instead of being forced onto system voice. Negative — none expected, as the ElevenLabs path is completely unchanged and the new path only activates for non-ElevenLabs providers. If the Gateway talk.speak RPC fails for any reason (network error, Gateway not connected, empty audio response), the existing error handling catches the error and falls back to system voice, preserving the current degraded behavior.
Root Cause
Trigger condition: iOS Talk Provider set to "Gateway Default" with a Gateway talk.config where resolved.provider is a speech/TTS provider (xiaomi, microsoft, etc.) that is not elevenlabs.
Root cause analysis:
Affected scope: iOS Talk with Gateway Default provider selection when the resolved provider is a speech/TTS provider with mode "stt-tts". Users with ElevenLabs API key configured are not affected because their activeTalkProvider resolves to "elevenlabs", which passes the old formula. macOS and Android clients are not affected because they already use the correct pattern. iOS users explicitly selecting ElevenLabs or OpenAI Realtime in the provider picker are not affected.
Linked context
Real behavior proof
Behavior addressed: iOS Talk now uses Gateway talk.speak for non-ElevenLabs speech providers instead of classifying them as realtime and falling back to system voice.
Real environment tested: Source code review against openclaw/openclaw upstream/main at commit 6cb82ea. Full end-to-end verification requires macOS with Xcode and iOS Simulator, which is not available in this Windows development environment. Source-level reproduction confirmed by ClawSweeper as denoted by the clawsweeper:source-repro label.
Exact steps or command run after this patch:
Source review — code path traced through 3 critical checkpoints:
Checkpoint 1 — applyLoadedTalkConfig (line 2760):
Checkpoint 2 — start() method dispatch (line 338):
Checkpoint 3 — playAssistant() routing (line 1618):
Diff stat:
After-fix evidence:
usesRealtimeConfig verified against all 5 provider scenarios:
playAssistant new branch logic:
Observed result after the fix: The usesRealtimeConfig value is correct in all 5 provider scenarios. The playAssistant function now correctly routes non-ElevenLabs providers to Gateway talk.speak instead of skipping directly to system voice. The macOS and Android sibling implementations confirm this is the correct playback pattern. The realtimeWebRTCEnabled flag is false for speech-only provider configs, causing start() to go directly to the native speech pipeline without attempting a WebRTC session.
What was not tested: Full iOS Simulator or device end-to-end test (requires macOS with Xcode, not available in this Windows environment). The ClawSweeper source-level reproduction (clawsweeper:source-repro) confirms the fix shape is correct. A maintainer or CI with macOS infrastructure should verify with a real iOS Simulator run before merging. The unit tests in TalkModeConfigParsingTests.swift can be run with xcodebuild on macOS to confirm the parsing logic is correct.
Tests and validation
Unit tests
Two new tests added to TalkModeConfigParsingTests.swift:
Test 1 — parsesGatewaySpeechProviderAsNativeExecutionMode:
Test 2 — testRealTimeConfigStillParsesAsRealtimeRelay (regression):
All 10 existing tests are expected to remain passing as the config parsing logic (TalkModeGatewayConfigParser) is unchanged. The only behavioral change is in applyLoadedTalkConfig and playAssistant within TalkModeManager.swift.
Code review validation
Expected test output
When run on macOS with Xcode 15+ and iOS 17+ Simulator:
Risk checklist
This change is backwards compatible
This change has been tested with existing configurations
I have updated relevant documentation
Breaking changes (if any) are documented in Summary
Auth-provider risk: Low. Fixes provider routing specifically for speech/TTS providers (xiaomi, microsoft, etc.) that were incorrectly routed to the realtime path. The ElevenLabs path is unchanged.
Compatibility risk: Low. Only the non-ElevenLabs Gateway Default path is modified. All existing provider selections (Explicit ElevenLabs, Explicit OpenAI Realtime) produce identical behavior.
Merge-risk explanation: Low risk. Two focused changes in a single source file (TalkModeManager.swift) plus test additions (TalkModeConfigParsingTests.swift). The change has been source-reviewed against all provider scenarios. Full end-to-end test requires macOS CI.
Current review state