Class: Gemini::Client

Inherits:
Object
  • Object
show all
Includes:
HTTP
Defined in:
lib/gemini/client.rb

Constant Summary collapse

SENSITIVE_ATTRIBUTES =
%i[@api_key @extra_headers].freeze
CONFIG_KEYS =
%i[api_key uri_base extra_headers log_errors request_timeout].freeze
VALID_THINKING_LEVELS =
%w[minimal low medium high].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from HTTP

#delete, #get, #json_post, #multipart_post, #post

Methods included from HTTPHeaders

#add_headers

Constructor Details

#initialize(api_key = nil, config = {}, &faraday_middleware) ⇒ Client

Returns a new instance of Client.

Raises:



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/gemini/client.rb', line 12

def initialize(api_key = nil, config = {}, &faraday_middleware)
  # Handle API key passed directly as argument
  config[:api_key] = api_key if api_key
  
  CONFIG_KEYS.each do |key|
    # Set instance variables. Use global config if no setting provided
    instance_variable_set(
      "@#{key}",
      config[key].nil? ? Gemini.configuration.send(key) : config[key]
    )
  end
  
  @api_key ||= ENV["GEMINI_API_KEY"]
  @faraday_middleware = faraday_middleware
  
  raise ConfigurationError, "API key is not set" unless @api_key
end

Instance Attribute Details

#api_key=(value) ⇒ Object (writeonly)

Sets the attribute api_key

Parameters:

  • value

    the value to set the attribute api_key to.



10
11
12
# File 'lib/gemini/client.rb', line 10

def api_key=(value)
  @api_key = value
end

Instance Method Details

#audioObject



45
46
47
# File 'lib/gemini/client.rb', line 45

def audio
  @audio ||= Gemini::Audio.new(client: self)
end

#cached_contentObject

キャッシュ管理アクセサ



69
70
71
# File 'lib/gemini/client.rb', line 69

def cached_content
  @cached_content ||= Gemini::CachedContent.new(client: self)
end

#chat(parameters: {}, &stream_callback) ⇒ Object

OpenAI chat-like text generation method for Gemini API Extended to support streaming callbacks



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/gemini/client.rb', line 95

def chat(parameters: {}, &stream_callback)
  model = parameters.delete(:model) || "gemini-2.5-flash"

  # thinking_budget / thinking_level をパラメータから抽出
  thinking_budget = parameters.delete(:thinking_budget)
  thinking_level = parameters.delete(:thinking_level)

  # Thinking設定
  thinking_config = build_thinking_config(thinking_budget, thinking_level)
  if thinking_config
    parameters[:generationConfig] ||= {}
    parameters[:generationConfig][:thinkingConfig] = thinking_config
  end

  # If streaming callback is provided
  if block_given?
    path = "models/#{model}:streamGenerateContent"
    # Set up stream callback
    stream_params = parameters.dup
    stream_params[:stream] = proc { |chunk| process_stream_chunk(chunk, &stream_callback) }
    response = json_post(path: path, parameters: stream_params)
    return Gemini::Response.new(response)
  else
    # Normal batch response mode
    path = "models/#{model}:generateContent"
    response = json_post(path: path, parameters: parameters)
    return Gemini::Response.new(response)
  end
end

#chat_with_file(file_path, prompt, model: "gemini-2.5-flash", **parameters) ⇒ Object

単一ファイルのヘルパー



342
343
344
# File 'lib/gemini/client.rb', line 342

def chat_with_file(file_path, prompt, model: "gemini-2.5-flash", **parameters)
  chat_with_multimodal([file_path], prompt, model: model, **parameters)
end

#chat_with_multimodal(file_paths, prompt, model: "gemini-2.5-flash", **parameters) ⇒ Object

ファイルを使った会話(複数ファイル対応)



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/gemini/client.rb', line 243

