Class: RobotLab::Tool
- Inherits:
-
RubyLLM::Tool
- Object
- RubyLLM::Tool
- RobotLab::Tool
- Defined in:
- lib/robot_lab/tool.rb
Overview
A tool that robots can use, built on RubyLLM::Tool.
Provides two patterns for defining tools:
-
**Subclass pattern** — for reusable, robot-aware tools:
class GetWeather < RobotLab::Tool
description "Get weather for a location"
param :location, type: "string", desc: "City name"
def execute(location:)
WeatherService.fetch(location)
end
end
-
**Factory pattern** — for dynamic/inline tools:
tool = RobotLab::Tool.create(
name: "get_time",
description: "Get the current time"
) { |args| Time.now.to_s }
Subclasses have access to the owning robot via an accessor, enabling tools that modify their robot’s state (temperature, system prompt, spawning, etc.).
Direct Known Subclasses
Class Attribute Summary collapse
-
.raise_on_error ⇒ Boolean
writeonly
When true, exceptions from #execute propagate instead of being caught.
Instance Attribute Summary collapse
-
#mcp ⇒ Object
readonly
Returns the value of attribute mcp.
-
#robot ⇒ Robot?
The robot that owns this tool.
Class Method Summary collapse
-
.create(name:, description: nil, parameters: nil, mcp: nil, robot: nil) {|args| ... } ⇒ Tool
Factory for dynamic tools (MCP wrappers, inline tools).
-
.ractor_safe(value = nil) ⇒ Boolean
(also: ractor_safe?)
Declare that this tool class is safe to run inside a Ractor.
- .raise_on_error? ⇒ Boolean
Instance Method Summary collapse
-
#call(args) ⇒ Object
Invokes the tool, routing through the Ractor worker pool if ractor_safe.
-
#initialize(robot: nil) ⇒ Tool
constructor
Creates a new Tool instance.
-
#mcp? ⇒ Boolean
Check if this is an MCP-provided tool.
-
#name ⇒ String
Override name to support explicit names for dynamic/MCP tools.
-
#to_h ⇒ Hash
Hash representation.
-
#to_json(*args) ⇒ String
JSON representation.
-
#to_json_schema ⇒ Hash
Convert to JSON Schema for LLM function calling.
Constructor Details
#initialize(robot: nil) ⇒ Tool
Creates a new Tool instance.
81 82 83 84 |
# File 'lib/robot_lab/tool.rb', line 81 def initialize(robot: nil) super() @robot = robot end |
Class Attribute Details
.raise_on_error=(value) ⇒ Boolean (writeonly)
When true, exceptions from #execute propagate instead of being caught. Default: false (graceful error handling).
44 45 46 |
# File 'lib/robot_lab/tool.rb', line 44 def raise_on_error=(value) @raise_on_error = value end |
Instance Attribute Details
#mcp ⇒ Object (readonly)
Returns the value of attribute mcp.
37 38 39 |
# File 'lib/robot_lab/tool.rb', line 37 def mcp @mcp end |
#robot ⇒ Robot?
Returns the robot that owns this tool.
33 34 35 |
# File 'lib/robot_lab/tool.rb', line 33 def robot @robot end |
Class Method Details
.create(name:, description: nil, parameters: nil, mcp: nil, robot: nil) {|args| ... } ⇒ Tool
Factory for dynamic tools (MCP wrappers, inline tools).
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/robot_lab/tool.rb', line 150 def self.create(name:, description: nil, parameters: nil, mcp: nil, robot: nil, &handler) desc_text = description params_hash = parameters block = handler tool_class = Class.new(self) do description(desc_text) if desc_text if params_hash.is_a?(Hash) && params_hash[:properties] required_list = Array(params_hash[:required]).map(&:to_s) params_hash[:properties].each do |pname, pdef| param pname.to_sym, type: pdef[:type] || "string", desc: pdef[:description], required: required_list.include?(pname.to_s) end end define_method(:execute) do |**args| block.call(args) end end instance = tool_class.new(robot: robot) instance.instance_variable_set(:@custom_name, name.to_s) instance.instance_variable_set(:@mcp, mcp) instance end |
.ractor_safe(value = nil) ⇒ Boolean Also known as: ractor_safe?
Declare that this tool class is safe to run inside a Ractor.
Ractor-safe tools must be stateless — no captured mutable closures and no non-shareable class-level state. The tool is instantiated fresh inside the Ractor worker for each call.
With no argument, acts as a getter (walks the inheritance chain). With a Boolean argument, sets the value.
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/robot_lab/tool.rb', line 61 def ractor_safe(value = nil) if value.nil? if instance_variable_defined?(:@ractor_safe) @ractor_safe elsif superclass.respond_to?(:ractor_safe) superclass.ractor_safe else false end else @ractor_safe = value end end |
.raise_on_error? ⇒ Boolean
46 47 48 |
# File 'lib/robot_lab/tool.rb', line 46 def raise_on_error? defined?(@raise_on_error) ? @raise_on_error : false end |
Instance Method Details
#call(args) ⇒ Object
Invokes the tool, routing through the Ractor worker pool if ractor_safe.
For Ractor-safe tools with a resolvable class name: submits to RobotLab.ractor_pool and blocks for the frozen result. Anonymous classes (name.nil?) fall through to the inline path.
For non-Ractor-safe tools: runs execute directly in the calling thread.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/robot_lab/tool.rb', line 96 def call(args) if self.class.ractor_safe? && !self.class.name.nil? RobotLab.ractor_pool.submit(self.class.name, args) else super end rescue RobotLab::ToolError => e raise if self.class.raise_on_error? "Error (#{name}): #{e.}" rescue StandardError => e raise if self.class.raise_on_error? RobotLab.config.logger.warn("Tool '#{name}' error: #{e.class}: #{e.}") "Error (#{name}): #{e.}" end |
#mcp? ⇒ Boolean
Check if this is an MCP-provided tool.
122 123 124 |
# File 'lib/robot_lab/tool.rb', line 122 def mcp? !@mcp.nil? end |
#name ⇒ String
Override name to support explicit names for dynamic/MCP tools.
115 116 117 |
# File 'lib/robot_lab/tool.rb', line 115 def name defined?(@custom_name) && @custom_name ? @custom_name : super end |
#to_h ⇒ Hash
Hash representation.
195 196 197 198 199 200 201 |
# File 'lib/robot_lab/tool.rb', line 195 def to_h { name: name, description: description, mcp: mcp }.compact end |
#to_json(*args) ⇒ String
JSON representation.
207 208 209 |
# File 'lib/robot_lab/tool.rb', line 207 def to_json(*args) to_h.to_json(*args) end |
#to_json_schema ⇒ Hash
Convert to JSON Schema for LLM function calling. Used by RobotLab adapters for provider-specific formatting.
183 184 185 186 187 188 189 190 |
# File 'lib/robot_lab/tool.rb', line 183 def to_json_schema schema = params_schema || { "type" => "object", "properties" => {}, "required" => [] } { name: name, description: description, parameters: deep_symbolize_keys(schema) }.compact end |