geminai 0.1.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 +25 -9
- data/lib/geminai/client.rb +27 -28
- data/lib/geminai/interaction.rb +84 -17
- 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
|
|
@@ -104,7 +98,29 @@ puts interaction_2.output_text
|
|
|
104
98
|
# => "Your favorite jihad is a butlerian jihad."
|
|
105
99
|
```
|
|
106
100
|
|
|
107
|
-
|
|
101
|
+
### 4. Video generation
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# Generate a video
|
|
105
|
+
interaction = client.generate_video(
|
|
106
|
+
"A beautiful sunset over a calm ocean.",
|
|
107
|
+
model: "gemini-omni-flash-preview"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if interaction.output_video
|
|
111
|
+
# Access base64 data
|
|
112
|
+
video_data = interaction.output_video[:data]
|
|
113
|
+
# Or access the URI if delivery: "uri" was used
|
|
114
|
+
video_uri = interaction.output_video[:uri]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Stateful video editing
|
|
118
|
+
edit_interaction = client.interact(
|
|
119
|
+
model: "gemini-omni-flash-preview",
|
|
120
|
+
input: "Make the sun more vibrant and red.",
|
|
121
|
+
previous_interaction_id: interaction.id
|
|
122
|
+
)
|
|
123
|
+
```
|
|
108
124
|
|
|
109
125
|
## Running Tests
|
|
110
126
|
|
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,20 +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
81
|
end
|
|
83
82
|
|
|
84
83
|
class ApiError < StandardError
|
data/lib/geminai/interaction.rb
CHANGED
|
@@ -26,29 +26,67 @@ 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
|
|
47
|
+
end
|
|
48
|
+
|
|
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] }
|
|
68
|
+
end
|
|
69
|
+
|
|
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" }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def output_video
|
|
85
|
+
output_videos.first
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def output_audio
|
|
89
|
+
files.select { |f| f[:type] == "audio" }.first
|
|
52
90
|
end
|
|
53
91
|
|
|
54
92
|
def grounding_metadata
|
|
@@ -93,5 +131,34 @@ module Geminai
|
|
|
93
131
|
search_suggestions: suggestions
|
|
94
132
|
)
|
|
95
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
|
|
96
163
|
end
|
|
97
164
|
end
|
data/lib/geminai/version.rb
CHANGED