From e98bba290ca2d0a683a8e45627d289bdb9c396ae Mon Sep 17 00:00:00 2001 From: Jeeho Lee Date: Thu, 8 Oct 2026 00:06:09 +0000 Subject: [PATCH] fix(runners): hide thoughts from nonverbose debug output Keep thought text out of run_debug's final-response display while preserving verbose output and all parts in returned events. Assisted-by: OpenAI Codex --- src/google/adk/utils/_debug_output.py | 5 +++- tests/unittests/test_runners.py | 28 ++++++++++++++++++++++ tests/unittests/utils/test_debug_output.py | 23 ++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/google/adk/utils/_debug_output.py b/src/google/adk/utils/_debug_output.py index ab435805ebc..3cfd0dd0c33 100644 --- a/src/google/adk/utils/_debug_output.py +++ b/src/google/adk/utils/_debug_output.py @@ -60,7 +60,10 @@ def flush_text() -> None: text_buffer.clear() for part in event.content.parts: - # Text parts are always shown regardless of verbose setting + if part.thought and not verbose: + continue + + # Response text is shown regardless of verbose setting # because they contain the actual agent responses users expect if part.text: text_buffer.append(part.text) diff --git a/tests/unittests/test_runners.py b/tests/unittests/test_runners.py index 27c205ba630..d2df33ba3c8 100644 --- a/tests/unittests/test_runners.py +++ b/tests/unittests/test_runners.py @@ -2853,6 +2853,34 @@ async def test_run_debug_passes_get_session_config(): assert events[0].author == "test_agent" +@pytest.mark.parametrize("verbose", [False, True]) +async def test_run_debug_hides_thoughts_unless_verbose(capsys, verbose): + """Console verbosity does not remove thoughts from the returned events.""" + + class ThoughtAgent(BaseAgent): + + async def _run_async_impl(self, ctx): + yield Event( + invocation_id=ctx.invocation_id, + author=self.name, + content=types.Content( + role="model", + parts=[ + types.Part(text="thinking ", thought=True), + types.Part(text="answer"), + ], + ), + ) + + runner = runners.InMemoryRunner(agent=ThoughtAgent(name="test_agent")) + + events = await runner.run_debug("hello", verbose=verbose) + + expected = "thinking answer" if verbose else "answer" + assert capsys.readouterr().out == f"test_agent > {expected}\n" + assert events[0].content.parts[0].thought is True + + @pytest.mark.asyncio async def test_get_session_config_limits_events(): """Verify that num_recent_events actually limits loaded events.""" diff --git a/tests/unittests/utils/test_debug_output.py b/tests/unittests/utils/test_debug_output.py index 6e105ff3d4a..7d83de76e62 100644 --- a/tests/unittests/utils/test_debug_output.py +++ b/tests/unittests/utils/test_debug_output.py @@ -48,6 +48,29 @@ def test_print_event_prints_text_with_author_prefix(capsys): assert _lines(capsys) == ['agent > hello'] +def test_print_event_hides_thought_text_when_not_verbose(capsys): + """Thoughts are not concatenated into the final answer.""" + print_event( + _event( + types.Part(text='thinking', thought=True), + types.Part(text='answer'), + ) + ) + assert _lines(capsys) == ['agent > answer'] + + +def test_print_event_with_only_thoughts_prints_nothing(capsys): + """An event containing only thoughts has no final answer to display.""" + print_event(_event(types.Part(text='thinking', thought=True))) + assert _lines(capsys) == [] + + +def test_print_event_keeps_thought_text_when_verbose(capsys): + """Verbose output retains thought text for debugging.""" + print_event(_event(types.Part(text='thinking', thought=True)), verbose=True) + assert _lines(capsys) == ['agent > thinking'] + + def test_print_event_coalesces_consecutive_text_parts_into_one_line(capsys): # A streamed answer arrives as several text parts; repeating the author # prefix per part would fragment one sentence across many lines.