Module: RubyLLM::Providers::OpenAIResponses::Tools

Included in:
RubyLLM::Providers::OpenAIResponses
Defined in:
lib/ruby_llm/providers/openai_responses/tools.rb

Overview

Tools/function calling methods for the OpenAI Responses API. Handles both custom function tools and built-in tools.

Constant Summary collapse

EMPTY_PARAMETERS_SCHEMA =
{
  'type' => 'object',
  'properties' => {},
  'required' => [],
  'additionalProperties' => false
}.freeze
BUILT_IN_TOOLS =

Built-in tool type constants

{
  web_search: { type: 'web_search_preview' },
  file_search: ->(vector_store_ids) { { type: 'file_search', vector_store_ids: vector_store_ids } },
  code_interpreter: { type: 'code_interpreter', container: { type: 'auto' } },
  image_generation: { type: 'image_generation' },
  computer_use: ->(opts) { { type: 'computer_use_preview', **opts } }
}.freeze

Class Method Summary collapse

Class Method Details

.build_schema_from_parameters(parameters) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 81

def build_schema_from_parameters(parameters)
  properties = {}
  required = []

  parameters.each do |name, param|
    properties[name.to_s] = {
      type: param.type || 'string',
      description: param.description
    }.compact

    required << name.to_s if param.required
  end

  {
    'type' => 'object',
    'properties' => properties,
    'required' => required,
    'additionalProperties' => false
  }
end

.code_interpreter_tool(container_type: 'auto') ⇒ Object



166
167
168
169
170
171
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 166

def code_interpreter_tool(container_type: 'auto')
  {
    type: 'code_interpreter',
    container: { type: container_type }
  }
end

.file_search_tool(vector_store_ids:, max_num_results: nil, ranking_options: nil) ⇒ Object



156
157
158
159
160
161
162
163
164
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 156

def file_search_tool(vector_store_ids:, max_num_results: nil, ranking_options: nil)
  tool = {
    type: 'file_search',
    vector_store_ids: Array(vector_store_ids)
  }
  tool[:max_num_results] = max_num_results if max_num_results
  tool[:ranking_options] = ranking_options if ranking_options
  tool
end

.format_tool_calls(tool_calls) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 102

def format_tool_calls(tool_calls)
  return nil unless tool_calls&.any?

  tool_calls.map do |_, tc|
    {
      type: 'function_call',
      call_id: tc.id,
      name: tc.name,
      arguments: tc.arguments.is_a?(String) ? tc.arguments : JSON.generate(tc.arguments)
    }
  end
end

.image_generation_tool(partial_images: nil) ⇒ Object



173
174
175
176
177
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 173

def image_generation_tool(partial_images: nil)
  tool = { type: 'image_generation' }
  tool[:partial_images] = partial_images if partial_images
  tool
end

.mcp_tool(server_label:, server_url:, require_approval: 'never', allowed_tools: nil, headers: nil) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 179

def mcp_tool(server_label:, server_url:, require_approval: 'never', allowed_tools: nil, headers: nil)
  tool = {
    type: 'mcp',
    server_label: server_label,
    server_url: server_url,
    require_approval: require_approval
  }
  tool[:allowed_tools] = allowed_tools if allowed_tools
  tool[:headers] = headers if headers
  tool
end

.parameters_schema_for(tool) ⇒ Object



59
60
61
62
63
64
65
66
67
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 59

def parameters_schema_for(tool)
  if tool.respond_to?(:params_schema) && tool.params_schema
    tool.params_schema
  elsif tool.respond_to?(:parameters)
    schema_from_parameters(tool.parameters)
  else
    EMPTY_PARAMETERS_SCHEMA
  end
end

.parse_tool_call_arguments(tool_call) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 135

def parse_tool_call_arguments(tool_call)
  arguments = tool_call['arguments']

  if arguments.nil? || arguments.empty?
    {}
  elsif arguments.is_a?(Hash)
    arguments
  else
    JSON.parse(arguments)
  end
rescue JSON::ParserError
  { raw: arguments }
end

.parse_tool_calls(tool_calls, parse_arguments: true) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 115

def parse_tool_calls(tool_calls, parse_arguments: true)
  return nil unless tool_calls&.any?

  tool_calls.to_h do |tc|
    call_id = tc['call_id'] || tc['id']
    [
      call_id,
      ToolCall.new(
        id: call_id,
        name: tc['name'],
        arguments: if parse_arguments
                     parse_tool_call_arguments(tc)
                   else
                     tc['arguments']
                   end
      )
    ]
  end
end

.schema_from_parameters(parameters) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 69

def schema_from_parameters(parameters)
  return EMPTY_PARAMETERS_SCHEMA if parameters.nil? || parameters.empty?

  if defined?(RubyLLM::Tool::SchemaDefinition)
    schema_definition = RubyLLM::Tool::SchemaDefinition.from_parameters(parameters)
    schema_definition&.json_schema || EMPTY_PARAMETERS_SCHEMA
  else
    # Fallback for older RubyLLM versions
    build_schema_from_parameters(parameters)
  end
end

.tool_for(tool) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 27

def tool_for(tool)
  # Check if it's a built-in tool specification
  return tool if tool.is_a?(Hash) && tool[:type]

  # Handle symbol references to built-in tools
  if tool.is_a?(Symbol) && BUILT_IN_TOOLS.key?(tool)
    built_in = BUILT_IN_TOOLS[tool]
    return built_in.is_a?(Proc) ? built_in.call([]) : built_in
  end

  # Standard function tool
  parameters_schema = parameters_schema_for(tool)

  definition = {
    type: 'function',
    name: tool.name,
    description: tool.description,
    parameters: parameters_schema
  }

  # Add strict mode if schema supports it
  definition[:strict] = true if parameters_schema['additionalProperties'] == false

  return definition if tool.respond_to?(:provider_params) && tool.provider_params.empty?

  if tool.respond_to?(:provider_params) && tool.provider_params.any?
    RubyLLM::Utils.deep_merge(definition, tool.provider_params)
  else
    definition
  end
end

.web_search_tool(search_context_size: nil) ⇒ Object

Helper to create built-in tool configurations



150
151
152
153
154
# File 'lib/ruby_llm/providers/openai_responses/tools.rb', line 150

def web_search_tool(search_context_size: nil)
  tool = { type: 'web_search_preview' }
  tool[:search_context_size] = search_context_size if search_context_size
  tool
end