Skip to content

Commit 01ecc4c

Browse files
committed
feat: optionally show tool calls in CLI
1 parent 8ab8bd0 commit 01ecc4c

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

pydantic_ai_slim/pydantic_ai/_cli.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from ._run_context import AgentDepsT
1919
from .agent import AbstractAgent, Agent
2020
from .exceptions import UserError
21-
from .messages import ModelMessage, ModelResponse
21+
from .messages import FunctionToolCallEvent, FunctionToolResultEvent, ModelMessage, ModelResponse
2222
from .models import KnownModelName, infer_model
2323
from .output import OutputDataT
2424

@@ -229,6 +229,7 @@ async def run_chat(
229229
config_dir: Path | None = None,
230230
deps: AgentDepsT = None,
231231
message_history: list[ModelMessage] | None = None,
232+
show_tool_calls: bool = False,
232233
) -> int:
233234
prompt_history_path = (config_dir or PYDANTIC_AI_HOME) / PROMPT_HISTORY_FILENAME
234235
prompt_history_path.parent.mkdir(parents=True, exist_ok=True)
@@ -255,7 +256,7 @@ async def run_chat(
255256
return exit_value
256257
else:
257258
try:
258-
messages = await ask_agent(agent, text, stream, console, code_theme, deps, messages)
259+
messages = await ask_agent(agent, text, stream, console, code_theme, deps, messages, show_tool_calls)
259260
except CancelledError: # pragma: no cover
260261
console.print('[dim]Interrupted[/dim]')
261262
except Exception as e: # pragma: no cover
@@ -273,6 +274,7 @@ async def ask_agent(
273274
code_theme: str,
274275
deps: AgentDepsT = None,
275276
messages: list[ModelMessage] | None = None,
277+
show_tool_calls: bool = False,
276278
) -> list[ModelMessage]:
277279
status = Status('[dim]Working on it…[/dim]', console=console)
278280

@@ -294,6 +296,17 @@ async def ask_agent(
294296

295297
async for content in handle_stream.stream_output(debounce_by=None):
296298
live.update(Markdown(str(content), code_theme=code_theme))
299+
elif show_tool_calls and Agent.is_call_tools_node(node):
300+
async with node.stream(agent_run.ctx) as handle_stream:
301+
async for event in handle_stream:
302+
if isinstance(event, FunctionToolCallEvent):
303+
console.print(
304+
Markdown(f'[Tool] {event.part.tool_name!r} called with args={event.part.args}')
305+
)
306+
elif isinstance(event, FunctionToolResultEvent):
307+
console.print(
308+
Markdown(f'[Tool] {event.result.tool_name!r} returned => {event.result.content}')
309+
)
297310

298311
assert agent_run.result is not None
299312
return agent_run.result.all_messages()

pydantic_ai_slim/pydantic_ai/agent/abstract.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,13 +1113,15 @@ async def to_cli(
11131113
deps: AgentDepsT = None,
11141114
prog_name: str = 'pydantic-ai',
11151115
message_history: list[_messages.ModelMessage] | None = None,
1116+
show_tool_calls: bool = False,
11161117
) -> None:
11171118
"""Run the agent in a CLI chat interface.
11181119
11191120
Args:
11201121
deps: The dependencies to pass to the agent.
11211122
prog_name: The name of the program to use for the CLI. Defaults to 'pydantic-ai'.
11221123
message_history: History of the conversation so far.
1124+
show_tool_calls: Whether to show tool calls in the CLI.
11231125
11241126
Example:
11251127
```python {title="agent_to_cli.py" test="skip"}
@@ -1143,20 +1145,23 @@ async def main():
11431145
code_theme='monokai',
11441146
prog_name=prog_name,
11451147
message_history=message_history,
1148+
show_tool_calls=show_tool_calls,
11461149
)
11471150

11481151
def to_cli_sync(
11491152
self: Self,
11501153
deps: AgentDepsT = None,
11511154
prog_name: str = 'pydantic-ai',
11521155
message_history: list[_messages.ModelMessage] | None = None,
1156+
show_tool_calls: bool = False,
11531157
) -> None:
11541158
"""Run the agent in a CLI chat interface with the non-async interface.
11551159
11561160
Args:
11571161
deps: The dependencies to pass to the agent.
11581162
prog_name: The name of the program to use for the CLI. Defaults to 'pydantic-ai'.
11591163
message_history: History of the conversation so far.
1164+
show_tool_calls: Whether to show tool calls in the CLI.
11601165
11611166
```python {title="agent_to_cli_sync.py" test="skip"}
11621167
from pydantic_ai import Agent
@@ -1167,5 +1172,7 @@ def to_cli_sync(
11671172
```
11681173
"""
11691174
return get_event_loop().run_until_complete(
1170-
self.to_cli(deps=deps, prog_name=prog_name, message_history=message_history)
1175+
self.to_cli(
1176+
deps=deps, prog_name=prog_name, message_history=message_history, show_tool_calls=show_tool_calls
1177+
)
11711178
)

tests/test_cli.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ def test_agent_to_cli_sync(mocker: MockerFixture, env: TestEnv):
293293
prog_name='pydantic-ai',
294294
deps=None,
295295
message_history=None,
296+
show_tool_calls=False,
296297
)
297298

298299

@@ -309,6 +310,7 @@ async def test_agent_to_cli_async(mocker: MockerFixture, env: TestEnv):
309310
prog_name='pydantic-ai',
310311
deps=None,
311312
message_history=None,
313+
show_tool_calls=False,
312314
)
313315

314316

@@ -329,6 +331,7 @@ async def test_agent_to_cli_with_message_history(mocker: MockerFixture, env: Tes
329331
prog_name='pydantic-ai',
330332
deps=None,
331333
message_history=test_messages,
334+
show_tool_calls=False,
332335
)
333336

334337

@@ -348,4 +351,5 @@ def test_agent_to_cli_sync_with_message_history(mocker: MockerFixture, env: Test
348351
prog_name='pydantic-ai',
349352
deps=None,
350353
message_history=test_messages,
354+
show_tool_calls=False,
351355
)

0 commit comments

Comments
 (0)