Skip to content

fix(ios/talk): use Gateway talk.speak for non-ElevenLabs speech providers#98214

Closed
LeonidasLux wants to merge 2 commits into
openclaw:mainfrom
LeonidasLux:fix/issue-98153-gateway-speech-tts-provider-treated-as-realtime
Closed

fix(ios/talk): use Gateway talk.speak for non-ElevenLabs speech providers#98214
LeonidasLux wants to merge 2 commits into
openclaw:mainfrom
LeonidasLux:fix/issue-98153-gateway-speech-tts-provider-treated-as-realtime

Conversation

@LeonidasLux

Copy link
Copy Markdown
Contributor

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 != .native is corrected to usesRealtimeConfig = executionMode != .native — removing the provider name comparison that incorrectly flagged all non-ElevenLabs providers as realtime. The executionMode is already correctly determined by resolvedExecutionMode() based on the config's talk.realtime.mode field. Second, a Gateway talk.speak playback path is added to playAssistant(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:

  • Line 2760: Realtime classification formula corrected (removed activeProvider != Self.defaultTalkProvider condition)
  • Line 1618-1636: New else if branch in playAssistant() for non-ElevenLabs providers that calls playGatewayTalkSpeak()
  • Lines 1712-1768: New playGatewayTalkSpeak() method that builds talk.speak RPC params, calls the Gateway, decodes base64 audio, and plays it via the existing MP3 streaming player
  • TalkModeConfigParsingTests.swift: Two new tests covering speech-only provider parsing and realtime regression

What did NOT change:

  • ElevenLabs TTS streaming path is completely unchanged (same for both nativeElevenLabs selection and gateway default with elevenlabs provider)
  • OpenAI Realtime explicit selection path unchanged
  • macOS and Android client code unchanged
  • Gateway config schema unchanged
  • TalkModeGatewayConfigParser unchanged — only the consumer of its output (applyLoadedTalkConfig) is fixed
  • No public API, configuration keys, or CLI interfaces were modified

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 by resolvedExecutionMode(), 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 via playbackPlan(provider:) returning .gatewayTalkSpeakThenSystemVoice for non-default providers, and Android via TalkSpeakClient.synthesize() calling talk.speak directly. 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:

Level 1: iOS Talk plays system voice instead of configured Xiaomi TTS
  Why? playAssistant() has no talk.speak RPC call — it only supports ElevenLabs streaming TTS -> system voice fallback

Level 2: playAssistant() only has two branches (ElevenLabs or system voice)
  Why? The iOS implementation assumed users either have ElevenLabs configured or should use system TTS

Level 3: start() tries realtime WebRTC first (fails), then reaches native speech pipeline
  Why? usesRealtimeConfig = true for any activeProvider != Self.defaultTalkProvider

Level 4: usesRealtimeConfig formula incorrectly added a provider-name comparison
  Why? executionMode (.native) was already correctly derived from resolvedExecutionMode() based on talk.realtime.mode

Level 5 (root): Provider name and realtime mode are orthogonal concepts
  The formula mixed them: executionMode (derived from talk.realtime.mode) was the correct signal for realtime classification, but an unrelated activeProvider name comparison was added as an additional condition, causing all non-ElevenLabs providers to be misclassified as realtime

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):

Before: let usesRealtimeConfig = activeProvider != Self.defaultTalkProvider || executionMode != .native
After:  let usesRealtimeConfig = executionMode != .native

Checkpoint 2 — start() method dispatch (line 338):

Before: realtimeWebRTCEnabled = true for xiaomi -> attempts realtime WebRTC session
After:  realtimeWebRTCEnabled = false for xiaomi -> goes directly to native speech pipeline

Checkpoint 3 — playAssistant() routing (line 1618):

Before: ElevenLabs TTS -> system voice (no talk.speak path for non-ElevenLabs providers)
After:  ElevenLabs TTS -> Gateway talk.speak -> system voice (3-tier fallback)

Diff stat:

 apps/ios/Sources/Voice/TalkModeManager.swift    | 82 ++++++++++++++++
 apps/ios/Tests/TalkModeConfigParsingTests.swift | 63 +++++++++++++++
 2 files changed, 144 insertions(+), 1 deletion(-)

