geminai 0.2.1 → 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: 79f9017e978b23a2c95b0916eb2bdd9b8d1657bede042832fbee9be77f48e1b7
4
- data.tar.gz: 06d690f6ad1264f10cbdf1cff433dc92a9decbf2c01ff020bed8f7c439dcf551
3
+ metadata.gz: 182008113410d8953e5d9dbb32d0b3d31e400f0b64dc6de753bd2b0a0ecd0e3b
4
+ data.tar.gz: 5e4f6e4c304a109ec19e1bb00e98d7c60cd27b3f9c36d88fd51721aef9f424a2
5
5
  SHA512:
6
- metadata.gz: bbb724f49b0a32ca075c686c6b72cd8316f093e5e89c745c5cf803fc5fb91140e840e0e914529ab1b3d1f8d1f74fbde5956e67e1122b2d9264a8a8bb4897f623
7
- data.tar.gz: 2bf82c43f4546d7c2eac35b0e783dabfa3247cd5ec8827af4391ededd2849b55a0c01203a542b133d3cc0edee47062743e955038f7255b901840cccbd2c1fc10
6
+ metadata.gz: f1c57d71925d784ff071a79f207b2f5fff3615774929b39a1dced79408c05dbb2bebbc561edc1a6b7b725d394bd9082c0590184ffe131b118d8d6c8edd02143d
7
+ data.tar.gz: bb3cf7288bfba9404ea343f75f931ba31b75154a17db76d435703d09acc989eaf7b586d39a8e22f7c5458ac2ebbcde3ad9144701e5a2074100366849b6001e29
data/README.md CHANGED
@@ -2,12 +2,6 @@
2
2
 
3
3
  A gem for gemini's interactions api
4
4
 
5
- * **Built-in Tools**: Native search grounding (`google_search`), mapping citations, and suggestions.
6
- * **Image Generation**: High-fidelity native image generation (`gemini-3.1-flash-image`) with direct config like `aspect_ratio` and `image_size`.
7
- * **Stateful Conversations**: Seamless multi-turn conversations managed server-side via `previous_interaction_id`.
8
- * **Stateless Conversations**: Client-managed conversation paths using `store: false`.
9
- * **Zero Dependencies**: Pure Ruby standard library (`net/http` and `json`)
10
-
11
5
  ## Install
12
6
 
13
7
  ```ruby
@@ -65,7 +59,7 @@ Geminai includes a helper method `generate_image` which calls the Interactions A
65
59
  ```ruby
66
60
  require 'base64'
67
61
 
