Skip to content

Add resilient TTS fallback and readiness status#806

Merged
shanselman merged 2 commits into
openclaw:mainfrom
bkudiess:bkudiess-voice-talk-parity
Jun 26, 2026
Merged

Add resilient TTS fallback and readiness status#806
shanselman merged 2 commits into
openclaw:mainfrom
bkudiess:bkudiess-voice-talk-parity

Conversation

@bkudiess

@bkudiess bkudiess commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds resilient text-to-speech provider handling for the Windows node.

  • tts.speak now falls back to Windows TTS when the configured/default provider is not usable.
  • Explicit per-call provider requests stay strict, so callers can rely on provider selection.
  • tts.speak reports requestedProvider and fellBack so callers can explain degraded playback.
  • Adds a read-only tts.status command that reports provider readiness without exposing voice IDs, API keys, device names, or exception details.
  • Wires tts.status through capability registration, MCP docs, skill.md, setup-generated allowlists, and tests.
  • Removes a stale Piper checksum TODO now that asset hash verification is already implemented.

Why

The Windows node already supports multiple TTS providers (piper, windows, elevenlabs), but the previous behavior failed hard when the configured provider was not ready. Examples:

  • Piper is selected by default, but the voice model is not downloaded.
  • ElevenLabs is selected by default, but no API key or voice ID is configured.
  • The configured Windows voice ID is stale or no longer installed.

Windows TTS is the reliable local fallback for configured/default provider degradation, so this change keeps speech working and makes provider state observable for callers. Explicit provider requests remain strict to preserve the existing provider-selection contract.

Behavior changes

tts.speak

Before:

  • Resolves the requested/configured provider.
  • Throws if that provider is unavailable.
  • Returns only { spoken, provider, contentType, durationMs }.

After:

  • Uses the requested/configured provider when ready.
  • Falls back to Windows TTS only when the configured/default provider is not ready.
  • Keeps explicit per-call provider requests strict. Explicit unavailable piper, explicit unavailable elevenlabs, and unsupported provider IDs do not silently reroute to Windows.
  • Drops provider-specific voiceId / model when falling back so Windows does not reject a Piper/ElevenLabs voice ID.
  • Treats a stale configured Windows voice as non-fatal and uses the system default voice.
  • Still throws for an explicit invalid per-call Windows voice, because that is a caller error.
  • Returns { spoken, provider, requestedProvider, fellBack, contentType, durationMs }.

tts.status

New read-only command returning:

{
  "configuredProvider": "piper",
  "effectiveProvider": "windows",
  "willFallBack": true,
  "providers": [
    { "provider": "piper", "readiness": "voice-not-downloaded", "isReady": false },
    { "provider": "windows", "readiness": "ready", "isReady": true },
    { "provider": "elevenlabs", "readiness": "needs-api-key", "isReady": false }
  ]
}

Readiness values are fixed strings:

  • ready
  • needs-api-key
  • needs-voice
  • voice-not-downloaded
  • unavailable

Files of interest

File Change
src/OpenClaw.Shared/Capabilities/TtsCapability.cs Adds tts.status, provider-readiness types, fallback resolver, and expanded speak result.
src/OpenClaw.Tray.WinUI/Services/TextToSpeech/TextToSpeechService.cs Implements provider readiness checks, configured/default fallback behavior, strict explicit-provider handling, and status generation.
src/OpenClaw.Tray.WinUI/Services/NodeService.cs Wires TtsCapability.StatusRequested.
src/OpenClaw.Shared/Models.cs Adds tts.status to privacy-gated command grouping.
src/OpenClaw.Shared/Mcp/McpToolBridge.cs Documents tts.status and updated tts.speak result shape.
src/OpenClaw.WinNode.Cli/skill.md Keeps CLI capability docs in sync.
src/OpenClaw.SetupEngine/SetupContext.cs Includes tts.status in setup-generated gateway.nodes.allowCommands.
src/OpenClaw.Shared/Audio/PiperVoiceManager.cs Removes stale checksum TODO.
tests/... Adds coverage for fallback resolution, explicit provider strictness, status projection, command grouping, and setup allowlist output.