After-fix evidence:

usesRealtimeConfig verified against all 5 provider scenarios:

Scenario                              | activeProvider | executionMode  | usesRealtimeConfig
Gateway Default + xiaomi (speech)     | xiaomi         | .native         | false (was true)
Gateway Default + openai (realtime)   | openai         | .realtimeRelay  | true (unchanged)
Explicit ElevenLabs                   | elevenlabs     | .native         | false (unchanged)
Explicit OpenAI Realtime              | openai         | .realtimeRelay  | true (unchanged)
openai keyword override (line 2751)   | openai         | .realtimeRelay  | true (unchanged)

playAssistant new branch logic:

Line 1618: } else if self.activeTalkProvider != Self.defaultTalkProvider {
               GatewayDiagnostics.log("talk tts: provider=gateway talk.speak")
               applyVoiceModeDescriptor(providerId: "gateway", transport: "native", isRealtime: false)
               try await playGatewayTalkSpeak(text:cleaned, directive:, voiceId:, modelId:, language:)
           } else {
               // Original system voice fallback for ElevenLabs users without configured API key
           }

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 func parsesGatewaySpeechProviderAsNativeExecutionMode() {
    // Config has talk.provider = xiaomi, no realtime block
    // Expected: executionMode = .native, realtimeProvider = nil
    #expect(parsed.activeProvider == "xiaomi")
    #expect(parsed.executionMode == .native)
    #expect(parsed.realtimeProvider == nil)
}

Test 2 — testRealTimeConfigStillParsesAsRealtimeRelay (regression):

@Test func testRealTimeConfigStillParsesAsRealtimeRelay() {
    // Config has talk.realtime.mode = "realtime"
    // Expected: executionMode = .realtimeRelay (regression, unchanged)
    #expect(parsed.executionMode == .realtimeRelay)
}

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

  • usesRealtimeConfig change verified against all 5 provider scenarios (table above)
  • playGatewayTalkSpeak uses same gateway.request(method:paramsJSON:timeoutSeconds:) pattern as existing RPC calls (talk.client.create, chat.send, etc.)
  • TalkSpeakResult type is imported via OpenClawProtocol (already imported in TalkModeManager.swift)
  • Error handling: Gateway not connected throws -> outer catch block -> system voice fallback
  • Empty audio response throws -> same error handling path
  • Audio playback failure throws -> same error handling path
  • macOS implementation pattern matched (TalkModeRuntime.swift playGatewayTalkSpeak)
  • No lockfile changes confirmed via git diff --name-only
  • No public API or configuration changes
  • JSONSerialization used for RPC params (same pattern as TalkRealtimeClientCreateParams for talk.client.create)

Expected test output

When run on macOS with Xcode 15+ and iOS 17+ Simulator:

Test Suite 'TalkModeConfigParsingTests' started
  ✓ parsesOpenAIRealtimeProviderModelAndVoice
  ✓ infersRealtimeProviderWhenProviderMapHasSingleEntry
  ✓ formatsGenericRealtimeVoiceModeWithoutNativeProviderFallback
  ✓ defaultsOpenAIRealtimeModelWhenProviderOmitsModel
  ✓ resolvesRealtimeVoicePickerOverrides
  ✓ formatsOpenAIRealtimeVoiceMode
  ✓ formatsNativeTalkVoiceMode
  ✓ detectsPCMFormatRejectionFromElevenLabsError
  ✓ ignoresGenericPlaybackFailuresForPCMFormatRejection
  ✓ parsesGatewaySpeechProviderAsNativeExecutionMode  (new)
  ✓ testRealTimeConfigStillParsesAsRealtimeRelay      (new regression)
Test Suite 'TalkModeConfigParsingTests' passed (11 tests, 0 failed)

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

  • Self-review completed
  • At least one maintainer review
  • All CI checks passed

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 12:47 PM ET / 16:47 UTC.

Summary
The PR changes iOS Talk Mode to keep Gateway speech providers on the native path, add Gateway talk.speak playback for non-ElevenLabs providers, and add Swift parsing regression tests.

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 talk.speak route on iOS native playback. I did not establish a live iOS simulator or device repro in this read-only review.