68
- interaction = client.generate_image(
62
+ interaction = client.interact(
69
63
  "An image of a pelican riding a bicycle",
70
64
  model: 'gemini-3.1-flash-image',
71
65
  aspect_ratio: '1:1',
@@ -74,7 +68,7 @@ interaction = client.generate_image(
74
68
  )
75
69
 
76
70
  # Extract and save generated images
77
- interaction.output_images.each_with_index do |img, index|
71
+ interaction.output_files.each_with_index do |img, index|
78
72
  File.binwrite("pelican#{index}.png", Base64.decode64(img[:data]))
79
73
  puts "Saved pelican_#{index}.png"
80
74
  end
@@ -23,23 +23,36 @@ module Geminai
23
23
  input: input
24
24
  }
25
25
 
26
- # Forward all recognized interactions API request parameters
27
- [
28
- :system_instruction,
29
- :generation_config,
30
- :response_format,
31
- :tools,
32
- :previous_interaction_id,
33
- :stream,
34
- :store
35
- ].each do |opt|
36
- body[opt] = options[opt] if options.key?(opt)
26
+ # Infer output shortcut from model name if not explicitly provided
27
+ output = options.delete(:output)
28
+ unless output
29
+ if model.include?("-tts-") || model.include?("-audio")
30
+ output = :audio
31
+ elsif model.include?("-image")
32
+ output = :image
33
+ elsif model.include?("-video") || model.include?("-generate")
34
+ output = :video
35
+ end
37
36
  end
38
37
 
39
- # Handle any extra keys passed in options as well to be fully future-proof
38
+ # Handle output shortcut
39
+ if output
40
+ body[:response_format] = {type: output.to_s}
41
+ body[:response_format][:aspect_ratio] = options.delete(:aspect_ratio) if options.key?(:aspect_ratio)
42
+ body[:response_format][:image_size] = options.delete(:image_size) if options.key?(:image_size)
43
+ end
44
+
45
+ if (voice = options.delete(:voice))
46
+ body[:generation_config] = {speech_config: [{voice: voice}]}
47
+ end
48
+
49
+ # Merge other options, allowing them to override shortcuts if explicitly provided
40
50
  options.each do |k, v|
41
- next if body.key?(k)
42
- body[k] = v
51
+ if body[k].is_a?(Hash) && v.is_a?(Hash)
52
+ body[k] = body[k].merge(v)
53
+ else
54
+ body[k] = v
55
+ end
43
56
  end
44
57
 
45
58
  http = Net::HTTP.new(uri.host, uri.port)
@@ -65,33 +78,6 @@ module Geminai
65
78
  data = JSON.parse(response.body, symbolize_names: true)
66
79
  Interaction.new(data)
67
80
  end
68
-
69
- # Helper method for image generation using response_format
70
- def generate_image(prompt, model:, aspect_ratio: "1:1", image_size: "1K", **options)
71
- interact(
72
- model: model,
73
- input: prompt,
74
- response_format: {
75
- type: "image",
76
- aspect_ratio: aspect_ratio,
77
- image_size: image_size
78
- },
79
- **options
80
- )
81
- end
82
-
83
- # Helper method for video generation using response_format
84
- def generate_video(prompt, model:, delivery: "data", **options)
85
- interact(
86
- model: model,
87
- input: prompt,
88
- response_format: {
89
- type: "video",
90
- delivery: delivery
91
- },
92
- **options
93
- )
94
- end
95
81
  end
96
82
 
97
83
  class ApiError < StandardError
@@ -26,61 +26,69 @@ module Geminai
26
26
  text_parts.join("")
27
27
  end
28
28
 
29
- def output_images
30
- images = []
29
+ def files
30
+ files = []
31
31
  @steps.each do |step|
32
32
  next unless step.model_output?
33
33
  (step.content || []).each do |part|
34
- if part[:type] == "image"
35
- images <<
36
- {
37
- data: part[:data],
38
- mime_type: part[:mime_type]
39
- }
40
- elsif part[:image]
41
- # Handle other possible image shapes in responses if applicable
42
- images <<
43
- {
44
- data: part[:image][:data] || part[:image][:image_bytes],
45
- mime_type: part[:image][:mime_type]
46
- }
47
- end
34
+ file_data = extract_file_data(part)
35
+ files << file_data if file_data
36
+ end
37
+ end
38
+
39
+ # Also include top-level outputs (like output_audio) if present in raw data
40
+ @raw_data.each do |key, value|
41
+ if key.to_s.start_with?("output_") && value.is_a?(Hash) && (value[:data] || value[:uri])
42
+ files << value.merge(type: key.to_s.sub("output_", ""))
48
43
  end
49
44
  end
50
45
 
51
- images
46
+ files
52
47
  end
53
48
 
54
- def output_videos
55
- videos = []
56
- @steps.each do |step|
57
- next unless step.model_output?
58
- (step.content || []).each do |part|
59
- if part[:type] == "video"
60
- videos <<
61
- {
62
- data: part[:data],
63
- uri: part[:uri],
64
- mime_type: part[:mime_type]
65
- }
66
- elsif part[:video]
67
- videos <<
68
- {
69
- data: part[:video][:data] || part[:video][:video_bytes],
70
- uri: part[:video][:uri],
71
- mime_type: part[:video][:mime_type]
72
- }
73
- end
74
- end
49
+ alias_method :output_files, :files
50
+
51
+ def base64(type = nil)
52
+ file = if type
53
+ files.find { |f| f[:type] == type.to_s && f[:data] }
54
+ else
55
+ files.find { |f| f[:data] }
56
+ end
57
+
58
+ file ? file[:data] : nil
59
+ end
60
+
61
+ alias_method :base64_file, :base64
62
+
63
+ def uri(type = nil)
64
+ file = if type
65
+ files.find { |f| f[:type] == type.to_s && f[:uri] }
66
+ else
67
+ files.find { |f| f[:uri] }
75
68
  end
76
69
 
77
- videos
70
+ file ? file[:uri] : nil
71
+ end
72
+
73
+ alias_method :uri_file, :uri
74
+
75
+ # Deprecated specific helpers kept for compatibility but powered by generic methods
76
+ def output_images
77
+ files.select { |f| f[:type] == "image" }
78
+ end
79
+
80
+ def output_videos
81
+ files.select { |f| f[:type] == "video" }
78
82
  end
79
83
 
80
84
  def output_video
81
85
  output_videos.first
82
86
  end
83
87
 
88
+ def output_audio
89
+ files.select { |f| f[:type] == "audio" }.first
90
+ end
91
+
84
92
  def grounding_metadata
85
93
  queries = []
86
94
  citations = []
@@ -123,5 +131,34 @@ module Geminai
123
131
  search_suggestions: suggestions
124
132
  )
125
133
  end
134
+
135
+ private
136
+
137
+ def extract_file_data(part)
138
+ # Check part[:type]
139
+ if %w[image video audio file].include?(part[:type])
140
+ return {
141
+ type: part[:type],
142
+ data: part[:data],
143
+ uri: part[:uri],
144
+ mime_type: part[:mime_type]
145
+ }
146
+ end
147
+
148
+ # Check for keys like :image, :video, :audio
149
+ [:image, :video, :audio].each do |key|
150
+ if part[key]
151
+ data = part[key]
152
+ return {
153
+ type: key.to_s,
154
+ data: data[:data] || data[:image_bytes] || data[:video_bytes] || data[:audio_bytes],
155
+ uri: data[:uri],
156
+ mime_type: data[:mime_type]
157
+ }
158
+ end
159
+ end
160
+
161
+ nil
162
+ end
126
163
  end
127
164
  end
@@ -1,3 +1,3 @@
1
1
  module Geminai
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geminai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - swlkr