ruby-gemini-api 0.1.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80e6cd429265c5341e1efcd3e701f9643cdb4785f18826234ec879178d4a236e
4
- data.tar.gz: c9a9c5201b616ce2d534393c1d99240563b9d36229dac4f564f71b4ed82ee42b
3
+ metadata.gz: 92b2d6f35fbff3fda1457ef3244ae730ee99e156909c310b62878540d47d4076
4
+ data.tar.gz: 710191ae9ac4f136ea586782be43b8aa18390d74a8ba1d844a9524fc120fdaa4
5
5
  SHA512:
6
- metadata.gz: b1ac2fe8cf4dacad20f21a7eab1872fa496a797b76d9af12cb401c5f563aeaed9cd58e4323334fcd6bbf67d74273902b5f9f076a5a44833474b4f3a4e8409148
7
- data.tar.gz: 17693cd9f1a87ad1fdf21c26153d1ac351ca9137c99e1c97cddd2524333db21190b0bbf5c08b995939c950338f0d5bf4607263a2ae0bc92c0343b28d439c2a6f
6
+ metadata.gz: 31a2f84ff0f7d9d5127fe3cfa2eba75c2395cc4105e5faa6d45b472ccc4dc8e6502f28635f6a7dfce0c071e445010af9a1781c833a18c6f491f55b9e2989957b
7
+ data.tar.gz: 499e659d3a3284f461deede5fbcaff16ebb3476ced2c3c5c44d4d5e333d3751e45483ed221a654b0b560651127496933342e0fed5bfea8307de40edab80faa21
data/CHANGELOG.md CHANGED
@@ -1,27 +1,50 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2025-04-05
4
- - Initial release
3
+ ## [1.0.0] - 2026-01-28
5
4
 
6
- ## [0.1.1] - 2025-05-04
7
- - Changed generate_contents to accept temperature parameter
5
+ ### Added
6
+ - Thinking feature support for Gemini 2.5 and Gemini 3 models
7
+ - `thinking_budget` parameter for Gemini 2.5 (1-32768 tokens, -1 for dynamic, 0 to disable)
8
+ - `thinking_level` parameter for Gemini 3 (:minimal, :low, :medium, :high)
9
+ - Thought Signatures support for Function Calling with Thinking
10
+ - `FunctionCallingHelper.build_continuation` for automatic signature management
11
+ - Response methods: `thought_signatures`, `first_thought_signature`, `has_thought_signature?`
12
+ - Response helper methods: `thoughts_token_count`, `model_version`, `gemini_3?`
8
13
 
9
- ## [0.1.2] - 2025-07-10
10
- - Add function calling
14
+ ## [0.1.7] - 2026-01-13
11
15
 
12
- ## [0.1.3] - 2025-10-09
13
- - Add support for multi-image input
14
-
15
- ## [0.1.4] - 2025-11-08
16
- - Add support for grounding search
17
-
18
- ## [0.1.5] - 2025-11-13
19
- - Add support for URL Context tool
20
- - Add simplified method for accessing grounding search sources
16
+ - Remove dotenv dependency
21
17
 
22
18
  ## [0.1.6] - 2025-12-11
19
+
23
20
  - Add support for video understanding
24
21
  - Analyze local video files (Files API and inline data)
25
22
  - Analyze YouTube videos
26
23
  - Helper methods: describe, ask, extract_timestamps, analyze_segment
27
24
  - Support for MP4, MPEG, MOV, AVI, FLV, WebM, WMV, 3GPP formats
