Class: RubyLLM::Agents::Tool

Inherits:
Tool
  • Object
show all
Defined in:
lib/ruby_llm/agents/tool.rb

Overview

Base class for tools that need access to the agent’s execution context.

Inherits from RubyLLM::Tool and adds:

  • ‘context` accessor: read agent params, tenant, execution ID

  • ‘timeout` DSL: per-tool timeout in seconds

  • Error handling: exceptions become error strings for the LLM

  • Tool execution tracking: records each tool call in the database

Users implement ‘execute()` — the standard RubyLLM convention. This class overrides `call()` to wrap execution with its features.

Examples:

Defining a tool

class BashTool < RubyLLM::Agents::Tool
  description "Run a shell command"
  timeout 30

  param :command, desc: "The command to run", required: true

  def execute(command:)
    context.container_id  # reads agent param
    # ... run command ...
  end
end

Using with an agent

class CodingAgent < ApplicationAgent
  param :container_id, required: true
  tools BashTool
end

CodingAgent.call(query: "list files", container_id: "abc123")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#contextToolContext? (readonly)

The execution context, set before each call. Provides access to agent params, tenant, execution ID.

Returns:



44
45
46
# File 'lib/ruby_llm/agents/tool.rb', line 44

def context
  @context
end

Class Method Details

.timeout(value = nil) ⇒ Integer?

Sets or gets the per-tool timeout in seconds.

Parameters:

  • value (Integer, nil) (defaults to: nil)

    Timeout in seconds (setter)

Returns:

  • (Integer, nil)

    The configured timeout (getter)



51
52
53
54
55
56
57
# File 'lib/ruby_llm/agents/tool.rb', line 51

def timeout(value = nil)
  if value
    @timeout = value
  else
    @timeout
  end
end

Instance Method Details

#call(args) ⇒ String, Tool::Halt

Wraps RubyLLM’s call() with context, timeout, tracking, and error handling.

RubyLLM’s Chat calls tool.call(args) during the tool loop. We set up context, create a tracking record, apply timeout, then delegate to super (which validates args and calls execute).

Parameters:

  • args (Hash)

    Tool arguments from the LLM

Returns:

  • (String, Tool::Halt)

    The tool result or a Halt signal



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ruby_llm/agents/tool.rb', line 68

def call(args)
  pipeline_context = Thread.current[:ruby_llm_agents_caller_context]
  @context = pipeline_context ? ToolContext.new(pipeline_context) : nil

  record = start_tool_tracking(pipeline_context, args)

  check_cancelled!(pipeline_context)

  timeout_seconds = self.class.timeout
  timeout_seconds ||= RubyLLM::Agents.configuration.default_tool_timeout

  result = if timeout_seconds
    Timeout.timeout(timeout_seconds) { super }
  else
    super
  end

  complete_tool_tracking(record, result, status: "success")
  result
rescue Timeout::Error
  complete_tool_tracking(record, nil, status: "timed_out", error: "Timed out after #{timeout_seconds}s")
  "TIMEOUT: Tool did not complete within #{timeout_seconds}s."
rescue RubyLLM::Agents::CancelledError
  complete_tool_tracking(record, nil, status: "cancelled")
  raise # Let cancellation propagate to BaseAgent
rescue => e
  complete_tool_tracking(record, nil, status: "error", error: e.message)
  "ERROR (#{e.class}): #{e.message}"
end