diff --git a/openless-all/app/src-tauri/src/asr/local/experimental/textstream/test_worker.py b/openless-all/app/src-tauri/src/asr/local/experimental/textstream/test_worker.py index c4ac6997d..df9ce4efe 100644 --- a/openless-all/app/src-tauri/src/asr/local/experimental/textstream/test_worker.py +++ b/openless-all/app/src-tauri/src/asr/local/experimental/textstream/test_worker.py @@ -136,8 +136,16 @@ def test_draft_is_revisable_and_final_comes_from_decoder(self): self.assertEqual(snapshots.update("你好", "你好")["draft"], "") final = snapshots.update("你好", "你好!", terminal=True) self.assertEqual((final["finalized"], final["draft"]), ("你好!", "")) - with self.assertRaises(ValueError): - snapshots.update("cannot match", "different") + # A rewritten decode must re-commit only the agreed prefix instead of + # failing: audio is already captured and the terminal text wins. + recovered = snapshots.update("cannot match", "different") + self.assertEqual(recovered["finalized"], "") + self.assertEqual(recovered["finalized"] + recovered["draft"], "different") + partial = snapshots.update("我们去了Co", "我们去了科蒂纳。") + self.assertEqual( + (partial["finalized"], partial["draft"]), ("我们去了", "科蒂纳。") + ) + self.assertNotEqual(snapshots.sequence, 0) def run_fake(self, finish=True, gpu_available=True): FakeSession.calls = [] diff --git a/openless-all/app/src-tauri/src/asr/local/experimental/textstream/worker.py b/openless-all/app/src-tauri/src/asr/local/experimental/textstream/worker.py index 7677307fb..3369e6233 100644 --- a/openless-all/app/src-tauri/src/asr/local/experimental/textstream/worker.py +++ b/openless-all/app/src-tauri/src/asr/local/experimental/textstream/worker.py @@ -99,7 +99,17 @@ def update(self, finalized: str, full: str, terminal: bool = False) -> dict: if terminal: finalized = full if not full.startswith(finalized): - raise ValueError("decoder snapshot does not contain its finalized prefix") + # A decode may rewrite text that an earlier snapshot reported as + # stable (script or normalisation flips). The protocol carries whole + # previews, and the terminal snapshot is authoritative, so keep the + # authoritative text and re-commit only the prefix both agree on + # instead of failing the whole transcription. + agreed = 0 + for previous, current in zip(finalized, full): + if previous != current: + break + agreed += 1 + finalized = finalized[:agreed] self.finalized, self.draft = finalized, full[len(finalized) :] self.sequence += 1 return {