Class: Brute::Tools::Adapter
- Inherits:
-
Object
- Object
- Brute::Tools::Adapter
- Defined in:
- lib/brute/tools/adapter.rb
Overview
Normalizes any tool shape into one neutral interface so the rest of Brute never has to care which tools library (if any) a tool was written with.
This solves three problems:
-
Using any tools library — anything that quacks like a tool (#name plus #call or #execute) is wrapped into the same interface.
-
Avoiding tool libraries entirely — Brute::Tool, Brute::Turn::ToolPipeline and Tools::SubAgent work without inheriting from a library class.
-
Quickly adding tools — a plain Hash with a proc is enough:
Brute::Tools::Adapter.wrap( name: "echo", description: "Echo the input back", params: { msg: { type: "string", required: true } }, execute: ->(msg:) { msg }, )
The neutral interface:
adapter.name # String
adapter.description # String
adapter.params # { key => { type:, desc:, required: } }
adapter.call(args) # execute with a (string- or symbol-keyed) Hash
The inline run proc converts adapters (via #to_h) into whatever its
LLM library expects; ToolPipeline executes them via #call.
Instance Attribute Summary collapse
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#original ⇒ Object
readonly
The tool object this adapter wraps (Brute::Tool, Brute::Turn::ToolPipeline, SubAgent, Hash definition, ...).
-
#params ⇒ Object
readonly
Returns the value of attribute params.
Class Method Summary collapse
-
.from_brute_tool(tool) ⇒ Object
A Brute::Tool instance.
-
.from_duck_type(tool) ⇒ Object
Anything tool-shaped: needs #name and #call or #execute.
-
.from_hash(definition) ⇒ Object
Quick inline tool: { name:, description:, params:, execute: }.
-
.wrap(tool) ⇒ Object
Wrap a single tool of any supported shape.
-
.wrap_all(tools) ⇒ Object
Wrap a list of tools into a { name_sym => adapter } lookup hash — the shape ToolPipeline works with.
Instance Method Summary collapse
-
#call(arguments = {}) ⇒ Object
Execute the tool.
-
#initialize(name:, description:, params:, handler:, schema: nil, original: nil) ⇒ Adapter
constructor
A new instance of Adapter.
-
#to_h ⇒ Object
Library-neutral tool definition (JSON-Schema-ish).
Constructor Details
#initialize(name:, description:, params:, handler:, schema: nil, original: nil) ⇒ Adapter
Returns a new instance of Adapter.
121 122 123 124 125 126 127 128 |
# File 'lib/brute/tools/adapter.rb', line 121 def initialize(name:, description:, params:, handler:, schema: nil, original: nil) @name = name @description = description @params = params || {} @schema = schema @handler = handler @original = original end |
Instance Attribute Details
#description ⇒ Object (readonly)
Returns the value of attribute description.
38 39 40 |
# File 'lib/brute/tools/adapter.rb', line 38 def description @description end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
38 39 40 |
# File 'lib/brute/tools/adapter.rb', line 38 def name @name end |
#original ⇒ Object (readonly)
The tool object this adapter wraps (Brute::Tool, Brute::Turn::ToolPipeline, SubAgent, Hash definition, ...).
132 133 134 |
# File 'lib/brute/tools/adapter.rb', line 132 def original @original end |
#params ⇒ Object (readonly)
Returns the value of attribute params.
38 39 40 |
# File 'lib/brute/tools/adapter.rb', line 38 def params @params end |
Class Method Details
.from_brute_tool(tool) ⇒ Object
A Brute::Tool instance. Tools declared with the params(...) schema DSL keep their full JSON schema.
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/brute/tools/adapter.rb', line 94 def self.from_brute_tool(tool) new( name: tool.name.to_s, description: tool.description, params: tool.params, schema: tool.params_schema, handler: ->(**args) { tool.call(args) }, original: tool, ) end |
.from_duck_type(tool) ⇒ Object
Anything tool-shaped: needs #name and #call or #execute.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/brute/tools/adapter.rb', line 106 def self.from_duck_type(tool) unless tool.respond_to?(:name) && (tool.respond_to?(:call) || tool.respond_to?(:execute)) raise ArgumentError, "don't know how to adapt #{tool.inspect} into a tool" end entry = tool.respond_to?(:execute) ? tool.method(:execute) : tool.method(:call) new( name: tool.name.to_s, description: tool.respond_to?(:description) ? tool.description : "", params: tool.respond_to?(:params) ? tool.params : {}, handler: ->(**args) { entry.call(**args) }, original: tool, ) end |
.from_hash(definition) ⇒ Object
Quick inline tool: { name:, description:, params:, execute: }
78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/brute/tools/adapter.rb', line 78 def self.from_hash(definition) definition = definition.transform_keys(&:to_sym) handler = definition.fetch(:execute) { definition[:handler] } raise ArgumentError, "inline tool needs an :execute proc" unless handler.respond_to?(:call) new( name: definition.fetch(:name).to_s, description: definition.fetch(:description, ""), params: definition.fetch(:params, {}), handler: ->(**args) { handler.call(**args) }, original: definition, ) end |
.wrap(tool) ⇒ Object
Wrap a single tool of any supported shape. Idempotent.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/brute/tools/adapter.rb', line 41 def self.wrap(tool) return tool if tool.is_a?(Adapter) tool = tool.new if tool.is_a?(Class) case tool when Hash then from_hash(tool) when ::Brute::Tool then from_brute_tool(tool) when Brute::Tools::SubAgent then new( name: tool.name, description: tool.description, params: tool.params, handler: ->(**args) { tool.execute(args) }, original: tool, ) when Brute::Turn::ToolPipeline then new( name: tool.name, description: tool.description, params: tool.params, handler: ->(**args) { tool.call(**args) }, original: tool, ) else from_duck_type(tool) end end |
.wrap_all(tools) ⇒ Object
Wrap a list of tools into a { name_sym => adapter } lookup hash — the shape ToolPipeline works with.
70 71 72 73 74 75 |
# File 'lib/brute/tools/adapter.rb', line 70 def self.wrap_all(tools) Array(tools).each_with_object({}) do |tool, hash| adapter = wrap(tool) hash[adapter.name.to_sym] = adapter end end |
Instance Method Details
#call(arguments = {}) ⇒ Object
Execute the tool. Accepts string- or symbol-keyed argument hashes, as delivered by LLM providers.
136 137 138 139 |
# File 'lib/brute/tools/adapter.rb', line 136 def call(arguments = {}) args = arguments.to_h.transform_keys(&:to_sym) @handler.call(**args) end |
#to_h ⇒ Object
Library-neutral tool definition (JSON-Schema-ish). The inline run
proc reshapes this into whatever its LLM library expects.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/brute/tools/adapter.rb', line 143 def to_h return { name: @name, description: @description, parameters: @schema.deep_symbolize_keys } if @schema properties = @params.transform_values do |opts| { type: opts[:type] || "string", description: opts[:desc] || opts[:description], items: opts[:items], enum: opts[:enum], }.compact end required = @params.select { |_k, opts| opts[:required] }.keys { name: @name, description: @description, parameters: { type: "object", properties: properties, required: required.map(&:to_s), }, } end |