Review follow-up: the bounded echo probe folded at most len(text)*3 raw characters, so a compact-equivalent suffix whose raw span was stretched by interior whitespace was missed - the echo strip was dropped and the interim text was duplicated in the restored transcript. The same retired window existed in the reverse helper (_strip_compact_echo_suffix), pinned by its own test. Both now share _find_compact_echo_suffix_start: a backward walk over value/suffix that skips whitespace in value, inspects only the echo span, and allocates nothing. It restores the previous output semantics (origin/master folded the whole reasoning text) and is cheaper than the windowed version (no per-call tail fold). Tests: new journal-reconstruction regression (a 5000-space-stretched echo must appear exactly once; genuine interim text must not be treated as an echo), the suffix-helper oracle updated to window-free semantics, and the window-contract test now pins that a stretched echo IS stripped.
105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
"""Journal-reconstruction regression: a whitespace-stretched reasoning echo
|
|
must be de-duplicated exactly once.
|
|
|
|
``_run_journal_live_snapshot`` strips reasoning echoes of interim assistant
|
|
text so the restored transcript does not show the same content in both the
|
|
thinking block and the assistant output (run-state consistency invariant:
|
|
replay must not duplicate interim assistant text). The previous bounded echo
|
|
probe could miss a compact-equivalent suffix whose raw span exceeded its fold
|
|
window, leaving the duplicate in the snapshot; the backward walk has no
|
|
window.
|
|
|
|
Both directions are pinned: a stretched echo is stripped (appears once), and
|
|
genuine interim text that is NOT a reasoning echo is left alone.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
|
|
import api.routes as routes
|
|
from api import run_journal as RJ
|
|
|
|
|
|
def _compact(value):
|
|
return re.sub(r"\s+", "", str(value or ""))
|
|
|
|
|
|
def _write_journal(root, session_id, run_id, events):
|
|
session_root = root / RJ.RUN_JOURNAL_DIR_NAME / session_id
|
|
session_root.mkdir(parents=True, exist_ok=True)
|
|
path = session_root / f"{run_id}.jsonl"
|
|
rows = []
|
|
for seq, (event, payload) in enumerate(events, start=1):
|
|
rows.append(
|
|
json.dumps(
|
|
{
|
|
"version": 1,
|
|
"event_id": f"{run_id}:{seq}",
|
|
"seq": seq,
|
|
"run_id": run_id,
|
|
"session_id": session_id,
|
|
"event": event,
|
|
"type": event,
|
|
"created_at": 1000 + seq,
|
|
"payload": payload,
|
|
}
|
|
)
|
|
)
|
|
path.write_text("\n".join(rows) + "\n", encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_whitespace_stretched_reasoning_echo_appears_once(tmp_path, monkeypatch):
|
|
gap = " " * 5000 # raw echo span far beyond any fixed fold window
|
|
_write_journal(
|
|
tmp_path,
|
|
"session_1",
|
|
"run_1",
|
|
[
|
|
("reasoning", {"text": "prefix " + "alpha" + gap + "beta"}),
|
|
("interim_assistant", {"text": "alpha beta"}),
|
|
],
|
|
)
|
|
monkeypatch.setattr(RJ, "_default_session_dir", lambda: tmp_path)
|
|
|
|
snapshot = routes._run_journal_live_snapshot("run_1")
|
|
|
|
message = snapshot["messages"][-1]
|
|
reasoning_text = message.get("reasoning") or ""
|
|
content_text = message.get("content") or ""
|
|
occurrences = _compact(reasoning_text).count("alphabeta") + _compact(
|
|
content_text
|
|
).count("alphabeta")
|
|
assert occurrences == 1, (
|
|
f"the interim echo must appear once after reconstruction; "
|
|
f"reasoning={reasoning_text[-60:]!r} content={content_text[-60:]!r}"
|
|
)
|
|
# The echo span is stripped from the reasoning copy; the prefix stays.
|
|
assert "alphabeta" not in _compact(reasoning_text)
|
|
assert "alpha beta" in content_text
|
|
|
|
|
|
def test_genuine_interim_text_is_not_treated_as_echo(tmp_path, monkeypatch):
|
|
gap = " " * 5000
|
|
_write_journal(
|
|
tmp_path,
|
|
"session_1",
|
|
"run_1",
|
|
[
|
|
("reasoning", {"text": "prefix " + "alpha" + gap + "beta"}),
|
|
("interim_assistant", {"text": "gamma delta"}),
|
|
],
|
|
)
|
|
monkeypatch.setattr(RJ, "_default_session_dir", lambda: tmp_path)
|
|
|
|
snapshot = routes._run_journal_live_snapshot("run_1")
|
|
|
|
message = snapshot["messages"][-1]
|
|
reasoning_text = message.get("reasoning") or ""
|
|
content_text = message.get("content") or ""
|
|
# The genuine interim answer is preserved in full...
|
|
assert "gamma delta" in content_text
|
|
# ...and the reasoning transcript is not suppressed by the matcher.
|
|
assert _compact(reasoning_text).count("alphabeta") == 1
|
|
assert "prefix" in reasoning_text
|