class RubyLLM::Tool
A Tool is an action an AI model can call during a chat. Subclasses describe themselves with ::description, declare their arguments, and implement execute:
class Weather < RubyLLM::Tool description "Gets current weather for a location" def execute(latitude:, longitude:) response = Faraday.get "https://api.open-meteo.com/v1/forecast", latitude: latitude, longitude: longitude, current: "temperature_2m,wind_speed_10m" JSON.parse(response.body) end end chat.with_tools(Weather).ask "What's the weather in Berlin?"
When no parameters are declared, the argument schema is inferred from execute’s keyword arguments: required keywords become required string parameters and optional keywords become optional ones. Use ::parameter or ::parameters when arguments need explicit types, descriptions, or structure.
Public Class Methods
# File lib/ruby_llm/tool.rb, line 93 def description(text = nil) return @description unless text @description = text end
Sets the description the model sees for this tool, or returns the current description when called without an argument.
class Weather < RubyLLM::Tool description "Gets current weather for a location" end
Source
# File lib/ruby_llm/tool.rb, line 110 def parameter(name, **options) declared_parameters[name] = Parameter.new(name, **options) end
Declares a parameter for the tool. options accepts type: (defaults to 'string'), description:, and required: (defaults to true).
class Distance < RubyLLM::Tool description "Calculates distance between two cities" parameter :origin, description: "Origin city name" parameter :destination, description: "Destination city name" parameter :units, type: :string, description: "metric or imperial", required: false end
# File lib/ruby_llm/tool.rb, line 135 def parameters(schema = nil, &block) if schema.nil? && block.nil? raise ArgumentError, 'parameters requires a schema or a block; declare single arguments with parameter' end @parameters_schema_definition = SchemaDefinition.new(schema:, block:) self end
Sets the JSON Schema for the tool’s arguments. Accepts a schema hash, a Schematist::Schema class or instance, or a block written in the schematist DSL. Returns self.
class Scheduler < RubyLLM::Tool description "Books a meeting" parameters do object :window, description: "Time window to reserve" do string :start, description: "ISO8601 start time" string :finish, description: "ISO8601 end time" end array :participants, of: :string end end
Raises ArgumentError when called without a schema or a block.
# File lib/ruby_llm/tool.rb, line 190 def provider_options(options = (get = true)) return @provider_options ||= {} if get raise ArgumentError, 'provider_options does not accept nil' if options.nil? @provider_options = options.to_h self end
Sets provider-specific metadata, such as Anthropic’s cache_control hints, merged verbatim into the tool payload sent to the provider. Without an argument, returns the current options.
provider_options cache_control: { type: "ephemeral" }
Raises ArgumentError if options is nil.
# File lib/ruby_llm/tool.rb, line 170 def requires_approval(&resolver) @requires_approval = true @approval_resolver = resolver end
Declares that this tool must be approved before it executes. The conversation loop pauses the tool call until a decision is recorded with Chat#approve or Chat#deny, so Chat#complete returns cleanly and can be called again once the decision exists. In Rails the decision persists on the tool call record and survives process restarts.
class IssueRefund < RubyLLM::Tool requires_approval def execute(order_id:) Refunds.issue!(order_id) end end
Pass a block to resolve the decision yourself instead of using the recorded one. The block receives the ToolCall and returns true to execute, false to deny, or nil while the decision is pending.
The block never runs at class definition. The loop consults it whenever it needs the decision, which can be several times while the call is pending, including after a crashed job resumes, so write it as an idempotent read. If it also creates the approval request, make that a find-or-create.
requires_approval { |tool_call| Approvals.status(tool_call.id) }
Source
# File lib/ruby_llm/tool.rb, line 76 def tool_name normalized = name.to_s.dup.force_encoding('UTF-8').unicode_normalize(:nfkd) ascii_name = normalized.encode('ASCII', replace: '').gsub(/[^a-zA-Z0-9_-]/, '-') Support::Utils.underscore(ascii_name).delete_suffix('_tool') end
Returns the name the model calls this tool by, derived from the class name: underscored, reduced to ASCII, with a trailing “_tool” removed. Override this method to choose a different name.
WeatherLookup.tool_name # => "weather_lookup"
Public Instance Methods
# File lib/ruby_llm/tool.rb, line 285 def call(tool_call: nil, **arguments) normalized_args = arguments.transform_keys(&:to_sym) validation_error = validate_keyword_arguments(normalized_args) return { error: "Invalid tool arguments: #{validation_error}" } if validation_error RubyLLM.logger.debug { "Tool #{name} called with: #{normalized_args.inspect}" } normalized_args[TOOL_CALL_KEYWORD] = tool_call if execute_accepts_tool_call? result = execute(**normalized_args) RubyLLM.logger.debug { "Tool #{name} returned: #{result.inspect}" } result end
Source
# File lib/ruby_llm/tool.rb, line 239 def description self.class.description end
Returns the tool description declared on the class with ::description.
Source
# File lib/ruby_llm/tool.rb, line 312 def execute(...) raise NotImplementedError, 'Subclasses must implement #execute' end
Runs the tool with the arguments chosen by the model. Subclasses must implement this method; the base implementation raises NotImplementedError. The return value is sent back to the model. Return a Hash like { error: "..." } to report a recoverable failure.
Declare an optional tool_call: keyword to receive the ToolCall being executed. The keyword is reserved: it never appears in the tool’s argument schema and is filled in by RubyLLM, not by the model.
def execute(query:, tool_call: nil) AuditLog.create!(tool_call_id: tool_call&.id) Search.run(query) end
Source
# File lib/ruby_llm/tool.rb, line 234 def name self.class.tool_name end
Returns the name the model calls this tool by, delegating to ::tool_name. Override either one to choose a different name.
WeatherLookup.new.name # => "weather_lookup"
Source
# File lib/ruby_llm/tool.rb, line 263 def parameters_schema return @parameters_schema if defined?(@parameters_schema) @parameters_schema = begin definition = self.class.parameters_schema_definition if definition&.present? definition.json_schema elsif declared_parameters.any? SchemaDefinition.from_parameters(declared_parameters)&.json_schema else SchemaDefinition.from_parameters(inferred_parameters, allow_empty: true)&.json_schema end end end
Returns the JSON Schema for the tool’s arguments, whether declared explicitly or inferred from the execute signature.
Source
# File lib/ruby_llm/tool.rb, line 257 def provider_options self.class.provider_options end
Returns the provider-specific tool metadata declared on the class.
Source
# File lib/ruby_llm/tool.rb, line 244 def requires_approval? self.class.requires_approval? end
Returns whether this tool was declared with ::requires_approval.