From 76bc27199fcd7379909c64c9be6ebac2f38bc929 Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 20 Mar 2026 10:02:42 -0700 Subject: [PATCH] fix(cli, agent): improve streaming handling and state management - Updated _stream_delta method in HermesCLI to handle None values, flushing the stream and resetting state for clean tool execution. - Enhanced quiet mode handling in AIAgent to ensure proper display closure before tool execution, preventing display issues with intermediate streamed content. These changes improve the robustness of the streaming functionality and ensure a smoother user experience during tool interactions. --- cli.py | 10 +++++++++- run_agent.py | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cli.py b/cli.py index af8ac4ef..e831dab3 100755 --- a/cli.py +++ b/cli.py @@ -1504,7 +1504,7 @@ class HermesCLI: _cprint(f"{_DIM}└{'─' * (w - 2)}┘{_RST}") self._reasoning_box_opened = False - def _stream_delta(self, text: str) -> None: + def _stream_delta(self, text) -> None: """Line-buffered streaming callback for real-time token rendering. Receives text deltas from the agent as tokens arrive. Buffers @@ -1514,7 +1514,15 @@ class HermesCLI: Reasoning/thinking blocks (, , etc.) are suppressed during streaming since they'd display raw XML tags. The agent strips them from the final response anyway. + + A ``None`` value signals an intermediate turn boundary (tools are + about to execute). Flushes any open boxes and resets state so + tool feed lines render cleanly between turns. """ + if text is None: + self._flush_stream() + self._reset_stream_state() + return if not text: return diff --git a/run_agent.py b/run_agent.py index 1c3b25fe..0e444b1a 100644 --- a/run_agent.py +++ b/run_agent.py @@ -4838,7 +4838,7 @@ class AIAgent: spinner.stop(cute_msg) elif self.quiet_mode: self._vprint(f" {cute_msg}") - elif self.quiet_mode and not self._has_stream_consumers(): + elif self.quiet_mode: face = random.choice(KawaiiSpinner.KAWAII_WAITING) emoji = _get_tool_emoji(function_name) preview = _build_tool_preview(function_name, function_args) or function_name @@ -6568,7 +6568,19 @@ class AIAgent: self._vprint(f" ┊ 💬 {clean}") messages.append(assistant_msg) - + + # Close any open streaming display (response box, reasoning + # box) before tool execution begins. Intermediate turns may + # have streamed early content that opened the response box; + # flushing here prevents it from wrapping tool feed lines. + # Only signal the display callback — TTS (_stream_callback) + # should NOT receive None (it uses None as end-of-stream). + if self.stream_delta_callback: + try: + self.stream_delta_callback(None) + except Exception: + pass + _msg_count_before_tools = len(messages) self._execute_tool_calls(assistant_message, messages, effective_task_id, api_call_count)