def chat_with_multimodal(file_paths, prompt, model: "gemini-2.5-flash", **parameters)
  # スレッドを作成
  thread = threads.create(parameters: { model: model })
  thread_id = thread["id"]
  
  # 複数のファイルをアップロードして追加
  file_infos = []
  
  begin
    # ファイルをアップロードしてメッセージとして追加
    file_paths.each do |file_path|
      file = File.open(file_path, "rb")
      begin
        upload_result = files.upload(file: file)
        file_uri = upload_result["file"]["uri"]
        file_name = upload_result["file"]["name"]
        mime_type = determine_mime_type(file_path)
        
        # ファイル情報を保存
        file_infos << {
          uri: file_uri,
          name: file_name,
          mime_type: mime_type
        }
        
        # ファイルをメッセージとして追加
        messages.create(
          thread_id: thread_id,
          parameters: {
            role: "user",
            content: [
              { file_data: { mime_type: mime_type, file_uri: file_uri } }
            ]
          }
        )
      ensure
        file.close
      end
    end
    
    # プロンプトメッセージを追加
    messages.create(
      thread_id: thread_id,
      parameters: {
        role: "user",
        content: prompt
      }
    )
    
    # 実行
    run = runs.create(thread_id: thread_id, parameters: parameters)
    
    # メッセージを取得
    messages_list = messages.list(thread_id: thread_id)
    
    # 結果とファイル情報を返す
    {
      messages: messages_list,
      run: run,
      file_infos: file_infos,
      thread_id: thread_id
    }
  rescue => e
    # エラー処理
    { error: e.message, file_infos: file_infos }
  end
end

#completions(parameters: {}, &stream_callback) ⇒ Object

Method corresponding to OpenAI’s completions Uses same endpoint as chat in Gemini API



150
151
152
# File 'lib/gemini/client.rb', line 150

def completions(parameters: {}, &stream_callback)
  chat(parameters: parameters, &stream_callback)
end

#conn(multipart: false) ⇒ Object

Access to conn (Faraday connection) for Audio features Wrapper to allow using private methods from HTTP module externally



89
90
91
# File 'lib/gemini/client.rb', line 89

def conn(multipart: false)
  super(multipart: multipart)
end

#determine_mime_type(path_or_url) ⇒ Object

MIMEタイプを判定するメソッド(パブリックに変更)



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/gemini/client.rb', line 389

