Class: LLM::Function

Inherits:
Object
  • Object
show all
Extended by:
Registry
Includes:
Tracing
Defined in:
lib/llm/function.rb,
lib/llm/function/fork.rb,
lib/llm/function/task.rb,
lib/llm/function/array.rb,
lib/llm/function/group.rb,
lib/llm/function/ractor.rb,
lib/llm/function/tracing.rb,
lib/llm/function/fork/job.rb,
lib/llm/function/registry.rb,
lib/llm/function/fork/task.rb,
lib/llm/function/ractor/job.rb,
lib/llm/function/ractor/task.rb,
lib/llm/function/ractor/mailbox.rb

Overview

The LLM::Function class represents a local function that can be called by an LLM. Most users should define tools as subclasses of Tool instead — Function is the lower-level building block that Tool wraps.

Examples:

Tool subclass (preferred for most users)

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

Inline function (block-form DSL)

LLM.function(:run_command) do |fn|
  fn.name "run-command"
  fn.description "Runs a shell command"
  fn.params do |schema|
    schema.object(command: schema.string.required)
  end
  fn.define do |command:|
    {success: Kernel.system(command)}
  end
end

Defined Under Namespace

Modules: Array, Async, Fiber, Fork, Ractor, Registry, Sequential, Thread, Tracing Classes: Group, Return, Task

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Registry

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

Constructor Details

#initialize(name) {|self| ... } ⇒ Function

Returns a new instance of Function.

Parameters:

  • name (String)

    The function name

Yield Parameters:



161
162
163
164
165
166
167
# File 'lib/llm/function.rb', line 161

def initialize(name, &b)
  @name = name
  @schema = LLM::Schema.new
  @called = false
  @cancelled = false
  yield(self) if block_given?
end

Instance Attribute Details

#argumentsHash, ...

Returns function arguments

Returns:



119
120
121
# File 'lib/llm/function.rb', line 119

def arguments
  @arguments
end

#idString?

Returns the function ID

Returns:

  • (String, nil)


114
115
116
# File 'lib/llm/function.rb', line 114

def id
  @id
end

#modelString?

Returns a model name, or nil

Returns:

  • (String, nil)


156
157
158
# File 'lib/llm/function.rb', line 156

def model
  @model
end

#tracerLLM::Tracer?

Returns a tracer, or nil

Returns:



151
152
153
# File 'lib/llm/function.rb', line 151

def tracer
  @tracer
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Compares functions by tool call ID when both sides have one.

Parameters:

Returns:

  • (Boolean)


133
134
135
136
137
138
# File 'lib/llm/function.rb', line 133

def ==(other)
  return true if equal?(other)
  return false unless self.class === other
  return false unless id && other.id
  id == other.id
end

#adapt(provider) ⇒ Hash

Returns:

  • (Hash)


356
357
358
# File 'lib/llm/function.rb', line 356

def adapt(provider)
  provider.adapt_function(self)
end

#callLLM::Function::Return

Call the function



223
224
225
226
227
228
# File 'lib/llm/function.rb', line 223

def call
  llm = @tracer&.llm
  llm ? llm.with_tracer(@tracer) { call_function } : call_function
ensure
  @called = true
end

#called?Boolean

Returns true when a function has been called

Returns:

  • (Boolean)


307
308
309
# File 'lib/llm/function.rb', line 307

def called?
  @called
end

#cancel(reason: "function call cancelled") ⇒ LLM::Function::Return

Returns a value that communicates that the function call was cancelled

Examples:

llm = LLM.openai(key: ENV["KEY"])
ctx = LLM::Context.new(llm, tools: [fn1, fn2])
ctx.talk "I want to run the functions"
ctx.talk ctx.pending_functions.map(&:cancel)

Returns:



286
287
288
289
290
# File 'lib/llm/function.rb', line 286

def cancel(reason: "function call cancelled")
  Return.new(id, name, {cancelled: true, reason:})
ensure
  @cancelled = true
end

#cancelled?Boolean

Returns true when a function has been cancelled

Returns:

  • (Boolean)


314
315
316
# File 'lib/llm/function.rb', line 314

def cancelled?
  @cancelled
end

#define(klass = nil, &b) ⇒ void Also known as: def

This method returns an undefined value.

Set the function implementation

Parameters:

  • b (Proc, Class)

    The function implementation



215
216
217
# File 'lib/llm/function.rb', line 215

def define(klass = nil, &b)
  @runner = klass || b
end

#description(desc = nil) ⇒ void

This method returns an undefined value.

Set (or get) the function description

Parameters:

  • desc (String) (defaults to: nil)

    The function description



185
186
187
188
189
190
191
# File 'lib/llm/function.rb', line 185

