Chat Event Handlers
Hook into the chat lifecycle with additive callbacks for UI updates, logging, and analytics
Table of contents
After reading this guide, you will know:
- Which lifecycle events you can register handlers for.
- How the
before_*andafter_*callbacks differ from the olderon_*handlers. - How to read token usage inside an
after_messagecallback. - How to observe tool calls and tool results as they happen.
- How to observe model fallback attempts.
- When callbacks fire for streaming versus non-streaming requests.
Chat Event Handlers
You can register blocks to be called when certain events occur during the chat lifecycle. This is particularly useful for UI updates, logging, analytics, or building real-time chat interfaces.
Available Event Handlers
RubyLLM provides two callback styles. The on_* handlers replace any previously registered handler for the same event, which is useful when you want to override behavior. The Rails-style before_* and after_* callbacks are additive, so multiple registrations for the same event all run. Additive callbacks are available from v1.15+.
chat = RubyLLM.chat
# Called at first chunk received from the assistant
chat.before_message do
print "Assistant > "
end
# Called after the complete assistant message (including tool calls/results) is received
chat.after_message do |message|
puts "Response complete!"
# Note: message might be nil if an error occurred during the request
if message&.tokens&.output
tokens =
message.tokens.input.to_i +
message.tokens.output.to_i +
message.tokens.cache_read.to_i +
message.tokens.cache_write.to_i
puts "Used #{tokens} tokens"
end
end
# Called when the AI decides to use a tool
chat.before_tool_call do |tool_call|
puts "AI is calling tool: #{tool_call.name} with arguments: #{tool_call.arguments}"
end
# Called after a tool returns its result
chat.after_tool_result do |result|
puts "Tool returned: #{result}"
end
# Called before RubyLLM tries a fallback model
chat.before_fallback do |fallback|
puts "Falling back from #{fallback.from.id} to #{fallback.to.id}"
end
# Called after a fallback model succeeds or fails
chat.after_fallback do |fallback|
puts "Fallback #{fallback.succeeded? ? 'succeeded' : 'failed'}"
end
# Message callbacks work for both streaming and non-streaming requests
chat.ask "What is metaprogramming in Ruby?"
Fallback callbacks run around each fallback attempt. before_fallback fires after the current model fails and before RubyLLM tries the fallback model. after_fallback fires when that fallback attempt succeeds or fails. The callback receives a RubyLLM::Fallback with from, to, error, attempt, response, fallback_error, streaming?, and chunks_yielded?.
Each callback is additive - register as many as you like, and they run alongside RubyLLM’s own bookkeeping (such as the Rails persistence callbacks). The older replacing handlers (on_new_message, on_end_message, on_tool_call, on_tool_result) were removed in 2.0.
Next Steps
- Chat - the core conversation interface these events fire on.
- Streaming - stream chunks as the assistant generates them.
- Tools - define the tools whose calls and results these callbacks observe.
- Error Handling - configure model fallbacks and fallback error handling.
- Rails Integration - see how persistence callbacks run alongside your own.