Module: RubyLLM::Providers::Gemini::Chat
Overview
Chat methods for the Gemini API implementation
Defined Under Namespace
Classes: GeminiSchema, MessageFormatter
Class Method Summary
collapse
Class Method Details
.build_json_schema(schema) ⇒ Object
112
113
114
115
116
117
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 112
def build_json_schema(schema)
normalized = RubyLLM::Utils.deep_dup(schema)
normalized.delete(:strict)
normalized.delete('strict')
RubyLLM::Utils.deep_stringify_keys(normalized)
end
|
.calculate_output_tokens(data) ⇒ Object
101
102
103
104
105
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 101
def calculate_output_tokens(data)
candidates = data.dig('usageMetadata', 'candidatesTokenCount') || 0
thoughts = data.dig('usageMetadata', 'thoughtsTokenCount') || 0
candidates + thoughts
end
|
.completion_url ⇒ Object
13
14
15
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 13
def completion_url
"models/#{@model}:generateContent"
end
|
.convert_schema_to_gemini(schema) ⇒ Object
78
79
80
81
82
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 78
def convert_schema_to_gemini(schema)
return nil unless schema
GeminiSchema.new(schema).to_h
end
|
151
152
153
154
155
156
157
158
159
160
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 151
def (text)
return nil unless text
match = text.match(/(\d+\.\d+|\d+)/)
return nil unless match
Gem::Version.new(match[1])
rescue ArgumentError
nil
end
|
34
35
36
37
38
39
40
41
42
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 34
def format_messages(messages)
formatter = MessageFormatter.new(
messages,
format_role: method(:format_role),
format_parts: method(:format_parts),
format_tool_result: method(:format_tool_result)
)
formatter.format
end
|
53
54
55
56
57
58
59
60
61
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 53
def format_parts(msg)
if msg.tool_call?
format_tool_call(msg)
elsif msg.tool_result?
format_tool_result(msg)
else
Media.format_content(msg.content)
end
end
|
44
45
46
47
48
49
50
51
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 44
def format_role(role)
case role
when :assistant then 'model'
when :system then 'user'
when :tool then 'function'
else role.to_s
end
end
|
.function_call?(candidate) ⇒ Boolean
96
97
98
99
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 96
def function_call?(candidate)
parts = candidate.dig('content', 'parts')
parts&.any? { |p| p['functionCall'] }
end
|
.gemini_version(model) ⇒ Object
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 119
def gemini_version(model)
return nil unless model
candidates = [
safe_string(model.id),
safe_string(model.respond_to?(:family) ? model.family : nil),
safe_string(model_metadata_value(model, :version)),
safe_string(model_metadata_value(model, 'version')),
safe_string(model_metadata_value(model, :description))
].compact
candidates.each do |candidate|
version = (candidate)
return version if version
end
nil
end
|
138
139
140
141
142
143
144
145
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 138
def model_metadata_value(model, key)
return unless model.respond_to?(:metadata)
metadata = model.metadata
return unless metadata.is_a?(Hash)
metadata[key] || metadata[key.to_s]
end
|
.parse_completion_response(response) ⇒ Object
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 63
def parse_completion_response(response)
data = response.body
tool_calls = (data)
Message.new(
role: :assistant,
content: parse_content(data),
tool_calls: tool_calls,
input_tokens: data.dig('usageMetadata', 'promptTokenCount'),
output_tokens: calculate_output_tokens(data),
model_id: data['modelVersion'] || response.env.url.path.split('/')[3].split(':')[0],
raw: response
)
end
|
.parse_content(data) ⇒ Object
84
85
86
87
88
89
90
91
92
93
94
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 84
def parse_content(data)
candidate = data.dig('candidates', 0)
return '' unless candidate
return '' if function_call?(candidate)
parts = candidate.dig('content', 'parts')
return '' unless parts&.any?
build_response_content(parts)
end
|
.render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists,Lint/UnusedMethodArgument
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 17
def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil) @model = model.id
payload = {
contents: format_messages(messages),
generationConfig: {}
}
payload[:generationConfig][:temperature] = temperature unless temperature.nil?
payload[:generationConfig].merge!(structured_output_config(schema, model)) if schema
payload[:tools] = format_tools(tools) if tools.any?
payload
end
|
.response_json_schema_supported?(model) ⇒ Boolean
107
108
109
110
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 107
def response_json_schema_supported?(model)
version = gemini_version(model)
version && version >= Gem::Version.new('2.5')
end
|
.safe_string(value) ⇒ Object
147
148
149
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 147
def safe_string(value)
value&.to_s
end
|
.structured_output_config(schema, model) ⇒ Object
162
163
164
165
166
167
168
169
170
171
172
|
# File 'lib/ruby_llm/providers/gemini/chat.rb', line 162
def structured_output_config(schema, model)
{
responseMimeType: 'application/json'
}.tap do |config|
if response_json_schema_supported?(model)
config[:responseJsonSchema] = build_json_schema(schema)
else
config[:responseSchema] = convert_schema_to_gemini(schema)
end
end
end
|