docs(openwebui): the browser-kokoro switch is the user's settings row, not browser storage #141

Merged
dank merged 2 commits from docs/openwebui-tts-kokoro-user-row into main 2026-11-04 12:14:35 -08:00
Owner

What broke

wilwheaton kept failing in OpenWebUI with:

Voice: "wilwheaton" not found. Should be one of: af_heart, …

That 29-name enum is Kokoro.js running in the browser — frank-tts never saw a request: podman logs open-webui held zero audio lines for 72 h while uvicorn logged every other request line, and the session had fetched /wasm/ort-wasm-simd-threaded.jsep.wasm (onnxruntime ⇒ Kokoro.js live in that browser).

Why the page was wrong

OpenWebUI.md recorded on 2026-10-18: "no user.settings row … carries an audio override ⇒ the choice lives in that browser's own storage. Fix in the UI: Settings → Audio → TTS Engine → Default." The row did carry it:

"ui": { "audio": { "tts": { "engine": "browser-kokoro", "voice": "alloy", "defaultVoice": "alloy", … } } }

Personal audio settings are the settings JSON column of table user, and the 0.12.x front end nests them under ui. The 2026-10-18 probe listed only top-level keys — correct for 0.11.x, where they sat at the top as audio.tts.* — found no audio key, and blamed browser storage. The 2026-10-30 pin bump to 0.12.5 moved the keys; the probe depth never followed.

Because the SPA re-reads that row at load and POST …/settings/update posts the whole blob back on any preference change, a tab holding the old copy writes browser-kokoro straight back — which is what made the toast return after it had been "fixed".

Repaired live (not a repo change)

user.settings for frank8465@protonmail.com: ui.audio.tts → {"engine": "", "voice": "wilwheaton", "defaultVoice": "wilwheaton"}.

Both fields matter. With engine "" the SPA still sends its stored voice, so the Kokoro-era alloy survives an engine-only fix as frank-tts's own rejection — measured: 400 unknown voice 'alloy'; available voices: dr.kern, garrickhagon, jeffhays, vaati, wilwheaton.

Proof

Check Result
GET /api/v1/users/user/settings as a real user over the vhost ui.audio.tts.engine: "", voice: "wilwheaton"
POST /api/v1/audio/speech as a real user over the vhost 200 audio/mpeg, frank-tts logs speech voice=wilwheaton
Handler's own outbound shape from inside the container ({"model":"frank-tts","input":…,"voice":"wilwheaton"} + Authorization: Bearer none) 200 audio/wav; add response_format: mp3 → 200 audio/mpeg
./tests/run.sh after merging origin/main 824/824 ALL TESTS PASSED

Docs change

  • Dankclaw/OpenWebUI.md § Text-to-speech: where the override really lives and its version-dependent nesting; the whole-blob update loop; repair engine and voice; the two one-line tells (an ORT-wasm fetch means Kokoro.js is live in that browser; uvicorn logs every request, so a missing /api/v1/audio/speech line is evidence, not a logging gap).
  • Deployed build is 0.12.5 (pin @sha256:0b399a8b, bumped 2026-10-30) — the page claimed 0.11.4 in three places. The duplicated 401 sentence in API access is gone and the loopback line is attributed only to what was actually re-measured.
  • Dankclaw/Frank TTS.md troubleshooting row: version-free, and it names the user row as the switch.

The two managed skills carrying the wrong probe path (openwebui-tts-chain-audit, frank-tts-consumer-voice-triage) were corrected outside the repo, same facts.

Docs-only: no deploy, no container touched.


cwd: /home/blank/.omp/wt/wt-20261104-104444-6d1fc1f · session: 01a1a7eb-c584-72d3-9d7e-c02d981cd662

