Module: RubyLLM::Protocols::Gemini::Tools

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

Overview

Tools methods for the Gemini API implementation

Constant Summary collapse

MULTIMODAL_FUNCTION_RESPONSE_GENERATION =
Gem::Version.new('3')

Instance Method Summary collapse

Instance Method Details

#extract_tool_calls(data) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/ruby_llm/protocols/gemini/tools.rb', line 69

def extract_tool_calls(data) # rubocop:disable Metrics/PerceivedComplexity
  return nil unless data

  candidate = data.is_a?(Hash) ? data.dig('candidates', 0) : nil
  return nil unless candidate

  parts = candidate.dig('content', 'parts')
  return nil unless parts.is_a?(Array)

  tool_calls = parts.each_with_object({}) do |part, result|
    function_data = part['functionCall']
    next unless function_data

    id = SecureRandom.uuid
    thought_signature = part['thoughtSignature'] || part['thought_signature']

    result[id] = ToolCall.new(
      id:,
      name: function_data['name'],
      arguments: function_data['args'] || {},
      thought_signature: thought_signature
    )
  end

  tool_calls.empty? ? nil : tool_calls
end

#format_tool_call(msg) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity



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
# File 'lib/ruby_llm/protocols/gemini/tools.rb', line 21

def format_tool_call(msg) # rubocop:disable Metrics/PerceivedComplexity
  parts = []

  parts.concat(Media.format_content(msg.content, msg.attachments)) if msg.content && !msg.content.empty?

  fallback_signature = msg.thinking&.signature
  used_fallback = false

  msg.tool_calls.each_value do |tool_call|
    part = {
      functionCall: {
        name: tool_call.name,
        args: tool_call.arguments
      }
    }

    signature = tool_call.thought_signature
    if signature.nil? && fallback_signature && !used_fallback
      signature = fallback_signature
      used_fallback = true
    end
    part[:thoughtSignature] = signature if signature
    parts << part
  end

  parts
end

#format_tool_result(msg, function_name = nil) ⇒ Object



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

def format_tool_result(msg, function_name = nil)
  function_name ||= msg.tool_call_id
  content = msg.content
  content = nil if content && content.empty?
  content = '(no output)' if content.nil? && msg.attachments.empty?

  function_response = {
    name: function_name,
    response: {
      name: function_name,
      content: Media.format_content(content)
    }
  }

  media_parts, sibling_parts = partition_tool_result_attachments(msg.attachments)
  function_response[:parts] = media_parts if media_parts.any?

  [{ functionResponse: function_response }, *sibling_parts]
end

#format_tools(tools) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/ruby_llm/protocols/gemini/tools.rb', line 13

def format_tools(tools)
  return [] if tools.empty?

  [{
    functionDeclarations: tools.values.map { |tool| function_declaration_for(tool) }
  }]
end