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:
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".
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
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.
## 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
`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`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What broke
wilwheatonkept failing in OpenWebUI with:That 29-name enum is Kokoro.js running in the browser — frank-tts never saw a request:
podman logs open-webuiheld zeroaudiolines 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.mdrecorded on 2026-10-18: "nouser.settingsrow … 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:Personal audio settings are the
settingsJSON column of tableuser, and the 0.12.x front end nests them underui. The 2026-10-18 probe listed only top-level keys — correct for 0.11.x, where they sat at the top asaudio.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/updateposts the whole blob back on any preference change, a tab holding the old copy writesbrowser-kokorostraight back — which is what made the toast return after it had been "fixed".Repaired live (not a repo change)
user.settingsforfrank8465@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-eraalloysurvives an engine-only fix as frank-tts's own rejection — measured:400 unknown voice 'alloy'; available voices: dr.kern, garrickhagon, jeffhays, vaati, wilwheaton.Proof
GET /api/v1/users/user/settingsas a real user over the vhostui.audio.tts.engine: "",voice: "wilwheaton"POST /api/v1/audio/speechas a real user over the vhostaudio/mpeg, frank-tts logsspeech voice=wilwheaton{"model":"frank-tts","input":…,"voice":"wilwheaton"}+Authorization: Bearer none)audio/wav; addresponse_format: mp3→ 200audio/mpeg./tests/run.shafter mergingorigin/mainALL TESTS PASSEDDocs 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/speechline is evidence, not a logging gap).@sha256:0b399a8b, bumped 2026-10-30) — the page claimed0.11.4in 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.mdtroubleshooting 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