Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/google/adk/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,12 @@ async def run_input_file(
) as agen:
async for event in agen:
if event.content and event.content.parts:
if text := ''.join(part.text or '' for part in event.content.parts):
# Thought parts are model reasoning, not part of the answer.
if text := ''.join(
part.text or ''
for part in event.content.parts
if not part.thought
):
click.echo(f'[{event.author}]: {text}')
return session

Expand Down Expand Up @@ -586,7 +591,7 @@ def _print_event(
# Human readable mode
author = event.author or 'unknown'
text_parts = (
[p.text for p in event.content.parts if p.text]
[p.text for p in event.content.parts if p.text and not p.thought]
if event.content and event.content.parts
else []
)
Expand Down
64 changes: 63 additions & 1 deletion tests/unittests/cli/utils/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ def _patch_types_and_runner(monkeypatch: pytest.MonkeyPatch) -> None:
# Dummy Part / Content
class _Part:

def __init__(self, text: str | None = "") -> None:
def __init__(self, text: str | None = "", thought: bool | None = None):
self.text = text
self.thought = thought

class _Content:

Expand Down Expand Up @@ -195,6 +196,45 @@ def _echo(msg: str) -> None:
assert any("[assistant]:" in line for line in recorder)


@pytest.mark.asyncio
async def test_run_input_file_hides_thought_parts(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""run_input_file echoes only the answer, not the model's thought parts."""
recorder: List[str] = []
monkeypatch.setattr(click, "echo", recorder.append)

class _ThinkingRunner:

def __init__(self, *a: Any, **k: Any) -> None:
...

async def run_async(self, *a: Any, **k: Any):
parts = [
cli.types.Part("thinking...", thought=True),
cli.types.Part("Hi!"),
]
yield types.SimpleNamespace(
author="assistant", content=cli.types.Content("model", parts)
)

monkeypatch.setattr(cli, "Runner", _ThinkingRunner)
input_path = tmp_path / "input.json"
input_path.write_text(json.dumps({"state": {}, "queries": ["hi"]}))

await cli.run_input_file(
app_name="app",
user_id="user",
agent_or_app=BaseAgent(name="root"),
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
credential_service=InMemoryCredentialService(),
input_path=str(input_path),
)

assert recorder == ["[user]: hi", "[assistant]: Hi!"]


# _run_cli (input_file branch)
@pytest.mark.asyncio
async def test_run_cli_with_input_file(fake_agent, tmp_path: Path) -> None:
Expand Down Expand Up @@ -686,6 +726,27 @@ def test_print_event_preserves_non_ascii_in_jsonl(
assert "\\u" not in echoed[0]


def test_print_event_hides_thought_parts(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""_print_event prints only the answer, not the model's thought parts."""
from google.adk.events.event import Event

echoed: list[str] = []
monkeypatch.setattr(click, "echo", lambda msg: echoed.append(msg))
event = Event.model_validate({
"author": "agent",
"content": {
"role": "model",
"parts": [{"text": "thinking...", "thought": True}, {"text": "Hi!"}],
},
})

cli._print_event(event)

assert echoed == ["[agent]: Hi!"]


@pytest.mark.asyncio
async def test_run_cli_in_memory_flag_sets_memory_service_uri(
fake_agent, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
Expand Down Expand Up @@ -1118,6 +1179,7 @@ def __init__(
) -> None:
self.text = text
self.function_response = function_response
self.thought = None

monkeypatch.setattr(cli.types, "Part", _PartWithFunctionResponse)

Expand Down
Loading