fix(cli): decouple tool_progress=verbose from global DEBUG logging (#31379)

PR #6a1aa420e coupled `display.tool_progress: verbose` (a per-tool display
toggle for full args / results / think blocks) to `self.verbose` — which
controls root-logger DEBUG level. Result: setting tool_progress: verbose
in config silently flipped every module in the process to DEBUG and
flooded the terminal with internal logging, far beyond just full tool
calls.

The two concepts are separate:
- `tool_progress_mode == 'verbose'` → display behavior (tool rendering)
- `self.verbose` → logging behavior (root logger → DEBUG, line 9795)

This change keeps PR #6a1aa420e's argparse.SUPPRESS / config-fallback
plumbing but severs the verbose-display → debug-logging link.

Changes:
- cli.py:2868 — `self.verbose` only follows explicit `verbose=` arg; no
  longer auto-True when tool_progress_mode == 'verbose'.
- cli.py:_toggle_verbose — slash-cycle through tool progress modes no
  longer flips `self.verbose` / `agent.verbose_logging` / `agent.quiet_mode`.
- cli.py:9355 — fix misleading label (drop 'and debug logs').
- tui_gateway/server.py:_make_agent — same decoupling on the TUI side
  (verbose_logging no longer derived from tool_progress_mode).
- tests/cli/test_tool_progress_scrollback.py — invert the test that
  asserted the broken coupling; add coverage for explicit `--verbose`
  still enabling DEBUG independent of tool_progress.

Live verified:
- tool_progress: verbose, no --verbose flag → 0 DEBUG/INFO log lines
- --verbose flag explicit → 32 DEBUG/INFO log lines (as expected)
This commit is contained in:
Teknium 2026-05-24 02:19:20 -07:00 committed by GitHub
parent 5848174374
commit c9b3eeabdc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 40 additions and 11 deletions

22
cli.py
View file

@ -2857,7 +2857,12 @@ class HermesCLI:
else:
self.busy_input_mode = "interrupt"
self.verbose = verbose if verbose is not None else (self.tool_progress_mode == "verbose")
# self.verbose ONLY controls global DEBUG logging (root logger level).
# display.tool_progress="verbose" controls tool-call rendering (full args,
# results, think blocks) and is independent — see _apply_logging_levels.
# Coupling the two (PR #6a1aa420e) caused all module DEBUG logs to spew
# to console whenever a user set tool_progress: verbose in config.
self.verbose = bool(verbose) if verbose is not None else False
# streaming: stream tokens to the terminal as they arrive (display.streaming in config.yaml)
self.streaming_enabled = CLI_CONFIG["display"].get("streaming", False)
@ -9329,18 +9334,23 @@ class HermesCLI:
_cprint(" Failed to save runtime_footer setting to config.yaml")
def _toggle_verbose(self):
"""Cycle tool progress mode: off → new → all → verbose → off."""
"""Cycle tool progress mode: off → new → all → verbose → off.
Tool-progress display (full args / results / think blocks at the
``verbose`` step) is INDEPENDENT of global DEBUG logging. Cycling
through here does not change ``self.verbose`` or the agent's
``verbose_logging`` / ``quiet_mode`` those remain under the
explicit ``-v``/``--verbose`` flag and the ``/verbose-logging``
toggle. See PR #6a1aa420e for the history that decoupled them.
"""
cycle = ["off", "new", "all", "verbose"]
try:
idx = cycle.index(self.tool_progress_mode)
except ValueError:
idx = 2 # default to "all"
self.tool_progress_mode = cycle[(idx + 1) % len(cycle)]
self.verbose = self.tool_progress_mode == "verbose"
if self.agent:
self.agent.verbose_logging = self.verbose
self.agent.quiet_mode = not self.verbose
self.agent.reasoning_callback = self._current_reasoning_callback()
# Use raw ANSI codes via _cprint so the output is routed through
@ -9352,7 +9362,7 @@ class HermesCLI:
"off": f"{_Colors.DIM}Tool progress: OFF{_Colors.RESET} — silent mode, just the final response.",
"new": f"{_Colors.YELLOW}Tool progress: NEW{_Colors.RESET} — show each new tool (skip repeats).",
"all": f"{_Colors.GREEN}Tool progress: ALL{_Colors.RESET} — show every tool call.",
"verbose": f"{_Colors.BOLD}{_Colors.GREEN}Tool progress: VERBOSE{_Colors.RESET} — full args, results, think blocks, and debug logs.",
"verbose": f"{_Colors.BOLD}{_Colors.GREEN}Tool progress: VERBOSE{_Colors.RESET} — full args, results, and think blocks.",
}
_cprint(labels.get(self.tool_progress_mode, ""))

View file

@ -178,15 +178,30 @@ class TestToolProgressScrollback:
mock_print.assert_not_called()
def test_verbose_mode_config_enables_cli_verbose_by_default(self):
"""Config-only display.tool_progress=verbose should enable verbose output."""
def test_verbose_mode_config_does_not_enable_global_debug_logging(self):
"""display.tool_progress=verbose controls TOOL-CALL DISPLAY ONLY.
It must NOT auto-flip self.verbose, which controls root-logger DEBUG
level for the entire process (every module spews to console). PR
#6a1aa420e had coupled them, causing all debug logs to flood the
terminal whenever a user picked tool_progress: verbose for richer
per-tool rendering.
"""
cli = _make_cli(tool_progress="verbose")
assert cli.tool_progress_mode == "verbose"
assert cli.verbose is False
def test_explicit_verbose_argument_wins_over_config(self):
"""Explicit verbose=True from the CLI flag still enables DEBUG logging
regardless of tool_progress_mode."""
cli = _make_cli(tool_progress="off", verbose=True)
assert cli.tool_progress_mode == "off"
assert cli.verbose is True
def test_explicit_non_verbose_argument_still_overrides_verbose_config(self):
"""An explicit non-verbose value should keep overriding the config fallback."""
def test_explicit_non_verbose_argument_keeps_debug_logging_off(self):
"""Explicit verbose=False overrides any default to enable DEBUG."""
cli = _make_cli(tool_progress="verbose", verbose=False)
assert cli.tool_progress_mode == "verbose"

View file

@ -2033,7 +2033,11 @@ def _make_agent(sid: str, key: str, session_id: str | None = None):
acp_args=runtime.get("args"),
credential_pool=runtime.get("credential_pool"),
quiet_mode=True,
verbose_logging=_load_tool_progress_mode() == "verbose",
# verbose_logging controls DEBUG-level agent logging; it is intentionally
# independent of tool_progress_mode (which only controls per-tool
# display detail). See cli.py PR (decoupling fix) for the matching
# change on the classic CLI side.
verbose_logging=False,
reasoning_config=_load_reasoning_config(),
service_tier=_load_service_tier(),
enabled_toolsets=_load_enabled_toolsets(),