Class: TurnKit::Tool
- Inherits:
-
Object
- Object
- TurnKit::Tool
- Defined in:
- lib/turnkit/tool.rb
Direct Known Subclasses
Constant Summary collapse
- TYPES =
%i[string integer number boolean array object enum].freeze
- NAME_PATTERN =
/\A[a-zA-Z_][a-zA-Z0-9_]*\z/
Class Method Summary collapse
- .call(arguments = {}, context:) ⇒ Object
- .completion_message(result) ⇒ Object
- .description(value = nil) ⇒ Object
- .ends_turn? ⇒ Boolean
- .input_schema ⇒ Object
- .invoke(instance, arguments = {}, context:) ⇒ Object
- .parameter(name, type = :string, required: false, description: "", default: nil, enum: nil, items: nil, properties: nil) ⇒ Object
- .parameters ⇒ Object
- .terminal!(message = nil, &block) ⇒ Object
- .tool_name(value = nil) ⇒ Object
- .usage_hint(value = nil) ⇒ Object
- .validate_arguments(arguments) ⇒ Object
- .validate_definition! ⇒ Object
Instance Method Summary collapse
- #completion_message(result) ⇒ Object
- #description ⇒ Object
- #ends_turn? ⇒ Boolean
- #input_schema ⇒ Object
- #parameters ⇒ Object
- #tool_name ⇒ Object
- #usage_hint ⇒ Object
- #validate_definition! ⇒ Object
Class Method Details
.call(arguments = {}, context:) ⇒ Object
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/turnkit/tool.rb', line 115 def call(arguments = {}, context:) instance = begin new rescue ArgumentError => error raise if error. !~ /wrong number of arguments|missing keyword/ raise ToolError, "#{tool_name} requires constructor arguments; register an instance instead" end invoke(instance, arguments, context: context) end |
.completion_message(result) ⇒ Object
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/turnkit/tool.rb', line 56 def (result) case @completion_message when nil nil when Proc @completion_message.call(result) else @completion_message.to_s end end |
.description(value = nil) ⇒ Object
14 15 16 17 |
# File 'lib/turnkit/tool.rb', line 14 def description(value = nil) @description = value.to_s if value @description.to_s end |
.ends_turn? ⇒ Boolean
52 53 54 |
# File 'lib/turnkit/tool.rb', line 52 def ends_turn? @ends_turn || false end |
.input_schema ⇒ Object
80 81 82 83 84 85 86 87 88 |
# File 'lib/turnkit/tool.rb', line 80 def input_schema properties = parameters.to_h { |param| [ param.fetch(:name), schema_for(param) ] } required = parameters.select { |param| param.fetch(:required) }.map { |param| param.fetch(:name) } { "type" => "object", "properties" => properties, "required" => required } end |
.invoke(instance, arguments = {}, context:) ⇒ Object
126 127 128 129 130 131 132 133 |
# File 'lib/turnkit/tool.rb', line 126 def invoke(instance, arguments = {}, context:) keyword_arguments = symbolize(validate_arguments(arguments)) if accepts_turnkit_context?(instance) instance.call(**keyword_arguments, turnkit_context: context) else instance.call(**keyword_arguments, context: context) end end |
.parameter(name, type = :string, required: false, description: "", default: nil, enum: nil, items: nil, properties: nil) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/turnkit/tool.rb', line 24 def parameter(name, type = :string, required: false, description: "", default: nil, enum: nil, items: nil, properties: nil) name = name.to_s raise ArgumentError, "unknown parameter type: #{type}" unless TYPES.include?(type) raise ArgumentError, "invalid parameter name: #{name}" unless NAME_PATTERN.match?(name) raise ArgumentError, "duplicate parameter: #{name}" if parameters.any? { |param| param.fetch(:name) == name } raise ArgumentError, "enum values are required for enum parameter: #{name}" if type == :enum && Array(enum).empty? parameters << { name: name, type: type, required: required ? true : false, description: description.to_s, default: default, enum: enum, items: items, properties: properties }.compact end |
.parameters ⇒ Object
43 44 45 |
# File 'lib/turnkit/tool.rb', line 43 def parameters @parameters ||= superclass.respond_to?(:parameters) ? superclass.parameters.dup : [] end |
.terminal!(message = nil, &block) ⇒ Object
47 48 49 50 |
# File 'lib/turnkit/tool.rb', line 47 def terminal!( = nil, &block) @ends_turn = true @completion_message = block || end |
.tool_name(value = nil) ⇒ Object
9 10 11 12 |
# File 'lib/turnkit/tool.rb', line 9 def tool_name(value = nil) @tool_name = value.to_s if value @tool_name ||= name.to_s.split("::").last.gsub(/([a-z\d])([A-Z])/, "\\1_\\2").downcase end |
.usage_hint(value = nil) ⇒ Object
19 20 21 22 |
# File 'lib/turnkit/tool.rb', line 19 def usage_hint(value = nil) @usage_hint = value.to_s if value @usage_hint.to_s end |
.validate_arguments(arguments) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/turnkit/tool.rb', line 90 def validate_arguments(arguments) attrs = arguments.respond_to?(:to_h) ? arguments.to_h.transform_keys(&:to_s) : {} allowed = parameters.map { |param| param.fetch(:name) } unknown = attrs.keys - allowed raise ToolValidationError, "unknown argument#{unknown.length == 1 ? "" : "s"}: #{unknown.join(", ")}" if unknown.any? normalized = {} parameters.each do |param| name = param.fetch(:name) if attrs.key?(name) value = attrs[name] elsif param.key?(:default) value = param[:default] elsif param.fetch(:required) raise ToolValidationError, "missing required argument: #{name}" else next end validate_value!(value, param) normalized[name] = value end normalized end |
.validate_definition! ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/turnkit/tool.rb', line 67 def validate_definition! raise ArgumentError, "tool name is required" if tool_name.empty? raise ArgumentError, "invalid tool name: #{tool_name}" unless NAME_PATTERN.match?(tool_name) parameters.each do |param| type = param.fetch(:type) raise ArgumentError, "unknown parameter type: #{type}" unless TYPES.include?(type) raise ArgumentError, "enum values are required for enum parameter: #{param.fetch(:name)}" if type == :enum && Array(param[:enum]).empty? validate_value!(param[:default], param) if param.key?(:default) end true end |
Instance Method Details
#completion_message(result) ⇒ Object
210 |
# File 'lib/turnkit/tool.rb', line 210 def (result) = self.class.(result) |
#description ⇒ Object
204 |
# File 'lib/turnkit/tool.rb', line 204 def description = self.class.description |
#ends_turn? ⇒ Boolean
209 |
# File 'lib/turnkit/tool.rb', line 209 def ends_turn? = self.class.ends_turn? |
#input_schema ⇒ Object
207 |
# File 'lib/turnkit/tool.rb', line 207 def input_schema = self.class.input_schema |
#parameters ⇒ Object
206 |
# File 'lib/turnkit/tool.rb', line 206 def parameters = self.class.parameters |
#tool_name ⇒ Object
203 |
# File 'lib/turnkit/tool.rb', line 203 def tool_name = self.class.tool_name |
#usage_hint ⇒ Object
205 |
# File 'lib/turnkit/tool.rb', line 205 def usage_hint = self.class.usage_hint |
#validate_definition! ⇒ Object
208 |
# File 'lib/turnkit/tool.rb', line 208 def validate_definition! = self.class.validate_definition! |