From ef0cdbf9f4817c5eb2d291fc2faa2fa59c213709 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Mon, 22 Jun 2026 03:04:55 -0700 Subject: [PATCH] =?UTF-8?q?stage=20#4689=20(franksong2702)=20v3:=20suppres?= =?UTF-8?q?s=20duplicate=20live=20process=20echoes=20=E2=80=94=20authorita?= =?UTF-8?q?tive=20head=2014e8fba6=20(#4697)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module-level _compact_for_echo_compare (strips ALL whitespace, \s+ -> '') so _is_visible_output_echo's endswith() suffix-match detects interim_assistant prose that only differs from visible token prose by line-break/paragraph formatting. Hoisted to module scope (importable) + behavior-based test (real token vs interim text equality). Codex SAFE (exact suffix match, not substring). 103 echo/journal tests. Co-authored-by: nesquena-hermes Co-authored-by: franksong2702 --- CHANGELOG.md | 6 ++++++ api/streaming.py | 8 +++++--- tests/test_run_journal_streaming_static.py | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f220e82d6..21a5f3178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.585] — 2026-06-22 — Release UR (suppress duplicate live process echoes) + +### Fixed + +- **Live progress text no longer appears twice during streaming.** When the same process prose surfaced through both the visible assistant stream and the live Thinking/interim updates (differing only by paragraph or line-break formatting), it could render duplicated. The server-side visible-output echo detection now ignores all whitespace when comparing, so an `interim_assistant` update that only reformats already-visible token prose is correctly recognized as an echo and suppressed at the source. Thanks @franksong2702. (#4689) + ## [v0.51.584] — 2026-06-22 — Release UQ (freeze /api/sessions cache during streaming) ### Fixed diff --git a/api/streaming.py b/api/streaming.py index 81d2a3dac..c31883d16 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -74,6 +74,11 @@ def _session_payload_with_full_messages(session, *, tool_calls=None): return raw +def _compact_for_echo_compare(value: str) -> str: + """Normalize visible stream text for duplicate echo detection.""" + return re.sub(r'\s+', '', str(value or '')) + + # Global lock for os.environ writes. Per-session locks (_agent_lock) prevent # concurrent runs of the SAME session, but two DIFFERENT sessions can still # interleave their os.environ writes. This global lock serializes the env @@ -6347,9 +6352,6 @@ def _run_agent_streaming( stats.setdefault('estimated', False) put('metering', stats) - def _compact_for_echo_compare(value: str) -> str: - return re.sub(r'\s+', ' ', str(value or '')).strip() - def _is_visible_output_echo(text: str) -> bool: candidate = _compact_for_echo_compare(text) if not candidate: diff --git a/tests/test_run_journal_streaming_static.py b/tests/test_run_journal_streaming_static.py index ea455a13c..a25421715 100644 --- a/tests/test_run_journal_streaming_static.py +++ b/tests/test_run_journal_streaming_static.py @@ -1,5 +1,7 @@ from pathlib import Path +from api.streaming import _compact_for_echo_compare + def test_streaming_initializes_one_run_journal_writer_per_stream(): src = Path("api/streaming.py").read_text(encoding="utf-8") @@ -21,3 +23,10 @@ def test_streaming_journals_sse_events_before_queue_delivery(): assert put_idx < journal_idx < queue_idx assert "Failed to append run journal event" in block assert "queue_item = (event, data, event_id) if event_id and hasattr(q, \"subscribe_with_snapshot\") else (event, data)" in block + + +def test_visible_process_echo_compare_ignores_all_whitespace(): + token_text = "先把 issue 4249 拉下来\n\n先看正文和评论" + interim_text = "先把 issue 4249 拉下来先看正文和评论" + + assert _compact_for_echo_compare(token_text) == _compact_for_echo_compare(interim_text)