def determine_mime_type(path_or_url)
  extension = File.extname(path_or_url).downcase
  
  # ドキュメント形式
  document_types = {
    ".pdf" => "application/pdf",
    ".js" => "application/x-javascript",
    ".py" => "application/x-python",
    ".txt" => "text/plain",
    ".html" => "text/html",
    ".htm" => "text/html",
    ".css" => "text/css",
    ".md" => "text/md",
    ".csv" => "text/csv",
    ".xml" => "text/xml",
    ".rtf" => "text/rtf"
  }
  
  # 画像形式
  image_types = {
    ".jpg" => "image/jpeg",
    ".jpeg" => "image/jpeg",
    ".png" => "image/png",
    ".gif" => "image/gif",
    ".webp" => "image/webp",
    ".heic" => "image/heic",
    ".heif" => "image/heif"
  }
  
  # 音声形式
  audio_types = {
    ".wav" => "audio/wav",
    ".mp3" => "audio/mp3",
    ".aiff" => "audio/aiff",
    ".aac" => "audio/aac",
    ".ogg" => "audio/ogg",
    ".flac" => "audio/flac"
  }
  
  # 拡張子からMIMEタイプを判定
  mime_type = document_types[extension] || image_types[extension] || audio_types[extension]
  return mime_type if mime_type
  
  # ファイルの内容から判定を試みる
  if File.exist?(path_or_url)
    # ファイルの最初の数バイトを読み込んで判定
    first_bytes = File.binread(path_or_url, 8).bytes
    case
    when first_bytes[0..1] == [0xFF, 0xD8]
      return "image/jpeg"  # JPEG
    when first_bytes[0..7] == [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
      return "image/png"   # PNG
    when first_bytes[0..2] == [0x47, 0x49, 0x46]
      return "image/gif"   # GIF
    when first_bytes[0..3] == [0x52, 0x49, 0x46, 0x46] && first_bytes[8..11] == [0x57, 0x45, 0x42, 0x50]
      return "image/webp"  # WEBP
    when first_bytes[0..3] == [0x25, 0x50, 0x44, 0x46]
      return "application/pdf" # PDF
    when first_bytes[0..1] == [0x49, 0x44]
      return "audio/mp3"   # MP3
    when first_bytes[0..3] == [0x52, 0x49, 0x46, 0x46]
      return "audio/wav"   # WAV
    end
  end
  
  # URLまたは判定できない場合
  if path_or_url.start_with?("http://", "https://")
    "application/octet-stream"
  else
    "application/octet-stream"
  end
end

#documentsObject

ドキュメント処理アクセサ



59
60
61
# File 'lib/gemini/client.rb', line 59

def documents
  @documents ||= Gemini::Documents.new(client: self)
end

#embed_content(input, model: Gemini::Embeddings::DEFAULT_MODEL, task_type: nil, title: nil, output_dimensionality: nil, **parameters) ⇒ Object

Generate embeddings for the given input. input can be a String (single embed) or Array of Strings (batch embed). Supports task_type, title (RETRIEVAL_DOCUMENT only), and output_dimensionality.



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/gemini/client.rb', line 128

def embed_content(input, model: Gemini::Embeddings::DEFAULT_MODEL, task_type: nil,
                  title: nil, output_dimensionality: nil, **parameters)
  embeddings_api.create(
    input: input,
    model: model,
    task_type: task_type,
    title: title,
    output_dimensionality: output_dimensionality,
    **parameters
  )
end

#embeddings(parameters: {}) ⇒ Object

Method corresponding to OpenAI’s embeddings (kept for compatibility)



141
142
143
144
145
146
# File 'lib/gemini/client.rb', line 141

def embeddings(parameters: {})
  model = parameters.delete(:model) || Gemini::Embeddings::DEFAULT_MODEL
  path = "models/#{model.to_s.delete_prefix("models/")}:embedContent"
  response = json_post(path: path, parameters: parameters)
  Gemini::Response.new(response)
end

#embeddings_apiObject

Embeddings APIアクセサ



79
80
81
# File 'lib/gemini/client.rb', line 79

def embeddings_api
  @embeddings_api ||= Gemini::Embeddings.new(client: self)
end

#filesObject



49
50
51
# File 'lib/gemini/client.rb', line 49

def files
  @files ||= Gemini::Files.new(client: self)
end

#generate_content(prompt, model: "gemini-2.5-flash", system_instruction: nil, response_mime_type: nil, response_schema: nil, temperature: 0.5, tools: nil, url_context: false, google_search: false, thinking_budget: nil, thinking_level: nil, **parameters, &stream_callback) ⇒ Object

Method with usage similar to OpenAI’s chat



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/gemini/client.rb', line 162

def generate_content(prompt, model: "gemini-2.5-flash", system_instruction: nil,
                    response_mime_type: nil, response_schema: nil, temperature: 0.5, tools: nil,
                    url_context: false, google_search: false,
                    thinking_budget: nil, thinking_level: nil,
                    **parameters, &stream_callback)
  content = format_content(prompt)
  params = {
    contents: [content],
    model: model
  }

  if system_instruction
    params[:system_instruction] = format_content(system_instruction)
  end
  params[:generation_config] ||= {}
  params[:generation_config]["temperature"] = temperature
  if response_mime_type
    params[:generation_config]["response_mime_type"] = response_mime_type
  end

  if response_schema
    params[:generation_config]["response_schema"] = response_schema
  end

  # Thinking設定を追加
  thinking_config = build_thinking_config(thinking_budget, thinking_level)
  if thinking_config
    params[:generation_config][:thinkingConfig] = thinking_config
  end

  # Handle tool shortcuts
  tools = build_tools_array(tools, url_context: url_context, google_search: google_search)
  params[:tools] = tools if tools && !tools.empty?

  params.merge!(parameters)

  if block_given?
    chat(parameters: params, &stream_callback)
  else
    chat(parameters: params)
  end
end

#generate_content_stream(prompt, model: "gemini-2.5-flash", system_instruction: nil, response_mime_type: nil, response_schema: nil, temperature: 0.5, url_context: false, google_search: false, **parameters, &block) ⇒ Object

Streaming text generation

Raises:

  • (ArgumentError)


206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/gemini/client.rb', line 206

def generate_content_stream(prompt, model: "gemini-2.5-flash", system_instruction: nil,
                          response_mime_type: nil, response_schema: nil, temperature: 0.5,
                          url_context: false, google_search: false, **parameters, &block)
  raise ArgumentError, "Block is required for streaming" unless block_given?

  content = format_content(prompt)
  params = {
    contents: [content],
    model: model
  }

  if system_instruction
    params[:system_instruction] = format_content(system_instruction)
  end

  params[:generation_config] ||= {}

  if response_mime_type
    params[:generation_config][:response_mime_type] = response_mime_type
  end

  if response_schema
    params[:generation_config][:response_schema] = response_schema
  end
  params[:generation_config]["temperature"] = temperature

  # Handle tool shortcuts
  tools = build_tools_array(nil, url_context: url_context, google_search: google_search)
  params[:tools] = tools if tools && !tools.empty?

  # Merge other parameters
  params.merge!(parameters)

  chat(parameters: params, &block)
end

#generate_content_with_cache(prompt, cached_content:, model: "gemini-2.5-flash", **parameters) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/gemini/client.rb', line 311

def generate_content_with_cache(prompt, cached_content:, model: "gemini-2.5-flash", **parameters)
  # モデル名にmodels/プレフィックスを追加
  model_name = model.start_with?("models/") ? model : "models/#{model}"
  
  # リクエストパラメータを構築
  params = {
    contents: [
      {
        parts: [{ text: prompt }],
        role: "user"
      }
    ],
    cachedContent: cached_content
  }
  
  # その他のパラメータをマージ
  params.merge!(parameters)
  
  # 直接エンドポイントURLを構築
  endpoint = "#{model_name}:generateContent"
  
  # APIリクエスト
  response = json_post(
    path: endpoint,
    parameters: params
  )
  
  Gemini::Response.new(response)
end

#imagesObject

画像生成アクセサ



54
55
56
# File 'lib/gemini/client.rb', line 54

def images
  @images ||= Gemini::Images.new(client: self)
end

#inspectObject

Debug inspect method



380
381
382
383
384
385
386
# File 'lib/gemini/client.rb', line 380

def inspect
  vars = instance_variables.map do |var|
    value = instance_variable_get(var)
    SENSITIVE_ATTRIBUTES.include?(var) ? "#{var}=[REDACTED]" : "#{var}=#{value.inspect}"
  end
  "#<#{self.class}:#{object_id} #{vars.join(', ')}>"
end

#liveObject

Live APIアクセサ



74
75
76
# File 'lib/gemini/client.rb', line 74

def live
  @live ||= Gemini::Live.new(client: self)
end

#messagesObject

Message management accessor



36
37
38
# File 'lib/gemini/client.rb', line 36

def messages
  @messages ||= Gemini::Messages.new(client: self)
end

#modelsObject

Accessor for sub-clients



155
156
157
# File 'lib/gemini/client.rb', line 155

def models
  @models ||= Gemini::Models.new(client: self)
end

#reset_headersObject



83
84
85
# File 'lib/gemini/client.rb', line 83

def reset_headers
  @extra_headers = {}
end

#runsObject

Run management accessor



41
42
43
# File 'lib/gemini/client.rb', line 41

def runs
  @runs ||= Gemini::Runs.new(client: self)
end

#threadsObject

Thread management accessor



31
32
33
# File 'lib/gemini/client.rb', line 31

def threads
  @threads ||= Gemini::Threads.new(client: self)
end

#upload_and_process_file(file_path, prompt, content_type: nil, model: "gemini-2.5-flash", **parameters) ⇒ Object

ファイルをアップロードして質問するシンプルなヘルパー



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/gemini/client.rb', line 347

def upload_and_process_file(file_path, prompt, content_type: nil, model: "gemini-2.5-flash", **parameters)
  # MIMEタイプを自動判定
  mime_type = content_type || determine_mime_type(file_path)
  
  # ファイルをアップロード
  file = File.open(file_path, "rb")
  begin
    upload_result = files.upload(file: file)
    file_uri = upload_result["file"]["uri"]
    file_name = upload_result["file"]["name"]
    
    # コンテンツを生成
    response = generate_content(
      [
        { text: prompt },
        { file_data: { mime_type: mime_type, file_uri: file_uri } }
      ],
      model: model,
      **parameters
    )
    
    # レスポンスと一緒にファイル情報も返す
    {
      response: response,
      file_uri: file_uri,
      file_name: file_name
    }
  ensure
    file.close
  end
end

#videoObject

動画処理アクセサ



64
65
66
# File 'lib/gemini/client.rb', line 64

def video
  @video ||= Gemini::Video.new(client: self)
end