Add targeted dialogue-repair pass and auto-repair chain, fix rollback on stopped runs (v1.20.16)

Spoken lines that PDF extraction typed as narration were invisible to
"Identify unknown characters" (it only scans type==='dialogue'), so no number
of re-runs could fix them, while the pass that could re-sent all ~1000
narration segments to the LLM. Adds a detector that scores narration for
direct-speech signals and re-checks only those: 36 candidates instead of 1036
on a real novel. Adds "Auto-repair cast" to chain the passes in the right
order automatically after casting.

Also fixes a rollback gap where stopping a run skipped the safety net that
undoes a pass which increased Unknown speakers (confirmed live: 80 -> 159
with no warning), corrects an attribution rule that taught the model the
listener is the speaker, and stops the threshold loop from re-verifying every
narration segment each round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-08-12 08:40:38 +02:00
parent 193cbb202d
commit 4c686165b3
5 changed files with 193 additions and 35 deletions

View File

@ -22,6 +22,19 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
- **Emotion instructions are now always written in English**, even for non-English voices (the spoken text and the native-accent clause stay in the book's own language). Confirmed by controlled A/B testing — same line, same voice, only the instruct language varying — that Qwen3-TTS follows English emotion instructions far more reliably: German instructs produced barely-differentiated output, while English instructs yield a clean, correctly-ordered prosodic gradient (whisper 128 Hz → sad 142 → neutral 179 → scared 203 → happy 225 → angry 269 Hz), with sensible duration changes too (sad slowest, scared fastest).
- **Fish-Speech generation parameters (`temperature` / `top_p` / `repetition_penalty`) were never forwarded.** Every Fish-Speech line synthesized at the server's fixed defaults, ignoring the app's per-backend stability settings — the only backend not routed through the shared `_apply_tts_extra_params` helper.
## [1.20.16] — 2026-08-12
### Added
- **"Repair mislabeled dialogue"** — a new casting pass for spoken lines that PDF extraction typed as narration (usually because the `» «` quote marks were lost). These were invisible to "Identify unknown characters", which only scans `type === 'dialogue'`, so re-running it could never fix them; the only pass that could ("Verify all characters") re-sent every narration segment to the LLM. The new pass scores narration for direct-speech signals (a bare inquit in the next segment, stray quote marks, imperative opening, 1st/2nd person, ending in `!`/`?`) and re-checks only the suspicious ones — measured on a real novel: **36 candidates instead of 1036, 29x fewer LLM calls**.
- **"Auto-repair cast"** — chains the repair sequence automatically (repair mislabeled → resolve unknown, repeated while it still helps, then a full verification only if still above target). Runs on its own after a completed cast when more than 10 Unknown speakers remain; never after a stopped run. Disable via `localStorage['ab-auto-repair-enabled'] = '0'`.
- **German speaker-attribution rules 1319** in both the casting and verification prompts: listener/vocative exclusion (a name beside a quote is often the listener, not the speaker), split quotes with pronoun inquits, ranked disambiguation among multiple candidates, continuation over ping-pong, unnamed actors as speakers, epithet resolution ("die Elfe" → Nyrilla), and splitting segments that fuse narration with dialogue or two speakers' turns.
### Fixed
- **A stopped casting run could silently leave the cast worse than before.** The automatic rollback that undoes a pass which increases Unknown speakers was gated behind `!_audiobook.cancel`, so stopping a run skipped it entirely — confirmed live: an interrupted pass took a book from 80 Unknown lines to 159 with no warning and no way back. Rollback now applies to interrupted runs too.
- **Corrected a misleading attribution rule.** Rule 4 told the model that whoever acts before *or after* a quote is usually the speaker, but in German prose the neighbouring name is frequently the listener ("Marcian spürte ihre Tränen", "Marcian hörte darüber hinweg") or the person being addressed. It now applies only to active verbs, with perception/reaction verbs marking the listener.
- **"Run until < N unknown" re-sent every narration segment to the LLM on every iteration** (`includeNarrator`), making each round enormously expensive and re-litigating lines that were already correct. It now uses the targeted repair pass.
- Identify Characters menu reordered to the order the passes should actually be run in: identify all → repair mislabeled → identify unknown → run until threshold → verify all → check voice consistency.
## [1.20.5] — 2026-08-11
### Added

View File

@ -1 +1 @@
1.20.8
1.20.16

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@
<meta name="format-detection" content="telephone=no">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#2563EB">
<meta name="app-version" content="1.20.8">
<meta name="app-version" content="1.20.16">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/static/icon.svg">
@ -27,7 +27,7 @@
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/style.css?v=1.20.8">
<link rel="stylesheet" href="/static/style.css?v=1.20.16">
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
@ -378,7 +378,7 @@ window.toggleNavTree = function(treeId, chevronId) {
</script>
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
<script src="/static/loader.js?v=1.20.8"></script>
<script src="/static/loader.js?v=1.20.16"></script>
</body>
</html>

File diff suppressed because one or more lines are too long