mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
fix: streaming display — show tool feed lines and fix response box framing
Two display bugs when streaming is enabled in the interactive CLI: 1. Tool feed lines (┊ 📖 read, ┊ 💻 $, etc.) were invisible during streaming sessions. The sequential tool execution path at line 4840 had a guard `not self._has_stream_consumers()` that skipped the entire spinner + cute message display when a stream_delta_callback was registered. But no tokens are streaming during tool execution — the _executing_tools flag already handles this for _vprint. Removed the unnecessary guard. 2. Response box (╭─ ⚕ Hermes ─╮) could wrap intermediate tool-calling turns instead of only the final response. Content tokens arrive before tool_call tokens during streaming, so the box opens before the agent knows tool calls are coming. Now sends a None sentinel through the stream callback when tool_calls are confirmed, which tells the CLI to close any open box and reset stream state. Only the actual final response gets the Hermes border. Live tested: reasoning blocks → tool feed → reasoning → response box now display in the correct sequence.
This commit is contained in:
parent
d2305cee21
commit
2ef06a04bb
2 changed files with 20 additions and 2 deletions
12
cli.py
12
cli.py
|
|
@ -1511,10 +1511,22 @@ class HermesCLI:
|
|||
partial lines and emits complete lines via _cprint to work
|
||||
reliably with prompt_toolkit's patch_stdout.
|
||||
|
||||
A ``None`` sentinel signals that an intermediate turn (with tool
|
||||
calls) just completed — close any open response/reasoning box
|
||||
and reset so the final response gets a clean frame.
|
||||
|
||||
Reasoning/thinking blocks (<REASONING_SCRATCHPAD>, <think>, etc.)
|
||||
are suppressed during streaming since they'd display raw XML tags.
|
||||
The agent strips them from the final response anyway.
|
||||
"""
|
||||
if text is None:
|
||||
# Intermediate turn ended — discard any open box and reset
|
||||
self._close_reasoning_box()
|
||||
if self._stream_box_opened:
|
||||
w = shutil.get_terminal_size().columns
|
||||
_cprint(f"{_GOLD}╰{'─' * (w - 2)}╯{_RST}")
|
||||
self._reset_stream_state()
|
||||
return
|
||||
if not text:
|
||||
return
|
||||
|
||||
|
|
|
|||
10
run_agent.py
10
run_agent.py
|
|
@ -4837,7 +4837,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
|
||||
|
|
@ -6552,7 +6552,13 @@ class AIAgent:
|
|||
)
|
||||
|
||||
assistant_msg = self._build_assistant_message(assistant_message, finish_reason)
|
||||
|
||||
|
||||
# Signal streaming display that this was an intermediate turn.
|
||||
# Content may have been streamed (opening the response box) before
|
||||
# tool_calls were detected. The None sentinel tells the CLI to
|
||||
# close any open box and reset so the final response gets a clean frame.
|
||||
self._fire_stream_delta(None)
|
||||
|
||||
# If this turn has both content AND tool_calls, capture the content
|
||||
# as a fallback final response. Common pattern: model delivers its
|
||||
# answer and calls memory/skill tools as a side-effect in the same
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue