fix(agent): silence quiet_mode in python library use

This commit is contained in:
helix4u 2026-04-18 14:28:50 -06:00 committed by kshitij
parent 361675018f
commit cd59af17cc
2 changed files with 50 additions and 7 deletions

View File

@ -1916,13 +1916,16 @@ class AIAgent:
def _should_emit_quiet_tool_messages(self) -> bool:
"""Return True when quiet-mode tool summaries should print directly.
When the caller provides ``tool_progress_callback`` (for example the CLI
TUI or a gateway progress renderer), that callback owns progress display.
Emitting quiet-mode summary lines here duplicates progress and leaks tool
previews into flows that are expected to stay silent, such as
``hermes chat -q``.
Quiet mode is used by both the interactive CLI and embedded/library
callers. The CLI may still want compact progress hints when no callback
owns rendering. Embedded/library callers, on the other hand, expect
quiet mode to be truly silent.
"""
return self.quiet_mode and not self.tool_progress_callback
return (
self.quiet_mode
and not self.tool_progress_callback
and getattr(self, "platform", "") == "cli"
)
def _emit_status(self, message: str) -> None:
"""Emit a lifecycle status message to both CLI and gateway channels.
@ -11184,7 +11187,7 @@ class AIAgent:
self._last_content_tools_all_housekeeping = _all_housekeeping
if _all_housekeeping and self._has_stream_consumers():
self._mute_post_response = True
elif self.quiet_mode:
elif self.quiet_mode and getattr(self, "platform", "") == "cli":
clean = self._strip_think_blocks(turn_content).strip()
if clean:
relayed = False

View File

@ -1285,6 +1285,7 @@ class TestExecuteToolCalls:
tc = _mock_tool_call(name="web_search", arguments='{"q":"test"}', call_id="c1")
mock_msg = _mock_assistant_msg(content="", tool_calls=[tc])
messages = []
agent.platform = "cli"
agent.tool_progress_callback = None
with patch("run_agent.handle_function_call", return_value="search result"), \
@ -1296,6 +1297,21 @@ class TestExecuteToolCalls:
assert len(messages) == 1
assert messages[0]["role"] == "tool"
def test_quiet_tool_output_suppressed_without_progress_callback_for_non_cli_agent(self, agent):
tc = _mock_tool_call(name="web_search", arguments='{"q":"test"}', call_id="c1")
mock_msg = _mock_assistant_msg(content="", tool_calls=[tc])
messages = []
agent.platform = None
agent.tool_progress_callback = None
with patch("run_agent.handle_function_call", return_value="search result"), \
patch.object(agent, "_safe_print") as mock_print:
agent._execute_tool_calls(mock_msg, messages, "task-1")
mock_print.assert_not_called()
assert len(messages) == 1
assert messages[0]["role"] == "tool"
def test_vprint_suppressed_in_parseable_quiet_mode(self, agent):
agent.suppress_status_output = True
@ -1876,6 +1892,30 @@ class TestRunConversation:
assert all("message_count" in c and "messages" not in c for c in pre_request_calls)
assert all("usage" in c and "response" not in c for c in post_request_calls)
def test_content_with_tool_calls_stays_silent_for_non_cli_quiet_mode(self, agent):
self._setup_agent(agent)
agent.platform = None
tc = _mock_tool_call(name="web_search", arguments="{}", call_id="c1")
resp1 = _mock_response(
content="I'll search for that.",
finish_reason="tool_calls",
tool_calls=[tc],
)
resp2 = _mock_response(content="Done searching", finish_reason="stop")
agent.client.chat.completions.create.side_effect = [resp1, resp2]
with (
patch("run_agent.handle_function_call", return_value="search result"),
patch.object(agent, "_safe_print") as mock_print,
patch.object(agent, "_persist_session"),
patch.object(agent, "_save_trajectory"),
patch.object(agent, "_cleanup_task_resources"),
):
result = agent.run_conversation("search something")
assert result["final_response"] == "Done searching"
mock_print.assert_not_called()
def test_interrupt_breaks_loop(self, agent):
self._setup_agent(agent)