Proof

Validated locally on branch head a9fc0c62, rebased onto current origin/main.

Check Result
./build.ps1 ✅ all 5 projects built
dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj --no-restore ✅ 2437 passed, 29 skipped
dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --no-restore ✅ 1163 passed on rerun
dotnet test ./tests/OpenClaw.WinNode.Cli.Tests/OpenClaw.WinNode.Cli.Tests.csproj --no-restore ✅ 120 passed
dotnet test ./tests/OpenClaw.SetupEngine.Tests/OpenClaw.SetupEngine.Tests.csproj --no-restore ✅ 282 passed
Targeted TTS/status tests ✅ 29 passed
Docs/skill drift tests after wording fix ✅ 120 passed
Exact failed CI WebSocket test ✅ 1 passed locally

Runtime proof through the tray-local MCP path

I launched the actual Windows tray from this branch in an isolated data directory with:

  • local MCP enabled
  • node mode disabled
  • TTS capability enabled
  • configured TTS provider set to elevenlabs
  • no ElevenLabs API key or voice ID configured
  • Piper voice left undownloaded

Then I invoked the real local MCP endpoint through winnode. This exercises:

winnode → local MCP HTTP server → NodeServiceTtsCapabilityTextToSpeechService

Terminal output:

=== winnode tools/list includes tts ===
      "name": "tts.speak",
      "name": "tts.status",

=== tts.status through live tray MCP ===
{
  "configuredProvider": "elevenlabs",
  "effectiveProvider": "windows",
  "willFallBack": true,
  "providers": [
    {
      "provider": "piper",
      "readiness": "voice-not-downloaded",
      "isReady": false
    },
    {
      "provider": "windows",
      "readiness": "ready",
      "isReady": true
    },
    {
      "provider": "elevenlabs",
      "readiness": "needs-api-key",
      "isReady": false
    }
  ]
}

=== tts.speak omitted-provider fallback through live tray MCP/TextToSpeechService ===
{
  "spoken": true,
  "provider": "windows",
  "requestedProvider": "elevenlabs",
  "fellBack": true,
  "contentType": "audio/wav",
  "durationMs": 4230
}

=== explicit provider remains strict (no fallback) ===
Speak failed
exit=1

This confirms the registered runtime tool surface, readiness payload, configured/default fallback through TextToSpeechService, and strict explicit-provider behavior.

Additional verification:

  • PR is based on main, is 0 commits behind origin/main, and contains 1 commit / 11 files.
  • GitHub reports the PR as mergeable (UNSTABLE means CI is pending or has a non-merge failure, not a conflict).

Current CI note

The current red test job is not caused by this TTS change. The job log shows:

  1. Gateway LKG drift gate: pinned 2026.6.9, npm latest 2026.6.10; the workflow explicitly says to run gateway-lkg-update to refresh the standing draft PR.
  2. Unrelated Shared test failure: WebSocketClientBaseTests.ReconnectBackoff_ReconnectsCurrentClosingSocket_WhenSupersededLoopIsActive failed an Assert.True at WebSocketClientBaseTests.cs:411. This PR does not touch WebSocket client code, and the exact test passes locally as listed above.

Notes / limits

  • This does not add UI controls. It only makes TTS provider behavior resilient and observable.
  • tts.status.willFallBack reflects configured defaults. A specific tts.speak call with its own voiceId stays strict and may not match the default snapshot.
  • tts.status is grouped with the other voice commands behind NodeTtsEnabled.

@clawsweeper

clawsweeper Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed June 25, 2026, 10:28 PM ET / 02:28 UTC.

Summary
The branch adds Windows fallback for configured/default TTS provider failures, exposes tts.status, expands the tts.speak result shape, wires setup/MCP/skill command surfaces, and adds tests.

Reproducibility: not applicable. as a bug reproduction because this is a feature/API PR. Source inspection confirms current main lacks tts.status and configured/default fallback.

