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:



168
169
170
171
172
173
174
# File 'lib/llm/function.rb', line 168

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

#guardClass<LLM::Guard>?

Returns the guard class that protects this function, or nil. The context stamps the guard onto the functions it binds, so any task built from this function checks it before the tool runs.

Returns:



163
164
165
# File 'lib/llm/function.rb', line 163

def guard
  @guard
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)


383
384
385
# File 'lib/llm/function.rb', line 383

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

#budget_spentLLM::Function::Return

Returns an in-band error that indicates the tool call budget has been spent.



358
359
360
361
362
363
364
365
# File 'lib/llm/function.rb', line 358

def budget_spent
  LLM::Function::Return.new(id, name, {
    error: true,
    type: "LLM::BudgetSpentError",
    message: "the tool call budget for this turn has been spent. " \
             "try to solve the problem with less tool calls."
  })
end

#callLLM::Function::Return

Call the function



230
231
232
233
234
235
# File 'lib/llm/function.rb', line 230

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)


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

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:



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

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)


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

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



222
223
224
# File 'lib/llm/function.rb', line 222

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



192
193
194
195
196
197
198
# File 'lib/llm/function.rb', line 192

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)


308
309
310
311
312
# File 'lib/llm/function.rb', line 308

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



180
181
182
183
184
185
186
# File 'lib/llm/function.rb', line 180

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:



204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/llm/function.rb', line 204

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 || LLM::Schema::Object.new({})
  end
end

#pending?Boolean

Returns true when a function has neither been called nor cancelled

Returns:

  • (Boolean)


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

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

#returnLLM::Function::Return

Note:

return is a Ruby keyword, so this is defined via Kernel#define_method.

Builds an LLM::Function::Return for this function, using its own id and name. The given keywords become the return's value.

Parameters:

  • value (Hash)

    The return content, eg {error: true, type: ..., message: ...}.

Returns:



377
378
379
# File 'lib/llm/function.rb', line 377

define_method(:return) do |value|
  Return.new(id, name, value)
end

#runnerObject

Returns the bound function runner instance.

Returns:



390
391
392
393
394
# File 'lib/llm/function.rb', line 390

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)


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

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:



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/llm/function.rb', line 259

def task(strategy, options = {})
  ##
  # Check the function's guard on the calling thread before handing
  # the tool to the strategy. The task carries the blocked result and
  # returns it without running if the guard intervenes.
  options = options.merge(guarded: @guard&.call(function: self))
  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.



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

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