gemini-ai 3.1.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c4cbfd3191a65f27ae580b99a8774309f552f288b5e2bbe339d761ad199615a
4
- data.tar.gz: 0616acf06021892ec8ef85088c8c0ae6f486e8c73d774e72ad9a6a961d02c398
3
+ metadata.gz: b66c146b7230d838c888d5f90107f07bcc2827c7710b77c1284c52315b3c9515
4
+ data.tar.gz: c125664051e260270bb08b81ecc932a3e4b730ea1c69601ba93af4749af54fcf
5
5
  SHA512:
6
- metadata.gz: e68c49731057a162bd9ada268c1343b166593acd74dccc6be4bdd59cdbdef2401400618073df7e24781e7e5d00f23bb277b77d083e168afe20959a6a5a937dcc
7
- data.tar.gz: 98bb928f6e8ee5e0bea7b9001868efd17079513da54fbf4a3084f1a75d0a07504220174d001ae4bde77fbedee083d3943e873ee6ad46760e8c34ee2794eed272
6
+ metadata.gz: dd67f092295620ff45cb75f07b67dc0753d54c92ff598284a0f10ce4a45e7f395aad0235320fb95ab86bb12f9033c21e4b3fe776bb99bd6d98f8228054bc6bca
7
+ data.tar.gz: c03ab797796745870124159762e11f0ea5a56ee9cb033f97c7b62088dd0a2d2dae85f1fa874245ddc3fa3482ae7739f1a311546807ee78e1dbd17e7f5807cc52
data/.gitignore CHANGED
@@ -1,3 +1,7 @@
1
1
  *.gem
2
2
  .devcontainer
3
3
  .env
4
+ *.tmp
5
+ *.temp
6
+ temp.rb
7
+ tmp.rb
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  group :test, :development do
8
- gem 'dotenv', '~> 2.8', '>= 2.8.1'
8
+ gem 'dotenv', '~> 3.1', '>= 3.1.2'
9
9
  gem 'pry-byebug', '~> 3.10', '>= 3.10.1'
10
- gem 'rubocop', '~> 1.58'
10
+ gem 'rubocop', '~> 1.63', '>= 1.63.5'
11
11
  end
data/Gemfile.lock CHANGED
@@ -1,10 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gemini-ai (3.1.3)
4
+ gemini-ai (4.0.0)
5
5
  event_stream_parser (~> 1.0)
6
6
  faraday (~> 2.9)
7
+ faraday-typhoeus (~> 1.1)
7
8
  googleauth (~> 1.8)
9
+ typhoeus (~> 1.4, >= 1.4.1)
8
10
 
9
11
  GEM
10
12
  remote: https://rubygems.org/
@@ -12,33 +14,41 @@ GEM
12
14
  addressable (2.8.6)
13
15
  public_suffix (>= 2.0.2, < 6.0)
14
16
  ast (2.4.2)
17
+ base64 (0.2.0)
15
18
  byebug (11.1.3)
16
19
  coderay (1.1.3)
17
- dotenv (2.8.1)
20
+ dotenv (3.1.2)
21
+ ethon (0.16.0)
22
+ ffi (>= 1.15.0)
18
23
  event_stream_parser (1.0.0)
19
24
  faraday (2.9.0)
20
25
  faraday-net_http (>= 2.0, < 3.2)
21
26
  faraday-net_http (3.1.0)
22
27
  net-http
23
- google-cloud-env (2.1.0)
28
+ faraday-typhoeus (1.1.0)
29
+ faraday (~> 2.0)
30
+ typhoeus (~> 1.4)
31
+ ffi (1.16.3)
32
+ google-cloud-env (2.1.1)
24
33
  faraday (>= 1.0, < 3.a)
25
- googleauth (1.9.1)
34
+ googleauth (1.11.0)
26
35
  faraday (>= 1.0, < 3.a)
27
36
  google-cloud-env (~> 2.1)
28
37
  jwt (>= 1.4, < 3.0)
29
38
  multi_json (~> 1.11)
30
39
  os (>= 0.9, < 2.0)
31
40
  signet (>= 0.16, < 2.a)
32
- json (2.7.1)
33
- jwt (2.7.1)
41
+ json (2.7.2)
42
+ jwt (2.8.1)
43
+ base64
34
44
  language_server-protocol (3.17.0.3)
35
- method_source (1.0.0)
45
+ method_source (1.1.0)
36
46
  multi_json (1.15.0)
37
47
  net-http (0.4.1)
38
48
  uri
39
49
  os (1.1.4)
40
50
  parallel (1.24.0)
41
- parser (3.3.0.3)
51
+ parser (3.3.1.0)
42
52
  ast (~> 2.4.1)
43
53
  racc
44
54
  pry (0.14.2)
@@ -47,30 +57,34 @@ GEM
47
57
  pry-byebug (3.10.1)
48
58
  byebug (~> 11.0)
49
59
  pry (>= 0.13, < 0.15)
50
- public_suffix (5.0.4)
60
+ public_suffix (5.0.5)
51
61
  racc (1.7.3)
52
62
  rainbow (3.1.1)
53
- regexp_parser (2.9.0)
54
- rexml (3.2.6)
55
- rubocop (1.59.0)
63
+ regexp_parser (2.9.2)
64
+ rexml (3.2.8)
65
+ strscan (>= 3.0.9)
66
+ rubocop (1.63.5)
56
67
  json (~> 2.3)
57
68
  language_server-protocol (>= 3.17.0)
