gemini-ai 2.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +2 -2
- data/README.md +280 -21
- data/components/errors.rb +26 -0
- data/controllers/client.rb +25 -6
- data/ports/dsl/gemini-ai/errors.rb +5 -0
- data/static/gem.rb +2 -2
- data/template.md +267 -17
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7c5bd6e6cd1d2195b7a437fb0664cd553dc0acbf0d293042d93c3d701e6b6e0
|
4
|
+
data.tar.gz: 665458cc152b00efae9f8e2b730fe000ef2caac63f835944c1e76a83b9a0e627
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ca1f3f87c61276902259d937f4d57f324a756d1e63c1e5781680ba970313f3c3c29a2da49c4eb1ec0bf351f984bee378e86b22d1656b2423638e1a70bd5dddf
|
7
|
+
data.tar.gz: 50988d0881d37f561e0c75ac1beea4fc9b34c757d0f16fb2dcb2f63d3e63f867194e0f826800c8429ce9f33500dd74c3fa4c5d747a0975378c81cf3e0ad4a61b
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gemini-ai (2.
|
4
|
+
gemini-ai (2.2.0)
|
5
5
|
event_stream_parser (~> 1.0)
|
6
6
|
faraday (~> 2.7, >= 2.7.12)
|
7
7
|
googleauth (~> 1.9, >= 1.9.1)
|
@@ -36,7 +36,7 @@ GEM
|
|
36
36
|
method_source (1.0.0)
|
37
37
|
multi_json (1.15.0)
|
38
38
|
os (1.1.4)
|
39
|
-
parallel (1.
|
39
|
+
parallel (1.24.0)
|
40
40
|
parser (3.2.2.4)
|
41
41
|
ast (~> 2.4.1)
|
42
42
|
racc
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Gemini AI
|
2
2
|
|
3
|
-
A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/gemini/) through [Vertex AI](https://cloud.google.com/vertex-ai) or [AI Studio](https://makersuite.google.com), Google's generative AI services.
|
3
|
+
A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/gemini/) through [Vertex AI](https://cloud.google.com/vertex-ai), [Generative Language API](https://ai.google.dev/api/rest), or [AI Studio](https://makersuite.google.com), Google's generative AI services.
|
4
4
|
|
5
5
|
![The logo shows a gemstone split into red and blue halves, symbolizing Ruby programming and Gemini AI. It's surrounded by a circuit-like design on a dark blue backdrop.](https://raw.githubusercontent.com/gbaptista/assets/main/gemini-ai/ruby-gemini-ai.png)
|
6
6
|
|
@@ -9,7 +9,7 @@ A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/ge
|
|
9
9
|
## TL;DR and Quick Start
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
gem 'gemini-ai', '~> 2.0'
|
12
|
+
gem 'gemini-ai', '~> 2.2.0'
|
13
13
|
```
|
14
14
|
|
15
15
|
```ruby
|
@@ -24,12 +24,20 @@ client = Gemini.new(
|
|
24
24
|
options: { model: 'gemini-pro', stream: false }
|
25
25
|
)
|
26
26
|
|
27
|
-
# With a Service Account
|
27
|
+
# With a Service Account Credentials File
|
28
28
|
client = Gemini.new(
|
29
29
|
credentials: {
|
30
30
|
service: 'vertex-ai-api',
|
31
31
|
file_path: 'google-credentials.json',
|
32
|
-
|
32
|
+
region: 'us-east4'
|
33
|
+
},
|
34
|
+
options: { model: 'gemini-pro', stream: false }
|
35
|
+
)
|
36
|
+
|
37
|
+
# With Application Default Credentials
|
38
|
+
client = Gemini.new(
|
39
|
+
credentials: {
|
40
|
+
service: 'vertex-ai-api',
|
33
41
|
region: 'us-east4'
|
34
42
|
},
|
35
43
|
options: { model: 'gemini-pro', stream: false }
|
@@ -67,18 +75,27 @@ Result:
|
|
67
75
|
- [Setup](#setup)
|
68
76
|
- [Installing](#installing)
|
69
77
|
- [Credentials](#credentials)
|
70
|
-
- [Option 1: API Key](#option-1-api-key)
|
71
|
-
- [Option 2: Service Account](#option-2-service-account)
|
78
|
+
- [Option 1: API Key (Generative Language API)](#option-1-api-key-generative-language-api)
|
79
|
+
- [Option 2: Service Account Credentials File (Vertex AI API)](#option-2-service-account-credentials-file-vertex-ai-api)
|
80
|
+
- [Option 3: Application Default Credentials (Vertex AI API)](#option-3-application-default-credentials-vertex-ai-api)
|
72
81
|
- [Required Data](#required-data)
|
73
82
|
- [Usage](#usage)
|
74
83
|
- [Client](#client)
|
75
84
|
- [Generate Content](#generate-content)
|
85
|
+
- [Modes](#modes)
|
86
|
+
- [Text](#text)
|
87
|
+
- [Image](#image)
|
88
|
+
- [Video](#video)
|
76
89
|
- [Synchronous](#synchronous)
|
77
90
|
- [Streaming](#streaming)
|
78
91
|
- [Streaming Hang](#streaming-hang)
|
79
|
-
|
80
|
-
|
92
|
+
- [Back-and-Forth Conversations](#back-and-forth-conversations)
|
93
|
+
- [Tools (Functions) Calling](#tools-functions-calling)
|
81
94
|
- [New Functionalities and APIs](#new-functionalities-and-apis)
|
95
|
+
- [Error Handling](#error-handling)
|
96
|
+
- [Rescuing](#rescuing)
|
97
|
+
- [For Short](#for-short)
|
98
|
+
- [Errors](#errors)
|
82
99
|
- [Development](#development)
|
83
100
|
- [Purpose](#purpose)
|
84
101
|
- [Publish to RubyGems](#publish-to-rubygems)
|
@@ -91,18 +108,23 @@ Result:
|
|
91
108
|
### Installing
|
92
109
|
|
93
110
|
```sh
|
94
|
-
gem install gemini-ai -v 2.
|
111
|
+
gem install gemini-ai -v 2.2.0
|
95
112
|
```
|
96
113
|
|
97
114
|
```sh
|
98
|
-
gem 'gemini-ai', '~> 2.0'
|
115
|
+
gem 'gemini-ai', '~> 2.2.0'
|
99
116
|
```
|
100
117
|
|
101
118
|
### Credentials
|
102
119
|
|
120
|
+
- [Option 1: API Key (Generative Language API)](#option-1-api-key-generative-language-api)
|
121
|
+
- [Option 2: Service Account Credentials File (Vertex AI API)](#option-2-service-account-credentials-file-vertex-ai-api)
|
122
|
+
- [Option 3: Application Default Credentials (Vertex AI API)](#option-3-application-default-credentials-vertex-ai-api)
|
123
|
+
- [Required Data](#required-data)
|
124
|
+
|
103
125
|
> ⚠️ DISCLAIMER: Be careful with what you are doing, and never trust others' code related to this. These commands and instructions alter the level of access to your Google Cloud Account, and running them naively can lead to security risks as well as financial risks. People with access to your account can use it to steal data or incur charges. Run these commands at your own responsibility and due diligence; expect no warranties from the contributors of this project.
|
104
126
|
|
105
|
-
#### Option 1: API Key
|
127
|
+
#### Option 1: API Key (Generative Language API)
|
106
128
|
|
107
129
|
You need a [Google Cloud](https://console.cloud.google.com) [_Project_](https://cloud.google.com/resource-manager/docs/creating-managing-projects), and then you can generate an API Key through the Google Cloud Console [here](https://console.cloud.google.com/apis/credentials).
|
108
130
|
|
@@ -111,7 +133,7 @@ You also need to enable the _Generative Language API_ service in your Google Clo
|
|
111
133
|
|
112
134
|
Alternatively, you can generate an API Key through _Google AI Studio_ [here](https://makersuite.google.com/app/apikey). However, this approach will automatically create a project for you in your Google Cloud Account.
|
113
135
|
|
114
|
-
#### Option 2: Service Account
|
136
|
+
#### Option 2: Service Account Credentials File (Vertex AI API)
|
115
137
|
|
116
138
|
You need a [Google Cloud](https://console.cloud.google.com) [_Project_](https://cloud.google.com/resource-manager/docs/creating-managing-projects) and a [_Service Account_](https://cloud.google.com/iam/docs/service-account-overview) to use [Vertex AI](https://cloud.google.com/vertex-ai) API.
|
117
139
|
|
@@ -165,11 +187,24 @@ gcloud projects add-iam-policy-binding PROJECT_ID \
|
|
165
187
|
--role='roles/ml.admin'
|
166
188
|
```
|
167
189
|
|
190
|
+
#### Option 3: Application Default Credentials (Vertex AI API)
|
191
|
+
|
192
|
+
Similar to [Option 2](#option-2-service-account-credentials-file-vertex-ai-api), but you don't need to download a `google-credentials.json`. [_Application Default Credentials_](https://cloud.google.com/docs/authentication/application-default-credentials) automatically find credentials based on the application environment.
|
193
|
+
|
194
|
+
For local development, you can generate your default credentials using the [gcloud CLI](https://cloud.google.com/sdk/gcloud) as follows:
|
195
|
+
|
196
|
+
```sh
|
197
|
+
gcloud auth application-default login
|
198
|
+
```
|
199
|
+
|
200
|
+
For more details about alternative methods and different environments, check the official documentation:
|
201
|
+
[Set up Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc)
|
202
|
+
|
168
203
|
#### Required Data
|
169
204
|
|
170
205
|
After choosing an option, you should have all the necessary data and access to use Gemini.
|
171
206
|
|
172
|
-
|
207
|
+
**Option 1**, for API Key:
|
173
208
|
|
174
209
|
```ruby
|
175
210
|
{
|
@@ -187,13 +222,21 @@ Remember that hardcoding your API key in code is unsafe; it's preferable to use
|
|
187
222
|
}
|
188
223
|
```
|
189
224
|
|
190
|
-
|
225
|
+
**Option 2**: For the Service Account, provide a `google-credentials.json` file and a `REGION`:
|
191
226
|
|
192
227
|
```ruby
|
193
228
|
{
|
194
229
|
service: 'vertex-ai-api',
|
195
230
|
file_path: 'google-credentials.json',
|
196
|
-
|
231
|
+
region: 'us-east4'
|
232
|
+
}
|
233
|
+
```
|
234
|
+
|
235
|
+
**Option 3**: For _Application Default Credentials_, omit both the `api_key` and the `file_path`:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
{
|
239
|
+
service: 'vertex-ai-api',
|
197
240
|
region: 'us-east4'
|
198
241
|
}
|
199
242
|
```
|
@@ -212,6 +255,15 @@ Tokyo, Japan (asia-northeast1)
|
|
212
255
|
|
213
256
|
You can follow here if new regions are available: [Gemini API](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini)
|
214
257
|
|
258
|
+
You might want to explicitly set a Google Cloud Project ID, which you can do as follows:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
{
|
262
|
+
service: 'vertex-ai-api',
|
263
|
+
project_id: 'PROJECT_ID'
|
264
|
+
}
|
265
|
+
```
|
266
|
+
|
215
267
|
## Usage
|
216
268
|
|
217
269
|
### Client
|
@@ -230,12 +282,20 @@ client = Gemini.new(
|
|
230
282
|
options: { model: 'gemini-pro', stream: false }
|
231
283
|
)
|
232
284
|
|
233
|
-
# With a Service Account
|
285
|
+
# With a Service Account Credentials File
|
234
286
|
client = Gemini.new(
|
235
287
|
credentials: {
|
236
288
|
service: 'vertex-ai-api',
|
237
289
|
file_path: 'google-credentials.json',
|
238
|
-
|
290
|
+
region: 'us-east4'
|
291
|
+
},
|
292
|
+
options: { model: 'gemini-pro', stream: false }
|
293
|
+
)
|
294
|
+
|
295
|
+
# With Application Default Credentials
|
296
|
+
client = Gemini.new(
|
297
|
+
credentials: {
|
298
|
+
service: 'vertex-ai-api',
|
239
299
|
region: 'us-east4'
|
240
300
|
},
|
241
301
|
options: { model: 'gemini-pro', stream: false }
|
@@ -244,6 +304,153 @@ client = Gemini.new(
|
|
244
304
|
|
245
305
|
### Generate Content
|
246
306
|
|
307
|
+
#### Modes
|
308
|
+
|
309
|
+
##### Text
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
result = client.stream_generate_content({
|
313
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
314
|
+
})
|
315
|
+
```
|
316
|
+
|
317
|
+
Result:
|
318
|
+
```ruby
|
319
|
+
[{ 'candidates' =>
|
320
|
+
[{ 'content' => {
|
321
|
+
'role' => 'model',
|
322
|
+
'parts' => [{ 'text' => 'Hello! How may I assist you?' }]
|
323
|
+
},
|
324
|
+
'finishReason' => 'STOP',
|
325
|
+
'safetyRatings' =>
|
326
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
327
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
328
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
329
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }],
|
330
|
+
'usageMetadata' => {
|
331
|
+
'promptTokenCount' => 2,
|
332
|
+
'candidatesTokenCount' => 8,
|
333
|
+
'totalTokenCount' => 10
|
334
|
+
} }]
|
335
|
+
```
|
336
|
+
|
337
|
+
##### Image
|
338
|
+
|
339
|
+
![A black and white image of an old piano. The piano is an upright model, with the keys on the right side of the image. The piano is sitting on a tiled floor. There is a small round object on the top of the piano.](https://raw.githubusercontent.com/gbaptista/assets/main/gemini-ai/piano.jpg)
|
340
|
+
|
341
|
+
> _Courtesy of [Unsplash](https://unsplash.com/photos/greyscale-photo-of-grand-piano-czPs0z3-Ggg)_
|
342
|
+
|
343
|
+
Switch to the `gemini-pro-vision` model:
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
client = Gemini.new(
|
347
|
+
credentials: { service: 'vertex-ai-api', region: 'us-east4' },
|
348
|
+
options: { model: 'gemini-pro-vision', stream: true }
|
349
|
+
)
|
350
|
+
```
|
351
|
+
|
352
|
+
Then, encode the image as [Base64](https://en.wikipedia.org/wiki/Base64) and add its [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types):
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
require 'base64'
|
356
|
+
|
357
|
+
result = client.stream_generate_content(
|
358
|
+
{ contents: [
|
359
|
+
{ role: 'user', parts: [
|
360
|
+
{ text: 'Please describe this image.' },
|
361
|
+
{ inline_data: {
|
362
|
+
mime_type: 'image/jpeg',
|
363
|
+
data: Base64.strict_encode64(File.read('piano.jpg'))
|
364
|
+
} }
|
365
|
+
] }
|
366
|
+
] }
|
367
|
+
)
|
368
|
+
```
|
369
|
+
|
370
|
+
The result:
|
371
|
+
```ruby
|
372
|
+
[{ 'candidates' =>
|
373
|
+
[{ 'content' =>
|
374
|
+
{ 'role' => 'model',
|
375
|
+
'parts' =>
|
376
|
+
[{ 'text' =>
|
377
|
+
' A black and white image of an old piano. The piano is an upright model, with the keys on the right side of the image. The piano is' }] },
|
378
|
+
'safetyRatings' =>
|
379
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
380
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
381
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
382
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }] },
|
383
|
+
{ 'candidates' =>
|
384
|
+
[{ 'content' => { 'role' => 'model', 'parts' => [{ 'text' => ' sitting on a tiled floor. There is a small round object on the top of the piano.' }] },
|
385
|
+
'finishReason' => 'STOP',
|
386
|
+
'safetyRatings' =>
|
387
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
388
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
389
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
390
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }],
|
391
|
+
'usageMetadata' => { 'promptTokenCount' => 263, 'candidatesTokenCount' => 50, 'totalTokenCount' => 313 } }]
|
392
|
+
```
|
393
|
+
|
394
|
+
##### Video
|
395
|
+
|
396
|
+
https://gist.github.com/assets/29520/f82bccbf-02d2-4899-9c48-eb8a0a5ef741
|
397
|
+
|
398
|
+
> ALT: A white and gold cup is being filled with coffee. The coffee is dark and rich. The cup is sitting on a black surface. The background is blurred.
|
399
|
+
|
400
|
+
> _Courtesy of [Pexels](https://www.pexels.com/video/pouring-of-coffee-855391/)_
|
401
|
+
|
402
|
+
Switch to the `gemini-pro-vision` model:
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
client = Gemini.new(
|
406
|
+
credentials: { service: 'vertex-ai-api', region: 'us-east4' },
|
407
|
+
options: { model: 'gemini-pro-vision', stream: true }
|
408
|
+
)
|
409
|
+
```
|
410
|
+
|
411
|
+
Then, encode the video as [Base64](https://en.wikipedia.org/wiki/Base64) and add its [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types):
|
412
|
+
|
413
|
+
```ruby
|
414
|
+
require 'base64'
|
415
|
+
|
416
|
+
result = client.stream_generate_content(
|
417
|
+
{ contents: [
|
418
|
+
{ role: 'user', parts: [
|
419
|
+
{ text: 'Please describe this video.' },
|
420
|
+
{ inline_data: {
|
421
|
+
mime_type: 'video/mp4',
|
422
|
+
data: Base64.strict_encode64(File.read('coffee.mp4'))
|
423
|
+
} }
|
424
|
+
] }
|
425
|
+
] }
|
426
|
+
)
|
427
|
+
```
|
428
|
+
|
429
|
+
The result:
|
430
|
+
```ruby
|
431
|
+
[{"candidates"=>
|
432
|
+
[{"content"=>
|
433
|
+
{"role"=>"model",
|
434
|
+
"parts"=>
|
435
|
+
[{"text"=>
|
436
|
+
" A white and gold cup is being filled with coffee. The coffee is dark and rich. The cup is sitting on a black surface. The background is blurred"}]},
|
437
|
+
"safetyRatings"=>
|
438
|
+
[{"category"=>"HARM_CATEGORY_HARASSMENT", "probability"=>"NEGLIGIBLE"},
|
439
|
+
{"category"=>"HARM_CATEGORY_HATE_SPEECH", "probability"=>"NEGLIGIBLE"},
|
440
|
+
{"category"=>"HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability"=>"NEGLIGIBLE"},
|
441
|
+
{"category"=>"HARM_CATEGORY_DANGEROUS_CONTENT", "probability"=>"NEGLIGIBLE"}]}],
|
442
|
+
"usageMetadata"=>{"promptTokenCount"=>1037, "candidatesTokenCount"=>31, "totalTokenCount"=>1068}},
|
443
|
+
{"candidates"=>
|
444
|
+
[{"content"=>{"role"=>"model", "parts"=>[{"text"=>"."}]},
|
445
|
+
"finishReason"=>"STOP",
|
446
|
+
"safetyRatings"=>
|
447
|
+
[{"category"=>"HARM_CATEGORY_HARASSMENT", "probability"=>"NEGLIGIBLE"},
|
448
|
+
{"category"=>"HARM_CATEGORY_HATE_SPEECH", "probability"=>"NEGLIGIBLE"},
|
449
|
+
{"category"=>"HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability"=>"NEGLIGIBLE"},
|
450
|
+
{"category"=>"HARM_CATEGORY_DANGEROUS_CONTENT", "probability"=>"NEGLIGIBLE"}]}],
|
451
|
+
"usageMetadata"=>{"promptTokenCount"=>1037, "candidatesTokenCount"=>32, "totalTokenCount"=>1069}}]
|
452
|
+
```
|
453
|
+
|
247
454
|
#### Synchronous
|
248
455
|
|
249
456
|
```ruby
|
@@ -351,7 +558,7 @@ Result:
|
|
351
558
|
} }]
|
352
559
|
```
|
353
560
|
|
354
|
-
|
561
|
+
#### Back-and-Forth Conversations
|
355
562
|
|
356
563
|
To maintain a back-and-forth conversation, you need to append the received responses and build a history for your requests:
|
357
564
|
|
@@ -393,9 +600,9 @@ Result:
|
|
393
600
|
} }]
|
394
601
|
```
|
395
602
|
|
396
|
-
|
603
|
+
#### Tools (Functions) Calling
|
397
604
|
|
398
|
-
> As of the writing of this README, only the `gemini-pro` model [supports](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models) tools (functions) calls.
|
605
|
+
> As of the writing of this README, only the `vertex-ai-api` service and the `gemini-pro` model [supports](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models) tools (functions) calls.
|
399
606
|
|
400
607
|
You can provide specifications for [tools (functions)](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling) using [JSON Schema](https://json-schema.org) to generate potential calls to them:
|
401
608
|
|
@@ -549,6 +756,58 @@ result = client.request(
|
|
549
756
|
)
|
550
757
|
```
|
551
758
|
|
759
|
+
### Error Handling
|
760
|
+
|
761
|
+
#### Rescuing
|
762
|
+
|
763
|
+
```ruby
|
764
|
+
require 'gemini-ai'
|
765
|
+
|
766
|
+
begin
|
767
|
+
client.stream_generate_content({
|
768
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
769
|
+
})
|
770
|
+
rescue Gemini::Errors::GeminiError => error
|
771
|
+
puts error.class # Gemini::Errors::RequestError
|
772
|
+
puts error.message # 'the server responded with status 500'
|
773
|
+
|
774
|
+
puts error.payload
|
775
|
+
# { contents: [{ role: 'user', parts: { text: 'hi!' } }],
|
776
|
+
# generationConfig: { candidateCount: 1 },
|
777
|
+
# ...
|
778
|
+
# }
|
779
|
+
|
780
|
+
puts error.request
|
781
|
+
# #<Faraday::ServerError response={:status=>500, :headers...
|
782
|
+
end
|
783
|
+
```
|
784
|
+
|
785
|
+
#### For Short
|
786
|
+
|
787
|
+
```ruby
|
788
|
+
require 'gemini-ai/errors'
|
789
|
+
|
790
|
+
begin
|
791
|
+
client.stream_generate_content({
|
792
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
793
|
+
})
|
794
|
+
rescue GeminiError => error
|
795
|
+
puts error.class # Gemini::Errors::RequestError
|
796
|
+
end
|
797
|
+
```
|
798
|
+
|
799
|
+
#### Errors
|
800
|
+
|
801
|
+
```ruby
|
802
|
+
GeminiError
|
803
|
+
|
804
|
+
MissingProjectIdError
|
805
|
+
UnsupportedServiceError
|
806
|
+
BlockWithoutStreamError
|
807
|
+
|
808
|
+
RequestError
|
809
|
+
```
|
810
|
+
|
552
811
|
## Development
|
553
812
|
|
554
813
|
```bash
|
@@ -567,7 +826,7 @@ gem build gemini-ai.gemspec
|
|
567
826
|
|
568
827
|
gem signin
|
569
828
|
|
570
|
-
gem push gemini-ai-2.
|
829
|
+
gem push gemini-ai-2.2.0.gem
|
571
830
|
```
|
572
831
|
|
573
832
|
### Updating the README
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gemini
|
4
|
+
module Errors
|
5
|
+
class GeminiError < StandardError
|
6
|
+
def initialize(message = nil)
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MissingProjectIdError < GeminiError; end
|
12
|
+
class UnsupportedServiceError < GeminiError; end
|
13
|
+
class BlockWithoutStreamError < GeminiError; end
|
14
|
+
|
15
|
+
class RequestError < GeminiError
|
16
|
+
attr_reader :request, :payload
|
17
|
+
|
18
|
+
def initialize(message = nil, request: nil, payload: nil)
|
19
|
+
@request = request
|
20
|
+
@payload = payload
|
21
|
+
|
22
|
+
super(message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/controllers/client.rb
CHANGED
@@ -5,6 +5,8 @@ require 'faraday'
|
|
5
5
|
require 'json'
|
6
6
|
require 'googleauth'
|
7
7
|
|
8
|
+
require_relative '../components/errors'
|
9
|
+
|
8
10
|
module Gemini
|
9
11
|
module Controllers
|
10
12
|
class Client
|
@@ -12,21 +14,34 @@ module Gemini
|
|
12
14
|
if config[:credentials][:api_key]
|
13
15
|
@authentication = :api_key
|
14
16
|
@api_key = config[:credentials][:api_key]
|
15
|
-
|
17
|
+
elsif config[:credentials][:file_path]
|
16
18
|
@authentication = :service_account
|
17
19
|
@authorizer = ::Google::Auth::ServiceAccountCredentials.make_creds(
|
18
20
|
json_key_io: File.open(config[:credentials][:file_path]),
|
19
21
|
scope: 'https://www.googleapis.com/auth/cloud-platform'
|
20
22
|
)
|
23
|
+
else
|
24
|
+
@authentication = :default_credentials
|
25
|
+
@authorizer = ::Google::Auth.get_application_default
|
26
|
+
end
|
27
|
+
|
28
|
+
if @authentication == :service_account || @authentication == :default_credentials
|
29
|
+
@project_id = if config[:credentials][:project_id].nil?
|
30
|
+
@authorizer.project_id || @authorizer.quota_project_id
|
31
|
+
else
|
32
|
+
config[:credentials][:project_id]
|
33
|
+
end
|
34
|
+
|
35
|
+
raise MissingProjectIdError, 'Could not determine project_id, which is required.' if @project_id.nil?
|
21
36
|
end
|
22
37
|
|
23
38
|
@address = case config[:credentials][:service]
|
24
39
|
when 'vertex-ai-api'
|
25
|
-
"https://#{config[:credentials][:region]}-aiplatform.googleapis.com/v1/projects/#{
|
40
|
+
"https://#{config[:credentials][:region]}-aiplatform.googleapis.com/v1/projects/#{@project_id}/locations/#{config[:credentials][:region]}/publishers/google/models/#{config[:options][:model]}"
|
26
41
|
when 'generative-language-api'
|
27
42
|
"https://generativelanguage.googleapis.com/v1/models/#{config[:options][:model]}"
|
28
43
|
else
|
29
|
-
raise
|
44
|
+
raise UnsupportedServiceError, "Unsupported service: #{config[:credentials][:service]}"
|
30
45
|
end
|
31
46
|
|
32
47
|
@stream = config[:options][:stream]
|
@@ -47,15 +62,17 @@ module Gemini
|
|
47
62
|
url += "?#{params.join('&')}" if params.size.positive?
|
48
63
|
|
49
64
|
if !callback.nil? && !stream_enabled
|
50
|
-
raise
|
65
|
+
raise BlockWithoutStreamError, 'You are trying to use a block without stream enabled.'
|
51
66
|
end
|
52
67
|
|
53
68
|
results = []
|
54
69
|
|
55
|
-
response = Faraday.new
|
70
|
+
response = Faraday.new do |faraday|
|
71
|
+
faraday.response :raise_error
|
72
|
+
end.post do |request|
|
56
73
|
request.url url
|
57
74
|
request.headers['Content-Type'] = 'application/json'
|
58
|
-
if @authentication == :service_account
|
75
|
+
if @authentication == :service_account || @authentication == :default_credentials
|
59
76
|
request.headers['Authorization'] = "Bearer #{@authorizer.fetch_access_token!['access_token']}"
|
60
77
|
end
|
61
78
|
|
@@ -93,6 +110,8 @@ module Gemini
|
|
93
110
|
return safe_parse_json(response.body) unless stream_enabled
|
94
111
|
|
95
112
|
results.map { |result| result[:event] }
|
113
|
+
rescue Faraday::ServerError => e
|
114
|
+
raise RequestError.new(e.message, request: e, payload:)
|
96
115
|
end
|
97
116
|
|
98
117
|
def safe_parse_json(raw)
|
data/static/gem.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
module Gemini
|
4
4
|
GEM = {
|
5
5
|
name: 'gemini-ai',
|
6
|
-
version: '2.
|
6
|
+
version: '2.2.0',
|
7
7
|
author: 'gbaptista',
|
8
8
|
summary: "Interact with Google's Gemini AI.",
|
9
|
-
description: "A Ruby Gem for interacting with Gemini through Vertex AI or AI Studio, Google's generative AI services.",
|
9
|
+
description: "A Ruby Gem for interacting with Gemini through Vertex AI, Generative Language API, or AI Studio, Google's generative AI services.",
|
10
10
|
github: 'https://github.com/gbaptista/gemini-ai',
|
11
11
|
gem_server: 'https://rubygems.org',
|
12
12
|
license: 'MIT',
|
data/template.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Gemini AI
|
2
2
|
|
3
|
-
A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/gemini/) through [Vertex AI](https://cloud.google.com/vertex-ai) or [AI Studio](https://makersuite.google.com), Google's generative AI services.
|
3
|
+
A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/gemini/) through [Vertex AI](https://cloud.google.com/vertex-ai), [Generative Language API](https://ai.google.dev/api/rest), or [AI Studio](https://makersuite.google.com), Google's generative AI services.
|
4
4
|
|
5
5
|
![The logo shows a gemstone split into red and blue halves, symbolizing Ruby programming and Gemini AI. It's surrounded by a circuit-like design on a dark blue backdrop.](https://raw.githubusercontent.com/gbaptista/assets/main/gemini-ai/ruby-gemini-ai.png)
|
6
6
|
|
@@ -9,7 +9,7 @@ A Ruby Gem for interacting with [Gemini](https://deepmind.google/technologies/ge
|
|
9
9
|
## TL;DR and Quick Start
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
gem 'gemini-ai', '~> 2.0'
|
12
|
+
gem 'gemini-ai', '~> 2.2.0'
|
13
13
|
```
|
14
14
|
|
15
15
|
```ruby
|
@@ -24,12 +24,20 @@ client = Gemini.new(
|
|
24
24
|
options: { model: 'gemini-pro', stream: false }
|
25
25
|
)
|
26
26
|
|
27
|
-
# With a Service Account
|
27
|
+
# With a Service Account Credentials File
|
28
28
|
client = Gemini.new(
|
29
29
|
credentials: {
|
30
30
|
service: 'vertex-ai-api',
|
31
31
|
file_path: 'google-credentials.json',
|
32
|
-
|
32
|
+
region: 'us-east4'
|
33
|
+
},
|
34
|
+
options: { model: 'gemini-pro', stream: false }
|
35
|
+
)
|
36
|
+
|
37
|
+
# With Application Default Credentials
|
38
|
+
client = Gemini.new(
|
39
|
+
credentials: {
|
40
|
+
service: 'vertex-ai-api',
|
33
41
|
region: 'us-east4'
|
34
42
|
},
|
35
43
|
options: { model: 'gemini-pro', stream: false }
|
@@ -69,18 +77,23 @@ Result:
|
|
69
77
|
### Installing
|
70
78
|
|
71
79
|
```sh
|
72
|
-
gem install gemini-ai -v 2.
|
80
|
+
gem install gemini-ai -v 2.2.0
|
73
81
|
```
|
74
82
|
|
75
83
|
```sh
|
76
|
-
gem 'gemini-ai', '~> 2.0'
|
84
|
+
gem 'gemini-ai', '~> 2.2.0'
|
77
85
|
```
|
78
86
|
|
79
87
|
### Credentials
|
80
88
|
|
89
|
+
- [Option 1: API Key (Generative Language API)](#option-1-api-key-generative-language-api)
|
90
|
+
- [Option 2: Service Account Credentials File (Vertex AI API)](#option-2-service-account-credentials-file-vertex-ai-api)
|
91
|
+
- [Option 3: Application Default Credentials (Vertex AI API)](#option-3-application-default-credentials-vertex-ai-api)
|
92
|
+
- [Required Data](#required-data)
|
93
|
+
|
81
94
|
> ⚠️ DISCLAIMER: Be careful with what you are doing, and never trust others' code related to this. These commands and instructions alter the level of access to your Google Cloud Account, and running them naively can lead to security risks as well as financial risks. People with access to your account can use it to steal data or incur charges. Run these commands at your own responsibility and due diligence; expect no warranties from the contributors of this project.
|
82
95
|
|
83
|
-
#### Option 1: API Key
|
96
|
+
#### Option 1: API Key (Generative Language API)
|
84
97
|
|
85
98
|
You need a [Google Cloud](https://console.cloud.google.com) [_Project_](https://cloud.google.com/resource-manager/docs/creating-managing-projects), and then you can generate an API Key through the Google Cloud Console [here](https://console.cloud.google.com/apis/credentials).
|
86
99
|
|
@@ -89,7 +102,7 @@ You also need to enable the _Generative Language API_ service in your Google Clo
|
|
89
102
|
|
90
103
|
Alternatively, you can generate an API Key through _Google AI Studio_ [here](https://makersuite.google.com/app/apikey). However, this approach will automatically create a project for you in your Google Cloud Account.
|
91
104
|
|
92
|
-
#### Option 2: Service Account
|
105
|
+
#### Option 2: Service Account Credentials File (Vertex AI API)
|
93
106
|
|
94
107
|
You need a [Google Cloud](https://console.cloud.google.com) [_Project_](https://cloud.google.com/resource-manager/docs/creating-managing-projects) and a [_Service Account_](https://cloud.google.com/iam/docs/service-account-overview) to use [Vertex AI](https://cloud.google.com/vertex-ai) API.
|
95
108
|
|
@@ -143,11 +156,24 @@ gcloud projects add-iam-policy-binding PROJECT_ID \
|
|
143
156
|
--role='roles/ml.admin'
|
144
157
|
```
|
145
158
|
|
159
|
+
#### Option 3: Application Default Credentials (Vertex AI API)
|
160
|
+
|
161
|
+
Similar to [Option 2](#option-2-service-account-credentials-file-vertex-ai-api), but you don't need to download a `google-credentials.json`. [_Application Default Credentials_](https://cloud.google.com/docs/authentication/application-default-credentials) automatically find credentials based on the application environment.
|
162
|
+
|
163
|
+
For local development, you can generate your default credentials using the [gcloud CLI](https://cloud.google.com/sdk/gcloud) as follows:
|
164
|
+
|
165
|
+
```sh
|
166
|
+
gcloud auth application-default login
|
167
|
+
```
|
168
|
+
|
169
|
+
For more details about alternative methods and different environments, check the official documentation:
|
170
|
+
[Set up Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc)
|
171
|
+
|
146
172
|
#### Required Data
|
147
173
|
|
148
174
|
After choosing an option, you should have all the necessary data and access to use Gemini.
|
149
175
|
|
150
|
-
|
176
|
+
**Option 1**, for API Key:
|
151
177
|
|
152
178
|
```ruby
|
153
179
|
{
|
@@ -165,13 +191,21 @@ Remember that hardcoding your API key in code is unsafe; it's preferable to use
|
|
165
191
|
}
|
166
192
|
```
|
167
193
|
|
168
|
-
|
194
|
+
**Option 2**: For the Service Account, provide a `google-credentials.json` file and a `REGION`:
|
169
195
|
|
170
196
|
```ruby
|
171
197
|
{
|
172
198
|
service: 'vertex-ai-api',
|
173
199
|
file_path: 'google-credentials.json',
|
174
|
-
|
200
|
+
region: 'us-east4'
|
201
|
+
}
|
202
|
+
```
|
203
|
+
|
204
|
+
**Option 3**: For _Application Default Credentials_, omit both the `api_key` and the `file_path`:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
{
|
208
|
+
service: 'vertex-ai-api',
|
175
209
|
region: 'us-east4'
|
176
210
|
}
|
177
211
|
```
|
@@ -190,6 +224,15 @@ Tokyo, Japan (asia-northeast1)
|
|
190
224
|
|
191
225
|
You can follow here if new regions are available: [Gemini API](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini)
|
192
226
|
|
227
|
+
You might want to explicitly set a Google Cloud Project ID, which you can do as follows:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
{
|
231
|
+
service: 'vertex-ai-api',
|
232
|
+
project_id: 'PROJECT_ID'
|
233
|
+
}
|
234
|
+
```
|
235
|
+
|
193
236
|
## Usage
|
194
237
|
|
195
238
|
### Client
|
@@ -208,12 +251,20 @@ client = Gemini.new(
|
|
208
251
|
options: { model: 'gemini-pro', stream: false }
|
209
252
|
)
|
210
253
|
|
211
|
-
# With a Service Account
|
254
|
+
# With a Service Account Credentials File
|
212
255
|
client = Gemini.new(
|
213
256
|
credentials: {
|
214
257
|
service: 'vertex-ai-api',
|
215
258
|
file_path: 'google-credentials.json',
|
216
|
-
|
259
|
+
region: 'us-east4'
|
260
|
+
},
|
261
|
+
options: { model: 'gemini-pro', stream: false }
|
262
|
+
)
|
263
|
+
|
264
|
+
# With Application Default Credentials
|
265
|
+
client = Gemini.new(
|
266
|
+
credentials: {
|
267
|
+
service: 'vertex-ai-api',
|
217
268
|
region: 'us-east4'
|
218
269
|
},
|
219
270
|
options: { model: 'gemini-pro', stream: false }
|
@@ -222,6 +273,153 @@ client = Gemini.new(
|
|
222
273
|
|
223
274
|
### Generate Content
|
224
275
|
|
276
|
+
#### Modes
|
277
|
+
|
278
|
+
##### Text
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
result = client.stream_generate_content({
|
282
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
283
|
+
})
|
284
|
+
```
|
285
|
+
|
286
|
+
Result:
|
287
|
+
```ruby
|
288
|
+
[{ 'candidates' =>
|
289
|
+
[{ 'content' => {
|
290
|
+
'role' => 'model',
|
291
|
+
'parts' => [{ 'text' => 'Hello! How may I assist you?' }]
|
292
|
+
},
|
293
|
+
'finishReason' => 'STOP',
|
294
|
+
'safetyRatings' =>
|
295
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
296
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
297
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
298
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }],
|
299
|
+
'usageMetadata' => {
|
300
|
+
'promptTokenCount' => 2,
|
301
|
+
'candidatesTokenCount' => 8,
|
302
|
+
'totalTokenCount' => 10
|
303
|
+
} }]
|
304
|
+
```
|
305
|
+
|
306
|
+
##### Image
|
307
|
+
|
308
|
+
![A black and white image of an old piano. The piano is an upright model, with the keys on the right side of the image. The piano is sitting on a tiled floor. There is a small round object on the top of the piano.](https://raw.githubusercontent.com/gbaptista/assets/main/gemini-ai/piano.jpg)
|
309
|
+
|
310
|
+
> _Courtesy of [Unsplash](https://unsplash.com/photos/greyscale-photo-of-grand-piano-czPs0z3-Ggg)_
|
311
|
+
|
312
|
+
Switch to the `gemini-pro-vision` model:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
client = Gemini.new(
|
316
|
+
credentials: { service: 'vertex-ai-api', region: 'us-east4' },
|
317
|
+
options: { model: 'gemini-pro-vision', stream: true }
|
318
|
+
)
|
319
|
+
```
|
320
|
+
|
321
|
+
Then, encode the image as [Base64](https://en.wikipedia.org/wiki/Base64) and add its [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types):
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
require 'base64'
|
325
|
+
|
326
|
+
result = client.stream_generate_content(
|
327
|
+
{ contents: [
|
328
|
+
{ role: 'user', parts: [
|
329
|
+
{ text: 'Please describe this image.' },
|
330
|
+
{ inline_data: {
|
331
|
+
mime_type: 'image/jpeg',
|
332
|
+
data: Base64.strict_encode64(File.read('piano.jpg'))
|
333
|
+
} }
|
334
|
+
] }
|
335
|
+
] }
|
336
|
+
)
|
337
|
+
```
|
338
|
+
|
339
|
+
The result:
|
340
|
+
```ruby
|
341
|
+
[{ 'candidates' =>
|
342
|
+
[{ 'content' =>
|
343
|
+
{ 'role' => 'model',
|
344
|
+
'parts' =>
|
345
|
+
[{ 'text' =>
|
346
|
+
' A black and white image of an old piano. The piano is an upright model, with the keys on the right side of the image. The piano is' }] },
|
347
|
+
'safetyRatings' =>
|
348
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
349
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
350
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
351
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }] },
|
352
|
+
{ 'candidates' =>
|
353
|
+
[{ 'content' => { 'role' => 'model', 'parts' => [{ 'text' => ' sitting on a tiled floor. There is a small round object on the top of the piano.' }] },
|
354
|
+
'finishReason' => 'STOP',
|
355
|
+
'safetyRatings' =>
|
356
|
+
[{ 'category' => 'HARM_CATEGORY_HARASSMENT', 'probability' => 'NEGLIGIBLE' },
|
357
|
+
{ 'category' => 'HARM_CATEGORY_HATE_SPEECH', 'probability' => 'NEGLIGIBLE' },
|
358
|
+
{ 'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability' => 'NEGLIGIBLE' },
|
359
|
+
{ 'category' => 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability' => 'NEGLIGIBLE' }] }],
|
360
|
+
'usageMetadata' => { 'promptTokenCount' => 263, 'candidatesTokenCount' => 50, 'totalTokenCount' => 313 } }]
|
361
|
+
```
|
362
|
+
|
363
|
+
##### Video
|
364
|
+
|
365
|
+
https://gist.github.com/assets/29520/f82bccbf-02d2-4899-9c48-eb8a0a5ef741
|
366
|
+
|
367
|
+
> ALT: A white and gold cup is being filled with coffee. The coffee is dark and rich. The cup is sitting on a black surface. The background is blurred.
|
368
|
+
|
369
|
+
> _Courtesy of [Pexels](https://www.pexels.com/video/pouring-of-coffee-855391/)_
|
370
|
+
|
371
|
+
Switch to the `gemini-pro-vision` model:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
client = Gemini.new(
|
375
|
+
credentials: { service: 'vertex-ai-api', region: 'us-east4' },
|
376
|
+
options: { model: 'gemini-pro-vision', stream: true }
|
377
|
+
)
|
378
|
+
```
|
379
|
+
|
380
|
+
Then, encode the video as [Base64](https://en.wikipedia.org/wiki/Base64) and add its [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types):
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
require 'base64'
|
384
|
+
|
385
|
+
result = client.stream_generate_content(
|
386
|
+
{ contents: [
|
387
|
+
{ role: 'user', parts: [
|
388
|
+
{ text: 'Please describe this video.' },
|
389
|
+
{ inline_data: {
|
390
|
+
mime_type: 'video/mp4',
|
391
|
+
data: Base64.strict_encode64(File.read('coffee.mp4'))
|
392
|
+
} }
|
393
|
+
] }
|
394
|
+
] }
|
395
|
+
)
|
396
|
+
```
|
397
|
+
|
398
|
+
The result:
|
399
|
+
```ruby
|
400
|
+
[{"candidates"=>
|
401
|
+
[{"content"=>
|
402
|
+
{"role"=>"model",
|
403
|
+
"parts"=>
|
404
|
+
[{"text"=>
|
405
|
+
" A white and gold cup is being filled with coffee. The coffee is dark and rich. The cup is sitting on a black surface. The background is blurred"}]},
|
406
|
+
"safetyRatings"=>
|
407
|
+
[{"category"=>"HARM_CATEGORY_HARASSMENT", "probability"=>"NEGLIGIBLE"},
|
408
|
+
{"category"=>"HARM_CATEGORY_HATE_SPEECH", "probability"=>"NEGLIGIBLE"},
|
409
|
+
{"category"=>"HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability"=>"NEGLIGIBLE"},
|
410
|
+
{"category"=>"HARM_CATEGORY_DANGEROUS_CONTENT", "probability"=>"NEGLIGIBLE"}]}],
|
411
|
+
"usageMetadata"=>{"promptTokenCount"=>1037, "candidatesTokenCount"=>31, "totalTokenCount"=>1068}},
|
412
|
+
{"candidates"=>
|
413
|
+
[{"content"=>{"role"=>"model", "parts"=>[{"text"=>"."}]},
|
414
|
+
"finishReason"=>"STOP",
|
415
|
+
"safetyRatings"=>
|
416
|
+
[{"category"=>"HARM_CATEGORY_HARASSMENT", "probability"=>"NEGLIGIBLE"},
|
417
|
+
{"category"=>"HARM_CATEGORY_HATE_SPEECH", "probability"=>"NEGLIGIBLE"},
|
418
|
+
{"category"=>"HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability"=>"NEGLIGIBLE"},
|
419
|
+
{"category"=>"HARM_CATEGORY_DANGEROUS_CONTENT", "probability"=>"NEGLIGIBLE"}]}],
|
420
|
+
"usageMetadata"=>{"promptTokenCount"=>1037, "candidatesTokenCount"=>32, "totalTokenCount"=>1069}}]
|
421
|
+
```
|
422
|
+
|
225
423
|
#### Synchronous
|
226
424
|
|
227
425
|
```ruby
|
@@ -329,7 +527,7 @@ Result:
|
|
329
527
|
} }]
|
330
528
|
```
|
331
529
|
|
332
|
-
|
530
|
+
#### Back-and-Forth Conversations
|
333
531
|
|
334
532
|
To maintain a back-and-forth conversation, you need to append the received responses and build a history for your requests:
|
335
533
|
|
@@ -371,9 +569,9 @@ Result:
|
|
371
569
|
} }]
|
372
570
|
```
|
373
571
|
|
374
|
-
|
572
|
+
#### Tools (Functions) Calling
|
375
573
|
|
376
|
-
> As of the writing of this README, only the `gemini-pro` model [supports](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models) tools (functions) calls.
|
574
|
+
> As of the writing of this README, only the `vertex-ai-api` service and the `gemini-pro` model [supports](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models) tools (functions) calls.
|
377
575
|
|
378
576
|
You can provide specifications for [tools (functions)](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling) using [JSON Schema](https://json-schema.org) to generate potential calls to them:
|
379
577
|
|
@@ -527,6 +725,58 @@ result = client.request(
|
|
527
725
|
)
|
528
726
|
```
|
529
727
|
|
728
|
+
### Error Handling
|
729
|
+
|
730
|
+
#### Rescuing
|
731
|
+
|
732
|
+
```ruby
|
733
|
+
require 'gemini-ai'
|
734
|
+
|
735
|
+
begin
|
736
|
+
client.stream_generate_content({
|
737
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
738
|
+
})
|
739
|
+
rescue Gemini::Errors::GeminiError => error
|
740
|
+
puts error.class # Gemini::Errors::RequestError
|
741
|
+
puts error.message # 'the server responded with status 500'
|
742
|
+
|
743
|
+
puts error.payload
|
744
|
+
# { contents: [{ role: 'user', parts: { text: 'hi!' } }],
|
745
|
+
# generationConfig: { candidateCount: 1 },
|
746
|
+
# ...
|
747
|
+
# }
|
748
|
+
|
749
|
+
puts error.request
|
750
|
+
# #<Faraday::ServerError response={:status=>500, :headers...
|
751
|
+
end
|
752
|
+
```
|
753
|
+
|
754
|
+
#### For Short
|
755
|
+
|
756
|
+
```ruby
|
757
|
+
require 'gemini-ai/errors'
|
758
|
+
|
759
|
+
begin
|
760
|
+
client.stream_generate_content({
|
761
|
+
contents: { role: 'user', parts: { text: 'hi!' } }
|
762
|
+
})
|
763
|
+
rescue GeminiError => error
|
764
|
+
puts error.class # Gemini::Errors::RequestError
|
765
|
+
end
|
766
|
+
```
|
767
|
+
|
768
|
+
#### Errors
|
769
|
+
|
770
|
+
```ruby
|
771
|
+
GeminiError
|
772
|
+
|
773
|
+
MissingProjectIdError
|
774
|
+
UnsupportedServiceError
|
775
|
+
BlockWithoutStreamError
|
776
|
+
|
777
|
+
RequestError
|
778
|
+
```
|
779
|
+
|
530
780
|
## Development
|
531
781
|
|
532
782
|
```bash
|
@@ -545,7 +795,7 @@ gem build gemini-ai.gemspec
|
|
545
795
|
|
546
796
|
gem signin
|
547
797
|
|
548
|
-
gem push gemini-ai-2.
|
798
|
+
gem push gemini-ai-2.2.0.gem
|
549
799
|
```
|
550
800
|
|
551
801
|
### Updating the README
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gemini-ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gbaptista
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|
@@ -64,8 +64,8 @@ dependencies:
|
|
64
64
|
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: 1.9.1
|
67
|
-
description: A Ruby Gem for interacting with Gemini through Vertex AI
|
68
|
-
Google's generative AI services.
|
67
|
+
description: A Ruby Gem for interacting with Gemini through Vertex AI, Generative
|
68
|
+
Language API, or AI Studio, Google's generative AI services.
|
69
69
|
email:
|
70
70
|
executables: []
|
71
71
|
extensions: []
|
@@ -78,9 +78,11 @@ files:
|
|
78
78
|
- Gemfile.lock
|
79
79
|
- LICENSE
|
80
80
|
- README.md
|
81
|
+
- components/errors.rb
|
81
82
|
- controllers/client.rb
|
82
83
|
- gemini-ai.gemspec
|
83
84
|
- ports/dsl/gemini-ai.rb
|
85
|
+
- ports/dsl/gemini-ai/errors.rb
|
84
86
|
- static/gem.rb
|
85
87
|
- tasks/generate-readme.clj
|
86
88
|
- template.md
|