Class: Xeno::Tool
- Inherits:
-
RubyLLM::Tool
- Object
- RubyLLM::Tool
- Xeno::Tool
- Defined in:
- lib/xeno/tool.rb
Overview
Tools
A tool is something the agent can do besides talk: look up an order, charge a card, file a ticket. Each tool is a class under agent/tools/, and the model decides when to call it based on the description and parameters you declare:
# agent/tools/get_weather.rb
class Xeno::Tools::GetWeather < Xeno::Tool
description "Return current weather for a city."
parameter :city, description: "City name"
def execute(city:)
{ city: city, condition: "Sunny" }
end
end
The file name is the tool's name on the wire — get_weather.rb is callable as get_weather — and whatever execute returns becomes the tool result the model reads next.
Beyond a plain RubyLLM::Tool, a xeno tool runs inside a durable session: it can keep working state in #state, see the #session it runs in, and declare an ::approval policy so a human signs off before anything irreversible happens.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#session ⇒ Object
The session this call is running in — set by the runner before
call.
Class Method Summary collapse
-
.approval(policy = nil) ⇒ Object
The human-in-the-loop gate.
-
.approval_declared? ⇒ Boolean
:nodoc:.
-
.tool_name ⇒ Object
The runtime name the model calls this tool by, taken from the path: agent/tools/get_weather.rb defines Xeno::Tools::GetWeather, and the demodulized class name matches the file basename.
Instance Method Summary collapse
-
#state ⇒ Object
The session-scoped KV store: JSON-typed values that survive restarts, never cross sessions, and are cleared by reset.
Instance Attribute Details
#session ⇒ Object
The session this call is running in — set by the runner before call. nil when the tool is
exercised outside a session (unit tests calling Tool.new.call directly).
60 61 62 |
# File 'lib/xeno/tool.rb', line 60 def session @session end |
Class Method Details
.approval(policy = nil) ⇒ Object
The human-in-the-loop gate. Gate anything irreversible or externally visible.
approval :never # default: runs without asking
approval :once # asks the first time in a session, then remembered
approval :always # asks on every call
approval ->(ctx) { ctx.principal&.dig("role") != "admin" }
The lambda receives an ApprovalContext (session, turn, tool_name, arguments, principal); truthy means approval is required.
45 46 47 48 49 50 |
# File 'lib/xeno/tool.rb', line 45 def approval(policy = nil) @approval = policy unless policy.nil? return @approval if defined?(@approval) && @approval superclass.respond_to?(:approval) ? superclass.approval : :never end |
.approval_declared? ⇒ Boolean
:nodoc:
52 53 54 55 |
# File 'lib/xeno/tool.rb', line 52 def approval_declared? # :nodoc: (defined?(@approval) && !@approval.nil?) || (superclass.respond_to?(:approval_declared?) && superclass.approval_declared?) end |
.tool_name ⇒ Object
The runtime name the model calls this tool by, taken from the path: agent/tools/get_weather.rb defines Xeno::Tools::GetWeather, and the demodulized class name matches the file basename. No suffix stripping: a charge_card_tool.rb must stay callable as charge_card_tool or the slug-keyed approval lookup misses.
32 33 34 |
# File 'lib/xeno/tool.rb', line 32 def tool_name name.demodulize.underscore end |
Instance Method Details
#state ⇒ Object
The session-scoped KV store: JSON-typed values that survive restarts, never cross sessions, and are cleared by reset. Working state, not long-term memory.
def execute(city:)
searches = state.get("searches").to_i + 1
state.update("searches" => searches, "last_city" => city)
...
end
70 71 72 73 74 |
# File 'lib/xeno/tool.rb', line 70 def state raise Xeno::Error, "session state is only available while running inside a session" unless session session.state end |