Review metrics: 1 noteworthy metric.

  • iOS Talk routing surfaces changed: 1 realtime gate changed, 2 Gateway TTS playback entry points added. Provider routing and fallback behavior are compatibility-sensitive for existing iOS Talk users using Gateway Default speech providers.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98153
Summary: This PR is a candidate fix for the canonical iOS Gateway Default speech-provider misclassification and missing talk.speak playback issue.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Forward the full talk.speak directive payload from iOS instead of the current partial string-only payload.
  • [P1] Add redacted iOS simulator or device proof showing Gateway Default with a non-ElevenLabs provider stays native and plays through talk.speak; after updating the PR body, ClawSweeper should re-review automatically, or a maintainer can comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body reports source review only and no after-fix iOS simulator or device run; it needs redacted proof showing Gateway Default with a non-ElevenLabs provider using native talk.speak audio. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P2] The PR changes provider routing and fallback behavior for existing iOS Gateway Default users, so upgrade/runtime proof is needed before merge.
  • [P1] The new iOS talk.speak helper still drops most directive fields supported by the Gateway protocol and sibling clients, so per-reply voice controls can be ignored for Gateway speech providers.
  • [P1] Another open PR targets the same canonical bug, so maintainers should choose one final branch rather than landing overlapping implementations.

Maintainer options:

  1. Fix the iOS Gateway speak payload before merge (recommended)
    Update the helper to use the full TalkSpeakParams/sibling-client directive mapping, then add redacted iOS runtime proof for a non-ElevenLabs Gateway provider.
  2. Pick one canonical iOS branch
    Compare this PR with fix(ios): keep gateway speech providers native #98212 and land only the branch with the cleaner talk.speak implementation and proof.
  3. Accept reduced directive support intentionally
    Maintainers could merge with directive loss as a known limitation, but that should be an explicit compatibility decision because other native clients already forward those fields.

Next step before merge

  • [P1] Human PR review is needed because the branch still has a concrete protocol-contract finding, overlaps another open candidate PR, and needs contributor-provided real iOS behavior proof that automation cannot supply for their setup.

Security
Cleared: The diff touches iOS Gateway RPC playback logic and tests, with no new dependencies, workflows, lockfiles, package execution paths, or secret-handling expansion.

Review findings

  • [P2] Forward the complete talk.speak directive payload — apps/ios/Sources/Voice/TalkModeManager.swift:1730-1736
Review details

Best possible solution:

Land one canonical iOS fix branch that routes native speech through a complete Gateway talk.speak request, preserves explicit OpenAI realtime and ElevenLabs behavior, and includes redacted iOS simulator or device proof.

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 talk.speak route on iOS native playback. I did not establish a live iOS simulator or device repro in this read-only review.

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 talk.speak directive payload like the protocol and sibling native clients do, and the PR still needs real iOS behavior proof.

Full review comments:

  • [P2] Forward the complete talk.speak directive payload — apps/ios/Sources/Voice/TalkModeManager.swift:1730-1736
    This helper accepts directive but only sends string fields for text, voice, model, default output format, and language. The generated TalkSpeakParams contract and the macOS/Android clients also forward speed, rateWpm, stability, similarity, style, speakerBoost, seed, normalize, and latencyTier, so iOS Gateway speech can silently ignore per-reply voice controls after this PR.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 6cb82eaab865.

Label changes

Label justifications:

  • P1: This PR targets a broken user-facing iOS Talk workflow where configured Gateway speech providers are ignored and the app can attempt the wrong realtime provider route.
  • merge-risk: 🚨 compatibility: The diff changes fallback and routing behavior for existing iOS Talk Gateway Default setups, so upgrade/runtime behavior needs proof before merge.
  • merge-risk: 🚨 auth-provider: The central behavior changes provider routing and model/voice choice among Gateway speech providers, local ElevenLabs, system speech, and OpenAI realtime.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body reports source review only and no after-fix iOS simulator or device run; it needs redacted proof showing Gateway Default with a non-ElevenLabs provider using native talk.speak audio. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Other +162. Total +162 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 2 166 4 +162
