Skip to content

Tool call

ui.ToolCall is a component for one action of an AI agent (reading a file, editing it, running a command), drawn like in Zed: a line with its status, the title and what it works on (detail), and a click unfolds the input and the output (plus any images). Edits, commands (kind edit, delete, move or execute) and calls that wait are cards instead. diffs show the edits it made as colored diffs, and while it runs progress shows the last lines a command printed. While status is "waiting" it asks right on the card: Always allow, Allow or Reject, reported through on_allow, on_allow_always and on_deny. The component remembers whether it is unfolded; your app only keeps the data.

ui.ToolCall · Category: AI agents · Full reference →

Tool callTool call
  • In an agent’s chat, one per step the agent takes.
  • With status="waiting", to ask before a risky step such as writing files or running a command.
  • For an edit on its own, outside a tool call, use Diff view.
import pyzed as pz
from pyzed import ui
class Chat(pz.App):
def __init__(self):
self.status = "waiting"
def answer(self, allowed):
self.status = "done" if allowed else "rejected"
def view(self):
return ui.ToolCall(title="Run", detail="pytest -q", kind="execute", status=self.status,
input={"command": "pytest -q"}, output="24 passed",
on_allow=lambda e: self.answer(True), on_deny=lambda e: self.answer(False))

Pending and running show a spinner, waiting asks with Always allow, Allow and Reject, failed and rejected say so in red.

Tool call: status="pending"Tool call: status="pending"
status="pending"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="pending"),
width=360)
Tool call: status="running"Tool call: status="running"
status="running"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="running"),
width=360)
Tool call: status="done"Tool call: status="done"
status="done"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="done"),
width=360)
Tool call: status="waiting"Tool call: status="waiting"
status="waiting"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="waiting",
on_allow=on_allow, on_allow_always=on_allow_always, on_deny=on_deny),
width=360)
Tool call: status="failed"Tool call: status="failed"
status="failed"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="failed"),
width=360)
Tool call: status="rejected"Tool call: status="rejected"
status="rejected"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="rejected"),
width=360)

The kind picks the icon of a finished call.

Tool call: kind="read"Tool call: kind="read"
kind="read"
Code
pz.container(ui.ToolCall(title="Read file", detail="app.py", kind="read"), width=300)
Tool call: kind="edit"Tool call: kind="edit"
kind="edit"
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit"), width=300)
Tool call: kind="delete"Tool call: kind="delete"
kind="delete"
Code
pz.container(ui.ToolCall(title="Delete file", detail="old.py", kind="delete"), width=300)
Tool call: kind="move"Tool call: kind="move"
kind="move"
Code
pz.container(ui.ToolCall(title="Move file", detail="a.py → b.py", kind="move"), width=300)
Tool call: kind="search"Tool call: kind="search"
kind="search"
Code
pz.container(ui.ToolCall(title="Search", detail="def save", kind="search"), width=300)
Tool call: kind="execute"Tool call: kind="execute"
kind="execute"
Code
pz.container(ui.ToolCall(title="Run", detail="pytest -q", kind="execute"), width=300)
Tool call: kind="think"Tool call: kind="think"
kind="think"
Code
pz.container(ui.ToolCall(title="Think", kind="think"), width=300)
Tool call: kind="fetch"Tool call: kind="fetch"
kind="fetch"
Code
pz.container(ui.ToolCall(title="Fetch", detail="https://example.com", kind="fetch"),
width=300)
Tool call: kind="switch_mode"Tool call: kind="switch_mode"
kind="switch_mode"
Code
pz.container(ui.ToolCall(title="Switch mode", detail="plan", kind="switch_mode"),
width=300)
Tool call: kind="other"Tool call: kind="other"
kind="other"
Code
pz.container(ui.ToolCall(title="Tool", detail="custom", kind="other"), width=300)

Unfolded ones are drawn as they look after a click on the header.

Tool call: progress=Tool call: progress=
progress=
Code
pz.container(ui.ToolCall(title="Run", detail="pytest -q", kind="execute", status="running",
progress="collected 24 items\n............\n............"),
width=360)
Tool call: diffs=Tool call: diffs=
diffs=
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit", status="running",
diffs=[{"path": "app.py", "old": "x = 1\ny = 2\n", "new": "x = 10\ny = 2\n"}]),
width=360)
Tool call: allow_always=FalseTool call: allow_always=False
allow_always=False
Code
pz.container(ui.ToolCall(title="Run", detail="rm -rf build", kind="execute", status="waiting",
allow_always=False, on_allow=on_allow, on_deny=on_deny),
width=360)
Tool call: input=, output=Tool call: input=, output=
input=, output=
Code
pz.container(ui.ToolCall(title="Read file", detail="app.py", kind="read",
input={"path": "app.py"}, output="import pyzed as pz\n\npz.run(App)"),
width=360)
Tool call: Unfolded diffsTool call: Unfolded diffs
Unfolded diffs
Code
pz.container(ui.ToolCall(title="Edit file", detail="app.py", kind="edit",
diffs=[{"path": "app.py", "old": "x = 1\ny = 2\n", "new": "x = 10\ny = 2\n"}]),
width=360)

Tool call has 3 events of its own, listed below. Like every widget it also has the 18 common events (click, double_click, right_click, mouse_enter, drag, key_down, resize…), attached with .on(...): see Events and Events of every component.

It is a pz.Component, so it also raises on_mount and on_unmount, and its handlers get a pz.Event whose value holds the data.

Event Handler gets When
on_allow (event) Allow was pressed.
on_allow_always (event) Always allow was pressed.
on_deny (event) Reject was pressed.

Handler gets is what Pyzed passes to your callable: () means nothing. Any handler can also be a message (a value that is not callable), which goes to update().

The same events written three ways. Imperative: methods of the app change self. Declarative: the handlers produce messages and one update() decides. Functional: a pure function draws an immutable state and asks for changes with set_state(...). See Imperative, declarative and functional.

class Chat(pz.App):
def __init__(self):
self.status = "waiting"
def answer(self, status):
self.status = status
def view(self): # a pz.Component: handlers get a pz.Event
return ui.ToolCall(title="Run", detail="pytest -q", kind="execute", status=self.status,
input={"command": "pytest -q"},
on_allow=lambda e: self.answer("done"),
on_allow_always=lambda e: self.answer("done"),
on_deny=lambda e: self.answer("rejected"))
class ToolCall

A component for one action of an AI agent, drawn like in Zed’s Agent panel: a line with its icon, what it does and where (it unfolds to show the input and the output), or a card for edits and commands, with the diff or the output. While status is "waiting" it asks right on the card: Always allow, Allow or Reject. See AI agents.

Property Default Description
title "" What it does (“Edit file”).
detail "" What it works on, in a monospace font.
kind "" ACP’s kind: read, edit, delete, move, search, execute, think, fetch or other (its icon).
status "done" pending, running, waiting, done, failed or rejected.
input None The arguments (a dict or text).
output None The answer (text or a dict).
images None PNG bytes shown under the output.
diffs None The edits it made: {"path", "old", "new"} dicts, shown as colored diffs (see ui.diff_view).
progress "" What it printed so far (a command’s output); its last lines show under the header while it runs.
allow_always True Shows Always allow.
theme None Theme.

Events: on_allow, on_allow_always, on_deny (events).

Full reference →