Class: RubyPi::Tools::Executor
- Inherits:
-
Object
- Object
- RubyPi::Tools::Executor
- Defined in:
- lib/ruby_pi/tools/executor.rb
Constant Summary collapse
- DEFAULT_TIMEOUT =
Default timeout for each tool execution, in seconds.
30
Instance Attribute Summary collapse
-
#mode ⇒ Symbol
readonly
The execution mode (:parallel or :sequential).
-
#timeout ⇒ Numeric
readonly
The per-tool timeout in seconds.
Class Method Summary collapse
-
.deep_symbolize_keys(obj) ⇒ Object
Recursively converts all string keys in a hash to symbols so that tool implementations can use idiomatic Ruby symbol-key access (e.g. ‘args`) regardless of whether the LLM provider returned string-keyed JSON.
Instance Method Summary collapse
-
#execute(calls) ⇒ Array<RubyPi::Tools::Result>
Executes a list of tool calls and returns their results.
-
#initialize(registry, mode: :parallel, timeout: DEFAULT_TIMEOUT) ⇒ Executor
constructor
Creates a new Executor.
Constructor Details
#initialize(registry, mode: :parallel, timeout: DEFAULT_TIMEOUT) ⇒ Executor
Creates a new Executor.
44 45 46 47 48 49 50 51 52 |
# File 'lib/ruby_pi/tools/executor.rb', line 44 def initialize(registry, mode: :parallel, timeout: DEFAULT_TIMEOUT) unless %i[parallel sequential].include?(mode) raise ArgumentError, "Mode must be :parallel or :sequential, got #{mode.inspect}" end @registry = registry @mode = mode @timeout = timeout end |
Instance Attribute Details
#mode ⇒ Symbol (readonly)
Returns The execution mode (:parallel or :sequential).
33 34 35 |
# File 'lib/ruby_pi/tools/executor.rb', line 33 def mode @mode end |
#timeout ⇒ Numeric (readonly)
Returns The per-tool timeout in seconds.
36 37 38 |
# File 'lib/ruby_pi/tools/executor.rb', line 36 def timeout @timeout end |
Class Method Details
.deep_symbolize_keys(obj) ⇒ Object
Recursively converts all string keys in a hash to symbols so that tool implementations can use idiomatic Ruby symbol-key access (e.g. ‘args`) regardless of whether the LLM provider returned string-keyed JSON. Exposed as a class method so the agent loop can apply the same transformation to tool_call arguments before recording them in `tool_calls_made`, keeping the agent’s observable arguments shape consistent with what tool blocks see.
255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/ruby_pi/tools/executor.rb', line 255 def self.deep_symbolize_keys(obj) case obj when Hash obj.each_with_object({}) do |(key, value), result| result[key.to_sym] = deep_symbolize_keys(value) end when Array obj.map { |item| deep_symbolize_keys(item) } else obj end end |
Instance Method Details
#execute(calls) ⇒ Array<RubyPi::Tools::Result>
Executes a list of tool calls and returns their results.
Each call is a hash with ‘:name` (String or Symbol) and `:arguments` (Hash). Tools are looked up in the registry; if a tool is not found, a failure Result is returned for that call.
Issue #17: Raises NoToolsRegisteredError if the registry is nil and tool calls are attempted, preventing a confusing NoMethodError.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/ruby_pi/tools/executor.rb', line 66 def execute(calls) # Issue #17: Guard against nil registry — if the LLM hallucinated tool # calls but no tools are registered, raise a typed error immediately # rather than crashing with NoMethodError on nil.find. if @registry.nil? raise RubyPi::NoToolsRegisteredError, "Model returned #{calls.size} tool call(s) but no tools are registered" end case @mode when :parallel execute_parallel(calls) when :sequential execute_sequential(calls) end end |