Class: LLM::Tool

Inherits:
Object
  • Object
show all
Extended by:
Function::Registry, Param
Defined in:
lib/llm/tool.rb,
lib/llm/tools/ls.rb,
lib/llm/tools/rg.rb,
lib/llm/tools/git.rb,
lib/llm/tools/pwd.rb,
lib/llm/tool/param.rb,
lib/llm/tools/exec.rb,
lib/llm/tools/ruby.rb,
lib/llm/tools/chdir.rb,
lib/llm/tools/mkdir.rb,
lib/llm/tools/utils.rb,
lib/llm/tools/which.rb,
lib/llm/tools/bundle.rb,
lib/llm/tools/dir_tree.rb,
lib/llm/tools/edit-file.rb,
lib/llm/tools/not_found.rb,
lib/llm/tools/read_file.rb,
lib/llm/tools/write_file.rb

Overview

The LLM::Tool class represents a local tool that can be called by an LLM. Under the hood, it is a wrapper around LLM::Function but allows the definition of a function (also known as a tool) as a class.

Examples:

Declarative form (preferred)

class ReadFile < LLM::Tool
  name "read-file"
  description "Read a file from disk"
  parameter :path, String, "The filename or path"
  required %i[path]

  def call(path:)
    {contents: File.read(path)}
  end
end

Block-form DSL (also supported)

class RunCommand < LLM::Tool
  name "run-command"
  description "Runs a shell command"
  params do |schema|
    schema.object(command: schema.string.required)
  end

  def call(command:)
    {success: Kernel.system(command)}
  end
end

See Also:

Direct Known Subclasses

Bundle, Chdir, DirTree, EditFile, Exec, Git, Ls, Mkdir, Pwd, ReadFile, Rg, Ruby, Which, WriteFile

Defined Under Namespace

Modules: Param, Utils Classes: Bundle, Chdir, DirTree, EditFile, Exec, Git, Ls, Mkdir, Pwd, ReadFile, Rg, Ruby, Which, WriteFile

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Param

defaults, param, required

Methods included from Function::Registry

clear_registry!, find_by_name, lock, register, registry, registry_key, tool_name, unregister

Class Method Details

.NotFound(name:) ⇒ LLM::Function

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The NotFound tool is an internal the tool the runtime uses to represent a tool the model requested but is not known to the runtime.

The tool has the same name the model requested, and when the tool is called it will return an in-band error.

Ractor-safe.

Returns:



18
19
20
21
22
23
24
25
26
27
# File 'lib/llm/tools/not_found.rb', line 18

def self.NotFound(name:)
  klass = Class.new(LLM::Tool)
  klass.name(name)
  klass.class_eval <<~STR, __FILE__, 1
    def call
      raise LLM::NoSuchToolError, "tool '#{name}' does not exist"
    end
  STR
  klass.function
end

.description(desc = nil) ⇒ String

Returns (or sets) the tool description

Parameters:

  • desc (String, nil) (defaults to: nil) —

    The tool description

Returns:

  • (String)


70
71
72
73
74
# File 'lib/llm/tool.rb', line 70

def self.description(desc = nil)
  lock do
    desc ? function.description(desc) : function.description
  end
end

.params(p = nil) {|schema| ... } ⇒ LLM::Schema

Returns (or sets) tool parameters

Parameters:

  • p (Hash, nil) (defaults to: nil) —

    Optional set of params to set

Yield Parameters:

  • schema (LLM::Schema) —

    The schema object to define parameters

Returns:



83
84
85
86
87
# File 'lib/llm/tool.rb', line 83

def self.params(p = nil, &)
  lock do
    function.tap { _1.params(p, &) }
  end
end

.subclasses ⇒ Array<Class<LLM::Tool>>

Returns:



91
92
93
# File 'lib/llm/tool.rb', line 91

def self.subclasses
  super.reject(&:abstract?)
end

.abstract!

This method returns an undefined value.