def description(desc = nil)
  if desc
    @description = desc
  else
    @description
  end
end

#hashInteger

Returns a hash value compatible with #==.

Returns:

  • (Integer)


144
145
146
# File 'lib/llm/function.rb', line 144

def hash
  id ? id.hash : object_id.hash
end

#interrupt!nil Also known as: cancel!

Notifies the function runner that the call was interrupted. This is cooperative and only applies to runners that implement on_interrupt.

Returns:

  • (nil)


297
298
299
300
301
# File 'lib/llm/function.rb', line 297

def interrupt!
  hook = %i[on_cancel on_interrupt].find { @runner.respond_to?(_1) }
  @runner.public_send(hook) if hook
  nil
end

#name(name = nil) ⇒ void

This method returns an undefined value.

Set (or get) the function name

Parameters:

  • name (String) (defaults to: nil)

    The function name



173
174
175
176
177
178
179
# File 'lib/llm/function.rb', line 173

def name(name = nil)
  if name
    @name = name.to_s
  else
    @name
  end
end

#params {|schema| ... } ⇒ LLM::Schema::Leaf?

Set (or get) the function parameters

Yield Parameters:

Returns:



197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/llm/function.rb', line 197

def params
  if block_given?
    params = yield(@schema)
    params = LLM::Schema.parse(params) if Hash === params
    if @params
      @params.merge!(params)
    else
      @params = params
    end
  else
    @params
  end
end

#pending?Boolean

Returns true when a function has neither been called nor cancelled

Returns:

  • (Boolean)


328
329
330
# File 'lib/llm/function.rb', line 328

def pending?
  !@called && !@cancelled
end

#rate_limitLLM::Function::Return

Returns an in-band error for a tool loop rate limit.



346
347
348
349
350
351
352
# File 'lib/llm/function.rb', line 346

def rate_limit
  LLM::Function::Return.new(id, name, {
    error: true,
    type: LLM::ToolLoopError.name,
    message: "tool loop rate limit reached"
  })
end

#runnerObject

Returns the bound function runner instance.

Returns:



363
364
365
366
367
# File 'lib/llm/function.rb', line 363

def runner
  runner = Class === @runner ? @runner.new : @runner
  runner.tracer = @tracer if runner.respond_to?(:tracer=)
  runner
end

#skill?Boolean

Returns true when this function is backed by a skill tool.

Returns:

  • (Boolean)


321
322
323
# File 'lib/llm/function.rb', line 321

def skill?
  @runner.respond_to?(:skill?) and @runner.skill?
end

#task(strategy, options = {}) ⇒ LLM::Function::Task

Returns a function as a LLM::Function::Task.

Examples:

# As a group
ctx.talk(ctx.pending_functions.wait)

# As a task
task = tool.task(:thread)
result = task.value

Parameters:

  • strategy (Symbol)

    Controls concurrency strategy:

    • :sequential: Call the function sequentially
    • :thread: Use threads
    • :async: Use async tasks (requires async gem)
    • :fork: Use a forked child process (requires xchan.rb support)
    • :fiber: Use scheduler-backed fibers (requires Fiber.scheduler)
    • :ractor: Use Ruby ractors (class-based tools only; MCP tools are not supported)

Returns:



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/llm/function.rb', line 253

def task(strategy, options = {})
  case strategy
  when :sequential
    Sequential::Task.new(self, options)
  when :async
    LLM.require "async" unless defined?(::Async)
    Async::Task.new(self, options)
  when :thread
    Thread::Task.new(self, options)
  when :fiber
    Fiber::Task.new(self, options)
  when :fork
    LLM.require "xchan", "~> 0.22" unless defined?(::Chan::UNIXSocket)
    Fork::Task.new(self, options.merge(tracer: @tracer))
  when :ractor
    raise LLM::RactorError, "Ractor concurrency only supports class-based tools" unless Class === @runner
    if @runner.respond_to?(:skill?) && @runner.skill?
      raise LLM::RactorError, "Ractor concurrency does not support skill-backed tools"
    end
    Ractor::Task.new(self, options.merge(runner_class: @runner, id:, name:, arguments:, tracer: @tracer, model:))
  else
    raise ArgumentError, "Unknown strategy: #{strategy.inspect}. Expected :sequential, :thread, :fiber, :async, :fork, or :ractor"
  end
end

#unavailableLLM::Function::Return

Returns an in-band error for an unresolved function call.



335
336
337
338
339
340
341
# File 'lib/llm/function.rb', line 335

def unavailable
  Return.new(id, name, {
    error: true,
    type: LLM::NoSuchToolError.name,
    message: "tool not found"
  })
end