fix(agent): separate continuation fragments so joined text does not glue
truncated_response_parts were joined with no separator at both the ceiling exit and the success path, so a fragment ending mid-word ran straight into the next one (#78577). insert a newline only when the previous fragment ends non-whitespace and the next starts non-whitespace, so existing separators are not doubled.
This commit is contained in:
@@ -182,6 +182,16 @@ _API_CALL_MODULES = frozenset({
|
||||
})
|
||||
|
||||
|
||||
def _join_truncated_parts(parts: List[str]) -> str:
|
||||
"""Join continuation fragments, adding a newline where two would glue together (#78577)."""
|
||||
joined = ""
|
||||
for part in parts:
|
||||
if joined and not joined[-1].isspace() and part and not part[0].isspace():
|
||||
joined += "\n"
|
||||
joined += part
|
||||
return joined
|
||||
|
||||
|
||||
def _apply_active_turn_redirect(agent: Any, messages: List[Dict[str, Any]], text: str) -> None:
|
||||
"""Append a provider-safe checkpoint and correction to the live turn.
|
||||
|
||||
@@ -3291,7 +3301,7 @@ def run_conversation(
|
||||
_retry.restart_with_length_continuation = True
|
||||
break
|
||||
|
||||
partial_response = agent._strip_think_blocks("".join(truncated_response_parts)).strip()
|
||||
partial_response = agent._strip_think_blocks(_join_truncated_parts(truncated_response_parts)).strip()
|
||||
if partial_response:
|
||||
agent._vprint(
|
||||
f"{agent.log_prefix}⚠️ Response still truncated "
|
||||
@@ -7199,7 +7209,7 @@ def run_conversation(
|
||||
codex_ack_continuations = 0
|
||||
|
||||
if truncated_response_parts:
|
||||
final_response = "".join(truncated_response_parts) + final_response
|
||||
final_response = _join_truncated_parts([*truncated_response_parts, final_response])
|
||||
truncated_response_parts = []
|
||||
length_continue_retries = 0
|
||||
# The continuation recovered, so the fragments stay in the transcript.
|
||||
|
||||
@@ -229,3 +229,24 @@ class TestContinuationCeilingWedge:
|
||||
)
|
||||
assert "second turn partial" in result2["final_response"]
|
||||
assert "and the rest." in result2["final_response"]
|
||||
|
||||
|
||||
class TestTruncatedPartJoining:
|
||||
"""#78577 — parts joined with no separator glued text together."""
|
||||
|
||||
def test_glued_parts_get_a_newline(self):
|
||||
from agent.conversation_loop import _join_truncated_parts
|
||||
assert _join_truncated_parts(
|
||||
["Edited index.html", "Review the 5 changes"]
|
||||
) == "Edited index.html\nReview the 5 changes"
|
||||
|
||||
def test_existing_whitespace_is_not_doubled(self):
|
||||
from agent.conversation_loop import _join_truncated_parts
|
||||
assert _join_truncated_parts(["line one\n", "line two"]) == "line one\nline two"
|
||||
assert _join_truncated_parts(["word", " next"]) == "word next"
|
||||
|
||||
def test_degenerate_inputs(self):
|
||||
from agent.conversation_loop import _join_truncated_parts
|
||||
assert _join_truncated_parts([]) == ""
|
||||
assert _join_truncated_parts(["only"]) == "only"
|
||||
assert _join_truncated_parts(["a", "", "b"]) == "a\nb"
|
||||
|
||||
Reference in New Issue
Block a user