Class: Gemini::Response
- Inherits:
-
Object
- Object
- Gemini::Response
- Defined in:
- lib/gemini/response.rb
Instance Attribute Summary collapse
-
#raw_data ⇒ Object
readonly
Raw response data from API.
Instance Method Summary collapse
- #as_json_array(model_class) ⇒ Object
- #as_json_object(model_class) ⇒ Object
- #as_json_with_keys(*keys) ⇒ Object
-
#audio_data ⇒ Object
Base64-encoded audio data from a TTS response.
-
#audio_mime_type ⇒ Object
MIME type of the audio payload (e.g. “audio/L16;codec=pcm;rate=24000”).
-
#audio_part ⇒ Object
Get the first audio inlineData part (TTS responses use camelCase “inlineData”).
-
#audio_response? ⇒ Boolean
True if the response contains audio inlineData.
-
#build_function_call_parts_with_signature ⇒ Object
関数呼び出しにSignatureを付与してパーツを構築.
-
#cached_content_token_count ⇒ Object
Cached content token count reported by countTokens.
-
#candidates ⇒ Object
Get all candidates (if multiple candidates are present).
-
#code_execution? ⇒ Boolean
True if the response contains generated code or execution results.
-
#code_execution_outcome ⇒ Object
Get the first execution outcome (for example “OUTCOME_OK”).
-
#code_execution_output ⇒ Object
Get the first execution output string from a Code Execution response.
-
#code_execution_results ⇒ Object
Get all codeExecutionResult parts returned by the Code Execution tool.
-
#code_execution_success? ⇒ Boolean
True when the first Code Execution result completed successfully.
-
#completion_tokens ⇒ Object
Get number of tokens used for completion.
-
#count_tokens ⇒ Object
Total tokens reported by the countTokens API (top-level totalTokens).
-
#count_tokens_response? ⇒ Boolean
Check whether this response is a countTokens API result.
-
#embedding ⇒ Object
Get the embedding values as an Array of Floats.
-
#embedding_dimension ⇒ Object
Get the dimensionality (length) of the first embedding vector.
-
#embedding_response? ⇒ Boolean
Check if the raw response contains embedding data.
-
#embeddings ⇒ Object
Get all embedding value arrays for batch responses.
-
#error ⇒ Object
Get error message if any.
-
#executable_code ⇒ Object
Get the first generated code string from a Code Execution response.
-
#executable_codes ⇒ Object
Get all executableCode parts returned by the Code Execution tool.
-
#finish_reason ⇒ Object
Get finish reason (STOP, SAFETY, etc.).
-
#first_candidate ⇒ Object
Get the first candidate.
-
#first_thought_signature ⇒ Object
最初のThought Signatureを取得.
-
#formatted_text ⇒ Object
Get formatted text (HTML/markdown, etc.).
-
#full_content ⇒ Object
Get all content with string representation.
-
#function_calls ⇒ Object
Get function call information.
-
#gemini_3? ⇒ Boolean
Gemini 3系かどうか.
-
#grounded? ⇒ Boolean
Check if response has grounding metadata.
-
#grounding_chunks ⇒ Object
Get grounding chunks (source references).
-
#grounding_metadata ⇒ Object
Get grounding metadata (for Google Search grounding).
-
#grounding_sources ⇒ Object
Get formatted grounding sources (simplified access).
-
#has_thought_signature? ⇒ Boolean
Signatureが存在するか.
-
#image ⇒ Object
画像生成結果から最初の画像を取得(Base64エンコード形式).
-
#image_mime_types ⇒ Object
画像のMIMEタイプを取得.
-
#image_parts ⇒ Object
Get image parts (if any).
-
#image_urls ⇒ Object
Get image URLs from multimodal responses (if any).
-
#images ⇒ Object
画像生成結果からすべての画像を取得(Base64エンコード形式の配列).
-
#initialize(response_data) ⇒ Response
constructor
A new instance of Response.
-
#inspect ⇒ Object
Inspection method for debugging.
- #json ⇒ Object
- #json? ⇒ Boolean
-
#model_version ⇒ Object
モデルバージョンを取得.
-
#parts ⇒ Object
Get all content parts.
-
#prompt_tokens ⇒ Object
Get number of prompt tokens used.
-
#prompt_tokens_details ⇒ Object
Per-modality token breakdown reported by countTokens.
-
#retrieved_urls ⇒ Object
Get retrieved URLs from URL context.
-
#role ⇒ Object
Get response role (usually “model”).
-
#safety_blocked? ⇒ Boolean
Check if response was blocked for safety reasons.
-
#safety_ratings ⇒ Object
Get safety ratings.
-
#save_audio(filepath) ⇒ Object
Save audio to a file.
-
#save_image(filepath) ⇒ Object
最初の画像をファイルに保存.
-
#save_images(filepaths) ⇒ Object
複数の画像をファイルに保存.
-
#search_entry_point ⇒ Object
Get search entry point URL (if available).
-
#stream_chunks ⇒ Object
Process chunks for streaming responses.
-
#success? ⇒ Boolean
Check if response was successful.
-
#text ⇒ Object
Get simple text response (combines multiple parts if present).
-
#text_parts ⇒ Object
Get all text parts as an array.
-
#thought_signatures ⇒ Object
Thought Signatureを取得(配列).
-
#thoughts_token_count ⇒ Object
思考トークン数を取得.
- #to_formatted_json(pretty: false) ⇒ Object
-
#to_s ⇒ Object
Override to_s method to return text.
-
#total_tokens ⇒ Object
Get total tokens used.
-
#url_context? ⇒ Boolean
Check if response has URL context metadata.
-
#url_context_metadata ⇒ Object
Get URL context metadata (for URL Context tool).
-
#url_retrieval_statuses ⇒ Object
Get URL retrieval statuses.
-
#usage ⇒ Object
Get token usage information.
-
#valid? ⇒ Boolean
Check if response is valid.
Constructor Details
#initialize(response_data) ⇒ Response
Returns a new instance of Response.
6 7 8 |
# File 'lib/gemini/response.rb', line 6 def initialize(response_data) @raw_data = response_data end |
Instance Attribute Details
#raw_data ⇒ Object (readonly)
Raw response data from API
4 5 6 |
# File 'lib/gemini/response.rb', line 4 def raw_data @raw_data end |
Instance Method Details
#as_json_array(model_class) ⇒ Object
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 |
# File 'lib/gemini/response.rb', line 651 def as_json_array(model_class) json_data = json return [] unless json_data && json_data.is_a?(Array) begin json_data.map do |item| if model_class.respond_to?(:from_json) model_class.from_json(item) elsif defined?(ActiveModel::Model) && model_class.ancestors.include?(ActiveModel::Model) model_class.new(item) else instance = model_class.new item.each do |key, value| setter_method = "#{key}=" if instance.respond_to?(setter_method) instance.send(setter_method, value) end end instance end end rescue => e [] end end |
#as_json_object(model_class) ⇒ Object
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/gemini/response.rb', line 625 def as_json_object(model_class) json_data = json return nil unless json_data begin if model_class.respond_to?(:from_json) model_class.from_json(json_data) elsif defined?(ActiveModel::Model) && model_class.ancestors.include?(ActiveModel::Model) model_class.new(json_data) else instance = model_class.new json_data.each do |key, value| setter_method = "#{key}=" if instance.respond_to?(setter_method) instance.send(setter_method, value) end end instance end rescue => e nil end end |
#as_json_with_keys(*keys) ⇒ Object
679 680 681 682 683 684 685 686 687 688 |
# File 'lib/gemini/response.rb', line 679 def as_json_with_keys(*keys) json_data = json return [] unless json_data && json_data.is_a?(Array) json_data.map do |item| keys.each_with_object({}) do |key, result| result[key.to_s] = item[key.to_s] if item.key?(key.to_s) end end end |
#audio_data ⇒ Object
Base64-encoded audio data from a TTS response
109 110 111 112 113 114 |
# File 'lib/gemini/response.rb', line 109 def audio_data part = audio_part return nil unless part data_key = part["inlineData"] || part["inline_data"] data_key["data"] end |
#audio_mime_type ⇒ Object
MIME type of the audio payload (e.g. “audio/L16;codec=pcm;rate=24000”)
117 118 119 120 121 122 |
# File 'lib/gemini/response.rb', line 117 def audio_mime_type part = audio_part return nil unless part data_key = part["inlineData"] || part["inline_data"] data_key["mimeType"] || data_key["mime_type"] end |
#audio_part ⇒ Object
Get the first audio inlineData part (TTS responses use camelCase “inlineData”)
97 98 99 100 101 102 103 104 105 106 |
# File 'lib/gemini/response.rb', line 97 def audio_part return nil unless valid? parts.find do |part| data_key = part["inlineData"] || part["inline_data"] next false unless data_key mt = data_key["mimeType"] || data_key["mime_type"] mt.is_a?(String) && mt.start_with?("audio/") end end |
#audio_response? ⇒ Boolean
True if the response contains audio inlineData
125 126 127 |
# File 'lib/gemini/response.rb', line 125 def audio_response? !audio_part.nil? end |
#build_function_call_parts_with_signature ⇒ Object
関数呼び出しにSignatureを付与してパーツを構築
445 446 447 448 449 450 451 452 453 454 455 |
# File 'lib/gemini/response.rb', line 445 def build_function_call_parts_with_signature function_call_parts = parts.select { |p| p['functionCall'] } signature = first_thought_signature function_call_parts.map.with_index do |part, index| fc_part = { functionCall: part['functionCall'] } # 最初のパートにのみSignatureを付与 fc_part[:thoughtSignature] = signature if index == 0 && signature fc_part end end |
#cached_content_token_count ⇒ Object
Cached content token count reported by countTokens
371 372 373 |
# File 'lib/gemini/response.rb', line 371 def cached_content_token_count @raw_data&.dig("cachedContentTokenCount") || 0 end |
#candidates ⇒ Object
Get all candidates (if multiple candidates are present)
189 190 191 |
# File 'lib/gemini/response.rb', line 189 def candidates @raw_data&.dig("candidates") || [] end |
#code_execution? ⇒ Boolean
True if the response contains generated code or execution results.
85 86 87 |
# File 'lib/gemini/response.rb', line 85 def code_execution? !executable_codes.empty? || !code_execution_results.empty? end |
#code_execution_outcome ⇒ Object
Get the first execution outcome (for example “OUTCOME_OK”).
72 73 74 75 76 77 |
# File 'lib/gemini/response.rb', line 72 def code_execution_outcome result_part = code_execution_results.first return nil unless result_part result_part["outcome"] || result_part[:outcome] end |
#code_execution_output ⇒ Object
Get the first execution output string from a Code Execution response.
64 65 66 67 68 69 |
# File 'lib/gemini/response.rb', line 64 def code_execution_output result_part = code_execution_results.first return nil unless result_part result_part["output"] || result_part[:output] end |
#code_execution_results ⇒ Object
Get all codeExecutionResult parts returned by the Code Execution tool.
57 58 59 60 61 |
# File 'lib/gemini/response.rb', line 57 def code_execution_results return [] unless valid? parts.map { |part| part["codeExecutionResult"] || part["code_execution_result"] }.compact end |
#code_execution_success? ⇒ Boolean
True when the first Code Execution result completed successfully.
80 81 82 |
# File 'lib/gemini/response.rb', line 80 def code_execution_success? code_execution_outcome == "OUTCOME_OK" end |
#completion_tokens ⇒ Object
Get number of tokens used for completion
349 350 351 |
# File 'lib/gemini/response.rb', line 349 def completion_tokens usage&.dig("candidateTokens") || 0 end |
#count_tokens ⇒ Object
Total tokens reported by the countTokens API (top-level totalTokens)
366 367 368 |
# File 'lib/gemini/response.rb', line 366 def count_tokens @raw_data&.dig("totalTokens") end |
#count_tokens_response? ⇒ Boolean
Check whether this response is a countTokens API result
359 360 361 362 363 |
# File 'lib/gemini/response.rb', line 359 def count_tokens_response? !@raw_data.nil? && @raw_data.key?("totalTokens") && !@raw_data.key?("candidates") && !@raw_data.key?("predictions") && ! end |
#embedding ⇒ Object
Get the embedding values as an Array of Floats. For single embedContent responses returns the values array. For batchEmbedContents responses returns the first embedding’s values.
212 213 214 215 216 217 218 219 |
# File 'lib/gemini/response.rb', line 212 def return nil unless @raw_data if @raw_data["embedding"].is_a?(Hash) @raw_data["embedding"]["values"] elsif @raw_data["embeddings"].is_a?(Array) && @raw_data["embeddings"].first.is_a?(Hash) @raw_data["embeddings"].first["values"] end end |
#embedding_dimension ⇒ Object
Get the dimensionality (length) of the first embedding vector
236 237 238 239 |
# File 'lib/gemini/response.rb', line 236 def values = values.is_a?(Array) ? values.length : 0 end |
#embedding_response? ⇒ Boolean
Check if the raw response contains embedding data
203 204 205 206 207 |
# File 'lib/gemini/response.rb', line 203 def return false if @raw_data.nil? (@raw_data.key?("embedding") && !@raw_data["embedding"].nil?) || (@raw_data.key?("embeddings") && @raw_data["embeddings"].is_a?(Array) && !@raw_data["embeddings"].empty?) end |
#embeddings ⇒ Object
Get all embedding value arrays for batch responses. Returns an Array of Arrays of Floats. For single embedContent responses, returns a single-element array.
224 225 226 227 228 229 230 231 232 233 |
# File 'lib/gemini/response.rb', line 224 def return [] unless @raw_data if @raw_data["embeddings"].is_a?(Array) @raw_data["embeddings"].map { |e| e["values"] }.compact elsif @raw_data["embedding"].is_a?(Hash) && @raw_data["embedding"]["values"] [@raw_data["embedding"]["values"]] else [] end end |
#error ⇒ Object
Get error message if any
242 243 244 245 246 247 248 249 |
# File 'lib/gemini/response.rb', line 242 def error return nil if valid? # Return nil for empty responses (to display "Empty response" in to_s method) return nil if @raw_data.nil? || @raw_data.empty? @raw_data&.dig("error", "message") || "Unknown error" end |
#executable_code ⇒ Object
Get the first generated code string from a Code Execution response.
49 50 51 52 53 54 |
# File 'lib/gemini/response.rb', line 49 def executable_code code_part = executable_codes.first return nil unless code_part code_part["code"] || code_part[:code] end |
#executable_codes ⇒ Object
Get all executableCode parts returned by the Code Execution tool.
42 43 44 45 46 |
# File 'lib/gemini/response.rb', line 42 def executable_codes return [] unless valid? parts.map { |part| part["executableCode"] || part["executable_code"] }.compact end |
#finish_reason ⇒ Object
Get finish reason (STOP, SAFETY, etc.)
257 258 259 |
# File 'lib/gemini/response.rb', line 257 def finish_reason first_candidate&.dig("finishReason") end |
#first_candidate ⇒ Object
Get the first candidate
184 185 186 |
# File 'lib/gemini/response.rb', line 184 def first_candidate @raw_data&.dig("candidates", 0) end |
#first_thought_signature ⇒ Object
最初のThought Signatureを取得
425 426 427 |
# File 'lib/gemini/response.rb', line 425 def first_thought_signature thought_signatures.first end |
#formatted_text ⇒ Object
Get formatted text (HTML/markdown, etc.)
21 22 23 24 25 |
# File 'lib/gemini/response.rb', line 21 def formatted_text return nil unless valid? text # Currently returns plain text, but could add formatting in the future end |
#full_content ⇒ Object
Get all content with string representation
171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/gemini/response.rb', line 171 def full_content parts.map do |part| if part.key?("text") part["text"] elsif part.key?("inline_data") && part["inline_data"]["mime_type"].start_with?("image/") "[IMAGE: #{part["inline_data"]["mime_type"]}]" else "[UNKNOWN CONTENT]" end end.join("\n") end |
#function_calls ⇒ Object
Get function call information
397 398 399 400 |
# File 'lib/gemini/response.rb', line 397 def function_calls parts = first_candidate.dig("content", "parts") || [] parts.map { |part| part["functionCall"] }.compact end |
#gemini_3? ⇒ Boolean
Gemini 3系かどうか
440 441 442 |
# File 'lib/gemini/response.rb', line 440 def gemini_3? model_version&.start_with?('gemini-3') || false end |
#grounded? ⇒ Boolean
Check if response has grounding metadata
272 273 274 |
# File 'lib/gemini/response.rb', line 272 def grounded? !.nil? && !.empty? end |
#grounding_chunks ⇒ Object
Get grounding chunks (source references)
277 278 279 |
# File 'lib/gemini/response.rb', line 277 def grounding_chunks &.dig("groundingChunks") || [] end |
#grounding_metadata ⇒ Object
Get grounding metadata (for Google Search grounding)
267 268 269 |
# File 'lib/gemini/response.rb', line 267 def first_candidate&.dig("groundingMetadata") end |
#grounding_sources ⇒ Object
Get formatted grounding sources (simplified access)
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/gemini/response.rb', line 287 def grounding_sources return [] unless grounded? grounding_chunks.map do |chunk| if chunk["web"] { url: chunk["web"]["uri"], title: chunk["web"]["title"], type: "web" } else # Handle other potential chunk types { type: "unknown", data: chunk } end end end |
#has_thought_signature? ⇒ Boolean
Signatureが存在するか
430 431 432 |
# File 'lib/gemini/response.rb', line 430 def has_thought_signature? !thought_signatures.empty? end |
#image ⇒ Object
画像生成結果から最初の画像を取得(Base64エンコード形式)
458 459 460 |
# File 'lib/gemini/response.rb', line 458 def image images.first end |
#image_mime_types ⇒ Object
画像のMIMEタイプを取得
519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/gemini/response.rb', line 519 def image_mime_types return [] unless valid? if first_candidate&.dig("content", "parts") first_candidate["content"]["parts"] .select { |part| part.key?("inline_data") && part["inline_data"]["mime_type"].start_with?("image/") } .map { |part| part["inline_data"]["mime_type"] } else # Imagen 3のデフォルトはPNG Array.new(images.size, "image/png") end end |
#image_parts ⇒ Object
Get image parts (if any)
90 91 92 93 94 |
# File 'lib/gemini/response.rb', line 90 def image_parts return [] unless valid? parts.select { |part| part.key?("inline_data") && part["inline_data"]["mime_type"].start_with?("image/") } end |
#image_urls ⇒ Object
Get image URLs from multimodal responses (if any)
388 389 390 391 392 393 394 |
# File 'lib/gemini/response.rb', line 388 def image_urls return [] unless valid? first_candidate&.dig("content", "parts") &.select { |part| part.key?("image_url") } &.map { |part| part.dig("image_url", "url") } || [] end |
#images ⇒ Object
画像生成結果からすべての画像を取得(Base64エンコード形式の配列)
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'lib/gemini/response.rb', line 463 def images image_array = [] return image_array unless @raw_data # Gemini 2.0スタイルレスポンスを正確に解析 # キーはcamelCase形式で使用されているので注意(inlineDataなど) if @raw_data.key?('candidates') && !@raw_data['candidates'].empty? candidate = @raw_data['candidates'][0] if candidate.key?('content') && candidate['content'].key?('parts') parts = candidate['content']['parts'] parts.each do |part| # キャメルケースでアクセス(inlineData) if part.key?('inlineData') inline_data = part['inlineData'] if inline_data.key?('mimeType') && inline_data['mimeType'].to_s.start_with?('image/') && inline_data.key?('data') # 画像データを追加 image_array << inline_data['data'] puts "画像データを検出しました: #{inline_data['mimeType']}" if ENV["DEBUG"] end end end end # Imagen 3スタイルレスポンスのチェック elsif @raw_data.key?('predictions') @raw_data['predictions'].each do |pred| if pred.key?('bytesBase64Encoded') image_array << pred['bytesBase64Encoded'] puts "Imagen 3形式の画像データを検出しました" if ENV["DEBUG"] end end end # フォールバック:直接JSONから抽出 if image_array.empty? puts "標準的な方法で画像データが見つかりませんでした。正規表現による抽出を試みます..." if ENV["DEBUG"] raw_json = @raw_data.to_json # "data"キーで長いBase64文字列を検索 base64_matches = raw_json.scan(/"data":"([A-Za-z0-9+\/=]{100,})"/) if !base64_matches.empty? puts "検出したBase64データ: #{base64_matches.size}個" if ENV["DEBUG"] base64_matches.each do |match| image_array << match[0] end end end puts "検出した画像データ数: #{image_array.size}" if ENV["DEBUG"] image_array end |
#inspect ⇒ Object
Inspection method for debugging
600 601 602 |
# File 'lib/gemini/response.rb', line 600 def inspect "#<Gemini::Response text=#{text ? text[0..30] + (text.length > 30 ? '...' : '') : 'nil'} success=#{success?}>" end |
#json ⇒ Object
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/gemini/response.rb', line 604 def json return nil unless valid? text_content = text return nil unless text_content begin if text_content.strip.start_with?('{') || text_content.strip.start_with?('[') JSON.parse(text_content) else nil end rescue JSON::ParserError => e nil end end |
#json? ⇒ Boolean
621 622 623 |
# File 'lib/gemini/response.rb', line 621 def json? !json.nil? end |
#model_version ⇒ Object
モデルバージョンを取得
435 436 437 |
# File 'lib/gemini/response.rb', line 435 def model_version @raw_data['modelVersion'] end |
#parts ⇒ Object
Get all content parts
28 29 30 31 32 |
# File 'lib/gemini/response.rb', line 28 def parts return [] unless valid? first_candidate&.dig("content", "parts") || [] end |
#prompt_tokens ⇒ Object
Get number of prompt tokens used
344 345 346 |
# File 'lib/gemini/response.rb', line 344 def prompt_tokens usage&.dig("promptTokens") || 0 end |
#prompt_tokens_details ⇒ Object
Per-modality token breakdown reported by countTokens
376 377 378 |
# File 'lib/gemini/response.rb', line 376 def prompt_tokens_details @raw_data&.dig("promptTokensDetails") || [] end |
#retrieved_urls ⇒ Object
Get retrieved URLs from URL context
318 319 320 321 322 |
# File 'lib/gemini/response.rb', line 318 def retrieved_urls return [] unless url_context? &.dig("urlMetadata") || [] end |
#role ⇒ Object
Get response role (usually “model”)
403 404 405 |
# File 'lib/gemini/response.rb', line 403 def role first_candidate&.dig("content", "role") end |
#safety_blocked? ⇒ Boolean
Check if response was blocked for safety reasons
262 263 264 |
# File 'lib/gemini/response.rb', line 262 def safety_blocked? finish_reason == "SAFETY" end |
#safety_ratings ⇒ Object
Get safety ratings
408 409 410 |
# File 'lib/gemini/response.rb', line 408 def first_candidate&.dig("safetyRatings") || [] end |
#save_audio(filepath) ⇒ Object
Save audio to a file. PCM (L16) payloads are wrapped in a WAV header so the result is directly playable; other audio MIME types are written as-is. Returns the written file path or nil if no audio is present.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/gemini/response.rb', line 132 def save_audio(filepath) data_b64 = audio_data return nil unless data_b64 require 'base64' raw = Base64.strict_decode64(data_b64) mime = audio_mime_type.to_s if mime.include?("L16") || mime.include?("pcm") rate = mime[/rate=(\d+)/, 1]&.to_i || 24000 channels = 1 bits_per_sample = 16 byte_rate = rate * channels * bits_per_sample / 8 block_align = channels * bits_per_sample / 8 data_size = raw.bytesize header = +"" header << "RIFF" header << [36 + data_size].pack("V") header << "WAVE" header << "fmt " header << [16].pack("V") header << [1].pack("v") header << [channels].pack("v") header << [rate].pack("V") header << [byte_rate].pack("V") header << [block_align].pack("v") header << [bits_per_sample].pack("v") header << "data" header << [data_size].pack("V") File.binwrite(filepath, header + raw) else File.binwrite(filepath, raw) end filepath end |
#save_image(filepath) ⇒ Object
最初の画像をファイルに保存
533 534 535 |
# File 'lib/gemini/response.rb', line 533 def save_image(filepath) save_images([filepath]).first end |
#save_images(filepaths) ⇒ Object
複数の画像をファイルに保存
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/gemini/response.rb', line 538 def save_images(filepaths) require 'base64' result = [] image_data = images puts "保存する画像データ数: #{image_data.size}" if ENV["DEBUG"] # ファイルパスと画像データの数が一致しない場合 if filepaths.size < image_data.size puts "警告: ファイルパスの数(#{filepaths.size})が画像データの数(#{image_data.size})より少ないです" if ENV["DEBUG"] # ファイルパスの数に合わせて画像データを切り詰める image_data = image_data[0...filepaths.size] elsif filepaths.size > image_data.size puts "警告: ファイルパスの数(#{filepaths.size})が画像データの数(#{image_data.size})より多いです" if ENV["DEBUG"] # 画像データの数に合わせてファイルパスを切り詰める filepaths = filepaths[0...image_data.size] end image_data.each_with_index do |data, i| begin if !data || data.empty? puts "警告: インデックス #{i} の画像データが空です" if ENV["DEBUG"] result << nil next end # データがBase64エンコードされていることを確認 if data.match?(/^[A-Za-z0-9+\/=]+$/) # 一般的なBase64データ decoded_data = Base64.strict_decode64(data) else # データプレフィックスがある場合など(例: data:image/png;base64,xxxxx) if data.include?('base64,') base64_part = data.split('base64,').last decoded_data = Base64.strict_decode64(base64_part) else puts "警告: インデックス #{i} のデータはBase64形式ではありません" if ENV["DEBUG"] decoded_data = data # 既にバイナリかもしれない end end File.open(filepaths[i], 'wb') do |f| f.write(decoded_data) end result << filepaths[i] rescue => e puts "エラー: 画像 #{i} の保存中にエラーが発生しました: #{e.}" if ENV["DEBUG"] puts e.backtrace.join("\n") if ENV["DEBUG"] result << nil end end result end |
#search_entry_point ⇒ Object
Get search entry point URL (if available)
282 283 284 |
# File 'lib/gemini/response.rb', line 282 def search_entry_point &.dig("searchEntryPoint", "renderedContent") end |
#stream_chunks ⇒ Object
Process chunks for streaming responses
381 382 383 384 385 |
# File 'lib/gemini/response.rb', line 381 def stream_chunks return [] unless @raw_data.is_a?(Array) @raw_data end |
#success? ⇒ Boolean
Check if response was successful
252 253 254 |
# File 'lib/gemini/response.rb', line 252 def success? valid? && !@raw_data.key?("error") end |
#text ⇒ Object
Get simple text response (combines multiple parts if present)
11 12 13 14 15 16 17 18 |
# File 'lib/gemini/response.rb', line 11 def text return nil unless valid? first_candidate&.dig("content", "parts") &.select { |part| part.key?("text") } &.map { |part| part["text"] } &.join("\n") || "" end |
#text_parts ⇒ Object
Get all text parts as an array
35 36 37 38 39 |
# File 'lib/gemini/response.rb', line 35 def text_parts return [] unless valid? parts.select { |part| part.key?("text") }.map { |part| part["text"] } end |
#thought_signatures ⇒ Object
Thought Signatureを取得(配列)
420 421 422 |
# File 'lib/gemini/response.rb', line 420 def thought_signatures parts.filter_map { |p| p['thoughtSignature'] } end |
#thoughts_token_count ⇒ Object
思考トークン数を取得
415 416 417 |
# File 'lib/gemini/response.rb', line 415 def thoughts_token_count @raw_data.dig('usageMetadata', 'thoughtsTokenCount') end |
#to_formatted_json(pretty: false) ⇒ Object
690 691 692 693 694 695 696 697 698 699 |
# File 'lib/gemini/response.rb', line 690 def to_formatted_json(pretty: false) json_data = json return nil unless json_data if pretty JSON.pretty_generate(json_data) else JSON.generate(json_data) end end |
#to_s ⇒ Object
Override to_s method to return text
595 596 597 |
# File 'lib/gemini/response.rb', line 595 def to_s text || error || "Empty response" end |
#total_tokens ⇒ Object
Get total tokens used
354 355 356 |
# File 'lib/gemini/response.rb', line 354 def total_tokens usage&.dig("totalTokens") || 0 end |
#url_context? ⇒ Boolean
Check if response has URL context metadata
313 314 315 |
# File 'lib/gemini/response.rb', line 313 def url_context? !.nil? && !.empty? end |
#url_context_metadata ⇒ Object
Get URL context metadata (for URL Context tool)
308 309 310 |
# File 'lib/gemini/response.rb', line 308 def first_candidate&.dig("urlContextMetadata") end |
#url_retrieval_statuses ⇒ Object
Get URL retrieval statuses
325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/gemini/response.rb', line 325 def url_retrieval_statuses return [] unless url_context? retrieved_urls.map do |url_info| { url: url_info["retrievedUrl"], status: url_info["urlRetrievalStatus"], title: url_info["title"] } end end |
#usage ⇒ Object
Get token usage information
339 340 341 |
# File 'lib/gemini/response.rb', line 339 def usage @raw_data&.dig("usage") || {} end |
#valid? ⇒ Boolean
Check if response is valid
194 195 196 197 198 199 200 |
# File 'lib/gemini/response.rb', line 194 def valid? !@raw_data.nil? && ((@raw_data.key?("candidates") && !@raw_data["candidates"].empty?) || (@raw_data.key?("predictions") && !@raw_data["predictions"].empty?) || || count_tokens_response?) end |