Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/guides/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,11 @@ Refer to the [Working with Models Guide]({% link guides/models.md %}) for detail

## Chat Event Handlers

You can register blocks to be called when certain events occur during the chat lifecycle, useful for UI updates or logging.
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 three event handlers that cover the complete chat lifecycle:

```ruby
chat = RubyLLM.chat
Expand All @@ -483,6 +487,11 @@ chat.on_end_message do |message|
end
end

# Called when the AI decides to use a tool
chat.on_tool_call do |tool_call|
puts "AI is calling tool: #{tool_call.name} with arguments: #{tool_call.arguments}"
end

# These callbacks work for both streaming and non-streaming requests
chat.ask "What is metaprogramming in Ruby?"
```
Expand Down
9 changes: 8 additions & 1 deletion lib/ruby_llm/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def initialize(model: nil, provider: nil, assume_model_exists: false, context: n
@schema = nil
@on = {
new_message: nil,
end_message: nil
end_message: nil,
tool_call: nil
}
end

Expand Down Expand Up @@ -112,6 +113,11 @@ def on_end_message(&block)
self
end

def on_tool_call(&block)
@on[:tool_call] = block
self
end

def each(&)
messages.each(&)
end
Expand Down Expand Up @@ -181,6 +187,7 @@ def wrap_streaming_block(&block)
def handle_tool_calls(response, &)
response.tool_calls.each_value do |tool_call|
@on[:new_message]&.call
@on[:tool_call]&.call(tool_call)
result = execute_tool tool_call
message = add_message role: :tool, content: result.to_s, tool_call_id: tool_call.id
@on[:end_message]&.call(message)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions spec/ruby_llm/chat_tools_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,25 @@ def execute
end
end

describe 'tool call callbacks' do
it 'calls on_tool_call callback when tools are used' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations
tool_calls_received = []

chat = RubyLLM.chat
.with_tool(Weather)
.on_tool_call { |tool_call| tool_calls_received << tool_call }

response = chat.ask("What's the weather in Berlin? (52.5200, 13.4050)")

expect(tool_calls_received).not_to be_empty
expect(tool_calls_received.first).to respond_to(:name)
expect(tool_calls_received.first).to respond_to(:arguments)
expect(tool_calls_received.first.name).to eq('weather')
expect(response.content).to include('15')
expect(response.content).to include('10')
end
end

describe 'error handling' do
it 'raises an error when tool execution fails' do # rubocop:disable RSpec/MultipleExpectations
chat = RubyLLM.chat.with_tool(BrokenTool)
Expand Down