Class: Pikuri::Tool::Parameters
- Inherits:
-
Object
- Object
- Pikuri::Tool::Parameters
- Defined in:
- lib/pikuri/tool/parameters.rb
Overview
Schema for a Pikuri::Tool‘s arguments. Built up via the fluent <required|optional>_<type> methods, then frozen by Parameters.build; serializes to the OpenAI JSON-Schema shape via #to_h and validates LLM-supplied argument hashes via #validate.
Defined Under Namespace
Classes: ValidationError
Class Method Summary collapse
-
.build {|builder| ... } ⇒ Parameters
Yield a fresh builder, freeze it, and return it.
Instance Method Summary collapse
-
#freeze ⇒ self
Freeze the builder along with its internal collections, so post-build mutation attempts raise
FrozenErrorinstead of silently succeeding. - #initialize ⇒ Parameters constructor
-
#optional_boolean(name, description) ⇒ self
Add an optional
booleanproperty. -
#optional_enum(name, description, values:) ⇒ self
Add an optional
enumproperty. -
#optional_integer(name, description) ⇒ self
Add an optional
integerproperty. -
#optional_number(name, description) ⇒ self
Add an optional
numberproperty. -
#optional_string(name, description) ⇒ self
Add an optional
stringproperty. -
#required_boolean(name, description) ⇒ self
Add a required
booleanproperty. -
#required_enum(name, description, values:) ⇒ self
Add a required
enumproperty — astringfield constrained to one of a fixed set of values. -
#required_integer(name, description) ⇒ self
Add a required
integerproperty. -
#required_number(name, description) ⇒ self
Add a required
numberproperty (JSON-Schemanumber: Integer or finite Float). -
#required_string(name, description) ⇒ self
Add a required
stringproperty. -
#required_string_array(name, description) ⇒ self
Add a required
array-of-stringproperty — JSON-Schema {type: ‘array’, items: {type: ‘string’}}. -
#to_h ⇒ Hash
Schema in OpenAI JSON-Schema shape.
-
#validate(args) ⇒ Hash{Symbol=>Object}
Validate a tool-call argument hash against the declared schema.
Constructor Details
#initialize ⇒ Parameters
38 39 40 41 |
# File 'lib/pikuri/tool/parameters.rb', line 38 def initialize @properties = {} @required = [] end |
Class Method Details
.build {|builder| ... } ⇒ Parameters
Yield a fresh builder, freeze it, and return it.
31 32 33 34 35 |
# File 'lib/pikuri/tool/parameters.rb', line 31 def self.build builder = new yield builder builder.freeze end |
Instance Method Details
#freeze ⇒ self
Freeze the builder along with its internal collections, so post-build mutation attempts raise FrozenError instead of silently succeeding.
47 48 49 50 51 |
# File 'lib/pikuri/tool/parameters.rb', line 47 def freeze @properties.freeze @required.freeze super end |
#optional_boolean(name, description) ⇒ self
Add an optional boolean property. See #required_boolean for accepted shapes.
162 163 164 |
# File 'lib/pikuri/tool/parameters.rb', line 162 def optional_boolean(name, description) add(name, 'boolean', description, required: false) end |
#optional_enum(name, description, values:) ⇒ self
Add an optional enum property. See #required_enum for the values contract and validation behavior.
194 195 196 |
# File 'lib/pikuri/tool/parameters.rb', line 194 def optional_enum(name, description, values:) add_enum(name, description, values, required: false) end |
#optional_integer(name, description) ⇒ self
Add an optional integer property. See #required_integer for accepted shapes.
118 119 120 |
# File 'lib/pikuri/tool/parameters.rb', line 118 def optional_integer(name, description) add(name, 'integer', description, required: false) end |
#optional_number(name, description) ⇒ self
Add an optional number property. See #required_number for accepted shapes.
139 140 141 |
# File 'lib/pikuri/tool/parameters.rb', line 139 def optional_number(name, description) add(name, 'number', description, required: false) end |
#optional_string(name, description) ⇒ self
Add an optional string property.
67 68 69 |
# File 'lib/pikuri/tool/parameters.rb', line 67 def optional_string(name, description) add(name, 'string', description, required: false) end |
#required_boolean(name, description) ⇒ self
Add a required boolean property. Accepts Ruby true/false as-is, and the literal Strings “true”/“false” (some models surface JSON booleans as Strings) after trimming surrounding whitespace. Other Strings, numbers, and nil are rejected —there is no truthy-coercion of “yes” / 0 / etc.
152 153 154 |
# File 'lib/pikuri/tool/parameters.rb', line 152 def required_boolean(name, description) add(name, 'boolean', description, required: true) end |
#required_enum(name, description, values:) ⇒ self
Add a required enum property — a string field constrained to one of a fixed set of values. Emits JSON-Schema enum alongside type: ‘string’, which the LLM treats as a closed choice. Validation rejects any string outside the set with an LLM-actionable error message listing the allowed values.
181 182 183 |
# File 'lib/pikuri/tool/parameters.rb', line 181 def required_enum(name, description, values:) add_enum(name, description, values, required: true) end |
#required_integer(name, description) ⇒ self
Add a required integer property. Accepts Integers, Floats with a zero fractional part (e.g. 1.0), and base-10 numeric Strings (after trimming) that resolve to whole numbers; rejects everything else.
108 109 110 |
# File 'lib/pikuri/tool/parameters.rb', line 108 def required_integer(name, description) add(name, 'integer', description, required: true) end |
#required_number(name, description) ⇒ self
Add a required number property (JSON-Schema number: Integer or finite Float). Numeric Strings (after trimming) are parsed; NaN and Infinity are rejected.
129 130 131 |
# File 'lib/pikuri/tool/parameters.rb', line 129 def required_number(name, description) add(name, 'number', description, required: true) end |
#required_string(name, description) ⇒ self
Add a required string property.
58 59 60 |
# File 'lib/pikuri/tool/parameters.rb', line 58 def required_string(name, description) add(name, 'string', description, required: true) end |
#required_string_array(name, description) ⇒ self
Add a required array-of-string property — JSON-Schema {type: ‘array’, items: {type: ‘string’}}. The LLM sends a native JSON array in the tool-call arguments (the shape its training data overwhelmingly uses for list-valued parameters), so there is no in-band encoding for it to get wrong. The value must arrive as an Array — no JSON-encoded-array-in-a-string fallback. Element coercion mirrors the scalar fields’ one documented leniency, in reverse: Integers and finite Floats are converted to their to_s form (a model emitting [“Fix issue 12”, 42] meant a string list — the conversion is unambiguous), while booleans, nil, and nested structures are rejected — those signal a genuinely wrong call shape, not a representational quirk. An empty array is type-valid; rejecting it (if the tool needs at least one element) is the tool’s job, with a tool-specific error message.
91 92 93 94 95 96 97 98 99 |
# File 'lib/pikuri/tool/parameters.rb', line 91 def required_string_array(name, description) @properties[name] = { type: 'array', items: { type: 'string' }, description: description } @required << name.to_s self end |
#to_h ⇒ Hash
Schema in OpenAI JSON-Schema shape.
201 202 203 |
# File 'lib/pikuri/tool/parameters.rb', line 201 def to_h { type: 'object', properties: @properties, required: @required } end |
#validate(args) ⇒ Hash{Symbol=>Object}
Validate a tool-call argument hash against the declared schema. Returns a symbol-keyed hash safe to splat as kwargs into a tool’s execute Proc; raises ValidationError with an LLM-actionable message listing every missing/unknown/mistyped field and reprinting the schema.
Strict: unknown keys are rejected (with DidYouMean suggestions), wrong types are rejected. All issues are collected and reported together so the LLM can fix them in one round trip.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/pikuri/tool/parameters.rb', line 219 def validate(args) raise ValidationError, "Arguments must be an object, got #{args.class}." unless args.is_a?(Hash) symbolized = args.transform_keys(&:to_sym) errors = [] result = {} (symbolized.keys - @properties.keys).each do |unknown| errors << unknown_key_error(unknown) end @properties.each do |name, schema| if symbolized.key?(name) begin coerced = coerce(symbolized[name], schema[:type]) raise CoercionError, (schema[:enum], coerced) if schema[:enum] && !schema[:enum].include?(coerced) result[name] = coerced rescue CoercionError => e errors << "Parameter `#{name}` #{e.}." end elsif @required.include?(name.to_s) errors << (name, schema) end end return result if errors.empty? raise ValidationError, (errors) end |