Total 2 166 4 +162

What I checked:

  • Repository policy read: Root AGENTS.md was read fully and its PR review, compatibility, provider-routing, and proof rules apply; no scoped AGENTS.md exists under apps/. (AGENTS.md:1, 6cb82eaab865)
  • Current-main routing bug: Current main still sets realtime mode from activeProvider != Self.defaultTalkProvider || executionMode != .native, which source-reproduces the non-ElevenLabs speech-provider misclassification. (apps/ios/Sources/Voice/TalkModeManager.swift:2680, 6cb82eaab865)
  • PR head covers main playback routes: The current PR head adds Gateway talk.speak handling to both playAssistant and speakIncrementalSegment, addressing the earlier playAssistant-only defect at source level. (apps/ios/Sources/Voice/TalkModeManager.swift:1618, 83fd85470c64)
  • Remaining payload gap: The PR helper builds a string-only payload with text, voice, model, default output format, and language, while the generated protocol and macOS/Android sibling implementations support additional directive fields such as speed, rateWpm, stability, similarity, style, speakerBoost, seed, normalize, and latencyTier. (apps/ios/Sources/Voice/TalkModeManager.swift:1730, 83fd85470c64)
  • Talk contract: The user-facing Talk docs say native iOS/macOS/Android Talk uses local speech recognition, Gateway chat, and talk.speak TTS, matching the target behavior for the canonical bug. Public docs: docs/nodes/talk.md. (docs/nodes/talk.md:11, 6cb82eaab865)
  • Proof state: The PR body states that only source review was performed and that full iOS Simulator or device verification was not run, so external-contributor real behavior proof remains missing. (83fd85470c64)

Likely related people:

  • ngutman: Recent merged history touched iOS Talk realtime fallback, voice-mode display, playback, and parsing-test surfaces around the same manager boundary. (role: recent area contributor; confidence: high; commits: 47dbc675e953, 6897711d1991, 19e4c37c3775; files: apps/ios/Sources/Voice/TalkModeManager.swift, apps/ios/Tests/TalkModeConfigParsingTests.swift)
  • Solvely-Colin: Path history shows iOS gateway and realtime Talk flow work that wired the same TalkModeManager routing area. (role: iOS gateway flow contributor; confidence: medium; commits: f6e51ff99af4, e730e9bd0b83; files: apps/ios/Sources/Voice/TalkModeManager.swift, apps/ios/Sources/Voice/TalkModeGatewayConfig.swift, apps/ios/Tests/TalkModeConfigParsingTests.swift)
  • steipete: History includes shared Apple Talk config parsing, native node Talk handoff, and adjacent macOS/Android talk.speak behavior used as sibling contract evidence here. (role: adjacent native Talk owner; confidence: medium; commits: 4f482d2a2b4d, 466f7183207d, 98d593956493; files: apps/ios/Sources/Voice/TalkModeManager.swift, apps/ios/Tests/TalkModeConfigParsingTests.swift, apps/android/app/src/main/java/ai/openclaw/app/voice/TalkSpeakClient.kt)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 30, 2026
…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
@LeonidasLux
LeonidasLux force-pushed the fix/issue-98153-gateway-speech-tts-provider-treated-as-realtime branch from ae467f3 to a00fd39 Compare June 30, 2026 16:31
- 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]>
@LeonidasLux

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@github-actions github-actions Bot added duplicate This issue or pull request already exists close:duplicate Closed as duplicate dedupe:child Duplicate issue/PR child in dedupe cluster labels Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the fix. This is now covered by the landed #98376 / commit d0f6558.

Evidence: shared issue(s): #98153; overlapping changed hunks; shared file(s): apps/ios/Sources/Voice/TalkModeManager.swift.

Closing #98214 as a duplicate.

@github-actions github-actions Bot closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: ios App: ios close:duplicate Closed as duplicate dedupe:child Duplicate issue/PR child in dedupe cluster duplicate This issue or pull request already exists merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(ios/talk): Gateway speech TTS provider is treated as realtime and falls back to iOS system voice

1 participant