Review metrics: 3 noteworthy metrics.

  • Diff Scope: 12 files changed, +508/-16. The PR spans runtime behavior, shared command/result types, setup allowlists, MCP/CLI docs, and tests.
  • Command Surface: 1 command added, 2 result fields added. tts.status, requestedProvider, and fellBack become public node/MCP/gateway API surface.
  • Proof Currency: proof cites a9fc0c6; current head is 60da376. The latest chat playback change needs current-head real behavior proof before merge.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦐 gold shrimp
Patch quality: 🐚 platinum hermit
Result: blocked until stronger 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:

  • Update the PR body with current-head real behavior proof for live tts.status/fallback and the chat playback default-provider path.
  • [P2] Have maintainers explicitly accept the fallback/status contract and existing-allowlist upgrade behavior before merge.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes useful terminal proof for the earlier a9fc0c62 head, but current head 60da376e adds chat playback behavior; add current-head terminal output, logs, or a recording with private data redacted, then update the PR body to trigger re-review.

Risk before merge

  • [P1] Configured/default TTS failures would change from provider-specific failure into Windows playback, which may surprise callers that treat provider failure as meaningful state.
  • [P1] tts.status plus the new requestedProvider and fellBack fields become gateway/local-MCP-facing API surface and should be accepted as a stable command/result contract.
  • [P1] Existing gateway allowCommands setups that only allow tts.speak will not expose tts.status until operator policy is updated and the node is reapproved or re-paired.
  • [P1] The PR body validation and live terminal proof still cite a9fc0c62, while the current head is 60da376e and adds chat playback behavior.

Maintainer options:

  1. Refresh Current-Head Proof (recommended)
    Ask for terminal output, logs, or a short recording from current head showing the fallback/status path and the new chat playback default-provider path before merge.
  2. Accept The New TTS Contract
    Maintainers can explicitly accept that omitted-provider/configured-provider failures degrade to Windows playback while explicit provider requests remain strict.
  3. Pause For Strict Provider Policy
    If provider failure should remain fail-closed or become configurable, pause this PR until the permanent TTS provider policy is chosen.

Next step before merge

  • [P2] Human review is needed for API/fallback policy acceptance and updated current-head proof; there is no narrow automated code repair.

Security
Cleared: The diff adds sanitized status/fallback behavior without changing dependency sources, CI workflows, package execution, broad permissions, or secret storage.

Review details

Best possible solution:

Merge only after current-head live proof covers the runtime paths and maintainers explicitly accept the omitted-provider fallback plus tts.status contract.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction because this is a feature/API PR. Source inspection confirms current main lacks tts.status and configured/default fallback.

Is this the best way to solve the issue?

Unclear pending maintainer product/API acceptance. The implementation is narrow and preserves explicit-provider strictness, but the fallback behavior, new command/result contract, and current-head proof should be accepted before merge.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes useful terminal proof for the earlier a9fc0c62 head, but current head 60da376e adds chat playback behavior; add current-head terminal output, logs, or a recording with private data redacted, then update the PR body to trigger re-review.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.
  • remove status: 👀 ready for maintainer look: Current PR status label is status: 📣 needs proof.
  • remove proof: sufficient: Current real behavior proof status is insufficient, not sufficient.

Label justifications:

  • P2: This is a normal-priority TTS provider improvement with limited blast radius but real command/API impact.
  • merge-risk: 🚨 compatibility: The PR changes existing configured/default TTS failure behavior into Windows fallback and expands the tts.speak response shape.
  • merge-risk: 🚨 auth-provider: Missing ElevenLabs credentials can now route omitted-provider speech through Windows fallback, changing provider routing semantics even though explicit requests stay strict.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes useful terminal proof for the earlier a9fc0c62 head, but current head 60da376e adds chat playback behavior; add current-head terminal output, logs, or a recording with private data redacted, then update the PR body to trigger re-review.
Evidence reviewed

What I checked:

