Skip to content

fix(cli): flush per-round preambles separately instead of concatenating#261

Merged
StpMax merged 1 commit into
stagingfrom
fix/ENG-901
Jul 23, 2026
Merged

fix(cli): flush per-round preambles separately instead of concatenating#261
StpMax merged 1 commit into
stagingfrom
fix/ENG-901

Conversation

@StpMax

@StpMax StpMax commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Problem

In a multi-round turn (text → tool → text → tool → text), the CLI printed the text preambles of every round and the final answer concatenated back-to-back, with no separators. The user saw something like:

Now launching the backend:anton_state is not a public package — using SQLite directly, without it.Launched! Quickly checking the API:Everything works. Open the app in your browser: ...

This is purely a rendering bug in StreamDisplay (anton/chat_ui.py) — in the message history these are separate, correct assistant messages.

Root cause

StreamDisplay assumed a single-round shape: "a bit of inner-speech → tools → one final answer". It kept two accumulators — _initial_text (text before the first tool) and _buffer (everything after) — and printed _buffer exactly once, as a single Markdown block, in finish().

In a real multi-round turn every preamble except the first fell into _buffer, which was never reset between rounds and had no separator inserted between messages. So all intermediate preambles piled up together with the true final answer and were rendered glued together and misclassified as part of the answer.

Fix

Switch to a single-accumulator model with a flush at each tool boundary:

  • Replace _initial_text + _buffer with one _pending accumulator.
  • In on_tool_use_start, if _pending holds text, it is the current round's preamble — print it immediately as dimmed inner-speech (anton.muted) and clear _pending. This is the natural flush point and gives each preamble its own line, in the correct chronological position.
  • finish() now prints only whatever remains in _pending — the text after the last tool, i.e. the real final answer — with the anton> prompt as Markdown. The "no tools at all" case collapses into the same code path.
  • Remove the now-dead fields _in_tool_phase, _initial_printed, and _last_was_tool.

Resulting output order for a multi-round turn:

<preamble 1, dimmed> ← flushed at tool 1 start
✔ <tool 1 line>
<preamble 2, dimmed> ← flushed at tool 2 start
✔ <tool 2 line>
anton> <final answer, markdown> ← finish()

Concatenation is now impossible because round boundaries coincide with flush points.

Tests

tests/test_chat_ui.py:

  • Updated the tests that referenced the removed fields to assert against _pending.
  • Reworked test_finish_prints_activity_summary to verify the preamble is flushed dimmed at tool start (not in finish()).
  • Added coverage for: multi-round preambles flushed separately and a single Markdown answer block; consecutive tools with no text between them (no spurious flush); a turn that ends with a tool (no anton> answer block); and the no-tools regression (single Markdown answer).
    pytest tests/test_chat_ui.py — 22 passed. ruff check — clean.
    Fixes ENG-901.

@StpMax
StpMax requested a review from ea-rus July 22, 2026 12:19
@StpMax
StpMax merged commit d014d05 into staging Jul 23, 2026
5 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants