fix: explain why designed-voice benchmarks fail and stop batches on an unreachable service
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f1b12993a1
commit
4d7434f9af
@ -6,6 +6,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
---
|
||||
|
||||
## [Unreleased]
|
||||
### Fixed
|
||||
- **Voice benchmark failures now say why.** Designed voices are benchmarked through the Voice Design engine; when that service is down the row showed a bare "-" and the raw "Max retries exceeded" text. The error now names the service and the container to start, the Factor and WPM columns show `ERR` beside the time column, and a batch stops after three identical "unreachable" failures instead of retrying every remaining voice.
|
||||
### Added
|
||||
- `scripts/compare_magazines.py`: read-only comparison of two magazine folders (words, boxes, reading order, articles, quality numbers) for re-import checks.
|
||||
|
||||
|
||||
@ -310,7 +310,14 @@ def _voice_design_voice_request_audio(
|
||||
# randomness. See the config-side comment for the full story.
|
||||
_apply_tts_extra_params(payload, settings, "voice_design_playback")
|
||||
_apply_voice_pinned_seed(payload, voice, settings)
|
||||
resp = _post_tts_with_fallback(f"{vd_url}/v1/audio/speech", payload, vd_hdrs, timeout=180)
|
||||
try:
|
||||
resp = _post_tts_with_fallback(f"{vd_url}/v1/audio/speech", payload, vd_hdrs, timeout=180)
|
||||
except (requests.ConnectionError, requests.Timeout) as e:
|
||||
# The raw "Max retries exceeded with url ..." text told the user nothing about what to do.
|
||||
raise RuntimeError(
|
||||
f"Voice Design service unreachable at {vd_url} — start the "
|
||||
f"'tts-faster-qwen3-tts-voicedesign' container (Engines tab) and retry"
|
||||
) from e
|
||||
resp.raise_for_status()
|
||||
audio = resp.content
|
||||
if not audio:
|
||||
|
||||
@ -434,7 +434,9 @@ function voiceFactor(v) {
|
||||
|
||||
function fmtFactor(v) {
|
||||
const f = voiceFactor(v);
|
||||
return f != null ? f.toFixed(2) + 'x' : '-';
|
||||
if (f != null) return f.toFixed(2) + 'x';
|
||||
const b = voiceBenchmark(v);
|
||||
return b && !b.ok ? 'ERR' : '-';
|
||||
}
|
||||
|
||||
function fmtElapsed(v) {
|
||||
@ -932,7 +934,8 @@ async function runVoiceBenchmarkBatch() {
|
||||
});
|
||||
|
||||
setBenchmarkProgress(0, total, 'Starting benchmark...', stats);
|
||||
for (let i = 0; i < voices.length; i++) {
|
||||
let lastFailure = '', sameFailure = 0, aborted = false;
|
||||
for (let i = 0; i < voices.length && !aborted; i++) {
|
||||
const voice = voices[i];
|
||||
const row = document.querySelector(`.vl-row[data-id="${CSS.escape(voice.id)}"]`);
|
||||
if (row) {
|
||||
@ -971,6 +974,14 @@ async function runVoiceBenchmarkBatch() {
|
||||
} else {
|
||||
stats.errors++;
|
||||
stats.last = `${voice.id}: failed${b && b.error ? ' · ' + b.error : ''}`;
|
||||
// The same unreachable service failing repeatedly means the rest will fail too.
|
||||
const reason = b && b.error ? String(b.error) : '';
|
||||
sameFailure = reason && reason === lastFailure ? sameFailure + 1 : 1;
|
||||
lastFailure = reason;
|
||||
if (/unreachable/i.test(reason) && sameFailure >= 3) {
|
||||
toast(reason, 'error');
|
||||
aborted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
|
||||
34
tests/test_benchmark_errors.py
Normal file
34
tests/test_benchmark_errors.py
Normal file
@ -0,0 +1,34 @@
|
||||
"""A designed voice's benchmark must explain an unreachable Voice Design service."""
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
os.environ.setdefault("CONFIG_DIR", tempfile.mkdtemp(prefix="ttvc-test-config-"))
|
||||
|
||||
import requests
|
||||
|
||||
from core import tts_helpers
|
||||
|
||||
|
||||
class VoiceDesignUnreachableTest(unittest.TestCase):
|
||||
def test_connection_error_becomes_actionable_message(self):
|
||||
with mock.patch.object(tts_helpers, "_post_tts_with_fallback",
|
||||
side_effect=requests.ConnectionError("Max retries exceeded")):
|
||||
with self.assertRaises(RuntimeError) as ctx:
|
||||
tts_helpers._voice_design_voice_request_audio(
|
||||
"DE_F_Test", "Hallo", {"voice_design_url": "http://127.0.0.1:9"})
|
||||
self.assertIn("Voice Design service unreachable", str(ctx.exception))
|
||||
self.assertIn("tts-faster-qwen3-tts-voicedesign", str(ctx.exception))
|
||||
|
||||
def test_http_errors_are_not_masked(self):
|
||||
resp = mock.Mock(status_code=500)
|
||||
resp.raise_for_status.side_effect = requests.HTTPError("500")
|
||||
with mock.patch.object(tts_helpers, "_post_tts_with_fallback", return_value=resp):
|
||||
with self.assertRaises(requests.HTTPError):
|
||||
tts_helpers._voice_design_voice_request_audio(
|
||||
"DE_F_Test", "Hallo", {"voice_design_url": "http://127.0.0.1:9"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue
Block a user