Examples:

class LLM::Tool::Git < LLM::Tool
  abstract!
end


101
102
103
# File 'lib/llm/tool.rb', line 101

def self.abstract!
  @abstract = true
end

.abstract? ⇒ Boolean

Returns true when the tool is abstract

Returns:

  • (Boolean)


108
109
110
# File 'lib/llm/tool.rb', line 108

def self.abstract?
  !!@abstract
end

.mcp(mcp, tool) ⇒ Class<LLM::Tool>

Returns a subclass of LLM::Tool

Parameters:

  • mcp (LLM::MCP) —

    The MCP client that will execute the tool call

  • tool (Hash) —

    A tool (as a raw Hash)

Returns:

  • (Class<LLM::Tool>) —

    Returns a subclass of LLM::Tool



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/llm/tool.rb', line 119

def self.mcp(mcp, tool)
  klass  = Class.new(LLM::Tool)
  client = Ractor.make_shareable(mcp.to_h)
  klass.class_eval <<~STR, __FILE__, 1
    def self.inspect
      "<#{LLM::Utils.object_id(self)} name=#{tool["name"]} (mcp)>"
    end
    singleton_class.alias_method :to_s, :inspect

    def self.mcp?
      true
    end
  STR
  klass.instance_variable_set(:"@mcp", mcp)
  Ractor.make_shareable(klass)
  Ractor.make_shareable(tool)
  call = Ractor.shareable_proc do |**args|
    if Ractor.main?
      klass.instance_variable_get(:@mcp).call_tool(tool["name"], args)
    else
      LLM::MCP.new(**client).call_tool(tool["name"], args)
    end
  end
  ractorize(klass, tool, call)
end

.a2a(a2a, skill) ⇒ Class<LLM::Tool>

Returns a subclass of LLM::Tool

Parameters:

Returns:

  • (Class<LLM::Tool>) —

    Returns a subclass of LLM::Tool



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
178
179
180
181
182
183
# File 'lib/llm/tool.rb', line 152

def self.a2a(a2a, skill)
  name   = skill.name.gsub(" ", "-")
  client = Ractor.make_shareable(a2a.to_h)
  klass  = Ractor.make_shareable(Class.new(LLM::Tool))
  klass.class_eval <<~STR, __FILE__, 1
    def self.inspect
      "<#{LLM::Utils.object_id(self)} name=#{name} (a2a)>"
    end
    singleton_class.alias_method :to_s, :inspect

    def self.a2a?
      true
    end
  STR
  klass.instance_variable_set(:"@a2a", a2a)
  schema = LLM::Schema.new
  inputs = schema.object(input: schema.string.description("The input string").required).to_h
  tool = LLM.share!({
    "name" => name,
    "description" => skill.description,
    "inputSchema" => inputs
  })
  call = Ractor.shareable_proc do |**args|
    res = if Ractor.main?
      klass.instance_variable_get(:@a2a).send_message(args[:input])
    else
      LLM::A2A.new(**client).send_message(args[:input])
    end
    {task: res}
  end
  ractorize(klass, tool, call)
end

.clear_registry!

This method returns an undefined value.

Clear the registry



188
189
190
191
192
193
# File 'lib/llm/tool.rb', line 188

def self.clear_registry!
  lock do
    @__registry.each_value { LLM::Function.unregister(_1.function) }
    super
  end
end

.register(tool) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Registers a tool and its function.

Parameters:



199
200
201
202
# File 'lib/llm/tool.rb', line 199

def self.register(tool)
  super
  LLM::Function.register(tool.function)
end

.unregister(tool) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Unregister a tool from the registry

Parameters:



208
209
210
211
212
213
214
# File 'lib/llm/tool.rb', line 208

def self.unregister(tool)
  lock do
    LLM::Function.unregister(tool.function)
    super
    tool
  end
end

.inherited(tool)

This method returns an undefined value.

