ruby-gemini-api 0.1.7 → 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: 89f3cb8e2bfe7ee5c3e2319312a44b8eefe756d984aa0a26373102f5a3d7eb18
4
- data.tar.gz: 0b3272695fc70f9a841261f525799faea9b6da6562bf949d167ec511f1e04eb4
3
+ metadata.gz: 92b2d6f35fbff3fda1457ef3244ae730ee99e156909c310b62878540d47d4076
4
+ data.tar.gz: 710191ae9ac4f136ea586782be43b8aa18390d74a8ba1d844a9524fc120fdaa4
5
5
  SHA512:
6
- metadata.gz: 3ac51187c27d747ea663c269d63b87a8e31a52febdce56efcc7b17497ffa17987e9b61a957c1ee95ea265128594a22ac3ce937a0380c97e5422a81e6906b052c
7
- data.tar.gz: 8ca1da9ce78243b3435c7baf5b059253ecb1a32b98092520342fa3063135315713da4cb98c9ee789fcbf28453fadf1f2e2bbfb39d28b3e515b60549ac78f8012
6
+ metadata.gz: 31a2f84ff0f7d9d5127fe3cfa2eba75c2395cc4105e5faa6d45b472ccc4dc8e6502f28635f6a7dfce0c071e445010af9a1781c833a18c6f491f55b9e2989957b
7
+ data.tar.gz: 499e659d3a3284f461deede5fbcaff16ebb3476ced2c3c5c44d4d5e333d3751e45483ed221a654b0b560651127496933342e0fed5bfea8307de40edab80faa21
data/CHANGELOG.md CHANGED
@@ -1,39 +1,50 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2025-04-05
3
+ ## [1.0.0] - 2026-01-28
4
4
 
5
- - Initial release
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?`
6
13
 
7
- ## [0.1.1] - 2025-05-04
14
+ ## [0.1.7] - 2026-01-13
8
15
 
9
- - Changed generate_contents to accept temperature parameter
16
+ - Remove dotenv dependency
10
17
 
11
- ## [0.1.2] - 2025-07-10
18
+ ## [0.1.6] - 2025-12-11
12
19
 
13
- - Add function calling
20
+ - Add support for video understanding
21
+ - Analyze local video files (Files API and inline data)
22
+ - Analyze YouTube videos
23
+ - Helper methods: describe, ask, extract_timestamps, analyze_segment
24
+ - Support for MP4, MPEG, MOV, AVI, FLV, WebM, WMV, 3GPP formats
25
+ - Change default model to gemini-2.5-flash
14
26
 
15
- ## [0.1.3] - 2025-10-09
27
+ ## [0.1.5] - 2025-11-13
16
28
 
17
- - Add support for multi-image input
29
+ - Add support for URL Context tool
30
+ - Add simplified method for accessing grounding search sources
18
31
 
19
32
  ## [0.1.4] - 2025-11-08
20
33
 
21
34
  - Add support for grounding search
22
35
 
23
- ## [0.1.5] - 2025-11-13
36
+ ## [0.1.3] - 2025-10-09
24
37
 
25
- - Add support for URL Context tool
26
- - Add simplified method for accessing grounding search sources
38
+ - Add support for multi-image input
27
39
 
28
- ## [0.1.6] - 2025-12-11
40
+ ## [0.1.2] - 2025-07-10
29
41
 
30
- - Add support for video understanding
31
- - Analyze local video files (Files API and inline data)
32
- - Analyze YouTube videos
33
- - Helper methods: describe, ask, extract_timestamps, analyze_segment
34
- - Support for MP4, MPEG, MOV, AVI, FLV, WebM, WMV, 3GPP formats
35
- - Change default model to gemini-2.5-flash
42
+ - Add function calling
36
43
 
37
- ## [0.1.7] - 2026-01-13
44
+ ## [0.1.1] - 2025-05-04
38
45
 
39
- - Remove dotenv dependency
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.7"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/gemini.rb CHANGED
@@ -16,6 +16,7 @@ require_relative "gemini/audio"
16
16
  require_relative "gemini/files"
17
17
  require_relative "gemini/images"
18
18
  require_relative "gemini/response"
19
+ require_relative "gemini/function_calling_helper"
19
20
  require_relative "gemini/documents"
20
21
  require_relative "gemini/cached_content"
21
22
  require_relative "gemini/video"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-gemini-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rira100000000
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-01-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -153,6 +152,7 @@ files:
153
152
  - lib/gemini/documents.rb
154
153
  - lib/gemini/embeddings.rb
155
154
  - lib/gemini/files.rb
155
+ - lib/gemini/function_calling_helper.rb
156
156
  - lib/gemini/http.rb
157
157
  - lib/gemini/http_headers.rb
158
158
  - lib/gemini/images.rb
@@ -173,7 +173,6 @@ metadata:
173
173
  source_code_uri: https://github.com/rira100000000/ruby-gemini-api
174
174
  changelog_uri: https://github.com/rira100000000/ruby-gemini-api/blob/main/CHANGELOG.md
175
175
  rubygems_mfa_required: 'true'
176
- post_install_message:
177
176
  rdoc_options: []
178
177
  require_paths:
179
178
  - lib
@@ -188,8 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
187
  - !ruby/object:Gem::Version
189
188
  version: '0'
190
189
  requirements: []
191
- rubygems_version: 3.3.26
192
- signing_key:
190
+ rubygems_version: 3.7.2
193
191
  specification_version: 4
194
192
  summary: Ruby client for Google's Gemini API
195
193
  test_files: []