58
69
  parallel (~> 1.10)
59
- parser (>= 3.2.2.4)
70
+ parser (>= 3.3.0.2)
60
71
  rainbow (>= 2.2.2, < 4.0)
61
72
  regexp_parser (>= 1.8, < 3.0)
62
73
  rexml (>= 3.2.5, < 4.0)
63
- rubocop-ast (>= 1.30.0, < 2.0)
74
+ rubocop-ast (>= 1.31.1, < 2.0)
64
75
  ruby-progressbar (~> 1.7)
65
76
  unicode-display_width (>= 2.4.0, < 3.0)
66
- rubocop-ast (1.30.0)
67
- parser (>= 3.2.1.0)
77
+ rubocop-ast (1.31.3)
78
+ parser (>= 3.3.1.0)
68
79
  ruby-progressbar (1.13.0)
69
- signet (0.18.0)
80
+ signet (0.19.0)
70
81
  addressable (~> 2.8)
71
82
  faraday (>= 0.17.5, < 3.a)
72
83
  jwt (>= 1.5, < 3.0)
73
84
  multi_json (~> 1.10)
85
+ strscan (3.1.0)
86
+ typhoeus (1.4.1)
87
+ ethon (>= 0.9.0)
74
88
  unicode-display_width (2.5.0)
75
89
  uri (0.13.0)
76
90
 
@@ -78,10 +92,10 @@ PLATFORMS
78
92
  x86_64-linux
79
93
 
80
94
  DEPENDENCIES
81
- dotenv (~> 2.8, >= 2.8.1)
95
+ dotenv (~> 3.1, >= 3.1.2)
82
96
  gemini-ai!
83
97
  pry-byebug (~> 3.10, >= 3.10.1)
84
- rubocop (~> 1.58)
98
+ rubocop (~> 1.63, >= 1.63.5)
85
99
 
86
100
  BUNDLED WITH
87
101
  2.4.22
data/README.md CHANGED
@@ -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', '~> 3.1.3'
12
+ gem 'gemini-ai', '~> 4.0.0'
13
13
  ```
14
14
 
15
15
  ```ruby