Registers the tool as a function when inherited

Parameters:

  • tool (Class) —

    The subclass



220
221
222
223
224
225
226
# File 'lib/llm/tool.rb', line 220

def self.inherited(tool)
  LLM.lock(:inherited) do
    tool.instance_eval { @__monitor ||= Monitor.new }
    tool.function.define(tool)
    LLM::Tool.register(tool) unless tool.mcp? || tool.a2a?
  end
end

.set(properties)

This method returns an undefined value.

Assign multiple tool properties at once.

Examples:

class Tool < LLM::Tool
  set name: "my-tool",
      description: "my tool does this and that",
      parameters: [
        [:name, String , "this and that"],
        [:age , Integer, "this and that"]
      ],
      required: %i[name],
      defaults: {age: 42}
end

Parameters:

  • properties (Hash)


243
244
245
246
247
248
249
250
251
252
253
# File 'lib/llm/tool.rb', line 243

def self.set(properties)
  properties.each do
    if _1.to_s == "parameters"
      _2.each { |attributes| parameter(*attributes) }
    elsif respond_to?(_1)
      public_send(_1, _2)
    else
      raise KeyError, "key not found: #{_1}"
    end
  end
end

.registry ⇒ Array<Class<LLM::Tool>>

Returns all registered tool classes with definitions.

Returns:



258
259
260
# File 'lib/llm/tool.rb', line 258

def self.registry
  super.select(&:name).reject(&:abstract?)
end

.find_by_name!(name) ⇒ Class<LLM::Tool>

Finds a registered tool by name.

Parameters:

  • name (String)

Returns:

Raises:



267
268
269
# File 'lib/llm/tool.rb', line 267

def self.find_by_name!(name)
  find_by_name(name) || raise(LLM::NoSuchToolError, "tool '#{name}' not found")
end

.function ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



273
274
275
276
277
# File 'lib/llm/tool.rb', line 273

def self.function
  lock do
    @function ||= LLM::Function.new(nil)
  end
end

.mcp? ⇒ Boolean

Returns true if the tool is an MCP tool

Returns:

  • (Boolean)


282
283
284
# File 'lib/llm/tool.rb', line 282

def self.mcp?
  false
end

.a2a? ⇒ Boolean

Returns true if the tool is an A2A tool

Returns:

  • (Boolean)


289
290
291
# File 'lib/llm/tool.rb', line 289

def self.a2a?
  false
end

.name(name = nil) ⇒ String

Returns (or sets) the tool name

Parameters:

  • name (String, nil) (defaults to: nil) —

    The tool name

Returns:

  • (String)


60
61
62
63
64
# File 'lib/llm/tool.rb', line 60

def self.name(name = nil)
  lock do
    name ? function.name(name) : function.name
  end
end

Instance Method Details

#function ⇒ LLM::Function

Returns a function bound to this tool instance.

Returns:



311
312
313
# File 'lib/llm/tool.rb', line 311

def function
  @function ||= self.class.function.dup.tap { _1.define(self) }
end

#mcp? ⇒ Boolean

Returns true if the tool is an MCP tool

Returns:

  • (Boolean)


318
319
320
# File 'lib/llm/tool.rb', line 318

def mcp?
  self.class.mcp?
end

#a2a? ⇒ Boolean

Returns true if the tool is an A2A tool

Returns:

  • (Boolean)


325
326
327
# File 'lib/llm/tool.rb', line 325

def a2a?
  self.class.a2a?
end

#on_interrupt ⇒ nil

Called when an in-flight tool run is interrupted. Tools can override this to implement cooperative cleanup.

Returns:

  • (nil)


333
334
# File 'lib/llm/tool.rb', line 333

def on_interrupt
end

#on_cancel ⇒ nil

Called when an in-flight tool run is cancelled.

Returns:

  • (nil)


339
340
341
# File 'lib/llm/tool.rb', line 339

def on_cancel
  on_interrupt
end