diff --git a/patchwork/cli.py b/patchwork/cli.py index 8847ca2..cd51845 100644 --- a/patchwork/cli.py +++ b/patchwork/cli.py @@ -16,6 +16,7 @@ from patchwork.synth_definitions import load_synth_definitions console = Console() +stderr_console = Console(stderr=True) def _make_event_handler(verbose: bool, logger: logging.Logger): @@ -25,7 +26,7 @@ async def handle_events(ctx: RunContext[PatchworkDeps], events: AsyncIterable) - async for event in events: if isinstance(event, FunctionToolCallEvent): tool_name = event.part.tool_name - logger.info("tool call: %s", tool_name) + stderr_console.print(f"[dim]\U0001f6e0\ufe0f tool call: {tool_name}[/dim]") if verbose: try: @@ -33,7 +34,6 @@ async def handle_events(ctx: RunContext[PatchworkDeps], events: AsyncIterable) - except Exception: args = event.part.args logger.debug("tool args: %s %s", tool_name, json.dumps(args, default=str)) - console.print(f"[dim]⚙ {tool_name}[/dim]") return handle_events diff --git a/tests/test_tool_logging.py b/tests/test_tool_logging.py index 9db451e..3545360 100644 --- a/tests/test_tool_logging.py +++ b/tests/test_tool_logging.py @@ -33,15 +33,15 @@ def logger(): class TestToolCallEventHandler: @pytest.mark.asyncio - async def test_logs_tool_name(self, logger, caplog): + async def test_logs_tool_name_to_stderr(self, logger, capsys): handler = _make_event_handler(verbose=False, logger=logger) event = _make_mock_tool_call_event("send_cc") ctx = MagicMock() - with caplog.at_level(logging.INFO, logger=logger.name): - await handler(ctx, _to_async_iterable([event])) + await handler(ctx, _to_async_iterable([event])) - assert any("send_cc" in record.message for record in caplog.records) + captured = capsys.readouterr() + assert "send_cc" in captured.err @pytest.mark.asyncio async def test_verbose_logs_args(self, logger, caplog): @@ -72,7 +72,7 @@ async def test_ignores_non_tool_events(self, logger, caplog): assert len(tool_records) == 0 @pytest.mark.asyncio - async def test_handles_multiple_events(self, logger, caplog): + async def test_handles_multiple_events(self, logger, capsys): handler = _make_event_handler(verbose=False, logger=logger) events = [ _make_mock_tool_call_event("list_synths"), @@ -80,14 +80,14 @@ async def test_handles_multiple_events(self, logger, caplog): ] ctx = MagicMock() - with caplog.at_level(logging.INFO, logger=logger.name): - await handler(ctx, _to_async_iterable(events)) + await handler(ctx, _to_async_iterable(events)) - tool_records = [r for r in caplog.records if "tool call" in r.message] - assert len(tool_records) == 2 + captured = capsys.readouterr() + assert "list_synths" in captured.err + assert "send_cc" in captured.err @pytest.mark.asyncio - async def test_non_verbose_does_not_print_tool_indicator(self, logger, capsys): + async def test_tool_indicator_prints_to_stderr(self, logger, capsys): handler = _make_event_handler(verbose=False, logger=logger) event = _make_mock_tool_call_event("send_cc") ctx = MagicMock() @@ -95,4 +95,5 @@ async def test_non_verbose_does_not_print_tool_indicator(self, logger, capsys): await handler(ctx, _to_async_iterable([event])) captured = capsys.readouterr() - assert "⚙" not in captured.out + assert "\U0001f6e0\ufe0f" not in captured.out + assert "send_cc" in captured.err