Callbacks
Hook into agent lifecycle events for logging, cost tracking, and more
Callbacks let you hook into the agent's execution lifecycle. Use them to log events, track costs, save trajectories, limit context size, or add custom behavior at any point during agent execution.
Using Callbacks
Pass callbacks when creating your agent:
from agent import ComputerAgent
from agent.callbacks import LoggingCallback, BudgetManagerCallback
import logging
agent = ComputerAgent(
model="anthropic/claude-sonnet-4-5-20250929",
tools=[computer],
callbacks=[
LoggingCallback(level=logging.INFO),
BudgetManagerCallback(max_budget=5.0)
]
)Many callbacks also have shorthand parameters on ComputerAgent:
agent = ComputerAgent(
model="anthropic/claude-sonnet-4-5-20250929",
tools=[computer],
verbosity=logging.INFO, # Adds LoggingCallback
max_trajectory_budget=5.0, # Adds BudgetManagerCallback
only_n_most_recent_images=3, # Adds ImageRetentionCallback
trajectory_dir="trajectories", # Adds TrajectorySaverCallback
instructions="Be concise" # Adds PromptInstructionsCallback
)Built-in Callbacks
LoggingCallback
Logs agent lifecycle events with configurable verbosity:
from agent.callbacks import LoggingCallback
import logging
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[LoggingCallback(level=logging.DEBUG)]
)
# Or use shorthand
agent = ComputerAgent(
model="...",
tools=[computer],
verbosity=logging.INFO
)Logs include API calls, computer actions, text responses, and usage statistics. Image URLs are automatically sanitized in logs.
BudgetManagerCallback
Tracks costs and stops execution when a budget limit is reached:
from agent.callbacks import BudgetManagerCallback
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[BudgetManagerCallback(
max_budget=10.0, # Dollar limit
reset_after_each_run=True, # Reset per run (default)
raise_error=False # Graceful stop vs exception
)]
)
# Or use shorthand
agent = ComputerAgent(
model="...",
tools=[computer],
max_trajectory_budget=5.0
)When raise_error=True, the agent raises BudgetExceededError instead of stopping gracefully.
ImageRetentionCallback
Limits how many screenshots are kept in the message history to prevent context overflow:
from agent.callbacks import ImageRetentionCallback
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[ImageRetentionCallback(only_n_most_recent_images=3)]
)
# Or use shorthand
agent = ComputerAgent(
model="...",
tools=[computer],
only_n_most_recent_images=3
)Older screenshots are removed along with their associated computer call items.
TrajectorySaverCallback
Saves complete agent conversations for debugging and analysis:
from agent.callbacks import TrajectorySaverCallback
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[TrajectorySaverCallback(
trajectory_dir="trajectories",
reset_on_run=True, # New trajectory per run
screenshot_dir="screenshots" # Optional separate screenshot dir
)]
)
# Or use shorthand
agent = ComputerAgent(
model="...",
tools=[computer],
trajectory_dir="trajectories"
)See Trajectories for details on the saved format.
PromptInstructionsCallback
Prepends custom instructions to every LLM call:
from agent.callbacks import PromptInstructionsCallback
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[PromptInstructionsCallback("Always confirm before clicking buttons")]
)
# Or use shorthand
agent = ComputerAgent(
model="...",
tools=[computer],
instructions="Always confirm before clicking buttons"
)This is a simple way to guide agent behavior without modifying agent loops.
Lifecycle Hooks
Callbacks can implement these hooks (all are optional):
from agent.callbacks.base import AsyncCallbackHandler
class MyCallback(AsyncCallbackHandler):
async def on_run_start(self, kwargs, old_items):
"""Called when agent.run() begins"""
pass
async def on_run_continue(self, kwargs, old_items, new_items) -> bool:
"""Called before each iteration. Return False to stop."""
return True
async def on_llm_start(self, messages):
"""Preprocess messages before LLM call. Return modified messages."""
return messages
async def on_llm_end(self, messages):
"""Postprocess messages after LLM call. Return modified messages."""
return messages
async def on_usage(self, usage):
"""Called with token usage info after each LLM call"""
print(f"Tokens: {usage.total_tokens}, Cost: ${usage.response_cost:.4f}")
async def on_computer_call_start(self, item):
"""Called before a computer action (click, type, etc.)"""
pass
async def on_computer_call_end(self, item, result):
"""Called after a computer action completes"""
pass
async def on_screenshot(self, screenshot, name):
"""Called when a screenshot is taken"""
pass
async def on_run_end(self, kwargs, old_items, new_items):
"""Called when agent.run() completes"""
passCreating Custom Callbacks
Build your own callback by extending AsyncCallbackHandler:
from agent.callbacks.base import AsyncCallbackHandler
class ActionCounterCallback(AsyncCallbackHandler):
def __init__(self):
self.action_count = 0
async def on_computer_call_end(self, item, result):
self.action_count += 1
print(f"Actions taken: {self.action_count}")
async def on_run_end(self, kwargs, old_items, new_items):
print(f"Total actions in run: {self.action_count}")
self.action_count = 0 # Reset for next run
# Use it
counter = ActionCounterCallback()
agent = ComputerAgent(
model="...",
tools=[computer],
callbacks=[counter]
)
await agent.run("Open Firefox and search for Cua")
print(f"Final count: {counter.action_count}")Callback Execution Order
Callbacks execute in this order during a run:
on_run_start- Once at the beginning- For each iteration:
on_run_continue- Check if should continueon_llm_start- Before LLM callon_api_start- Before API requeston_api_end- After API responseon_usage- With usage statson_llm_end- After LLM processingon_responses- With model responseson_text/on_computer_call_start/on_computer_call_end- Per response itemon_screenshot- When screenshots are taken
on_run_end- Once at the end
Multiple callbacks are called in the order they appear in the callbacks list.
Was this page helpful?