Skip to content

fix(agent): join truncated response parts with whitespace-aware separator (#78577) - #78730

Open
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix-78577-work
Open

fix(agent): join truncated response parts with whitespace-aware separator (#78577)#78730
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix-78577-work

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Summary

Fixes truncated responses whose continuation parts get glued together with no separator. When _should_treat_stop_as_truncated() fires and the agent requests a continuation, each partial chunk is collected into truncated_response_parts and later joined with "".join(...). If the truncation cut mid-token (no whitespace on either boundary), the end of one part sticks directly onto the start of the next, producing corrupted output such as index.htmlReview the 5 changes.

Root Cause

Two call sites in agent/conversation_loop.py join the truncated parts with an empty separator:

  • run_conversation() — exhausted-continuation path (~L3103):
    partial_response = agent._strip_think_blocks("".join(truncated_response_parts)).strip()
  • run_conversation() — codex finalization path (~L6926):
    final_response = "".join(truncated_response_parts) + final_response

Neither boundary check exists: a part ending without whitespace followed by a part starting without whitespace is concatenated verbatim.

Change

Add a module-level helper _join_truncated_parts(parts, trailing="") in agent/conversation_loop.py:

  • Joins parts with "\n" only when the previous part does not end with whitespace AND the next part does not start with whitespace.
  • Skips empty parts (an empty chunk must not force a separator decision between its non-empty neighbors).
  • Applies the same whitespace-aware boundary treatment to an optional trailing suffix (the final, untruncated response) against the last part.

Both call sites now use the helper. Output that already carries whitespace at the boundary is left byte-for-byte unchanged.

Verification

  • New unit tests in tests/run_agent/test_truncated_parts_join.py cover:
    • parts that would glue (no whitespace on either boundary) get a newline inserted (including the exact index.html / Review the 5 changes case from the issue);
    • parts already separated by whitespace (space, tab, newline) are preserved unchanged;
    • empty parts are skipped, including all-empty and single-part lists;
    • the trailing suffix boundary, with and without existing whitespace.
  • pytest tests/run_agent/test_truncated_parts_join.py tests/run_agent/test_anthropic_truncation_continuation.py -q -> 20 passed.
  • pytest tests/ -k "truncat" -q -> 183 passed, 3 skipped, 1 failed. The single failure (test_discord_clarify_buttons.py::test_truncates_long_no_space_choice_on_soft_boundary) is order-dependent flakiness in the batch: it passes in isolation and in its full file with this diff applied.

Closes

Closes #78577

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/streaming Streaming responses: gateway delivery, provider wire labels Aug 4, 2026
This was referenced Aug 5, 2026
@spfcraze

spfcraze commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Summary:
The join fix this PR describes is already on main: _join_truncated_parts exists in the tree and both call sites the root-cause section names already route through it, so the diff is a refactor of code already on main, not the fix the description presents.

Problems:

  • On origin/main, the exhausted-continuation path calls _join_truncated_parts(truncated_response_parts) (agent/conversation_loop.py:3314) and the codex finalization path calls _join_truncated_parts([*truncated_response_parts, final_response]) (agent/conversation_loop.py:7266) — both with the newline-insertion boundary check this PR's diff extends with a trailing parameter.
  • The root-cause section quotes both sites as "".join(truncated_response_parts) joins and states "Neither boundary check exists"; the PR's own diff base already contains _join_truncated_parts with that check.
  • The diff's call-site change is the codex finalization site moving to _join_truncated_parts(truncated_response_parts, trailing=final_response); the exhausted-continuation site is not part of the diff.

Solution:
Update the description to state that the fix is already on main and to present the diff as a refactor of the existing helper (the trailing parameter and empty-part skip) rather than as the first fix for #78577. The PR's Closes #78577 will auto-close the issue on merge; the behavior it describes is already fixed on main.


Checked against 38e44ed — the tip of fix-78577-work when this was written — and 1c94338, main at the same moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Truncated response parts joined with no separator, gluing text together

3 participants