Step down on landing late, not on a deep queue

Build 118. Build 117 got the first half right and the second half wrong.

117 compared the time taken against the audio the chunk held, which was correct,
and then required a queue five deep as proof the machine was losing. Watching a
campus M4 Pro go red disproved that: the queue sat at 2-3 while the lag held at
15 s and the verdict was already red. A machine can be persistently late without
ever stacking work up — it finishes each sentence just slightly too slowly, for
ever, and the backlog never gets deep enough to notice.

The second test is now how far behind the speaker the sentence actually landed,
which is what the operator is complaining about in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-24 13:32:37 -04:00
co-authored by Claude Opus 5
parent 92e86e415f
commit 6f91bc1357
2 changed files with 23 additions and 12 deletions
+21 -10
View File
@@ -1931,23 +1931,34 @@ async function pump() {
* drains does not recover on its own. * drains does not recover on its own.
*/ */
/* /*
* 0.85 and a backlog of five, both measured rather than chosen. * SLOWER THAN THE AUDIO IT COVERS, AND LANDING LATE.
* *
* The Mini's sentences ran 4.4 s against 4.5 s of audio — nominally FASTER * This used to fire only past 8 s, on the reasoning that 8 s meant slower
* than real time, while the queue sat at 46 and the lag climbed past 150 s. * than people talk. That held when a chunk could be 8 s long; chunks are
* Per-sentence time alone therefore misses it: the work outside this timer * capped at HARD_CUT_MAX, 5.5 s, so a 4.4 s sentence is already losing and
* (segmenting, the preview model sharing the GPU, the scheduler) is enough * never came near the old threshold.
* to lose on its own. The margin catches "near enough to real time to be *
* losing"; the queue depth is the proof that it actually is, and keeps one * Both numbers below were measured on a campus M4 Pro, and the second one
* slow sentence on a healthy machine from touching anything. * only after a first attempt got it wrong. 0.85, because that Mac ran 4.4 s
* against 4.5 s of audio — nominally FASTER than real time while losing
* badly, since the work outside this timer (segmenting, the preview model
* sharing the GPU, the scheduler) loses on its own.
*
* The second test was a queue depth, and watching that Mac go red disproved
* it: the queue sat at 2-3 while the lag held at 15 s and the verdict was
* already red. A machine can be persistently late without ever stacking work
* up — it finishes each sentence just slightly too slowly, for ever. So the
* test is how far behind the speaker the sentence actually landed, which is
* what the operator is complaining about in the first place.
*/ */
const losingGround = jobAudioMs > 0 && took > jobAudioMs * 0.85 && queue.length >= 5; const behindMs = Date.now() - job.at;
const losingGround = jobAudioMs > 0 && took > jobAudioMs * 0.85 && behindMs > 8000;
if (took > 8000 || losingGround) { if (took > 8000 || losingGround) {
state.slowRun = (state.slowRun || 0) + 1; state.slowRun = (state.slowRun || 0) + 1;
if (state.slowRun >= 3 && !respawning && lighterAsr(state.asrModelInUse || state.cfg.asrModel)) { if (state.slowRun >= 3 && !respawning && lighterAsr(state.asrModelInUse || state.cfg.asrModel)) {
state.slowRun = 0; state.slowRun = 0;
log('transcription is slower than people are speaking (' + took + ' ms for ' + log('transcription is slower than people are speaking (' + took + ' ms for ' +
Math.round(jobAudioMs) + ' ms of audio, ' + queue.length + ' waiting) — ' + Math.round(jobAudioMs) + ' ms of audio, ' + Math.round(behindMs / 1000) + 's behind) — ' +
'stepping down to a lighter model', 'err'); 'stepping down to a lighter model', 'err');
respawnWorker(state.asrWorker); respawnWorker(state.asrWorker);
} }
+2 -2
View File
@@ -2,7 +2,7 @@
"name": "church-translate-engine", "name": "church-translate-engine",
"productName": "Church Translate", "productName": "Church Translate",
"version": "3.23.2", "version": "3.23.2",
"buildNumber": 117, "buildNumber": 118,
"private": true, "private": true,
"description": "Live sermon translation — self-contained: captures audio, transcribes, translates, and serves the congregation page itself", "description": "Live sermon translation — self-contained: captures audio, transcribes, translates, and serves the congregation page itself",
"main": "main.js", "main": "main.js",
@@ -106,7 +106,7 @@
] ]
}, },
"asar": false, "asar": false,
"buildVersion": "117", "buildVersion": "118",
"win": { "win": {
"target": [ "target": [
{ {