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

#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



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

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



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

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



146
147
148
149
150
151
152
153
154
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 146

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



134
135
136
137
138
139
140
141
142
143
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 134

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.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb', line 159

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

      # 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
# 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

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

#on_end_message(&block) ⇒ Object



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

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

#on_new_message(&block) ⇒ Object

Override callback registration methods to support multi-subscriber



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

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

#on_tool_call(&block) ⇒ Object



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

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

#on_tool_result(&block) ⇒ Object



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

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



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

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



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

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