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
Constant Summary collapse
- EMPTY =
new.freeze
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-+string+ property (+'array', items: {type: 'string'}+).
-
#to_openai ⇒ Hash
Schema in OpenAI JSON-Schema shape.
-
#validate(args) ⇒ Hash{Symbol=>Object}
Validate a tool-call argument hash against the 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.
50 51 52 53 54 |
# File 'lib/pikuri/tool/parameters.rb', line 50 def freeze @properties.freeze @required.freeze super end |
#optional_boolean(name, description) ⇒ self
Add an optional boolean property. See #required_boolean for
accepted shapes.
156 157 158 |
# File 'lib/pikuri/tool/parameters.rb', line 156 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.
188 189 190 |
# File 'lib/pikuri/tool/parameters.rb', line 188 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.
112 113 114 |
# File 'lib/pikuri/tool/parameters.rb', line 112 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.
133 134 135 |
# File 'lib/pikuri/tool/parameters.rb', line 133 def optional_number(name, description) add(name, 'number', description, required: false) end |
#optional_string(name, description) ⇒ self
Add an optional string property.
70 71 72 |
# File 'lib/pikuri/tool/parameters.rb', line 70 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.
146 147 148 |
# File 'lib/pikuri/tool/parameters.rb', line 146 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.
175 176 177 |
# File 'lib/pikuri/tool/parameters.rb', line 175 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.
102 103 104 |
# File 'lib/pikuri/tool/parameters.rb', line 102 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.
123 124 125 |
# File 'lib/pikuri/tool/parameters.rb', line 123 def required_number(name, description) add(name, 'number', description, required: true) end |
#required_string(name, description) ⇒ self
Add a required string property.
61 62 63 |
# File 'lib/pikuri/tool/parameters.rb', line 61 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 (+'array', items:
{type: 'string'}+). The value must arrive as a native JSON Array — no
JSON-in-a-string fallback. Element coercion mirrors the scalars in
reverse: Integers and finite Floats become their to_s (a model
emitting ["Fix issue 12", 42] meant a string list), while booleans,
nil, and nested structures are rejected. An empty array is type-valid
— rejecting it is the tool's job.
85 86 87 88 89 90 91 92 93 |
# File 'lib/pikuri/tool/parameters.rb', line 85 def required_string_array(name, description) @properties[name] = { type: 'array', items: { type: 'string' }, description: description } @required << name.to_s self end |
#to_openai ⇒ Hash
Schema in OpenAI JSON-Schema shape.
195 196 197 |
# File 'lib/pikuri/tool/parameters.rb', line 195 def to_openai { type: 'object', properties: @properties, required: @required } end |
#validate(args) ⇒ Hash{Symbol=>Object}
Validate a tool-call argument hash against the schema. Returns a
symbol-keyed hash safe to splat as kwargs into a tool's execute;
raises ValidationError with an LLM-actionable message listing every
missing/unknown/mistyped field and reprinting the schema. Strict:
unknown keys rejected (with DidYouMean suggestions), wrong types
rejected, all issues collected so the LLM fixes them in one round trip.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/pikuri/tool/parameters.rb', line 211 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 |