Class: Raix::FunctionToolAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/raix/function_tool_adapter.rb

Overview

Adapter to convert Raix function declarations to RubyLLM::Tool instances

Class Method Summary collapse

Class Method Details

.convert_tools_for_ruby_llm(raix_instance) ⇒ Object



50
51
52
53
54
55
56
57
# File 'lib/raix/function_tool_adapter.rb', line 50

def self.convert_tools_for_ruby_llm(raix_instance)
  return [] unless raix_instance.class.respond_to?(:functions)
  return [] if raix_instance.class.functions.blank?

  raix_instance.class.functions.map do |function_def|
    create_tool_from_function(function_def, raix_instance)
  end
end

.create_tool_from_function(function_def, instance) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/raix/function_tool_adapter.rb', line 6

def self.create_tool_from_function(function_def, instance)
  tool_class = Class.new(RubyLLM::Tool) do
    description function_def[:description] if function_def[:description]

    # Forward the full JSON-schema parameter dict to RubyLLM rather than
    # rebuilding it field-by-field via `param(...)`. The per-field path
    # only carried `type` and `description`, which silently dropped richer
    # schema like `additionalProperties`, `items`, `enum`, or nested
    # `properties` — leaving providers (notably Gemini's structured output)
    # to invent degenerate shapes for `type: object` arguments.
    if function_def[:parameters].is_a?(Hash) && function_def[:parameters][:properties].present?
      # RubyLLM's `params(schema)` path forwards the schema verbatim and, unlike the
      # per-field `param(...)` path, does not inject the OpenAI strict-mode guards. Default
      # them on so existing tools keep strict behavior, while letting a function declaration
      # override either by setting it explicitly.
      params({ additionalProperties: false, strict: true }.merge(function_def[:parameters]))
    end

    # Store reference to the instance and function name
    define_method(:raix_instance) { instance }
    define_method(:raix_function_name) { function_def[:name] }

    # Override execute to call the Raix function
    define_method(:execute) do |**args|
      raix_instance.public_send(raix_function_name, args.with_indifferent_access, nil)
    end
  end

  # Set a meaningful name for the tool class
  tool_class.define_singleton_method(:name) do
    "Raix::GeneratedTool::#{function_def[:name].to_s.camelize}"
  end

  tool_instance = tool_class.new

  # Override the name method to return the original function name
  # This ensures RubyLLM can match the tool call from the AI
  tool_instance.define_singleton_method(:name) do
    function_def[:name].to_s
  end

  tool_instance
end