## What broke `wilwheaton` kept failing in OpenWebUI with: ``` Voice: "wilwheaton" not found. Should be one of: af_heart, … ``` That 29-name enum is **Kokoro.js running in the browser** — frank-tts never saw a request: `podman logs open-webui` held zero `audio` lines for 72 h while uvicorn logged every other request line, and the session had fetched `/wasm/ort-wasm-simd-threaded.jsep.wasm` (onnxruntime ⇒ Kokoro.js live in that browser). ## Why the page was wrong `OpenWebUI.md` recorded on 2026-10-18: "no `user.settings` row … carries an audio override ⇒ the choice lives in that browser's own storage. Fix in the UI: Settings → Audio → TTS Engine → **Default**." The row **did** carry it: ```json "ui": { "audio": { "tts": { "engine": "browser-kokoro", "voice": "alloy", "defaultVoice": "alloy", … } } } ``` Personal audio settings are the `settings` JSON column of table `user`, and the 0.12.x front end **nests them under `ui`**. The 2026-10-18 probe listed only top-level keys — correct for 0.11.x, where they sat at the top as `audio.tts.*` — found no audio key, and blamed browser storage. The 2026-10-30 pin bump to 0.12.5 moved the keys; the probe depth never followed. Because the SPA re-reads that row at load and `POST …/settings/update` posts the **whole** blob back on any preference change, a tab holding the old copy writes `browser-kokoro` straight back — which is what made the toast return after it had been "fixed". ## Repaired live (not a repo change) `user.settings` for `frank8465@protonmail.com`: `ui.audio.tts` → `{"engine": "", "voice": "wilwheaton", "defaultVoice": "wilwheaton"}`. Both fields matter. With engine `""` the SPA still sends its stored voice, so the Kokoro-era `alloy` survives an engine-only fix as frank-tts's own rejection — measured: `400 unknown voice 'alloy'; available voices: dr.kern, garrickhagon, jeffhays, vaati, wilwheaton`. ## Proof | Check | Result | |---|---| | `GET /api/v1/users/user/settings` as a real user over the vhost | `ui.audio.tts.engine: ""`, `voice: "wilwheaton"` | | `POST /api/v1/audio/speech` as a real user over the vhost | **200 `audio/mpeg`**, frank-tts logs `speech voice=wilwheaton` | | Handler's own outbound shape from inside the container (`{"model":"frank-tts","input":…,"voice":"wilwheaton"}` + `Authorization: Bearer none`) | 200 `audio/wav`; add `response_format: mp3` → 200 `audio/mpeg` | | `./tests/run.sh` after merging `origin/main` | 824/824 `ALL TESTS PASSED` | ## Docs change - `Dankclaw/OpenWebUI.md` § Text-to-speech: where the override really lives and its version-dependent nesting; the whole-blob update loop; repair engine **and** voice; the two one-line tells (an ORT-wasm fetch means Kokoro.js is live in that browser; uvicorn logs every request, so a missing `/api/v1/audio/speech` line is evidence, not a logging gap). - Deployed build is **0.12.5** (pin `@sha256:0b399a8b`, bumped 2026-10-30) — the page claimed `0.11.4` in three places. The duplicated 401 sentence in *API access* is gone and the loopback line is attributed only to what was actually re-measured. - `Dankclaw/Frank TTS.md` troubleshooting row: version-free, and it names the user row as the switch. The two managed skills carrying the wrong probe path (`openwebui-tts-chain-audit`, `frank-tts-consumer-voice-triage`) were corrected outside the repo, same facts. Docs-only: no deploy, no container touched. --- cwd: /home/blank/.omp/wt/wt-20261104-104444-6d1fc1f · session: 01a1a7eb-c584-72d3-9d7e-c02d981cd662
dank added 2 commits 2026-11-04 12:13:44 -08:00
`wilwheaton` kept failing in OpenWebUI with the 29-name Kokoro enum even
after the dropdown was set to Default. The page's 2026-10-18 conclusion —
"no user.settings row carries an audio override, so the choice lives in that
browser's own storage" — was a probe-depth bug: the personal audio settings
live in table `user`, and the 0.12.x front end nests them under `ui`
(`settings.ui.audio.tts.engine`). The row did carry `browser-kokoro`, and the
SPA re-applies the server copy on every load, so the UI fix never stuck.

Corrected facts on the owner page:
- where the override really lives, and the version-dependent nesting;
- repair BOTH engine and voice: engine "" still sends the stored voice, so a
  Kokoro-era `voice: "alloy"` returns as frank-tts's own 400;
- the one-line tells: an `/wasm/ort-wasm-simd-threaded.jsep.wasm` fetch means
  Kokoro.js is live in that browser, and uvicorn logs every request so a
  missing `/api/v1/audio/speech` line is evidence;
- deployed build is 0.12.5 (pin `0b399a8b`, bumped 2026-10-30) — the page
  still claimed 0.11.4 — and the 401 paragraph's duplicated sentence is gone.

Verified 2026-11-04: with the row repaired, `POST /api/v1/audio/speech` as a
real user over the vhost returns 200 audio/mpeg and frank-tts logs
`speech voice=wilwheaton`.
dank merged commit 085a5d570c into main 2026-11-04 12:14:35 -08:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dank/serverAppConfigs#141