Class: Ask::Tool
- Inherits:
-
Object
- Object
- Ask::Tool
- Defined in:
- lib/ask/tools/tool.rb
Defined Under Namespace
Class Method Summary collapse
-
.approval_required(value = :_no_arg_given) ⇒ Boolean
Declare that calling this tool requires human approval.
-
.auto_approvable(value = :_no_arg_given) ⇒ Boolean
Declare that this tool may be auto-approved when the session's approval policy has auto-approval enabled for it.
- .build_schema_from_params ⇒ Object
- .deep_stringify_keys(obj) ⇒ Object
- .desc ⇒ Object
- .description(text = nil) ⇒ Object
- .inherited(subclass) ⇒ Object
-
.name(custom = :_no_arg_given) ⇒ Object
Declare a custom tool name.
- .param(name, type:, desc: nil, description: nil, required: true) ⇒ Object
- .parameters ⇒ Object
- .params(schema = nil, &block) ⇒ Object
- .params_schema ⇒ Object
- .provider_params ⇒ Object
- .resolve_params_schema(definition) ⇒ Object
Instance Method Summary collapse
-
#approval_required? ⇒ Boolean
Whether calling this tool requires human approval.
-
#auto_approvable? ⇒ Boolean
Whether this tool may be auto-approved under a session-level auto-approval rule.
- #call(args = {}, abort_controller = nil) ⇒ Object
- #description ⇒ Object
- #execute(**args) ⇒ Object
- #inspect ⇒ Object
- #name ⇒ Object
- #parameters ⇒ Object
- #params_schema ⇒ Object
- #provider_params ⇒ Object
- #tool_definition ⇒ Object
Class Method Details
.approval_required(value = :_no_arg_given) ⇒ Boolean
Declare that calling this tool requires human approval.
The tool is still registered and described to the LLM normally, but when an agent session runs with an approval queue enabled, calls to it are queued instead of executed — the agent gets a pending result, and the tool only runs after a human approves it.
Called with no argument returns the current value (default false).
63 64 65 66 67 68 69 |
# File 'lib/ask/tools/tool.rb', line 63 def approval_required(value = :_no_arg_given) if value == :_no_arg_given @approval_required == true else @approval_required = !!value end end |
.auto_approvable(value = :_no_arg_given) ⇒ Boolean
Declare that this tool may be auto-approved when the session's approval policy has auto-approval enabled for it. This is a per-action verdict only — the session-level user rule is still the binding gate. A tool that requires approval but is NOT marked auto-approvable always queues for human review.
Called with no argument returns the current value (default false).
81 82 83 84 85 86 87 |
# File 'lib/ask/tools/tool.rb', line 81 def auto_approvable(value = :_no_arg_given) if value == :_no_arg_given @auto_approvable == true else @auto_approvable = !!value end end |
.build_schema_from_params ⇒ Object
122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ask/tools/tool.rb', line 122 def build_schema_from_params properties = parameters.to_h do |_name, param| schema = { type: param.type } schema[:description] = param.description if param.description schema[:items] = { type: "string" } if param.type == "array" [param.name.to_s, schema] end required = parameters.select { |_, p| p.required }.keys.map(&:to_s) { type: "object", properties: properties, required: required, additionalProperties: false } end |
.deep_stringify_keys(obj) ⇒ Object
145 146 147 148 149 150 151 |
# File 'lib/ask/tools/tool.rb', line 145 def deep_stringify_keys(obj) case obj when Hash then obj.each_with_object({}) { |(k, v), h| h[k.to_s] = deep_stringify_keys(v) } when Array then obj.map { |v| deep_stringify_keys(v) } else obj end end |
.desc ⇒ Object
32 33 34 35 |
# File 'lib/ask/tools/tool.rb', line 32 def description(text = nil) return @description unless text @description = text end |
.description(text = nil) ⇒ Object
28 29 30 31 |
# File 'lib/ask/tools/tool.rb', line 28 def description(text = nil) return @description unless text @description = text end |
.inherited(subclass) ⇒ Object
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/ask/tools/tool.rb', line 17 def inherited(subclass) super @parameters = {} if @parameters.nil? subclass.instance_variable_set(:@description, nil) subclass.instance_variable_set(:@parameters, {}) subclass.instance_variable_set(:@params_schema_definition, nil) subclass.instance_variable_set(:@tool_name, nil) subclass.instance_variable_set(:@approval_required, nil) subclass.instance_variable_set(:@auto_approvable, nil) end |
.name(custom = :_no_arg_given) ⇒ Object
Declare a custom tool name. Called with no argument returns the class name (via Module#name). Called with a string stores a custom name for the instance. Example: name "my_custom_tool"
38 39 40 41 42 43 44 |
# File 'lib/ask/tools/tool.rb', line 38 def name(custom = :_no_arg_given) if custom == :_no_arg_given super() # Module#name, returns the Ruby class path else @tool_name = custom end end |
.param(name, type:, desc: nil, description: nil, required: true) ⇒ Object
89 90 91 92 93 94 95 96 |
# File 'lib/ask/tools/tool.rb', line 89 def param(name, type:, desc: nil, description: nil, required: true) type = type.to_s.downcase.to_sym validate_param_type!(type, name) parameters[name] = Parameter.new( name: name, type: map_type(type), description: desc || description, required: required ) end |
.parameters ⇒ Object
102 103 104 |
# File 'lib/ask/tools/tool.rb', line 102 def parameters @parameters ||= {} end |
.params(schema = nil, &block) ⇒ Object
98 99 100 |
# File 'lib/ask/tools/tool.rb', line 98 def params(schema = nil, &block) @params_schema_definition = schema || block end |
.params_schema ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/ask/tools/tool.rb', line 110 def params_schema @params_schema ||= begin if @params_schema_definition deep_stringify_keys(resolve_params_schema(@params_schema_definition)) elsif @parameters && @parameters.any? build_schema_from_params else nil end end end |
.provider_params ⇒ Object
106 107 108 |
# File 'lib/ask/tools/tool.rb', line 106 def provider_params @provider_params ||= {} end |
.resolve_params_schema(definition) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ask/tools/tool.rb', line 133 def resolve_params_schema(definition) case definition when Proc schema_class = Ask::Schema.create(&definition) schema_class.new.to_json_schema.dig(:schema) when Hash then definition when ->(d) { d.respond_to?(:to_json_schema) } definition.to_json_schema.dig(:schema) else nil end end |
Instance Method Details
#approval_required? ⇒ Boolean
Returns whether calling this tool requires human approval.
198 199 200 |
# File 'lib/ask/tools/tool.rb', line 198 def approval_required? self.class.approval_required end |
#auto_approvable? ⇒ Boolean
Returns whether this tool may be auto-approved under a session-level auto-approval rule.
204 205 206 |
# File 'lib/ask/tools/tool.rb', line 204 def auto_approvable? self.class.auto_approvable end |
#call(args = {}, abort_controller = nil) ⇒ Object
208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/ask/tools/tool.rb', line 208 def call(args = {}, abort_controller = nil) normalized = normalize_args(args) validation = validate(normalized) return Ask::Result.failure(validation) if validation normalized[:_abort_controller] = abort_controller if abort_controller execute_kwargs = normalized.reject { |k, _| k == :_abort_controller || k == :abort_controller } execute(**execute_kwargs) rescue Halt => e Ask::Result.ok(data: e.content, metadata: { halted: true }) rescue StandardError => e Ask::Result.failure("#{self.class.name.split('::').last} raised #{e.class}: #{e.}") end |
#description ⇒ Object
189 190 191 |
# File 'lib/ask/tools/tool.rb', line 189 def description self.class.description end |
#execute(**args) ⇒ Object
221 222 223 |
# File 'lib/ask/tools/tool.rb', line 221 def execute(**args) raise NotImplementedError, "#{self.class} must implement #execute(**args)" end |
#inspect ⇒ Object
244 245 246 |
# File 'lib/ask/tools/tool.rb', line 244 def inspect "#<#{self.class.name} name=#{name.inspect}>" end |
#name ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/ask/tools/tool.rb', line 175 def name custom = self.class.instance_variable_get(:@tool_name) return custom if custom klass_name = self.class.name.to_s.split("::").last || "" normalized = klass_name.dup.force_encoding("UTF-8").unicode_normalize(:nfkd) normalized.encode("ASCII", replace: "") .gsub(/[^a-zA-Z0-9_-]/, "-") .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase .delete_suffix("_tool") end |
#parameters ⇒ Object
193 194 195 |
# File 'lib/ask/tools/tool.rb', line 193 def parameters self.class.parameters end |
#params_schema ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/ask/tools/tool.rb', line 225 def params_schema return @params_schema if defined?(@params_schema) @params_schema = begin if params_schema_definition deep_stringify_keys(resolve_params_schema(params_schema_definition)) elsif parameters.any? build_schema_from_params else nil end end end |
#provider_params ⇒ Object
171 172 173 |
# File 'lib/ask/tools/tool.rb', line 171 def provider_params self.class.provider_params end |
#tool_definition ⇒ Object
238 239 240 241 242 |
# File 'lib/ask/tools/tool.rb', line 238 def tool_definition defn = { name: name, description: description } defn[:input_schema] = params_schema if params_schema defn end |