Class: Boxcars::Boxcar Abstract
- Inherits:
-
Object
- Object
- Boxcars::Boxcar
- Defined in:
- lib/boxcars/boxcar.rb
Overview
Direct Known Subclasses
EngineBoxcar, GoogleSearch, MCP::ToolBoxcar, RubyCalculator, StationAgent::HandoffBoxcar, URLText, WikipediaSearch
Constant Summary collapse
- SCHEMA_KEY_ALIASES =
{ additional_properties: "additionalProperties", one_of: "oneOf", any_of: "anyOf", all_of: "allOf" }.freeze
- TYPE_ALIASES =
{ int: "integer", integer: "integer", float: "number", double: "number", decimal: "number", number: "number", string: "string", bool: "boolean", boolean: "boolean", array: "array", object: "object", null: "null" }.freeze
Instance Attribute Summary collapse
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
#return_direct ⇒ Object
readonly
Returns the value of attribute return_direct.
Class Method Summary collapse
-
.assi(*strs) ⇒ Object
helpers for conversation prompt building assistant message.
-
.hist ⇒ Object
history entries.
-
.syst(*strs) ⇒ Object
system message.
-
.user(*strs) ⇒ Object
user message.
Instance Method Summary collapse
-
#apply(input_list:) ⇒ Array<Hash>
Apply the boxcar to a list of inputs.
-
#call(inputs:) ⇒ Hash
Run the core logic for one invocation.
-
#conduct ⇒ Hash
Run the boxcar and return full input/output context.
-
#conduct_result ⇒ Boxcars::Result?
Alias for ‘run_result` to make intent explicit when callers want full context first.
-
#initialize(description:, name: nil, return_direct: false, parameters: nil) ⇒ Boxcar
constructor
A Boxcar is a container for a single tool to run.
-
#input_keys ⇒ Object
Input keys this chain expects.
-
#output_keys ⇒ Object
Output keys this chain expects.
-
#parameters_json_schema ⇒ Object
Convert legacy Boxcar parameter definitions into JSON Schema.
-
#run ⇒ Object
Convenience wrapper around ‘conduct` that returns only the first output value.
-
#run_result ⇒ Boxcars::Result?
Convenience helper that returns the structured ‘Boxcars::Result` from `conduct`.
- #schema ⇒ Object
-
#tool_call_name(max_length: 64) ⇒ Object
A provider-safe function/tool name for LLM tool-calling APIs.
-
#tool_definition ⇒ Object
Provider-agnostic normalized tool definition.
-
#tool_spec ⇒ Object
OpenAI-compatible tool spec shape (also usable by many compatible providers).
-
#validate_inputs(inputs:) ⇒ Object
Check that all inputs are present.
-
#validate_outputs(outputs:) ⇒ Object
check that all outputs are present.
Constructor Details
#initialize(description:, name: nil, return_direct: false, parameters: nil) ⇒ Boxcar
A Boxcar is a container for a single tool to run.
35 36 37 38 39 40 |
# File 'lib/boxcars/boxcar.rb', line 35 def initialize(description:, name: nil, return_direct: false, parameters: nil) @name = name || self.class.name @description = description || @name @return_direct = return_direct @parameters = parameters || { question: { type: :string, description: "the input question", required: true } } end |
Instance Attribute Details
#description ⇒ Object (readonly)
Returns the value of attribute description.
6 7 8 |
# File 'lib/boxcars/boxcar.rb', line 6 def description @description end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
6 7 8 |
# File 'lib/boxcars/boxcar.rb', line 6 def name @name end |
#parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
6 7 8 |
# File 'lib/boxcars/boxcar.rb', line 6 def parameters @parameters end |
#return_direct ⇒ Object (readonly)
Returns the value of attribute return_direct.
6 7 8 |
# File 'lib/boxcars/boxcar.rb', line 6 def return_direct @return_direct end |
Class Method Details
.assi(*strs) ⇒ Object
helpers for conversation prompt building assistant message
136 137 138 |
# File 'lib/boxcars/boxcar.rb', line 136 def self.assi(*strs) [:assistant, strs.join] end |
.hist ⇒ Object
history entries
151 152 153 |
# File 'lib/boxcars/boxcar.rb', line 151 def self.hist [:history, ""] end |
.syst(*strs) ⇒ Object
system message
141 142 143 |
# File 'lib/boxcars/boxcar.rb', line 141 def self.syst(*strs) [:system, strs.join] end |
.user(*strs) ⇒ Object
user message
146 147 148 |
# File 'lib/boxcars/boxcar.rb', line 146 def self.user(*strs) [:user, strs.join] end |
Instance Method Details
#apply(input_list:) ⇒ Array<Hash>
Apply the boxcar to a list of inputs. Override this when a subclass can batch requests more efficiently.
85 86 87 |
# File 'lib/boxcars/boxcar.rb', line 85 def apply(input_list:) input_list.map { |inputs| call(inputs:) } end |
#call(inputs:) ⇒ Hash
Run the core logic for one invocation.
77 78 79 |
# File 'lib/boxcars/boxcar.rb', line 77 def call(inputs:) raise NotImplementedError end |
#conduct ⇒ Hash
Run the boxcar and return full input/output context. you can pass one or the other, but not both.
126 127 128 129 130 131 132 |
# File 'lib/boxcars/boxcar.rb', line 126 def conduct(*, **) Boxcars.info "> Entering #{name}#run", :gray, style: :bold rv = depart(*, **) remember_history(rv) Boxcars.info "< Exiting #{name}#run", :gray, style: :bold rv end |
#conduct_result ⇒ Boxcars::Result?
Alias for ‘run_result` to make intent explicit when callers want full context first.
117 118 119 |
# File 'lib/boxcars/boxcar.rb', line 117 def conduct_result(*, **) run_result(*, **) end |
#input_keys ⇒ Object
Input keys this chain expects.
43 44 45 |
# File 'lib/boxcars/boxcar.rb', line 43 def input_keys [:question] end |
#output_keys ⇒ Object
Output keys this chain expects.
48 49 50 |
# File 'lib/boxcars/boxcar.rb', line 48 def output_keys [:answer] end |
#parameters_json_schema ⇒ Object
Convert legacy Boxcar parameter definitions into JSON Schema.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/boxcars/boxcar.rb', line 178 def parameters_json_schema props = {} required = [] parameters.each do |param_name, info| param_key = param_name.to_s props[param_key] = parameter_descriptor_to_json_schema(info) required << param_key if parameter_required?(info) end schema = { "type" => "object", "properties" => props, "additionalProperties" => false } schema["required"] = required if required.any? schema end |
#run ⇒ Object
Convenience wrapper around ‘conduct` that returns only the first output value. you can pass one or the other, but not both.
95 96 97 98 99 100 101 102 103 |
# File 'lib/boxcars/boxcar.rb', line 95 def run(*, **) rv = conduct(*, **) result = Result.extract(rv) return result.answer if result return rv.output_for(output_keys[0]) if rv.respond_to?(:output_for) return rv[output_keys[0]] if rv.is_a?(Hash) rv end |
#run_result ⇒ Boxcars::Result?
Convenience helper that returns the structured ‘Boxcars::Result` from `conduct`.
109 110 111 |
# File 'lib/boxcars/boxcar.rb', line 109 def run_result(*, **) Result.extract(conduct(*, **)) end |
#schema ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/boxcars/boxcar.rb', line 155 def schema params = parameters.map do |name, info| "<param name=#{name.to_s.inspect} data-type=#{info[:type].to_s.inspect} required=\"#{info[:required] == true}\" " \ "description=#{info[:description].inspect} />" end.join("\n") <<~SCHEMA.freeze <tool name="#{name}" description="#{description}"> <params> #{params} </params> </tool> SCHEMA end |
#tool_call_name(max_length: 64) ⇒ Object
A provider-safe function/tool name for LLM tool-calling APIs.
170 171 172 173 174 175 |
# File 'lib/boxcars/boxcar.rb', line 170 def tool_call_name(max_length: 64) sanitized = name.to_s.gsub(/[^\w-]+/, "_").gsub(/\A_+|_+\z/, "") sanitized = "boxcar" if sanitized.empty? sanitized = "boxcar_#{sanitized}" unless sanitized.match?(/\A[a-zA-Z_]/) sanitized[0, max_length] end |
#tool_definition ⇒ Object
Provider-agnostic normalized tool definition.
198 199 200 201 202 203 204 205 |
# File 'lib/boxcars/boxcar.rb', line 198 def tool_definition { name: tool_call_name, display_name: name, description: description, input_schema: parameters_json_schema } end |
#tool_spec ⇒ Object
OpenAI-compatible tool spec shape (also usable by many compatible providers).
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/boxcars/boxcar.rb', line 208 def tool_spec { type: "function", function: { name: tool_call_name, description: description, parameters: parameters_json_schema } } end |
#validate_inputs(inputs:) ⇒ Object
Check that all inputs are present.
55 56 57 58 59 60 |
# File 'lib/boxcars/boxcar.rb', line 55 def validate_inputs(inputs:) missing_keys = input_keys.reject { |key| key_present?(inputs, key) } raise "Missing some input keys: #{missing_keys}" if missing_keys.any? inputs end |
#validate_outputs(outputs:) ⇒ Object
check that all outputs are present
65 66 67 68 69 70 71 72 |
# File 'lib/boxcars/boxcar.rb', line 65 def validate_outputs(outputs:) unexpected = outputs.reject do |key| output_keys.any? { |expected| same_key?(expected, key) } || same_key?(:log, key) end return if unexpected.empty? raise "Did not get output keys that were expected, got: #{outputs}. Expected: #{output_keys}" end |