@@ -73,39 +73,46 @@ Result:
73
73
  - [TL;DR and Quick Start](#tldr-and-quick-start)
74
74
  - [Index](#index)
75
75
  - [Setup](#setup)
76
- - [Installing](#installing)
77
- - [Credentials](#credentials)
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)
81
- - [Required Data](#required-data)
76
+ - [Installing](#installing)
77
+ - [Credentials](#credentials)
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)
81
+ - [Required Data](#required-data)
82
+ - [Custom Version](#custom-version)
83
+ - [Available Models](#available-models)
82
84
  - [Usage](#usage)
83
- - [Client](#client)
84
- - [Methods](#methods)
85
- - [stream_generate_content](#stream_generate_content)
86
- - [Receiving Stream Events](#receiving-stream-events)
87
- - [Without Events](#without-events)
88
- - [generate_content](#generate_content)
89
- - [Modes](#modes)
90
- - [Text](#text)
91
- - [Image](#image)
92
- - [Video](#video)
93
- - [Streaming vs. Server-Sent Events (SSE)](#streaming-vs-server-sent-events-sse)
94
- - [Server-Sent Events (SSE) Hang](#server-sent-events-sse-hang)
95
- - [Non-Streaming](#non-streaming)
96
- - [Back-and-Forth Conversations](#back-and-forth-conversations)
97
- - [Tools (Functions) Calling](#tools-functions-calling)
98
- - [New Functionalities and APIs](#new-functionalities-and-apis)
99
- - [Request Options](#request-options)
100
- - [Timeout](#timeout)
101
- - [Error Handling](#error-handling)
102
- - [Rescuing](#rescuing)
103
- - [For Short](#for-short)
104
- - [Errors](#errors)
85
+ - [Client](#client)
86
+ - [Methods](#methods)
87
+ - [Chat](#chat)
88
+ - [stream_generate_content](#stream_generate_content)
89
+ - [Receiving Stream Events](#receiving-stream-events)
90
+ - [Without Events](#without-events)
91
+ - [generate_content](#generate_content)
92
+ - [Embeddings](#embeddings)
93
+ - [predict](#predict)
94
+ - [embed_content](#embed_content)
95
+ - [Modes](#modes)
96
+ - [Text](#text)
97
+ - [Image](#image)
98
+ - [Video](#video)
99
+ - [Streaming vs. Server-Sent Events (SSE)](#streaming-vs-server-sent-events-sse)
100
+ - [Server-Sent Events (SSE) Hang](#server-sent-events-sse-hang)
101
+ - [Non-Streaming](#non-streaming)
102
+ - [Back-and-Forth Conversations](#back-and-forth-conversations)
103
+ - [Tools (Functions) Calling](#tools-functions-calling)
104
+ - [New Functionalities and APIs](#new-functionalities-and-apis)
105
+ - [Request Options](#request-options)
106
+ - [Adapter](#adapter)
107
+ - [Timeout](#timeout)
108
+ - [Error Handling](#error-handling)
109
+ - [Rescuing](#rescuing)
110
+ - [For Short](#for-short)
111
+ - [Errors](#errors)
105
112
  - [Development](#development)
106
- - [Purpose](#purpose)
107
- - [Publish to RubyGems](#publish-to-rubygems)
108
- - [Updating the README](#updating-the-readme)
113
+ - [Purpose](#purpose)
114
+ - [Publish to RubyGems](#publish-to-rubygems)
115
+ - [Updating the README](#updating-the-readme)
109
116
  - [Resources and References](#resources-and-references)
110
117
  - [Disclaimer](#disclaimer)
111
118
 
@@ -114,11 +121,11 @@ Result:
114
121
  ### Installing
115
122
 
116
123
  ```sh
117
- gem install gemini-ai -v 3.1.3
124
+ gem install gemini-ai -v 4.0.0
118
125
  ```
119
126
 
120
127
  ```sh
121
- gem 'gemini-ai', '~> 3.1.3'
128
+ gem 'gemini-ai', '~> 4.0.0'
122
129
  ```
123
130
 
124
131
  ### Credentials
@@ -270,6 +277,80 @@ You might want to explicitly set a Google Cloud Project ID, which you can do as
270
277
  }
271
278
  ```
272
279
 
280
+ ### Custom Version
281
+
282
+ By default, the gem uses the `v1` version of the APIs. You may want to use a different version:
283
+
284
+ ```ruby
285
+ # With an API key
286
+ client = Gemini.new(
287
+ credentials: {
288
+ service: 'generative-language-api',
289
+ api_key: ENV['GOOGLE_API_KEY'],
290
+ version: 'v1beta'
291
+ },
292
+ options: { model: 'gemini-pro', server_sent_events: true }
293
+ )
294
+
295
+ # With a Service Account Credentials File
296
+ client = Gemini.new(
297
+ credentials: {
298
+ service: 'vertex-ai-api',
299
+ file_path: 'google-credentials.json',
300
+ region: 'us-east4',
301
+ version: 'v1beta'
302
+ },
303
+ options: { model: 'gemini-pro', server_sent_events: true }
304
+ )
305
+
306
+ # With Application Default Credentials
307
+ client = Gemini.new(
308
+ credentials: {
309
+ service: 'vertex-ai-api',
310
+ region: 'us-east4',
311
+ version: 'v1beta'
312
+ },
313
+ options: { model: 'gemini-pro', server_sent_events: true }
314
+ )
315
+ ```
316
+
317
+ ## Available Models
318
+
319
+ These models are accessible to the repository **author** as of May 2025 in the `us-east4` region. Access to models may vary by region, user, and account. All models here are expected to work, if you can access them. This is just a reference of what a "typical" user may expect to have access to right away:
320
+
321
+ | Model | Vertex AI | Generative Language |
322
+ |------------------------------------------|:---------:|:-------------------:|
323
+ | gemini-pro-vision | ✅ | 🔒 |
324
+ | gemini-pro | ✅ | ✅ |
325
+ | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
326
+ | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
327
+ | gemini-1.5-pro | 🔒 | 🔒 |
328
+ | gemini-1.5-flash-preview-0514 | ✅ | 🔒 |
329
+ | gemini-1.5-flash | 🔒 | 🔒 |
330
+ | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
331
+ | gemini-1.0-pro-vision-001 | ✅ | 🔒 |
332
+ | gemini-1.0-pro-vision | ✅ | 🔒 |
333
+ | gemini-1.0-pro-latest | 🔒 | ✅ |
334
+ | gemini-1.0-pro-002 | ✅ | 🔒 |
335
+ | gemini-1.0-pro-001 | ✅ | ✅ |
336
+ | gemini-1.0-pro | ✅ | ✅ |
337
+ | text-embedding-004 | ✅ | ✅ |
338
+ | embedding-001 | 🔒 | ✅ |
339
+ | text-multilingual-embedding-002 | ✅ | 🔒 |
340
+ | textembedding-gecko-multilingual@001 | ✅ | 🔒 |
341
+ | textembedding-gecko-multilingual@latest | ✅ | 🔒 |
342
+ | textembedding-gecko@001 | ✅ | 🔒 |
343
+ | textembedding-gecko@002 | ✅ | 🔒 |
344
+ | textembedding-gecko@003 | ✅ | 🔒 |
345
+ | textembedding-gecko@latest | ✅ | 🔒 |
346
+
347
+ You can follow new models at:
348
+
349
+ - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
350
+ - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
351
+
352
+ This is [the code](https://gist.github.com/gbaptista/d7390901293bce81ee12ff4ec5fed62c) used for generating this table that you can use to explore your own access.
353
+
273
354
  ## Usage
274
355
 
275
356
  ### Client
@@ -310,9 +391,11 @@ client = Gemini.new(
310
391
 
311
392
  ### Methods
312
393
 
313
- #### stream_generate_content
394
+ #### Chat
395
+
396
+ ##### stream_generate_content
314
397
 
315
- ##### Receiving Stream Events
398
+ ###### Receiving Stream Events
316
399
 
317
400
  Ensure that you have enabled [Server-Sent Events](#streaming-vs-server-sent-events-sse) before using blocks for streaming:
318
401
 
@@ -344,7 +427,7 @@ Event:
344
427
  } }
345
428
  ```
346
429
 
347
- ##### Without Events
430
+ ###### Without Events
348
431
 
349
432
  You can use `stream_generate_content` without events:
350
433
 
@@ -384,7 +467,7 @@ result = client.stream_generate_content(
384
467
  end
385
468
  ```
386
469
 
387
- #### generate_content
470
+ ##### generate_content
388
471
 
389
472
  ```ruby
390
473
  result = client.generate_content(
@@ -413,6 +496,58 @@ Result:
413
496
 
414
497
  As of the writing of this README, only the `generative-language-api` service supports the `generate_content` method; `vertex-ai-api` does not.
415
498
 
499
+ #### Embeddings
500
+
501
+ ##### predict
502
+
503
+ Vertex AI API generates embeddings through the `predict` method ([documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)), and you need a client set up to use an embedding model (e.g. `text-embedding-004`):
504
+
505
+ ```ruby
506
+ result = client.predict(
507
+ { instances: [{ content: 'What is life?' }],
508
+ parameters: { autoTruncate: true } }
509
+ )
510
+ ```
511
+
512
+ Result:
513
+ ```ruby
514
+ { 'predictions' =>
515
+ [{ 'embeddings' =>
516
+ { 'statistics' => { 'truncated' => false, 'token_count' => 4 },
517
+ 'values' =>
518
+ [-0.006861076690256596,
519
+ 0.00020840796059928834,
520
+ -0.028549950569868088,
521
+ # ...
522
+ 0.0020092015620321035,
523
+ 0.03279878571629524,
524
+ -0.014905261807143688] } }],
525
+ 'metadata' => { 'billableCharacterCount' => 11 } }
526
+ ```
527
+
528
+ ##### embed_content
529
+
530
+ Generative Language API generates embeddings through the `embed_content` method ([documentation](https://ai.google.dev/api/rest/v1/models/embedContent)), and you need a client set up to use an embedding model (e.g. `text-embedding-004`):
531
+
532
+ ```ruby
533
+ result = client.embed_content(
534
+ { content: { parts: [{ text: 'What is life?' }] } }
535
+ )
536
+ ```
537
+
538
+ Result:
539
+ ```ruby
540
+ { 'embedding' =>
541
+ { 'values' =>
542
+ [-0.0065307906,
543
+ -0.0001632607,
544
+ -0.028370803,
545
+
546
+ 0.0019950708,
547
+ 0.032798845,
548
+ -0.014878989] } }
549
+ ```
550
+
416
551
  ### Modes
417
552
 
418
553
  #### Text
@@ -865,17 +1000,48 @@ Which will result in:
865
1000
 
866
1001
  ### New Functionalities and APIs
867
1002
 
868
- Google may launch a new endpoint that we haven't covered in the Gem yet. If that's the case, you may still be able to use it through the `request` method. For example, `stream_generate_content` is just a wrapper for `google/models/gemini-pro:streamGenerateContent`, which you can call directly like this:
1003
+ Google may launch a new endpoint that we haven't covered in the Gem yet. If that's the case, you may still be able to use it through the `request` method. For example, `stream_generate_content` is just a wrapper for `models/gemini-pro:streamGenerateContent` (Generative Language API) or `publishers/google/models/gemini-pro:streamGenerateContent` (Vertex AI API), which you can call directly like this:
869
1004
 
870
1005
  ```ruby
1006
+ # Generative Language API
871
1007
  result = client.request(
872
- 'streamGenerateContent',
873
- { contents: { role: 'user', parts: { text: 'hi!' } } }
1008
+ 'models/gemini-pro:streamGenerateContent',
1009
+ { contents: { role: 'user', parts: { text: 'hi!' } } },
1010
+ request_method: 'POST',
1011
+ server_sent_events: true
1012
+ )
1013
+ ```
1014
+
1015
+ ```ruby
1016
+ # Vertex AI API
1017
+ result = client.request(
1018
+ 'publishers/google/models/gemini-pro:streamGenerateContent',
1019
+ { contents: { role: 'user', parts: { text: 'hi!' } } },
1020
+ request_method: 'POST',
1021
+ server_sent_events: true
874
1022
  )
875
1023
  ```
876
1024
 
877
1025
  ### Request Options
878
1026
 
1027
+ #### Adapter
1028
+
1029
+ To enable streaming, the gem uses [Faraday](https://github.com/lostisland/faraday) with the [Typhoeus](https://github.com/typhoeus/typhoeus) adapter by default.
1030
+
1031
+ You can use a different adapter if you want:
1032
+
1033
+ ```ruby
1034
+ require 'faraday/net_http'
1035
+
1036
+ client = Gemini.new(
1037
+ credentials: { service: 'vertex-ai-api', region: 'us-east4' },
1038
+ options: {
1039
+ model: 'gemini-pro',
1040
+ connection: { adapter: :net_http }
1041
+ }
1042
+ )
1043
+ ```
1044
+
879
1045
  #### Timeout
880
1046
 
881
1047
  You can set the maximum number of seconds to wait for the request to complete with the `timeout` option:
@@ -982,7 +1148,7 @@ gem build gemini-ai.gemspec
982
1148
 
983
1149
  gem signin
984
1150
 
985
- gem push gemini-ai-3.1.3.gem
1151
+ gem push gemini-ai-4.0.0.gem
986
1152
  ```
987
1153
 
988
1154
  ### Updating the README
@@ -1026,6 +1192,9 @@ These resources and references may be useful throughout your learning process.
1026
1192
  - [Gemini API Documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini)
1027
1193
  - [Vertex AI API Documentation](https://cloud.google.com/vertex-ai/docs/reference)
1028
1194
  - [REST Documentation](https://cloud.google.com/vertex-ai/docs/reference/rest)
1195
+ - [Get text embeddings](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
1196
+ - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
1197
+ - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
1029
1198
  - [Google DeepMind Gemini](https://deepmind.google/technologies/gemini/)
1030
1199
  - [Stream responses from Generative AI models](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/streaming)
1031
1200
  - [Function calling](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'event_stream_parser'
4
4
  require 'faraday'
5
+ require 'faraday/typhoeus'
5
6
  require 'json'
6
7
  require 'googleauth'
7
8
 
@@ -12,7 +13,17 @@ module Gemini
12
13
  class Client
13
14
  ALLOWED_REQUEST_OPTIONS = %i[timeout open_timeout read_timeout write_timeout].freeze
14
15
 
16
+ DEFAULT_FARADAY_ADAPTER = :typhoeus
17
+
18
+ DEFAULT_SERVICE_VERSION = 'v1'
19
+
15
20
  def initialize(config)
21
+ @service = config[:credentials][:service]
22
+
23
+ unless %w[vertex-ai-api generative-language-api].include?(@service)
24
+ raise Errors::UnsupportedServiceError, "Unsupported service: #{@service}"
25
+ end
26
+
16
27
  if config[:credentials][:api_key]
17
28
  @authentication = :api_key
18
29
  @api_key = config[:credentials][:api_key]
@@ -33,21 +44,28 @@ module Gemini
33
44
  raise Errors::MissingProjectIdError, 'Could not determine project_id, which is required.' if @project_id.nil?
34
45
  end
35
46
 
36
- @service = config[:credentials][:service]
47
+ @service_version = config.dig(:credentials, :version) || DEFAULT_SERVICE_VERSION
37
48
 
38
- @address = case @service
39
- when 'vertex-ai-api'
40
- "https://#{config[:credentials][:region]}-aiplatform.googleapis.com/v1/projects/#{@project_id}/locations/#{config[:credentials][:region]}/publishers/google/models/#{config[:options][:model]}"
41
- when 'generative-language-api'
42
- "https://generativelanguage.googleapis.com/v1/models/#{config[:options][:model]}"
43
- else
44
- raise Errors::UnsupportedServiceError, "Unsupported service: #{@service}"
45
- end
49
+ @base_address = case @service
50
+ when 'vertex-ai-api'
51
+ "https://#{config[:credentials][:region]}-aiplatform.googleapis.com/#{@service_version}/projects/#{@project_id}/locations/#{config[:credentials][:region]}"
52
+ when 'generative-language-api'
53
+ "https://generativelanguage.googleapis.com/#{@service_version}"
54
+ end
55
+
56
+ @model_address = case @service
57
+ when 'vertex-ai-api'
58
+ "publishers/google/models/#{config[:options][:model]}"
59
+ when 'generative-language-api'
60
+ "models/#{config[:options][:model]}"
61
+ end
46
62
 
47
63
  @server_sent_events = config.dig(:options, :server_sent_events)
48
64
 
49
65
  @request_options = config.dig(:options, :connection, :request)
50
66
 
67
+ @faraday_adapter = config.dig(:options, :connection, :adapter) || DEFAULT_FARADAY_ADAPTER
68
+
51
69
  @request_options = if @request_options.is_a?(Hash)
52
70
  @request_options.select do |key, _|
53
71
  ALLOWED_REQUEST_OPTIONS.include?(key)
@@ -57,21 +75,59 @@ module Gemini
57
75
  end
58
76
  end
59
77
 
78
+ def predict(payload, server_sent_events: nil, &callback)
79
+ result = request(
80
+ "#{@model_address}:predict", payload,
81
+ server_sent_events:, &callback
82
+ )
83
+
84
+ return result.first if result.is_a?(Array) && result.size == 1
85
+
86
+ result
87
+ end
88
+
89
+ def embed_content(payload, server_sent_events: nil, &callback)
90
+ result = request(
91
+ "#{@model_address}:embedContent", payload,
92
+ server_sent_events:, &callback
93
+ )
94
+
95
+ return result.first if result.is_a?(Array) && result.size == 1
96
+
97
+ result
98
+ end
99
+
60
100
  def stream_generate_content(payload, server_sent_events: nil, &callback)
61
- request('streamGenerateContent', payload, server_sent_events:, &callback)
101
+ request("#{@model_address}:streamGenerateContent", payload, server_sent_events:, &callback)
102
+ end
103
+
104
+ def models(_server_sent_events: nil, &callback)
105
+ result = request(
106
+ 'models',
107
+ nil, server_sent_events: false, request_method: 'GET', &callback
108
+ )
109
+
110
+ return result.first if result.is_a?(Array) && result.size == 1
111
+
112
+ result
62
113
  end
63
114
 
64
115
  def generate_content(payload, server_sent_events: nil, &callback)
65
- result = request('generateContent', payload, server_sent_events:, &callback)
116
+ result = request(
117
+ "#{@model_address}:generateContent", payload,
118
+ server_sent_events:, &callback
119
+ )
66
120
 
67
121
  return result.first if result.is_a?(Array) && result.size == 1
68
122
 
69
123
  result
70
124
  end
71
125
 
72
- def request(path, payload, server_sent_events: nil, &callback)
126
+ def request(path, payload, server_sent_events: nil, request_method: 'POST', &callback)
73
127
  server_sent_events_enabled = server_sent_events.nil? ? @server_sent_events : server_sent_events
74
- url = "#{@address}:#{path}"
128
+
129
+ url = "#{@base_address}/#{path}"
130
+
75
131
  params = []
76
132
 
77
133
  params << 'alt=sse' if server_sent_events_enabled
@@ -86,18 +142,23 @@ module Gemini
86
142
 
87
143
  results = []
88
144
 
145
+ method_to_call = request_method.to_s.strip.downcase.to_sym
146
+
89
147
  response = Faraday.new(request: @request_options) do |faraday|
148
+ faraday.adapter @faraday_adapter
90
149
  faraday.response :raise_error
91
- end.post do |request|
150
+ end.send(method_to_call) do |request|
92
151
  request.url url
93
152
  request.headers['Content-Type'] = 'application/json'
94
153
  if @authentication == :service_account || @authentication == :default_credentials
95
154
  request.headers['Authorization'] = "Bearer #{@authorizer.fetch_access_token!['access_token']}"
96
155
  end
97
156
 
98
- request.body = payload.to_json
157
+ request.body = payload.to_json unless payload.nil?
99
158
 
100
159
  if server_sent_events_enabled
160
+ partial_json = ''
161
+
101
162
  parser = EventStreamParser::Parser.new
102
163
 
103
164
  request.options.on_data = proc do |chunk, bytes, env|
@@ -107,20 +168,27 @@ module Gemini
107
168
  end
108
169
 
109
170
  parser.feed(chunk) do |type, data, id, reconnection_time|
110
- parsed_data = safe_parse_json(data)
111
- result = {
112
- event: safe_parse_json(data),
113
- parsed: { type:, data:, id:, reconnection_time: },
114
- raw: { chunk:, bytes:, env: }
115
- }
171
+ partial_json += data
172
+
173
+ parsed_json = safe_parse_json(partial_json)
174
+
175
+ if parsed_json
176
+ result = {
177
+ event: parsed_json,
178
+ parsed: { type:, data:, id:, reconnection_time: },
179
+ raw: { chunk:, bytes:, env: }
180
+ }
181
+
182
+ callback.call(result[:event], result[:parsed], result[:raw]) unless callback.nil?
116
183
 
117
- callback.call(result[:event], result[:parsed], result[:raw]) unless callback.nil?
184
+ results << result
118
185
 
119
- results << result
186
+ partial_json = ''
120
187
 
121
- if parsed_data['candidates']
122
- parsed_data['candidates'].find do |candidate|
123
- !candidate['finishReason'].nil? && candidate['finishReason'] != ''
188
+ if parsed_json['candidates']
189
+ parsed_json['candidates'].find do |candidate|
190
+ !candidate['finishReason'].nil? && candidate['finishReason'] != ''
191
+ end
124
192
  end
125
193
  end
126
194
  end
@@ -136,9 +204,9 @@ module Gemini
136
204
  end
137
205
 
138
206
  def safe_parse_json(raw)
139
- raw.start_with?('{', '[') ? JSON.parse(raw) : raw
207
+ raw.to_s.lstrip.start_with?('{', '[') ? JSON.parse(raw) : nil
140
208
  rescue JSON::ParserError
141
- raw
209
+ nil
142
210
  end
143
211
  end
144
212
  end
data/gemini-ai.gemspec CHANGED
@@ -31,7 +31,13 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency 'event_stream_parser', '~> 1.0'
33
33
  spec.add_dependency 'faraday', '~> 2.9'
34
+ spec.add_dependency 'faraday-typhoeus', '~> 1.1'
35
+
36
+ # Before upgrading, check this:
37
+ # https://github.com/gbaptista/gemini-ai/pull/10
34
38
  spec.add_dependency 'googleauth', '~> 1.8'
35
39
 
40
+ spec.add_dependency 'typhoeus', '~> 1.4', '>= 1.4.1'
41
+
36
42
  spec.metadata['rubygems_mfa_required'] = 'true'
37
43
  end
data/static/gem.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Gemini
4
4
  GEM = {
5
5
  name: 'gemini-ai',
6
- version: '3.1.3',
6
+ version: '4.0.0',
7
7
  author: 'gbaptista',
8
8
  summary: "Interact with Google's Gemini AI.",
9
9
  description: "A Ruby Gem for interacting with Gemini through Vertex AI, Generative Language API, or AI Studio, Google's generative AI services.",
@@ -23,7 +23,7 @@
23
23
  (remove nil?))]
24
24
  (->> processed-lines
25
25
  (map (fn [{:keys [level title link]}]
26
- (str (apply str (repeat (* 4 (- level 2)) " "))
26
+ (str (apply str (repeat (* 2 (- level 2)) " "))
27
27
  "- ["
28
28
  title
29
29
  "](#"
data/template.md CHANGED
@@ -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', '~> 3.1.3'
12
+ gem 'gemini-ai', '~> 4.0.0'
13
13
  ```
14
14
 
15
15
  ```ruby
@@ -77,11 +77,11 @@ Result:
77
77
  ### Installing
78
78
 
79
79
  ```sh
80
- gem install gemini-ai -v 3.1.3
80
+ gem install gemini-ai -v 4.0.0
81
81
  ```
82
82
 
83
83
  ```sh
84
- gem 'gemini-ai', '~> 3.1.3'
84
+ gem 'gemini-ai', '~> 4.0.0'
85
85
  ```
86
86
 
87
87
  ### Credentials
@@ -233,6 +233,80 @@ You might want to explicitly set a Google Cloud Project ID, which you can do as
233
233
  }
234
234
  ```
235
235
 
236
+ ### Custom Version
237
+
238
+ By default, the gem uses the `v1` version of the APIs. You may want to use a different version:
239
+
240
+ ```ruby
241
+ # With an API key
242
+ client = Gemini.new(
243
+ credentials: {
244
+ service: 'generative-language-api',
245
+ api_key: ENV['GOOGLE_API_KEY'],
246
+ version: 'v1beta'
247
+ },
248
+ options: { model: 'gemini-pro', server_sent_events: true }
249
+ )
250
+
251
+ # With a Service Account Credentials File
252
+ client = Gemini.new(
253
+ credentials: {
254
+ service: 'vertex-ai-api',
255
+ file_path: 'google-credentials.json',
256
+ region: 'us-east4',
257
+ version: 'v1beta'
258
+ },
259
+ options: { model: 'gemini-pro', server_sent_events: true }
260
+ )
261
+
262
+ # With Application Default Credentials
263
+ client = Gemini.new(
264
+ credentials: {
265
+ service: 'vertex-ai-api',
266
+ region: 'us-east4',
267
+ version: 'v1beta'
268
+ },
269
+ options: { model: 'gemini-pro', server_sent_events: true }
270
+ )
271
+ ```
272
+
273
+ ## Available Models
274
+
275
+ These models are accessible to the repository **author** as of May 2025 in the `us-east4` region. Access to models may vary by region, user, and account. All models here are expected to work, if you can access them. This is just a reference of what a "typical" user may expect to have access to right away:
276
+
277
+ | Model | Vertex AI | Generative Language |
278
+ |------------------------------------------|:---------:|:-------------------:|
279
+ | gemini-pro-vision | ✅ | 🔒 |
280
+ | gemini-pro | ✅ | ✅ |
281
+ | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
282
+ | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
283
+ | gemini-1.5-pro | 🔒 | 🔒 |
284
+ | gemini-1.5-flash-preview-0514 | ✅ | 🔒 |
285
+ | gemini-1.5-flash | 🔒 | 🔒 |
286
+ | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
287
+ | gemini-1.0-pro-vision-001 | ✅ | 🔒 |
288
+ | gemini-1.0-pro-vision | ✅ | 🔒 |
289
+ | gemini-1.0-pro-latest | 🔒 | ✅ |
290
+ | gemini-1.0-pro-002 | ✅ | 🔒 |
291
+ | gemini-1.0-pro-001 | ✅ | ✅ |
292
+ | gemini-1.0-pro | ✅ | ✅ |
293
+ | text-embedding-004 | ✅ | ✅ |
294
+ | embedding-001 | 🔒 | ✅ |
295
+ | text-multilingual-embedding-002 | ✅ | 🔒 |
296
+ | textembedding-gecko-multilingual@001 | ✅ | 🔒 |
297
+ | textembedding-gecko-multilingual@latest | ✅ | 🔒 |
298
+ | textembedding-gecko@001 | ✅ | 🔒 |
299
+ | textembedding-gecko@002 | ✅ | 🔒 |
300
+ | textembedding-gecko@003 | ✅ | 🔒 |
301
+ | textembedding-gecko@latest | ✅ | 🔒 |
302
+
303
+ You can follow new models at:
304
+
305
+ - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
306
+ - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
307
+
308
+ This is [the code](https://gist.github.com/gbaptista/d7390901293bce81ee12ff4ec5fed62c) used for generating this table that you can use to explore your own access.
309
+
236
310
  ## Usage
237
311
 
238
312
  ### Client
@@ -273,9 +347,11 @@ client = Gemini.new(
273
347
 
274
348
  ### Methods
275
349
 
276
- #### stream_generate_content
350
+ #### Chat
351
+
352
+ ##### stream_generate_content
277
353
 
278
- ##### Receiving Stream Events
354
+ ###### Receiving Stream Events
279
355
 
280
356
  Ensure that you have enabled [Server-Sent Events](#streaming-vs-server-sent-events-sse) before using blocks for streaming:
281
357
 
@@ -307,7 +383,7 @@ Event:
307
383
  } }
308
384
  ```
309
385
 
310
- ##### Without Events
386
+ ###### Without Events
311
387
 
312
388
  You can use `stream_generate_content` without events:
313
389
 
@@ -347,7 +423,7 @@ result = client.stream_generate_content(
347
423
  end
348
424
  ```
349
425
 
350
- #### generate_content
426
+ ##### generate_content
351
427
 
352
428
  ```ruby
353
429
  result = client.generate_content(
@@ -376,6 +452,58 @@ Result:
376
452
 
377
453
  As of the writing of this README, only the `generative-language-api` service supports the `generate_content` method; `vertex-ai-api` does not.
378
454
 
455
+ #### Embeddings
456
+
457
+ ##### predict
458
+
459
+ Vertex AI API generates embeddings through the `predict` method ([documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)), and you need a client set up to use an embedding model (e.g. `text-embedding-004`):
460
+
461
+ ```ruby
462
+ result = client.predict(
463
+ { instances: [{ content: 'What is life?' }],
464
+ parameters: { autoTruncate: true } }
465
+ )
466
+ ```
467
+
468
+ Result:
469
+ ```ruby
470
+ { 'predictions' =>
471
+ [{ 'embeddings' =>
472
+ { 'statistics' => { 'truncated' => false, 'token_count' => 4 },
473
+ 'values' =>
474
+ [-0.006861076690256596,
475
+ 0.00020840796059928834,
476
+ -0.028549950569868088,
477
+ # ...
478
+ 0.0020092015620321035,
479
+ 0.03279878571629524,
480
+ -0.014905261807143688] } }],
481
+ 'metadata' => { 'billableCharacterCount' => 11 } }
482
+ ```
483
+
484
+ ##### embed_content
485
+
486
+ Generative Language API generates embeddings through the `embed_content` method ([documentation](https://ai.google.dev/api/rest/v1/models/embedContent)), and you need a client set up to use an embedding model (e.g. `text-embedding-004`):
487
+
488
+ ```ruby
489
+ result = client.embed_content(
490
+ { content: { parts: [{ text: 'What is life?' }] } }
491
+ )
492
+ ```
493
+
494
+ Result:
495
+ ```ruby
496
+ { 'embedding' =>
497
+ { 'values' =>
498
+ [-0.0065307906,
499
+ -0.0001632607,
500
+ -0.028370803,
501
+
502
+ 0.0019950708,
503
+ 0.032798845,
504
+ -0.014878989] } }
505
+ ```
506
+
379
507
  ### Modes
380
508
 
381
509
  #### Text
@@ -828,17 +956,48 @@ Which will result in:
828
956
 
829
957
  ### New Functionalities and APIs
830
958
 
831
- Google may launch a new endpoint that we haven't covered in the Gem yet. If that's the case, you may still be able to use it through the `request` method. For example, `stream_generate_content` is just a wrapper for `google/models/gemini-pro:streamGenerateContent`, which you can call directly like this:
959
+ Google may launch a new endpoint that we haven't covered in the Gem yet. If that's the case, you may still be able to use it through the `request` method. For example, `stream_generate_content` is just a wrapper for `models/gemini-pro:streamGenerateContent` (Generative Language API) or `publishers/google/models/gemini-pro:streamGenerateContent` (Vertex AI API), which you can call directly like this:
832
960
 
833
961
  ```ruby
962
+ # Generative Language API
834
963
  result = client.request(
835
- 'streamGenerateContent',
836
- { contents: { role: 'user', parts: { text: 'hi!' } } }
964
+ 'models/gemini-pro:streamGenerateContent',
965
+ { contents: { role: 'user', parts: { text: 'hi!' } } },
966
+ request_method: 'POST',
967
+ server_sent_events: true
968
+ )
969
+ ```
970
+
971
+ ```ruby
972
+ # Vertex AI API
973
+ result = client.request(
974
+ 'publishers/google/models/gemini-pro:streamGenerateContent',
975
+ { contents: { role: 'user', parts: { text: 'hi!' } } },
976
+ request_method: 'POST',
977
+ server_sent_events: true
837
978
  )
838
979
  ```
839
980
 
840
981
  ### Request Options
841
982
 
983
+ #### Adapter
984
+
985
+ To enable streaming, the gem uses [Faraday](https://github.com/lostisland/faraday) with the [Typhoeus](https://github.com/typhoeus/typhoeus) adapter by default.
986
+
987
+ You can use a different adapter if you want:
988
+
989
+ ```ruby
990
+ require 'faraday/net_http'
991
+
992
+ client = Gemini.new(
993
+ credentials: { service: 'vertex-ai-api', region: 'us-east4' },
994
+ options: {
995
+ model: 'gemini-pro',
996
+ connection: { adapter: :net_http }
997
+ }
998
+ )
999
+ ```
1000
+
842
1001
  #### Timeout
843
1002
 
844
1003
  You can set the maximum number of seconds to wait for the request to complete with the `timeout` option:
@@ -945,7 +1104,7 @@ gem build gemini-ai.gemspec
945
1104
 
946
1105
  gem signin
947
1106
 
948
- gem push gemini-ai-3.1.3.gem
1107
+ gem push gemini-ai-4.0.0.gem
949
1108
  ```
950
1109
 
951
1110
  ### Updating the README
@@ -989,6 +1148,9 @@ These resources and references may be useful throughout your learning process.
989
1148
  - [Gemini API Documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini)
990
1149
  - [Vertex AI API Documentation](https://cloud.google.com/vertex-ai/docs/reference)
991
1150
  - [REST Documentation](https://cloud.google.com/vertex-ai/docs/reference/rest)
1151
+ - [Get text embeddings](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
1152
+ - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
1153
+ - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
992
1154
  - [Google DeepMind Gemini](https://deepmind.google/technologies/gemini/)
993
1155
  - [Stream responses from Generative AI models](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/streaming)
994
1156
  - [Function calling](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling)
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: 3.1.3
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gbaptista
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-13 00:00:00.000000000 Z
11
+ date: 2024-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday-typhoeus
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: googleauth
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,9 +66,29 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: typhoeus
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 1.4.1
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '1.4'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.4.1
55
89
  description: A Ruby Gem for interacting with Gemini through Vertex AI, Generative
56
90
  Language API, or AI Studio, Google's generative AI services.
57
- email:
91
+ email:
58
92
  executables: []
59
93
  extensions: []
60
94
  extra_rdoc_files: []
@@ -82,7 +116,7 @@ metadata:
82
116
  homepage_uri: https://github.com/gbaptista/gemini-ai
83
117
  source_code_uri: https://github.com/gbaptista/gemini-ai
84
118
  rubygems_mfa_required: 'true'
85
- post_install_message:
119
+ post_install_message:
86
120
  rdoc_options: []
87
121
  require_paths:
88
122
  - ports/dsl
@@ -98,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
132
  version: '0'
99
133
  requirements: []
100
134
  rubygems_version: 3.3.3
101
- signing_key:
135
+ signing_key:
102
136
  specification_version: 4
103
137
  summary: Interact with Google's Gemini AI.
104
138
  test_files: []