Module: RubyLLM::Protocols::ChatCompletions::Tools

Defined in:
lib/ruby_llm/protocols/chat_completions/tools.rb

Overview

Tools methods of the OpenAI API integration

Constant Summary collapse

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

Class Method Summary collapse

Class Method Details

.build_tool_choice(tool_choice) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 103

def build_tool_choice(tool_choice)
  case tool_choice
  when :auto, :none, :required
    tool_choice
  else
    {
      type: 'function',
      function: {
        name: tool_choice
      }
    }
  end
end

.extract_tool_call_thought_signature(tool_call) ⇒ Object



117
118
119
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 117

def extract_tool_call_thought_signature(tool_call)
  tool_call.dig('extra_content', 'google', 'thought_signature')
end

.format_tool_calls(tool_calls) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 47

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

  tool_calls.map do |_, tc|
    call = {
      id: tc.id,
      type: 'function',
      function: {
        name: tc.name,
        arguments: JSON.generate(tc.arguments)
      }
    }
    if tc.thought_signature
      call[:extra_content] = {
        google: { thought_signature: tc.thought_signature }
      }
    end
    call
  end
end

.parameters_schema_for(tool) ⇒ Object



20
21
22
23
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 20

def parameters_schema_for(tool)
  tool.parameters_schema ||
    schema_from_parameters(tool.declared_parameters)
end

.parse_tool_call_arguments(tool_call, response: nil, finish_reason: nil) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 68

def parse_tool_call_arguments(tool_call, response: nil, finish_reason: nil)
  arguments = tool_call.dig('function', 'arguments')

  if arguments.nil? || arguments.empty?
    {}
  else
    JSON.parse(arguments)
  end
rescue JSON::ParserError => e
  raise ToolCallParseError.new(response: response, finish_reason: finish_reason), cause: e
end

.parse_tool_calls(tool_calls, parse_arguments: true, response: nil, finish_reason: nil, stream_keys: false) ⇒ Object

Streaming deltas key by index so id-less argument fragments find their call even when a delta batches or interleaves several calls; complete responses key by id, which tool results join on.



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

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

  tool_calls.to_h do |tc|
    [
      stream_keys ? tc['index'] || tc['id'] : tc['id'],
      ToolCall.new(
        id: tc['id'],
        name: tc.dig('function', 'name'),
        arguments: if parse_arguments
                     parse_tool_call_arguments(tc, response: response, finish_reason: finish_reason)
                   else
                     tc.dig('function', 'arguments')
                   end,
        thought_signature: extract_tool_call_thought_signature(tc)
      )
    ]
  end
end

.schema_from_parameters(parameters) ⇒ Object



25
26
27
28
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 25

def schema_from_parameters(parameters)
  schema_definition = RubyLLM::Tool::SchemaDefinition.from_parameters(parameters)
  schema_definition&.json_schema || EMPTY_PARAMETERS_SCHEMA
end

.tool_for(tool) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/ruby_llm/protocols/chat_completions/tools.rb', line 30

def tool_for(tool)
  parameters_schema = parameters_schema_for(tool)

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

  return definition if tool.provider_options.empty?

  RubyLLM::Support::Utils.deep_merge(definition, tool.provider_options)
end