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 +4 -4
- data/README.md +2 -8
- data/lib/geminai/client.rb +27 -41
- data/lib/geminai/interaction.rb +76 -39
- data/lib/geminai/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 182008113410d8953e5d9dbb32d0b3d31e400f0b64dc6de753bd2b0a0ecd0e3b
|
|
4
|
+
data.tar.gz: 5e4f6e4c304a109ec19e1bb00e98d7c60cd27b3f9c36d88fd51721aef9f424a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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
|
data/lib/geminai/client.rb
CHANGED
|
@@ -23,23 +23,36 @@ module Geminai
|
|
|
23
23
|
input: input
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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
|
-
|
|
42
|
-
|
|
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
|
data/lib/geminai/interaction.rb
CHANGED
|
@@ -26,61 +26,69 @@ module Geminai
|
|
|
26
26
|
text_parts.join("")
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
+
files
|
|
52
47
|
end
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
data/lib/geminai/version.rb
CHANGED