Module: RubyLLM::Chat::MultiSubscriberCallbacks

Included in:
RubyLLM::Chat
Defined in:
lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb

Overview

Module to prepend for multi-subscriber callbacks

Instance Method Summary collapse

Instance Method Details

#after_tool_calls { ... } ⇒ self

Sets a hook that fires once per tool-call round, after all tool results are committed to chat history and before the next LLM call

Skipped when a tool returns Tool::Halt — pending work stays queued for the next #ask call.

Yields:

  • Block called after tool results are added to history

Returns:

  • (self)

    for chaining



142
143
144
145
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 142

def after_tool_calls(&block)
  @after_tool_calls_hook = block
  self
end

#around_llm_request {|Array<Message>, Proc| ... } ⇒ self

Sets a hook to wrap LLM API requests with custom behavior

Yields:

  • (Array<Message>, Proc)

    Block called before each LLM request

Returns:

  • (self)

    for chaining



129
130
131
132
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 129

def around_llm_request(&block)
  @around_llm_request_hook = block
  self
end

#around_tool_execution {|ToolCall, Tool, Proc| ... } ⇒ self

Sets a hook to wrap tool execution with custom behavior

Yields:

  • (ToolCall, Tool, Proc)

    Block called for each tool execution

Returns:

  • (self)

    for chaining



120
121
122
123
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 120

def around_tool_execution(&block)
  @around_tool_execution_hook = block
  self
end

#callback_count(event = nil) ⇒ Object

Returns the number of callbacks registered for the specified event



160
161
162
163
164
165
166
167
168
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 160

def callback_count(event = nil)
  @callback_monitor.synchronize do
    if event
      @callbacks[event]&.size || 0
    else
      @callbacks.transform_values(&:size)
    end
  end
end

#clear_callbacks(event = nil) ⇒ Object

Clears all callbacks for the specified event, or all events if none specified



148
149
150
151
152
153
154
155
156
157
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 148

def clear_callbacks(event = nil)
  @callback_monitor.synchronize do
    if event
      @callbacks[event]&.clear
    else
      @callbacks.each_value(&:clear)
    end
  end
  self
end

#complete(&block) ⇒ Object

Override complete to use emit() and support around_llm_request hook. Uses a trampoline loop instead of mutual recursion with handle_tool_calls to avoid stack growth during multi-round tool-call conversations.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 173

def complete(&block)
  loop do
    response = execute_llm_request(&block)

    emit(:new_message) unless block_given?

    if @schema && response.content.is_a?(String)
      begin
        response.content = JSON.parse(response.content)
      rescue JSON::ParserError
        # If parsing fails, keep content as string
      end
    end

    add_message(response)
    emit(:end_message, response)

    if response.tool_call?
      halt_result = handle_tool_calls(response, &block)
      return halt_result if halt_result

      @after_tool_calls_hook&.call

      # Loop continues: next LLM call with zero stack growth
    else
      return response
    end
  end
end

#initialize(**kwargs) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 51

def initialize(**kwargs)
  super(**kwargs)

  # Replace single callback hash with multi-subscriber arrays
  @callbacks = {
    new_message: [],
    end_message: [],
    tool_call: [],
    tool_result: [],
  }
  @callback_monitor = Monitor.new

  # Initialize around hooks
  @around_tool_execution_hook = nil
  @around_llm_request_hook = nil
  @after_tool_calls_hook = nil

  # Keep @on for backward compatibility (read-only)
  @on = nil
end

#on_end_message(&block) ⇒ Object



101
102
103
104
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 101

def on_end_message(&block)
  subscribe(:end_message, &block)
  self
end

#on_new_message(&block) ⇒ Object

Override callback registration methods to support multi-subscriber



96
97
98
99
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 96

def on_new_message(&block)
  subscribe(:new_message, &block)
  self
end

#on_tool_call(&block) ⇒ Object



106
107
108
109
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 106

def on_tool_call(&block)
  subscribe(:tool_call, &block)
  self
end

#on_tool_result(&block) ⇒ Object



111
112
113
114
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 111

def on_tool_result(&block)
  subscribe(:tool_result, &block)
  self
end

#once(event, tag: nil, &block) ⇒ Object

Subscribe to an event that automatically unsubscribes after firing once



86
87
88
89
90
91
92
93
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 86

def once(event, tag: nil, &block)
  subscription = nil
  wrapper = lambda do |*args|
    subscription&.unsubscribe
    block.call(*args)
  end
  subscription = subscribe(event, tag: tag, &wrapper)
end

#subscribe(event, tag: nil, &block) ⇒ Object

Subscribe to an event with the given block Returns a Subscription that can be used to unsubscribe



74
75
76
77
78
79
80
81
82
83
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 74

def subscribe(event, tag: nil, &block)
  @callback_monitor.synchronize do
    unless @callbacks.key?(event)
      raise ArgumentError, "Unknown event: #{event}. Valid events: #{@callbacks.keys.join(", ")}"
    end

    @callbacks[event] << block
    Subscription.new(@callbacks[event], block, monitor: @callback_monitor, tag: tag)
  end
end