Class: Pikuri::Tool::Parameters

Inherits:
Object
  • Object
show all
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.

Examples:

params = Tool::Parameters.build { |p| p.required_string :query, 'The query.' }
params.to_h
# => {type: 'object',
#     properties: {query: {type: 'string', description: 'The query.'}},
#     required: ['query']}
params.validate('query' => 'cats') # => {query: 'cats'}

Defined Under Namespace

Classes: ValidationError

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeParameters



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.

Yield Parameters:

Returns:

  • (Parameters)

    frozen builder, safe to share between calls



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

#freezeself

Freeze the builder along with its internal collections, so post-build mutation attempts raise FrozenError instead of silently succeeding.

Returns:

  • (self)


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.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


132
133
134
# File 'lib/pikuri/tool/parameters.rb', line 132

def optional_boolean(name, description)
  add(name, 'boolean', description, required: false)
end

#optional_integer(name, description) ⇒ self

Add an optional integer property. See #required_integer for accepted shapes.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


88
89
90
# File 'lib/pikuri/tool/parameters.rb', line 88

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.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


109
110
111
# File 'lib/pikuri/tool/parameters.rb', line 109

def optional_number(name, description)
  add(name, 'number', description, required: false)
end

#optional_string(name, description) ⇒ self

Add an optional string property.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


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.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


122
123
124
# File 'lib/pikuri/tool/parameters.rb', line 122

def required_boolean(name, description)
  add(name, 'boolean', description, 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.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


78
79
80
# File 'lib/pikuri/tool/parameters.rb', line 78

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.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


99
100
101
# File 'lib/pikuri/tool/parameters.rb', line 99

def required_number(name, description)
  add(name, 'number', description, required: true)
end

#required_string(name, description) ⇒ self

Add a required string property.

Parameters:

  • name (Symbol)

    property name

  • description (String)

    human-readable description shown to the LLM

Returns:

  • (self)


58
59
60
# File 'lib/pikuri/tool/parameters.rb', line 58

def required_string(name, description)
  add(name, 'string', description, required: true)
end

#to_hHash

Schema in OpenAI JSON-Schema shape.

Returns:

  • (Hash)

    {type: ‘object’, properties: {…}, required: […]}



139
140
141
# File 'lib/pikuri/tool/parameters.rb', line 139

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.

Parameters:

  • args (Hash)

    arguments as decoded from the tool-call JSON; keys may be Strings or Symbols

Returns:

  • (Hash{Symbol=>Object})

    validated, symbol-keyed arguments

Raises:

  • (ValidationError)

    if args is not a Hash, contains unknown keys, omits a required key, or has a value of the wrong type



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/pikuri/tool/parameters.rb', line 157

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
        result[name] = coerce(symbolized[name], schema[:type])
      rescue CoercionError => e
        errors << "Parameter `#{name}` #{e.message}."
      end
    elsif @required.include?(name.to_s)
      errors << "Missing required parameter `#{name}` (#{schema[:type]}): #{schema[:description]}"
    end
  end

  return result if errors.empty?

  raise ValidationError, build_error_message(errors)
end