25
+ - Change default model to gemini-2.5-flash
26
+
27
+ ## [0.1.5] - 2025-11-13
28
+
29
+ - Add support for URL Context tool
30
+ - Add simplified method for accessing grounding search sources
31
+
32
+ ## [0.1.4] - 2025-11-08
33
+
34
+ - Add support for grounding search
35
+
36
+ ## [0.1.3] - 2025-10-09
37
+
38
+ - Add support for multi-image input
39
+
40
+ ## [0.1.2] - 2025-07-10
41
+
42
+ - Add function calling
43
+
44
+ ## [0.1.1] - 2025-05-04
45
+
46
+ - Changed generate_contents to accept temperature parameter
47
+
48
+ ## [0.1.0] - 2025-04-05
49
+
50
+ - Initial release
data/README.md CHANGED
@@ -1,10 +1,22 @@
1
1
  [README ‐ 日本語](https://github.com/rira100000000/ruby-gemini-api/blob/main/README_ja.md)
2
2
  # Ruby-Gemini-API
3
3
 
4
+ [![Gem Version](https://badge.fury.io/rb/ruby-gemini-api.svg)](https://badge.fury.io/rb/ruby-gemini-api)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
4
7
  A Ruby client library for Google's Gemini API. This gem provides a simple, intuitive interface for interacting with Gemini's generative AI capabilities, following patterns similar to other AI client libraries.
5
8
 
6
9
  This project is inspired by and pays homage to [ruby-openai](https://github.com/alexrudall/ruby-openai), aiming to provide a familiar and consistent experience for Ruby developers working with Gemini's AI models.
7
10
 
11
+ ## Why This Gem?
12
+
13
+ - **Familiar Interface**: API design inspired by ruby-openai for a smooth transition
14
+ - **Comprehensive Features**: Text generation, vision, audio, video, function calling, and more
15
+ - **Response Object**: Convenient wrapper for easy access to generated content
16
+ - **Streaming Support**: Real-time text generation with block-based API
17
+ - **Thinking Support**: Built-in support for Gemini 2.5/3 thinking features
18
+ - **Production Ready**: Stable 1.0 release with thorough documentation
19
+
8
20
  ## Features
9
21
 
10
22
  - Text generation with Gemini models
@@ -94,6 +106,105 @@ puts "After deleting a function: #{all_tools.list_functions}"
94
106
  # => After deleting a function: [:get_current_weather, :send_email]
95
107
  ```
96
108
 
109
+ ### Thinking Feature
110
+
111
+ Gemini 2.5 and later models support the Thinking feature, which enables the model to perform internal reasoning processes for complex problems to generate higher-quality answers.
112
+
113
+ #### Using with Gemini 2.5: `thinking_budget`
114
+
115
+ ```ruby
116
+ require 'gemini'
117
+
118
+ client = Gemini::Client.new(ENV['GEMINI_API_KEY'])
119
+
120
+ # Specify thinking token count (1-32768)
121
+ response = client.generate_content(
122
+ "Solve this complex math problem step by step",
123
+ model: "gemini-2.5-flash",
124
+ thinking_budget: 2048
125
+ )
126
+
127
+ puts "Thoughts token count: #{response.thoughts_token_count}"
128
+ puts "Answer: #{response.text}"
129
+
130
+ # Dynamic thinking (model decides automatically)
131
+ response = client.generate_content(
132
+ "A difficult logic puzzle",
133
+ model: "gemini-2.5-flash",
134
+ thinking_budget: -1
135
+ )
136
+
137
+ # Disable thinking
138
+ response = client.generate_content(
139
+ "Simple question",
140
+ thinking_budget: 0
141
+ )
142
+ ```
143
+
144
+ #### Using with Gemini 3: `thinking_level`
145
+
146
+ ```ruby
147
+ # Specify thinking level (:minimal, :low, :medium, :high)
148
+ response = client.generate_content(
149
+ "Complex analysis task",
150
+ model: "gemini-3-flash-preview",
151
+ thinking_level: :high
152
+ )
153
+ ```
154
+
155
+ #### Function Calling with Thinking (Thought Signatures)
156
+
157
+ When using Function Calling with Gemini 3, Thought Signatures must be managed. The library automatically handles signatures for you.
158
+
159
+ ```ruby
160
+ # Initial request
161
+ response = client.generate_content(
162
+ "What's the weather in Tokyo?",
163
+ tools: tools,
164
+ thinking_level: :medium
165
+ )
166
+
167
+ # If function calls are present
168
+ if response.function_calls.any?
169
+ # Execute the function
170
+ weather_data = get_weather("Tokyo")
171
+
172
+ # Build continuation request (with automatic signature attachment)
173
+ contents = Gemini::FunctionCallingHelper.build_continuation(
174
+ original_contents: [{ role: "user", parts: [{ text: "What's the weather in Tokyo?" }] }],
175
+ model_response: response,
176
+ function_responses: [
177
+ { name: "get_weather", response: weather_data }
178
+ ]
179
+ )
180
+
181
+ # Continuation request
182
+ final_response = client.generate_content(
183
+ contents,
184
+ tools: tools,
185
+ thinking_level: :medium
186
+ )
187
+
188
+ puts final_response.text
189
+ end
190
+ ```
191
+
192
+ #### Response Methods for Thinking
193
+
194
+ ```ruby
195
+ # Get thoughts token count
196
+ response.thoughts_token_count # => 150
197
+
198
+ # Get thought signatures (for Function Calling)
199
+ response.thought_signatures # => ["base64encoded..."]
200
+ response.first_thought_signature
201
+ response.has_thought_signature?
202
+
203
+ # Check model version
204
+ response.model_version # => "gemini-3-flash-preview"
205
+ response.gemini_3? # => true
206
+ ```
207
+
97
208
  ## Installation
98
209
 
99
210
  Add this line to your application's Gemfile:
@@ -1116,6 +1227,8 @@ The gem includes several demo applications that showcase its functionality:
1116
1227
  - `demo/file_audio_demo.rb` - Audio transcription with large audio files
1117
1228
  - `demo/structured_output_demo.rb` - Structured JSON output with schema
1118
1229
  - `demo/enum_response_demo.rb` - Enum-constrained responses
1230
+ - `demo/thinking_demo.rb` - Thinking feature (Gemini 2.5)
1231
+ - `demo/thinking_gemini3_demo.rb` - Thinking feature (Gemini 3)
1119
1232
  - `demo/document_chat_demo.rb` - Document processing
1120
1233
  - `demo/document_conversation_demo.rb` - Conversation with documents
1121
1234
  - `demo/document_cache_demo.rb` - Document caching
@@ -1159,6 +1272,12 @@ ruby demo/structured_output_demo.rb
1159
1272
  # Enum-constrained responses
1160
1273
  ruby demo/enum_response_demo.rb
1161
1274
 
1275
+ # Thinking feature (Gemini 2.5)
1276
+ ruby demo/thinking_demo.rb
1277
+
1278
+ # Thinking feature (Gemini 3)
1279
+ ruby demo/thinking_gemini3_demo.rb
1280
+
1162
1281
  # Document processing
1163
1282
  ruby demo/document_chat_demo.rb path/to/document.pdf
1164
1283
 
data/lib/gemini/client.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  module Gemini
2
2
  class Client
3
3
  include Gemini::HTTP
4
-
4
+
5
5
  SENSITIVE_ATTRIBUTES = %i[@api_key @extra_headers].freeze
6
6
  CONFIG_KEYS = %i[api_key uri_base extra_headers log_errors request_timeout].freeze
7
+ VALID_THINKING_LEVELS = %w[minimal low medium high].freeze
7
8
 
8
9
  attr_reader(*CONFIG_KEYS, :faraday_middleware)
9
10
  attr_writer :api_key
@@ -83,7 +84,18 @@ module Gemini
83
84
  # Extended to support streaming callbacks
84
85
  def chat(parameters: {}, &stream_callback)
85
86
  model = parameters.delete(:model) || "gemini-2.5-flash"
86
-
87
+
88
+ # thinking_budget / thinking_level をパラメータから抽出
89
+ thinking_budget = parameters.delete(:thinking_budget)
90
+ thinking_level = parameters.delete(:thinking_level)
91
+
92
+ # Thinking設定
93
+ thinking_config = build_thinking_config(thinking_budget, thinking_level)
94
+ if thinking_config
95
+ parameters[:generationConfig] ||= {}
96
+ parameters[:generationConfig][:thinkingConfig] = thinking_config
97
+ end
98
+
87
99
  # If streaming callback is provided
88
100
  if block_given?
89
101
  path = "models/#{model}:streamGenerateContent"
@@ -121,10 +133,12 @@ module Gemini
121
133
 
122
134
  # Helper methods for convenience
123
135
 
124
- # Method with usage similar to OpenAI's chat
136
+ # Method with usage similar to OpenAI's chat
125
137
  def generate_content(prompt, model: "gemini-2.5-flash", system_instruction: nil,
126
138
  response_mime_type: nil, response_schema: nil, temperature: 0.5, tools: nil,
127
- url_context: false, google_search: false, **parameters, &stream_callback)
139
+ url_context: false, google_search: false,
140
+ thinking_budget: nil, thinking_level: nil,
141
+ **parameters, &stream_callback)
128
142
  content = format_content(prompt)
129
143
  params = {
130
144
  contents: [content],
@@ -144,6 +158,12 @@ module Gemini
144
158
  params[:generation_config]["response_schema"] = response_schema
145
159
  end
146
160
 
161
+ # Thinking設定を追加
162
+ thinking_config = build_thinking_config(thinking_budget, thinking_level)
163
+ if thinking_config
164
+ params[:generation_config][:thinkingConfig] = thinking_config
165
+ end
166
+
147
167
  # Handle tool shortcuts
148
168
  tools = build_tools_array(tools, url_context: url_context, google_search: google_search)
149
169
  params[:tools] = tools if tools && !tools.empty?
@@ -416,6 +436,39 @@ module Gemini
416
436
 
417
437
  private
418
438
 
439
+ # Build thinking config from budget and level options
440
+ def build_thinking_config(budget, level)
441
+ return nil unless budget || level
442
+
443
+ config = {}
444
+
445
+ if budget
446
+ validate_thinking_budget!(budget)
447
+ config[:thinkingBudget] = budget
448
+ end
449
+
450
+ if level
451
+ level_str = level.to_s
452
+ validate_thinking_level!(level_str)
453
+ config[:thinkingLevel] = level_str
454
+ end
455
+
456
+ config
457
+ end
458
+
459
+ def validate_thinking_budget!(budget)
460
+ return if budget == -1 || budget == 0
461
+ unless budget.is_a?(Integer) && budget > 0 && budget <= 32768
462
+ raise ArgumentError, "thinking_budget must be -1, 0, or 1-32768"
463
+ end
464
+ end
465
+
466
+ def validate_thinking_level!(level)
467
+ unless VALID_THINKING_LEVELS.include?(level)
468
+ raise ArgumentError, "thinking_level must be one of: #{VALID_THINKING_LEVELS.join(', ')}"
469
+ end
470
+ end
471
+
419
472
  # Build tools array from explicit tools parameter and shortcuts
420
473
  def build_tools_array(tools, url_context: false, google_search: false)
421
474
  result_tools = []
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemini
4
+ module FunctionCallingHelper
5
+ # Function Callレスポンスから継続用のcontentsを構築
6
+ # Gemini 3では関数呼び出しの継続時にThought Signatureが必須
7
+ #
8
+ # @param original_contents [Array] 元の会話履歴
9
+ # @param model_response [Gemini::Response] モデルの応答(function call含む)
10
+ # @param function_responses [Array<Hash>] 関数の結果の配列
11
+ # 各要素は { name: "function_name", response: { ... } } の形式
12
+ # @return [Array] 継続リクエスト用のcontents配列
13
+ #
14
+ # @example
15
+ # contents = Gemini::FunctionCallingHelper.build_continuation(
16
+ # original_contents: [{ role: "user", parts: [{ text: "東京の天気を教えて" }] }],
17
+ # model_response: response,
18
+ # function_responses: [
19
+ # { name: "get_weather", response: { temperature: 20, condition: "晴れ" } }
20
+ # ]
21
+ # )
22
+ def self.build_continuation(original_contents:, model_response:, function_responses:)
23
+ # 元の会話履歴
24
+ contents = original_contents.dup
25
+
26
+ # モデルの応答(Signature付き)
27
+ contents << {
28
+ role: "model",
29
+ parts: model_response.build_function_call_parts_with_signature
30
+ }
31
+
32
+ # 関数の結果
33
+ function_response_parts = function_responses.map do |fr|
34
+ { functionResponse: fr }
35
+ end
36
+
37
+ contents << {
38
+ role: "user",
39
+ parts: function_response_parts
40
+ }
41
+
42
+ contents
43
+ end
44
+ end
45
+ end
@@ -223,7 +223,52 @@ module Gemini
223
223
  def safety_ratings
224
224
  first_candidate&.dig("safetyRatings") || []
225
225
  end
226
-
226
+
227
+ # Thinking関連メソッド
228
+
229
+ # 思考トークン数を取得
230
+ def thoughts_token_count
231
+ @raw_data.dig('usageMetadata', 'thoughtsTokenCount')
232
+ end
233
+
234
+ # Thought Signatureを取得(配列)
235
+ def thought_signatures
236
+ parts.filter_map { |p| p['thoughtSignature'] }
237
+ end
238
+
239
+ # 最初のThought Signatureを取得
240
+ def first_thought_signature
241
+ thought_signatures.first
242
+ end
243
+
244
+ # Signatureが存在するか
245
+ def has_thought_signature?
246
+ !thought_signatures.empty?
247
+ end
248
+
249
+ # モデルバージョンを取得
250
+ def model_version
251
+ @raw_data['modelVersion']
252
+ end
253
+
254
+ # Gemini 3系かどうか
255
+ def gemini_3?
256
+ model_version&.start_with?('gemini-3') || false
257
+ end
258
+
259
+ # 関数呼び出しにSignatureを付与してパーツを構築
260
+ def build_function_call_parts_with_signature
261
+ function_call_parts = parts.select { |p| p['functionCall'] }
262
+ signature = first_thought_signature
263
+
264
+ function_call_parts.map.with_index do |part, index|
265
+ fc_part = { functionCall: part['functionCall'] }
266
+ # 最初のパートにのみSignatureを付与
267
+ fc_part[:thoughtSignature] = signature if index == 0 && signature
268
+ fc_part
269
+ end
270
+ end
271
+
227
272
  # 画像生成結果から最初の画像を取得(Base64エンコード形式)
228
273
  def image
229
274
  images.first
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemini
4
- VERSION = "0.1.6"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/gemini.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  require "faraday"
2
2
  require "faraday/multipart"
3
3
  require "json"
4
- require 'dotenv/load'
5
4
 
6
- require_relative 'gemini/version'
7
- require_relative 'gemini/tool_definition'
5
+ require_relative "gemini/version"
6
+ require_relative "gemini/tool_definition"
8
7
  require_relative "gemini/http_headers"
9
8
  require_relative "gemini/http"
10
9
  require_relative "gemini/client"
@@ -17,9 +16,11 @@ require_relative "gemini/audio"
17
16
  require_relative "gemini/files"
18
17
  require_relative "gemini/images"
19
18
  require_relative "gemini/response"
19
+ require_relative "gemini/function_calling_helper"
20
20
  require_relative "gemini/documents"
21
21
  require_relative "gemini/cached_content"
22
22
  require_relative "gemini/video"
23
+
23
24
  module Gemini
24
25
  class Error < StandardError; end
25
26
  class ConfigurationError < Error; end
@@ -27,28 +28,29 @@ module Gemini
27
28
  class APIError < Error; end
28
29
  class RateLimitError < Error; end
29
30
  class InvalidRequestError < Error; end
30
-
31
+
31
32
  class MiddlewareErrors < Faraday::Middleware
32
33
  def call(env)
33
34
  @app.call(env)
34
35
  rescue Faraday::Error => e
35
36
  raise e unless e.response.is_a?(Hash)
37
+
36
38
  Gemini.log_message("Gemini HTTP Error", e.response[:body], :error)
37
39
  raise e
38
40
  end
39
41
  end
40
-
42
+
41
43
  class Configuration
42
44
  attr_accessor :api_key,
43
45
  :uri_base,
44
46
  :log_errors,
45
47
  :request_timeout,
46
48
  :extra_headers
47
-
49
+
48
50
  DEFAULT_URI_BASE = "https://generativelanguage.googleapis.com/v1beta".freeze
49
51
  DEFAULT_REQUEST_TIMEOUT = 120
50
52
  DEFAULT_LOG_ERRORS = false
51
-
53
+
52
54
  def initialize
53
55
  @api_key = nil
54
56
  @log_errors = DEFAULT_LOG_ERRORS
@@ -57,21 +59,21 @@ module Gemini
57
59
  @extra_headers = {}
58
60
  end
59
61
  end
60
-
62
+
61
63
  class << self
62
64
  attr_writer :configuration
63
-
65
+
64
66
  def configuration
65
67
  @configuration ||= Gemini::Configuration.new
66
68
  end
67
-
69
+
68
70
  def configure
69
71
  yield(configuration)
70
72
  end
71
-
73
+
72
74
  def log_message(prefix, message, level = :warn)
73
75
  return unless configuration.log_errors
74
-
76
+
75
77
  color = level == :error ? "\033[31m" : "\033[33m"
76
78
  logger = Logger.new($stdout)
77
79
  logger.formatter = proc do |_severity, _datetime, _progname, msg|
@@ -80,4 +82,4 @@ module Gemini
80
82
  logger.send(level, message)
81
83
  end
82
84
  end
83
- end
85
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-gemini-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rira100000000
@@ -152,6 +152,7 @@ files:
152
152
  - lib/gemini/documents.rb
153
153
  - lib/gemini/embeddings.rb
154
154
  - lib/gemini/files.rb
155
+ - lib/gemini/function_calling_helper.rb
155
156
  - lib/gemini/http.rb
156
157
  - lib/gemini/http_headers.rb
157
158
  - lib/gemini/images.rb