Likely related people:

  • RBrid: GitHub commit history for TtsCapability and TextToSpeechService points to the merged Windows TTS and Piper/STT capability foundation. (role: TTS/audio feature owner; confidence: high; commits: e0c40985a718, b0ba9affa25d; files: src/OpenClaw.Shared/Capabilities/TtsCapability.cs, src/OpenClaw.Tray.WinUI/Services/TextToSpeech/TextToSpeechService.cs)
  • christineyan4: Recent merged chat revamp and polish commits touch the chat coordinator area affected by the latest provider-null playback change. (role: chat playback area contributor; confidence: medium; commits: cd4acf013e38, 488f26509ced; files: src/OpenClaw.Tray.WinUI/Chat/OpenClawChatCoordinator.cs)
  • ranjeshj: GitHub commit history for SetupContext points to out-of-process SetupEngine and deterministic setup work that owns the generated allowlist surface. (role: setup engine area contributor; confidence: medium; commits: cefce3952ab1, 98d0760cb5b6; files: src/OpenClaw.SetupEngine/SetupContext.cs, tests/OpenClaw.SetupEngine.Tests/SetupConfigTests.cs)
  • shanselman: Current history shows recent hardening in the TTS service area, and the latest PR head commit changes chat playback fallback wiring. (role: recent TTS/chat adjacent contributor; confidence: medium; commits: d23f8ca50013, 60da376e1c26; files: src/OpenClaw.Tray.WinUI/Services/TextToSpeech/TextToSpeechService.cs, src/OpenClaw.Tray.WinUI/Chat/OpenClawChatCoordinator.cs)
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. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 23, 2026
@bkudiess bkudiess changed the base branch from master to main June 23, 2026 19:29
@bkudiess bkudiess force-pushed the bkudiess-voice-talk-parity branch 2 times, most recently from e14840d to a6722a1 Compare June 23, 2026 20:34
@bkudiess bkudiess changed the title Voice/Talk parity: TTS provider fallback + tts.status Add TTS provider fallback and tts.status readiness command Jun 23, 2026
@bkudiess bkudiess force-pushed the bkudiess-voice-talk-parity branch 4 times, most recently from a639b16 to 6afed4f Compare June 25, 2026 19:06
@bkudiess bkudiess changed the title Add TTS provider fallback and tts.status readiness command Add resilient TTS fallback and readiness status Jun 25, 2026
@bkudiess bkudiess marked this pull request as ready for review June 25, 2026 19:45
@bkudiess bkudiess force-pushed the bkudiess-voice-talk-parity branch from 6afed4f to 795eef1 Compare June 25, 2026 20:38
@bkudiess

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown

🦞🧹
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.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 25, 2026
@bkudiess bkudiess force-pushed the bkudiess-voice-talk-parity branch from 795eef1 to a9fc0c6 Compare June 25, 2026 23:16
@bkudiess

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown

🦞🧹
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.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 25, 2026
@bkudiess

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 25, 2026
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 25, 2026
00sadik-lab and others added 2 commits June 25, 2026 18:21
When the requested/configured TTS provider isn't usable (no ElevenLabs key,
Piper voice not downloaded), gracefully fall back to Windows TTS instead of
throwing, and report the effective vs requested provider plus a fellBack flag.
A stale configured Windows voice now degrades to the system default voice so
the fallback always speaks.

Add a tts.status node command that reports per-provider readiness
(ready/needs-api-key/needs-voice/voice-not-downloaded/unavailable) plus the
configured/effective provider and willFallBack. PII-free. Wired across
DangerousCommands gating, MCP descriptions, skill.md, and the setup allowlist
export. Fix a stale pre-GA SHA-256 TODO docstring in PiperVoiceManager
(verification is already implemented and enforced by AssetHashPinningTests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Leave chat playback provider unset so TextToSpeechService can distinguish configured/default playback from explicit provider requests and apply the new Windows fallback when the configured provider is unavailable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@shanselman shanselman force-pushed the bkudiess-voice-talk-parity branch from a9fc0c6 to 60da376 Compare June 26, 2026 02:24
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 26, 2026
@shanselman shanselman merged commit ff5cafb into openclaw:main Jun 26, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. 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.

3 participants