Class: Mistri::Tool

Inherits:
Object
  • Object
show all
Defined in:
lib/mistri/tool.rb

Overview

A tool the agent can call: a name, a description, a JSON Schema for its arguments, and a handler. The handler receives the parsed arguments hash (string keys, exactly as the model sent them) and returns a String, a Hash (serialized as JSON), or content blocks, so a tool can hand back images as naturally as text.

Constant Summary collapse

EMPTY_SCHEMA =

A no-argument tool still needs a valid object schema; providers reject a bare empty hash.

{ type: "object", properties: {} }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, description:, input_schema: EMPTY_SCHEMA, eager_input_streaming: false, needs_approval: false, timeout: nil, &handler) ⇒ Tool

Returns a new instance of Tool.

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/mistri/tool.rb', line 30

def initialize(name:, description:, input_schema: EMPTY_SCHEMA, eager_input_streaming: false,
               needs_approval: false, timeout: nil, &handler)
  raise ArgumentError, "tool #{name.inspect} needs a handler block" unless handler

  @name = name.to_s
  @description = description
  @input_schema = input_schema
  @eager_input_streaming = eager_input_streaming
  @needs_approval = needs_approval
  @timeout = timeout
  @handler = handler
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



16
17
18
# File 'lib/mistri/tool.rb', line 16

def description
  @description
end

#input_schemaObject (readonly)

Returns the value of attribute input_schema.



16
17
18
# File 'lib/mistri/tool.rb', line 16

def input_schema
  @input_schema
end

#nameObject (readonly)

Returns the value of attribute name.



16
17
18
# File 'lib/mistri/tool.rb', line 16

def name
  @name
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



16
17
18
# File 'lib/mistri/tool.rb', line 16

def timeout
  @timeout
end

Class Method Details

.define(name, description, input_schema: nil, schema: nil, &handler) ⇒ Object

Define a tool. Give the argument shape as a raw JSON Schema hash via input_schema:, or build it in Ruby with a schema: block.

Tool.define("get_weather", "Weather for a city",
          schema: -> { string :city, "City name", required: true }) do |args|
Weather.for(args["city"])
end


25
26
27
28
# File 'lib/mistri/tool.rb', line 25

def self.define(name, description, input_schema: nil, schema: nil, **, &handler)
  input_schema ||= schema ? Schema.build(&schema) : EMPTY_SCHEMA
  new(name: name, description: description, input_schema: input_schema, **, &handler)
end

Instance Method Details

#call(arguments, context = ToolContext.new) ⇒ Object

A handler may return a ToolResult to speak on two channels; its ui payload is canonicalized through JSON here so the live event and a reloaded session read the identical shape.

Handlers receive (arguments, context). A proc that declares one parameter ignores the context invisibly; a lambda opts in by arity.



49
50
51
52
53
54
55
# File 'lib/mistri/tool.rb', line 49

def call(arguments, context = ToolContext.new)
  result = invoke(arguments || {}, context)
  return serialize_result(result) unless result.is_a?(ToolResult)

  result.with(content: serialize_result(result.content),
              ui: result.ui && JSON.parse(JSON.generate(result.ui)))
end

#needs_approval?(arguments) ⇒ Boolean

Whether this call should pause for a human. true/false, or a callable given the parsed arguments so a tool can gate only the risky calls (needs_approval: ->(args) { args.to_i > 100 }).

Returns:

  • (Boolean)


60
61
62
# File 'lib/mistri/tool.rb', line 60

def needs_approval?(arguments)
  @needs_approval.respond_to?(:call) ? @needs_approval.call(arguments) : @needs_approval
end

#specObject

The provider-facing definition; every serializer accepts this shape.



65
66
67
68
69
# File 'lib/mistri/tool.rb', line 65

def spec
  definition = { name: @name, description: @description, input_schema: @input_schema }
  definition[:eager_input_streaming] = true if @eager_input_streaming
  definition
end