Callback Handlers¶
Callback handlers are a powerful feature of the Strands Agents SDK that allow you to intercept and process events as they happen during agent execution. This enables real-time monitoring, custom output formatting, and integration with external systems.
Callback handlers receive events in real-time as they occur during an agent's lifecycle:
- Text generation from the model
- Tool selection and execution
- Reasoning process
- Errors and completions
Note: For asynchronous applications such as web servers, Strands Agents also provides async iterators as an alternative to callback-based callback handlers.
Basic Usage¶
The simplest way to use a callback handler is to pass a callback function to your agent:
from strands import Agent
from strands_tools import calculator
def custom_callback_handler(**kwargs):
# Process stream data
if "data" in kwargs:
print(f"MODEL OUTPUT: {kwargs['data']}")
elif "current_tool_use" in kwargs and kwargs["current_tool_use"].get("name"):
print(f"\nUSING TOOL: {kwargs['current_tool_use']['name']}")
# Create an agent with custom callback handler
agent = Agent(
tools=[calculator],
callback_handler=custom_callback_handler
)
agent("Calculate 2+2")
Callback Handler Events¶
Callback handlers receive the same event types as async iterators, as keyword arguments:
Text Generation Events¶
data
: Text chunk from the model's outputcomplete
: Boolean indicating if this is the final chunkdelta
: Raw delta content from the model
Tool Events¶
current_tool_use
: Information about the current tool being used, including:toolUseId
: Unique ID for this tool usename
: Name of the toolinput
: Tool input parameters (accumulated as streaming occurs)
Lifecycle Events¶
init_event_loop
: True when the event loop is initializingstart_event_loop
: True when the event loop is startingstart
: True when a new cycle startsmessage
: Present when a new message is createdevent
: Raw event from the model streamforce_stop
: True if the event loop was forced to stopforce_stop_reason
: Reason for forced stop
Reasoning Events¶
reasoning
: True for reasoning eventsreasoningText
: Text from reasoning processreasoning_signature
: Signature from reasoning process
Default Callback Handler¶
Strands Agents provides a default callback handler that formats output to the console:
from strands import Agent
from strands.handlers.callback_handler import PrintingCallbackHandler
# The default callback handler prints text and shows tool usage
agent = Agent(callback_handler=PrintingCallbackHandler())
If you want to disable all output, specify None
for the callback handler:
from strands import Agent
# No output will be displayed
agent = Agent(callback_handler=None)
Custom Callback Handlers¶
Custom callback handlers enable you to have fine-grained control over what is streamed from your agents.
Example - Print all events in the stream sequence¶
Custom callback handlers can be useful to debug sequences of events in the agent loop:
from strands import Agent
from strands_tools import calculator
def debugger_callback_handler(**kwargs):
# Print the values in kwargs so that we can see everything
print(kwargs)
agent = Agent(
tools=[calculator],
callback_handler=debugger_callback_handler
)
agent("What is 922 + 5321")
This handler prints all calls to the callback handler including full event details.
Example - Buffering Output Per Message¶
This handler demonstrates how to buffer text and only show it when a complete message is generated. This pattern is useful for chat interfaces where you want to show polished, complete responses:
import json
from strands import Agent
from strands_tools import calculator
def message_buffer_handler(**kwargs):
# When a new message is created from the assistant, print its content
if "message" in kwargs and kwargs["message"].get("role") == "assistant":
print(json.dumps(kwargs["message"], indent=2))
# Usage with an agent
agent = Agent(
tools=[calculator],
callback_handler=message_buffer_handler
)
agent("What is 2+2 and tell me about AWS Lambda")
This handler leverages the message
event which is triggered when a complete message is created. By using this approach, we can buffer the incrementally streamed text and only display complete, coherent messages rather than partial fragments. This is particularly useful in conversational interfaces or when responses benefit from being processed as complete units.
Example - Event Loop Lifecycle Tracking¶
This callback handler illustrates the event loop lifecycle events and how they relate to each other. It's useful for understanding the flow of execution in the Strands agent:
from strands import Agent
from strands_tools import calculator
def event_loop_tracker(**kwargs):
# Track event loop lifecycle
if kwargs.get("init_event_loop", False):
print("🔄 Event loop initialized")
elif kwargs.get("start_event_loop", False):
print("▶️ Event loop cycle starting")
elif kwargs.get("start", False):
print("📝 New cycle started")
elif "message" in kwargs:
print(f"📬 New message created: {kwargs['message']['role']}")
elif kwargs.get("complete", False):
print("✅ Cycle completed")
elif kwargs.get("force_stop", False):
print(f"🛑 Event loop force-stopped: {kwargs.get('force_stop_reason', 'unknown reason')}")
# Track tool usage
if "current_tool_use" in kwargs and kwargs["current_tool_use"].get("name"):
tool_name = kwargs["current_tool_use"]["name"]
print(f"🔧 Using tool: {tool_name}")
# Show only a snippet of text to keep output clean
if "data" in kwargs:
# Only show first 20 chars of each chunk for demo purposes
data_snippet = kwargs["data"][:20] + ("..." if len(kwargs["data"]) > 20 else "")
print(f"📟 Text: {data_snippet}")
# Create agent with event loop tracker
agent = Agent(
tools=[calculator],
callback_handler=event_loop_tracker
)
# This will show the full event lifecycle in the console
agent("What is the capital of France and what is 42+7?")
The output will show the sequence of events:
- First the event loop initializes (
init_event_loop
) - Then the cycle begins (
start_event_loop
) - New cycles may start multiple times during execution (
start
) - Text generation and tool usage events occur during the cycle
- Finally, the cycle completes (
complete
) or may be force-stopped
Best Practices¶
When implementing callback handlers:
- Keep Them Fast: Callback handlers run in the critical path of agent execution
- Handle All Event Types: Be prepared for different event types
- Graceful Errors: Handle exceptions within your handler
- State Management: Store accumulated state in the
request_state