gemini-ai 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b66c146b7230d838c888d5f90107f07bcc2827c7710b77c1284c52315b3c9515
4
- data.tar.gz: c125664051e260270bb08b81ecc932a3e4b730ea1c69601ba93af4749af54fcf
3
+ metadata.gz: eda19196e43e0d519127fd3e91c477835d03035115d837849ac398788901d50a
4
+ data.tar.gz: dad8ccd200487954be14b3cd2e459848e5e557bb10961bcbfdedc70733191742
5
5
  SHA512:
6
- metadata.gz: dd67f092295620ff45cb75f07b67dc0753d54c92ff598284a0f10ce4a45e7f395aad0235320fb95ab86bb12f9033c21e4b3fe776bb99bd6d98f8228054bc6bca
7
- data.tar.gz: c03ab797796745870124159762e11f0ea5a56ee9cb033f97c7b62088dd0a2d2dae85f1fa874245ddc3fa3482ae7739f1a311546807ee78e1dbd17e7f5807cc52
6
+ metadata.gz: ee6e3a097d57d1a4f81d74bcec541a03aa23e1829d36b0296817345da6503fc1a198baad7e333e68996654f586ddc822704572159c297afe05e41ba687e2bba3
7
+ data.tar.gz: 55eae644861d7c4a45ab2551357e17c44b4490f0d73b706adeb26b12a1931f65db77a5ca64b589b2447367404fea7951cc15710ba12ce2b4561a7b12f84fc05e
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -4,3 +4,6 @@ AllCops:
4
4
 
5
5
  Style/Documentation:
6
6
  Enabled: false
7
+
8
+ require:
9
+ - rubocop-rspec
data/Gemfile CHANGED
@@ -7,5 +7,7 @@ gemspec
7
7
  group :test, :development do
8
8
  gem 'dotenv', '~> 3.1', '>= 3.1.2'
9
9
  gem 'pry-byebug', '~> 3.10', '>= 3.10.1'
10
- gem 'rubocop', '~> 1.63', '>= 1.63.5'
10
+ gem 'rspec', '~> 3.13'
11
+ gem 'rubocop', '~> 1.64', '>= 1.64.1'
12
+ gem 'rubocop-rspec', '~> 3.0', '>= 3.0.1'
11
13
  end
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gemini-ai (4.0.0)
4
+ gemini-ai (4.1.0)
5
5
  event_stream_parser (~> 1.0)
6
- faraday (~> 2.9)
6
+ faraday (~> 2.9, >= 2.9.2)
7
7
  faraday-typhoeus (~> 1.1)
8
8
  googleauth (~> 1.8)
9
9
  typhoeus (~> 1.4, >= 1.4.1)
@@ -11,24 +11,25 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- addressable (2.8.6)
15
- public_suffix (>= 2.0.2, < 6.0)
14
+ addressable (2.8.7)
15
+ public_suffix (>= 2.0.2, < 7.0)
16
16
  ast (2.4.2)
17
17
  base64 (0.2.0)
18
18
  byebug (11.1.3)
19
19
  coderay (1.1.3)
20
+ diff-lcs (1.5.1)
20
21
  dotenv (3.1.2)
21
22
  ethon (0.16.0)
22
23
  ffi (>= 1.15.0)
23
24
  event_stream_parser (1.0.0)
24
- faraday (2.9.0)
25
+ faraday (2.9.2)
25
26
  faraday-net_http (>= 2.0, < 3.2)
26
27
  faraday-net_http (3.1.0)
27
28
  net-http
28
29
  faraday-typhoeus (1.1.0)
29
30
  faraday (~> 2.0)
30
31
  typhoeus (~> 1.4)
31
- ffi (1.16.3)
32
+ ffi (1.17.0)
32
33
  google-cloud-env (2.1.1)
33
34
  faraday (>= 1.0, < 3.a)
34
35
  googleauth (1.11.0)
@@ -39,7 +40,7 @@ GEM
39
40
  os (>= 0.9, < 2.0)
40
41
  signet (>= 0.16, < 2.a)
41
42
  json (2.7.2)
42
- jwt (2.8.1)
43
+ jwt (2.8.2)
43
44
  base64
44
45
  language_server-protocol (3.17.0.3)
45
46
  method_source (1.1.0)
@@ -47,8 +48,8 @@ GEM
47
48
  net-http (0.4.1)
48
49
  uri
49
50
  os (1.1.4)
50
- parallel (1.24.0)
51
- parser (3.3.1.0)
51
+ parallel (1.25.1)
52
+ parser (3.3.3.0)
52
53
  ast (~> 2.4.1)
53
54
  racc
54
55
  pry (0.14.2)
@@ -57,13 +58,26 @@ GEM
57
58
  pry-byebug (3.10.1)
58
59
  byebug (~> 11.0)
59
60
  pry (>= 0.13, < 0.15)
60
- public_suffix (5.0.5)
61
- racc (1.7.3)
61
+ public_suffix (6.0.0)
62
+ racc (1.8.0)
62
63
  rainbow (3.1.1)
63
64
  regexp_parser (2.9.2)
64
- rexml (3.2.8)
65
- strscan (>= 3.0.9)
66
- rubocop (1.63.5)
65
+ rexml (3.3.0)
66
+ strscan
67
+ rspec (3.13.0)
68
+ rspec-core (~> 3.13.0)
69
+ rspec-expectations (~> 3.13.0)
70
+ rspec-mocks (~> 3.13.0)
71
+ rspec-core (3.13.0)
72
+ rspec-support (~> 3.13.0)
73
+ rspec-expectations (3.13.1)
74
+ diff-lcs (>= 1.2.0, < 2.0)
75
+ rspec-support (~> 3.13.0)
76
+ rspec-mocks (3.13.1)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.13.0)
79
+ rspec-support (3.13.1)
80
+ rubocop (1.64.1)
67
81
  json (~> 2.3)
68
82
  language_server-protocol (>= 3.17.0)
69
83
  parallel (~> 1.10)
@@ -76,6 +90,8 @@ GEM
76
90
  unicode-display_width (>= 2.4.0, < 3.0)
77
91
  rubocop-ast (1.31.3)
78
92
  parser (>= 3.3.1.0)
93
+ rubocop-rspec (3.0.1)
94
+ rubocop (~> 1.61)
79
95
  ruby-progressbar (1.13.0)
80
96
  signet (0.19.0)
81
97
  addressable (~> 2.8)
@@ -95,7 +111,9 @@ DEPENDENCIES
95
111
  dotenv (~> 3.1, >= 3.1.2)
96
112
  gemini-ai!
97
113
  pry-byebug (~> 3.10, >= 3.10.1)
98
- rubocop (~> 1.63, >= 1.63.5)
114
+ rspec (~> 3.13)
115
+ rubocop (~> 1.64, >= 1.64.1)
116
+ rubocop-rspec (~> 3.0, >= 3.0.1)
99
117
 
100
118
  BUNDLED WITH
101
119
  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', '~> 4.0.0'
12
+ gem 'gemini-ai', '~> 4.1.0'
13
13
  ```
14
14
 
15
15
  ```ruby
@@ -34,6 +34,17 @@ client = Gemini.new(
34
34
  options: { model: 'gemini-pro', server_sent_events: true }
35
35
  )
36
36
 
37
+ # With the Service Account Credentials File contents
38
+ client = Gemini.new(
39
+ credentials: {
40
+ service: 'vertex-ai-api',
41
+ file_contents: File.read('google-credentials.json'),
42
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
43
+ region: 'us-east4'
44
+ },
45
+ options: { model: 'gemini-pro', server_sent_events: true }
46
+ )
47
+
37
48
  # With Application Default Credentials
38
49
  client = Gemini.new(
39
50
  credentials: {
@@ -100,6 +111,11 @@ Result:
100
111
  - [Server-Sent Events (SSE) Hang](#server-sent-events-sse-hang)
101
112
  - [Non-Streaming](#non-streaming)
102
113
  - [Back-and-Forth Conversations](#back-and-forth-conversations)
114
+ - [Safety Settings](#safety-settings)
115
+ - [System Instructions](#system-instructions)
116
+ - [JSON Format Responses](#json-format-responses)
117
+ - [JSON Schema](#json-schema)
118
+ - [Models That Support JSON](#models-that-support-json)
103
119
  - [Tools (Functions) Calling](#tools-functions-calling)
104
120
  - [New Functionalities and APIs](#new-functionalities-and-apis)
105
121
  - [Request Options](#request-options)
@@ -121,11 +137,11 @@ Result:
121
137
  ### Installing
122
138
 
123
139
  ```sh
124
- gem install gemini-ai -v 4.0.0
140
+ gem install gemini-ai -v 4.1.0
125
141
  ```
126
142
 
127
143
  ```sh
128
- gem 'gemini-ai', '~> 4.0.0'
144
+ gem 'gemini-ai', '~> 4.1.0'
129
145
  ```
130
146
 
131
147
  ### Credentials
@@ -207,7 +223,7 @@ Similar to [Option 2](#option-2-service-account-credentials-file-vertex-ai-api),
207
223
  For local development, you can generate your default credentials using the [gcloud CLI](https://cloud.google.com/sdk/gcloud) as follows:
208
224
 
209
225
  ```sh
210
- gcloud auth application-default login
226
+ gcloud auth application-default login
211
227
  ```
212
228
 
213
229
  For more details about alternative methods and different environments, check the official documentation:
@@ -245,6 +261,23 @@ Remember that hardcoding your API key in code is unsafe; it's preferable to use
245
261
  }
246
262
  ```
247
263
 
264
+ Alternatively, you can pass the file contents instead of the path:
265
+ ```ruby
266
+ {
267
+ service: 'vertex-ai-api',
268
+ file_contents: File.read('google-credentials.json'),
269
+ region: 'us-east4'
270
+ }
271
+ ```
272
+
273
+ ```ruby
274
+ {
275
+ service: 'vertex-ai-api',
276
+ file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
277
+ region: 'us-east4'
278
+ }
279
+ ```
280
+
248
281
  **Option 3**: For _Application Default Credentials_, omit both the `api_key` and the `file_path`:
249
282
 
250
283
  ```ruby
@@ -303,6 +336,17 @@ client = Gemini.new(
303
336
  options: { model: 'gemini-pro', server_sent_events: true }
304
337
  )
305
338
 
339
+ # With the Service Account Credentials File contents
340
+ client = Gemini.new(
341
+ credentials: {
342
+ service: 'vertex-ai-api',
343
+ file_contents: File.read('google-credentials.json'),
344
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
345
+ region: 'us-east4'
346
+ },
347
+ options: { model: 'gemini-pro', server_sent_events: true }
348
+ )
349
+
306
350
  # With Application Default Credentials
307
351
  client = Gemini.new(
308
352
  credentials: {
@@ -316,7 +360,7 @@ client = Gemini.new(
316
360
 
317
361
  ## Available Models
318
362
 
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:
363
+ These models are accessible to the repository **author** as of June 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
364
 
321
365
  | Model | Vertex AI | Generative Language |
322
366
  |------------------------------------------|:---------:|:-------------------:|
@@ -324,9 +368,9 @@ These models are accessible to the repository **author** as of May 2025 in the `
324
368
  | gemini-pro | ✅ | ✅ |
325
369
  | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
326
370
  | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
327
- | gemini-1.5-pro | 🔒 | 🔒 |
371
+ | gemini-1.5-pro | | |
328
372
  | gemini-1.5-flash-preview-0514 | ✅ | 🔒 |
329
- | gemini-1.5-flash | 🔒 | 🔒 |
373
+ | gemini-1.5-flash | | |
330
374
  | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
331
375
  | gemini-1.0-pro-vision-001 | ✅ | 🔒 |
332
376
  | gemini-1.0-pro-vision | ✅ | 🔒 |
@@ -334,6 +378,11 @@ These models are accessible to the repository **author** as of May 2025 in the `
334
378
  | gemini-1.0-pro-002 | ✅ | 🔒 |
335
379
  | gemini-1.0-pro-001 | ✅ | ✅ |
336
380
  | gemini-1.0-pro | ✅ | ✅ |
381
+ | gemini-ultra | 🔒 | 🔒 |
382
+ | gemini-1.0-ultra | 🔒 | 🔒 |
383
+ | gemini-1.0-ultra-001 | 🔒 | 🔒 |
384
+ | text-embedding-preview-0514 | 🔒 | 🔒 |
385
+ | text-embedding-preview-0409 | 🔒 | 🔒 |
337
386
  | text-embedding-004 | ✅ | ✅ |
338
387
  | embedding-001 | 🔒 | ✅ |
339
388
  | text-multilingual-embedding-002 | ✅ | 🔒 |
@@ -379,6 +428,17 @@ client = Gemini.new(
379
428
  options: { model: 'gemini-pro', server_sent_events: true }
380
429
  )
381
430
 
431
+ # With the Service Account Credentials File contents
432
+ client = Gemini.new(
433
+ credentials: {
434
+ service: 'vertex-ai-api',
435
+ file_contents: File.read('google-credentials.json'),
436
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
437
+ region: 'us-east4'
438
+ },
439
+ options: { model: 'gemini-pro', server_sent_events: true }
440
+ )
441
+
382
442
  # With Application Default Credentials
383
443
  client = Gemini.new(
384
444
  credentials: {
@@ -853,6 +913,208 @@ Result:
853
913
  } }]
854
914
  ```
855
915
 
916
+ ### Safety Settings
917
+
918
+ You can [configure safety attributes](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-attributes) for your requests.
919
+
920
+ Harm Categories:
921
+ > `HARM_CATEGORY_UNSPECIFIED`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_DANGEROUS_CONTENT`.
922
+
923
+ Thresholds:
924
+ > `BLOCK_NONE`, `BLOCK_ONLY_HIGH`, `BLOCK_MEDIUM_AND_ABOVE`, `BLOCK_LOW_AND_ABOVE`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`.
925
+
926
+ Example:
927
+ ```ruby
928
+ client.stream_generate_content(
929
+ {
930
+ contents: { role: 'user', parts: { text: 'hi!' } },
931
+ safetySettings: [
932
+ {
933
+ category: 'HARM_CATEGORY_UNSPECIFIED',
934
+ threshold: 'BLOCK_ONLY_HIGH'
935
+ },
936
+ {
937
+ category: 'HARM_CATEGORY_HARASSMENT',
938
+ threshold: 'BLOCK_ONLY_HIGH'
939
+ },
940
+ {
941
+ category: 'HARM_CATEGORY_HATE_SPEECH',
942
+ threshold: 'BLOCK_ONLY_HIGH'
943
+ },
944
+ {
945
+ category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
946
+ threshold: 'BLOCK_ONLY_HIGH'
947
+ },
948
+ {
949
+ category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
950
+ threshold: 'BLOCK_ONLY_HIGH'
951
+ }
952
+ ]
953
+ }
954
+ )
955
+ ```
956
+
957
+ Google started to block the usage of `BLOCK_NONE` unless:
958
+
959
+ > _User has requested a restricted HarmBlockThreshold setting BLOCK_NONE. You can get access either (a) through an allowlist via your Google account team, or (b) by switching your account type to monthly invoiced billing via this instruction: https://cloud.google.com/billing/docs/how-to/invoiced-billing_
960
+
961
+ ### System Instructions
962
+
963
+ Some models support [system instructions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions):
964
+
965
+ ```ruby
966
+ client.stream_generate_content(
967
+ { contents: { role: 'user', parts: { text: 'Hi! Who are you?' } },
968
+ system_instruction: { role: 'user', parts: { text: 'Your name is Neko.' } } }
969
+ )
970
+ ```
971
+
972
+ Output:
973
+ ```text
974
+ Hi! I'm Neko, a factual language model from Google AI.
975
+ ```
976
+
977
+ ```ruby
978
+ client.stream_generate_content(
979
+ { contents: { role: 'user', parts: { text: 'Hi! Who are you?' } },
980
+ system_instruction: {
981
+ role: 'user', parts: [
982
+ { text: 'You are a cat.' },
983
+ { text: 'Your name is Neko.' }
984
+ ]
985
+ } }
986
+ )
987
+ ```
988
+
989
+ Output:
990
+ ```text
991
+ Meow! I'm Neko, a fluffy and playful cat. :3
992
+ ```
993
+
994
+ ### JSON Format Responses
995
+
996
+ > _As of the writing of this README, only the `vertex-ai-api` service and `gemini` models version `1.5` support this feature._
997
+
998
+ The Gemini API provides a configuration parameter to [request a response in JSON](https://ai.google.dev/gemini-api/docs/api-overview#json) format:
999
+
1000
+ ```ruby
1001
+ require 'json'
1002
+
1003
+ result = client.stream_generate_content(
1004
+ {
1005
+ contents: {
1006
+ role: 'user',
1007
+ parts: {
1008
+ text: 'List 3 random colors.'
1009
+ }
1010
+ },
1011
+ generation_config: {
1012
+ response_mime_type: 'application/json'
1013
+ }
1014
+
1015
+ }
1016
+ )
1017
+
1018
+ json_string = result
1019
+ .map { |response| response.dig('candidates', 0, 'content', 'parts') }
1020
+ .map { |parts| parts.map { |part| part['text'] }.join }
1021
+ .join
1022
+
1023
+ puts JSON.parse(json_string).inspect
1024
+ ```
1025
+
1026
+ Output:
1027
+ ```ruby
1028
+ { 'colors' => ['Dark Salmon', 'Indigo', 'Lavender'] }
1029
+ ```
1030
+
1031
+ #### JSON Schema
1032
+
1033
+ > _While Gemini 1.5 Flash models only accept a text description of the JSON schema you want returned, the Gemini 1.5 Pro models let you pass a schema object (or a Python type equivalent), and the model output will strictly follow that schema. This is also known as controlled generation or constrained decoding._
1034
+
1035
+ You can also provide a [JSON Schema](https://json-schema.org) for the expected JSON output:
1036
+
1037
+ ```ruby
1038
+ require 'json'
1039
+
1040
+ result = client.stream_generate_content(
1041
+ {
1042
+ contents: {
1043
+ role: 'user',
1044
+ parts: {
1045
+ text: 'List 3 random colors.'
1046
+ }
1047
+ },
1048
+ generation_config: {
1049
+ response_mime_type: 'application/json',
1050
+ response_schema: {
1051
+ type: 'object',
1052
+ properties: {
1053
+ colors: {
1054
+ type: 'array',
1055
+ items: {
1056
+ type: 'object',
1057
+ properties: {
1058
+ name: {
1059
+ type: 'string'
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ )
1069
+
1070
+ json_string = result
1071
+ .map { |response| response.dig('candidates', 0, 'content', 'parts') }
1072
+ .map { |parts| parts.map { |part| part['text'] }.join }
1073
+ .join
1074
+
1075
+ puts JSON.parse(json_string).inspect
1076
+ ```
1077
+
1078
+ Output:
1079
+
1080
+ ```ruby
1081
+ { 'colors' => [
1082
+ { 'name' => 'Lavender Blush' },
1083
+ { 'name' => 'Medium Turquoise' },
1084
+ { 'name' => 'Dark Slate Gray' }
1085
+ ] }
1086
+ ```
1087
+
1088
+ #### Models That Support JSON
1089
+
1090
+ These models are accessible to the repository **author** as of June 2025 in the `us-east4` region. Access to models may vary by region, user, and account.
1091
+
1092
+ - ❌ Does not support JSON mode.
1093
+ - 🟡 Supports JSON mode but not Schema.
1094
+ - ✅ Supports JSON mode and Schema.
1095
+ - 🔒 I don't have access to the model.
1096
+
1097
+ | Model | Vertex AI | Generative Language |
1098
+ |------------------------------------------|:---------:|:-------------------:|
1099
+ | gemini-pro-vision | ❌ | 🔒 |
1100
+ | gemini-pro | 🟡 | ❌ |
1101
+ | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
1102
+ | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
1103
+ | gemini-1.5-pro | ✅ | ❌ |
1104
+ | gemini-1.5-flash-preview-0514 | 🟡 | 🔒 |
1105
+ | gemini-1.5-flash | 🟡 | ❌ |
1106
+ | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
1107
+ | gemini-1.0-pro-vision-001 | ❌ | 🔒 |
1108
+ | gemini-1.0-pro-vision | ❌ | 🔒 |
1109
+ | gemini-1.0-pro-latest | 🔒 | ❌ |
1110
+ | gemini-1.0-pro-002 | 🟡 | 🔒 |
1111
+ | gemini-1.0-pro-001 | ❌ | ❌ |
1112
+ | gemini-1.0-pro | 🟡 | ❌ |
1113
+ | gemini-ultra | 🔒 | 🔒 |
1114
+ | gemini-1.0-ultra | 🔒 | 🔒 |
1115
+ | gemini-1.0-ultra-001 | 🔒 | 🔒 |
1116
+
1117
+
856
1118
  ### Tools (Functions) Calling
857
1119
 
858
1120
  > 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.
@@ -1123,6 +1385,7 @@ GeminiError
1123
1385
 
1124
1386
  MissingProjectIdError
1125
1387
  UnsupportedServiceError
1388
+ ConflictingCredentialsError
1126
1389
  BlockWithoutServerSentEventsError
1127
1390
 
1128
1391
  RequestError
@@ -1134,7 +1397,14 @@ RequestError
1134
1397
  bundle
1135
1398
  rubocop -A
1136
1399
 
1137
- bundle exec ruby spec/tasks/run-client.rb
1400
+ rspec
1401
+
1402
+ bundle exec ruby spec/tasks/run-available-models.rb
1403
+ bundle exec ruby spec/tasks/run-embed.rb
1404
+ bundle exec ruby spec/tasks/run-generate.rb
1405
+ bundle exec ruby spec/tasks/run-json.rb
1406
+ bundle exec ruby spec/tasks/run-safety.rb
1407
+ bundle exec ruby spec/tasks/run-system.rb
1138
1408
  ```
1139
1409
 
1140
1410
  ### Purpose
@@ -1148,7 +1418,7 @@ gem build gemini-ai.gemspec
1148
1418
 
1149
1419
  gem signin
1150
1420
 
1151
- gem push gemini-ai-4.0.0.gem
1421
+ gem push gemini-ai-4.1.0.gem
1152
1422
  ```
1153
1423
 
1154
1424
  ### Updating the README
@@ -1193,6 +1463,8 @@ These resources and references may be useful throughout your learning process.
1193
1463
  - [Vertex AI API Documentation](https://cloud.google.com/vertex-ai/docs/reference)
1194
1464
  - [REST Documentation](https://cloud.google.com/vertex-ai/docs/reference/rest)
1195
1465
  - [Get text embeddings](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
1466
+ - [Use system instructions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions)
1467
+ - [Configure safety attributes](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-attributes)
1196
1468
  - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
1197
1469
  - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
1198
1470
  - [Google DeepMind Gemini](https://deepmind.google/technologies/gemini/)
data/components/errors.rb CHANGED
@@ -4,12 +4,13 @@ module Gemini
4
4
  module Errors
5
5
  class GeminiError < StandardError
6
6
  def initialize(message = nil)
7
- super(message)
7
+ super
8
8
  end
9
9
  end
10
10
 
11
11
  class MissingProjectIdError < GeminiError; end
12
12
  class UnsupportedServiceError < GeminiError; end
13
+ class ConflictingCredentialsError < GeminiError; end
13
14
  class BlockWithoutServerSentEventsError < GeminiError; end
14
15
 
15
16
  class RequestError < GeminiError
@@ -21,16 +21,24 @@ module Gemini
21
21
  @service = config[:credentials][:service]
22
22
 
23
23
  unless %w[vertex-ai-api generative-language-api].include?(@service)
24
- raise Errors::UnsupportedServiceError, "Unsupported service: #{@service}"
24
+ raise Errors::UnsupportedServiceError, "Unsupported service: '#{@service}'."
25
25
  end
26
26
 
27
+ avoid_conflicting_credentials!(config[:credentials])
28
+
27
29
  if config[:credentials][:api_key]
28
30
  @authentication = :api_key
29
31
  @api_key = config[:credentials][:api_key]
30
- elsif config[:credentials][:file_path]
32
+ elsif config[:credentials][:file_path] || config[:credentials][:file_contents]
31
33
  @authentication = :service_account
34
+ json_key_io = if config[:credentials][:file_path]
35
+ File.open(config[:credentials][:file_path])
36
+ else
37
+ StringIO.new(config[:credentials][:file_contents])
38
+ end
39
+
32
40
  @authorizer = ::Google::Auth::ServiceAccountCredentials.make_creds(
33
- json_key_io: File.open(config[:credentials][:file_path]),
41
+ json_key_io:,
34
42
  scope: 'https://www.googleapis.com/auth/cloud-platform'
35
43
  )
36
44
  else
@@ -75,6 +83,21 @@ module Gemini
75
83
  end
76
84
  end
77
85
 
86
+ def avoid_conflicting_credentials!(credentials)
87
+ conflicting_keys = %i[api_key file_path file_contents]
88
+
89
+ found = credentials.keys.filter { |key| conflicting_keys.include?(key) }
90
+
91
+ return unless found.size > 1
92
+
93
+ message = found.sort.each_with_index.map do |key, i|
94
+ i == found.size - 1 ? "or '#{key}'" : "'#{key}'"
95
+ end.join(', ')
96
+
97
+ raise Errors::ConflictingCredentialsError,
98
+ "You must choose either #{message}."
99
+ end
100
+
78
101
  def predict(payload, server_sent_events: nil, &callback)
79
102
  result = request(
80
103
  "#{@model_address}:predict", payload,
data/gemini-ai.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ['ports/dsl']
31
31
 
32
32
  spec.add_dependency 'event_stream_parser', '~> 1.0'
33
- spec.add_dependency 'faraday', '~> 2.9'
33
+ spec.add_dependency 'faraday', '~> 2.9', '>= 2.9.2'
34
34
  spec.add_dependency 'faraday-typhoeus', '~> 1.1'
35
35
 
36
36
  # Before upgrading, check this:
data/static/gem.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Gemini
4
4
  GEM = {
5
5
  name: 'gemini-ai',
6
- version: '4.0.0',
6
+ version: '4.1.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.",
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', '~> 4.0.0'
12
+ gem 'gemini-ai', '~> 4.1.0'
13
13
  ```
14
14
 
15
15
  ```ruby
@@ -34,6 +34,17 @@ client = Gemini.new(
34
34
  options: { model: 'gemini-pro', server_sent_events: true }
35
35
  )
36
36
 
37
+ # With the Service Account Credentials File contents
38
+ client = Gemini.new(
39
+ credentials: {
40
+ service: 'vertex-ai-api',
41
+ file_contents: File.read('google-credentials.json'),
42
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
43
+ region: 'us-east4'
44
+ },
45
+ options: { model: 'gemini-pro', server_sent_events: true }
46
+ )
47
+
37
48
  # With Application Default Credentials
38
49
  client = Gemini.new(
39
50
  credentials: {
@@ -77,11 +88,11 @@ Result:
77
88
  ### Installing
78
89
 
79
90
  ```sh
80
- gem install gemini-ai -v 4.0.0
91
+ gem install gemini-ai -v 4.1.0
81
92
  ```
82
93
 
83
94
  ```sh
84
- gem 'gemini-ai', '~> 4.0.0'
95
+ gem 'gemini-ai', '~> 4.1.0'
85
96
  ```
86
97
 
87
98
  ### Credentials
@@ -163,7 +174,7 @@ Similar to [Option 2](#option-2-service-account-credentials-file-vertex-ai-api),
163
174
  For local development, you can generate your default credentials using the [gcloud CLI](https://cloud.google.com/sdk/gcloud) as follows:
164
175
 
165
176
  ```sh
166
- gcloud auth application-default login
177
+ gcloud auth application-default login
167
178
  ```
168
179
 
169
180
  For more details about alternative methods and different environments, check the official documentation:
@@ -201,6 +212,23 @@ Remember that hardcoding your API key in code is unsafe; it's preferable to use
201
212
  }
202
213
  ```
203
214
 
215
+ Alternatively, you can pass the file contents instead of the path:
216
+ ```ruby
217
+ {
218
+ service: 'vertex-ai-api',
219
+ file_contents: File.read('google-credentials.json'),
220
+ region: 'us-east4'
221
+ }
222
+ ```
223
+
224
+ ```ruby
225
+ {
226
+ service: 'vertex-ai-api',
227
+ file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
228
+ region: 'us-east4'
229
+ }
230
+ ```
231
+
204
232
  **Option 3**: For _Application Default Credentials_, omit both the `api_key` and the `file_path`:
205
233
 
206
234
  ```ruby
@@ -259,6 +287,17 @@ client = Gemini.new(
259
287
  options: { model: 'gemini-pro', server_sent_events: true }
260
288
  )
261
289
 
290
+ # With the Service Account Credentials File contents
291
+ client = Gemini.new(
292
+ credentials: {
293
+ service: 'vertex-ai-api',
294
+ file_contents: File.read('google-credentials.json'),
295
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
296
+ region: 'us-east4'
297
+ },
298
+ options: { model: 'gemini-pro', server_sent_events: true }
299
+ )
300
+
262
301
  # With Application Default Credentials
263
302
  client = Gemini.new(
264
303
  credentials: {
@@ -272,7 +311,7 @@ client = Gemini.new(
272
311
 
273
312
  ## Available Models
274
313
 
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:
314
+ These models are accessible to the repository **author** as of June 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
315
 
277
316
  | Model | Vertex AI | Generative Language |
278
317
  |------------------------------------------|:---------:|:-------------------:|
@@ -280,9 +319,9 @@ These models are accessible to the repository **author** as of May 2025 in the `
280
319
  | gemini-pro | ✅ | ✅ |
281
320
  | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
282
321
  | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
283
- | gemini-1.5-pro | 🔒 | 🔒 |
322
+ | gemini-1.5-pro | | |
284
323
  | gemini-1.5-flash-preview-0514 | ✅ | 🔒 |
285
- | gemini-1.5-flash | 🔒 | 🔒 |
324
+ | gemini-1.5-flash | | |
286
325
  | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
287
326
  | gemini-1.0-pro-vision-001 | ✅ | 🔒 |
288
327
  | gemini-1.0-pro-vision | ✅ | 🔒 |
@@ -290,6 +329,11 @@ These models are accessible to the repository **author** as of May 2025 in the `
290
329
  | gemini-1.0-pro-002 | ✅ | 🔒 |
291
330
  | gemini-1.0-pro-001 | ✅ | ✅ |
292
331
  | gemini-1.0-pro | ✅ | ✅ |
332
+ | gemini-ultra | 🔒 | 🔒 |
333
+ | gemini-1.0-ultra | 🔒 | 🔒 |
334
+ | gemini-1.0-ultra-001 | 🔒 | 🔒 |
335
+ | text-embedding-preview-0514 | 🔒 | 🔒 |
336
+ | text-embedding-preview-0409 | 🔒 | 🔒 |
293
337
  | text-embedding-004 | ✅ | ✅ |
294
338
  | embedding-001 | 🔒 | ✅ |
295
339
  | text-multilingual-embedding-002 | ✅ | 🔒 |
@@ -335,6 +379,17 @@ client = Gemini.new(
335
379
  options: { model: 'gemini-pro', server_sent_events: true }
336
380
  )
337
381
 
382
+ # With the Service Account Credentials File contents
383
+ client = Gemini.new(
384
+ credentials: {
385
+ service: 'vertex-ai-api',
386
+ file_contents: File.read('google-credentials.json'),
387
+ # file_contents: ENV['GOOGLE_CREDENTIALS_FILE_CONTENTS'],
388
+ region: 'us-east4'
389
+ },
390
+ options: { model: 'gemini-pro', server_sent_events: true }
391
+ )
392
+
338
393
  # With Application Default Credentials
339
394
  client = Gemini.new(
340
395
  credentials: {
@@ -809,6 +864,208 @@ Result:
809
864
  } }]
810
865
  ```
811
866
 
867
+ ### Safety Settings
868
+
869
+ You can [configure safety attributes](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-attributes) for your requests.
870
+
871
+ Harm Categories:
872
+ > `HARM_CATEGORY_UNSPECIFIED`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_DANGEROUS_CONTENT`.
873
+
874
+ Thresholds:
875
+ > `BLOCK_NONE`, `BLOCK_ONLY_HIGH`, `BLOCK_MEDIUM_AND_ABOVE`, `BLOCK_LOW_AND_ABOVE`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`.
876
+
877
+ Example:
878
+ ```ruby
879
+ client.stream_generate_content(
880
+ {
881
+ contents: { role: 'user', parts: { text: 'hi!' } },
882
+ safetySettings: [
883
+ {
884
+ category: 'HARM_CATEGORY_UNSPECIFIED',
885
+ threshold: 'BLOCK_ONLY_HIGH'
886
+ },
887
+ {
888
+ category: 'HARM_CATEGORY_HARASSMENT',
889
+ threshold: 'BLOCK_ONLY_HIGH'
890
+ },
891
+ {
892
+ category: 'HARM_CATEGORY_HATE_SPEECH',
893
+ threshold: 'BLOCK_ONLY_HIGH'
894
+ },
895
+ {
896
+ category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
897
+ threshold: 'BLOCK_ONLY_HIGH'
898
+ },
899
+ {
900
+ category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
901
+ threshold: 'BLOCK_ONLY_HIGH'
902
+ }
903
+ ]
904
+ }
905
+ )
906
+ ```
907
+
908
+ Google started to block the usage of `BLOCK_NONE` unless:
909
+
910
+ > _User has requested a restricted HarmBlockThreshold setting BLOCK_NONE. You can get access either (a) through an allowlist via your Google account team, or (b) by switching your account type to monthly invoiced billing via this instruction: https://cloud.google.com/billing/docs/how-to/invoiced-billing_
911
+
912
+ ### System Instructions
913
+
914
+ Some models support [system instructions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions):
915
+
916
+ ```ruby
917
+ client.stream_generate_content(
918
+ { contents: { role: 'user', parts: { text: 'Hi! Who are you?' } },
919
+ system_instruction: { role: 'user', parts: { text: 'Your name is Neko.' } } }
920
+ )
921
+ ```
922
+
923
+ Output:
924
+ ```text
925
+ Hi! I'm Neko, a factual language model from Google AI.
926
+ ```
927
+
928
+ ```ruby
929
+ client.stream_generate_content(
930
+ { contents: { role: 'user', parts: { text: 'Hi! Who are you?' } },
931
+ system_instruction: {
932
+ role: 'user', parts: [
933
+ { text: 'You are a cat.' },
934
+ { text: 'Your name is Neko.' }
935
+ ]
936
+ } }
937
+ )
938
+ ```
939
+
940
+ Output:
941
+ ```text
942
+ Meow! I'm Neko, a fluffy and playful cat. :3
943
+ ```
944
+
945
+ ### JSON Format Responses
946
+
947
+ > _As of the writing of this README, only the `vertex-ai-api` service and `gemini` models version `1.5` support this feature._
948
+
949
+ The Gemini API provides a configuration parameter to [request a response in JSON](https://ai.google.dev/gemini-api/docs/api-overview#json) format:
950
+
951
+ ```ruby
952
+ require 'json'
953
+
954
+ result = client.stream_generate_content(
955
+ {
956
+ contents: {
957
+ role: 'user',
958
+ parts: {
959
+ text: 'List 3 random colors.'
960
+ }
961
+ },
962
+ generation_config: {
963
+ response_mime_type: 'application/json'
964
+ }
965
+
966
+ }
967
+ )
968
+
969
+ json_string = result
970
+ .map { |response| response.dig('candidates', 0, 'content', 'parts') }
971
+ .map { |parts| parts.map { |part| part['text'] }.join }
972
+ .join
973
+
974
+ puts JSON.parse(json_string).inspect
975
+ ```
976
+
977
+ Output:
978
+ ```ruby
979
+ { 'colors' => ['Dark Salmon', 'Indigo', 'Lavender'] }
980
+ ```
981
+
982
+ #### JSON Schema
983
+
984
+ > _While Gemini 1.5 Flash models only accept a text description of the JSON schema you want returned, the Gemini 1.5 Pro models let you pass a schema object (or a Python type equivalent), and the model output will strictly follow that schema. This is also known as controlled generation or constrained decoding._
985
+
986
+ You can also provide a [JSON Schema](https://json-schema.org) for the expected JSON output:
987
+
988
+ ```ruby
989
+ require 'json'
990
+
991
+ result = client.stream_generate_content(
992
+ {
993
+ contents: {
994
+ role: 'user',
995
+ parts: {
996
+ text: 'List 3 random colors.'
997
+ }
998
+ },
999
+ generation_config: {
1000
+ response_mime_type: 'application/json',
1001
+ response_schema: {
1002
+ type: 'object',
1003
+ properties: {
1004
+ colors: {
1005
+ type: 'array',
1006
+ items: {
1007
+ type: 'object',
1008
+ properties: {
1009
+ name: {
1010
+ type: 'string'
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ )
1020
+
1021
+ json_string = result
1022
+ .map { |response| response.dig('candidates', 0, 'content', 'parts') }
1023
+ .map { |parts| parts.map { |part| part['text'] }.join }
1024
+ .join
1025
+
1026
+ puts JSON.parse(json_string).inspect
1027
+ ```
1028
+
1029
+ Output:
1030
+
1031
+ ```ruby
1032
+ { 'colors' => [
1033
+ { 'name' => 'Lavender Blush' },
1034
+ { 'name' => 'Medium Turquoise' },
1035
+ { 'name' => 'Dark Slate Gray' }
1036
+ ] }
1037
+ ```
1038
+
1039
+ #### Models That Support JSON
1040
+
1041
+ These models are accessible to the repository **author** as of June 2025 in the `us-east4` region. Access to models may vary by region, user, and account.
1042
+
1043
+ - ❌ Does not support JSON mode.
1044
+ - 🟡 Supports JSON mode but not Schema.
1045
+ - ✅ Supports JSON mode and Schema.
1046
+ - 🔒 I don't have access to the model.
1047
+
1048
+ | Model | Vertex AI | Generative Language |
1049
+ |------------------------------------------|:---------:|:-------------------:|
1050
+ | gemini-pro-vision | ❌ | 🔒 |
1051
+ | gemini-pro | 🟡 | ❌ |
1052
+ | gemini-1.5-pro-preview-0514 | ✅ | 🔒 |
1053
+ | gemini-1.5-pro-preview-0409 | ✅ | 🔒 |
1054
+ | gemini-1.5-pro | ✅ | ❌ |
1055
+ | gemini-1.5-flash-preview-0514 | 🟡 | 🔒 |
1056
+ | gemini-1.5-flash | 🟡 | ❌ |
1057
+ | gemini-1.0-pro-vision-latest | 🔒 | 🔒 |
1058
+ | gemini-1.0-pro-vision-001 | ❌ | 🔒 |
1059
+ | gemini-1.0-pro-vision | ❌ | 🔒 |
1060
+ | gemini-1.0-pro-latest | 🔒 | ❌ |
1061
+ | gemini-1.0-pro-002 | 🟡 | 🔒 |
1062
+ | gemini-1.0-pro-001 | ❌ | ❌ |
1063
+ | gemini-1.0-pro | 🟡 | ❌ |
1064
+ | gemini-ultra | 🔒 | 🔒 |
1065
+ | gemini-1.0-ultra | 🔒 | 🔒 |
1066
+ | gemini-1.0-ultra-001 | 🔒 | 🔒 |
1067
+
1068
+
812
1069
  ### Tools (Functions) Calling
813
1070
 
814
1071
  > 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.
@@ -1079,6 +1336,7 @@ GeminiError
1079
1336
 
1080
1337
  MissingProjectIdError
1081
1338
  UnsupportedServiceError
1339
+ ConflictingCredentialsError
1082
1340
  BlockWithoutServerSentEventsError
1083
1341
 
1084
1342
  RequestError
@@ -1090,7 +1348,14 @@ RequestError
1090
1348
  bundle
1091
1349
  rubocop -A
1092
1350
 
1093
- bundle exec ruby spec/tasks/run-client.rb
1351
+ rspec
1352
+
1353
+ bundle exec ruby spec/tasks/run-available-models.rb
1354
+ bundle exec ruby spec/tasks/run-embed.rb
1355
+ bundle exec ruby spec/tasks/run-generate.rb
1356
+ bundle exec ruby spec/tasks/run-json.rb
1357
+ bundle exec ruby spec/tasks/run-safety.rb
1358
+ bundle exec ruby spec/tasks/run-system.rb
1094
1359
  ```
1095
1360
 
1096
1361
  ### Purpose
@@ -1104,7 +1369,7 @@ gem build gemini-ai.gemspec
1104
1369
 
1105
1370
  gem signin
1106
1371
 
1107
- gem push gemini-ai-4.0.0.gem
1372
+ gem push gemini-ai-4.1.0.gem
1108
1373
  ```
1109
1374
 
1110
1375
  ### Updating the README
@@ -1149,6 +1414,8 @@ These resources and references may be useful throughout your learning process.
1149
1414
  - [Vertex AI API Documentation](https://cloud.google.com/vertex-ai/docs/reference)
1150
1415
  - [REST Documentation](https://cloud.google.com/vertex-ai/docs/reference/rest)
1151
1416
  - [Get text embeddings](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
1417
+ - [Use system instructions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions)
1418
+ - [Configure safety attributes](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-attributes)
1152
1419
  - [Google models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models)
1153
1420
  - [Model versions and lifecycle](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning)
1154
1421
  - [Google DeepMind Gemini](https://deepmind.google/technologies/gemini/)
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: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gbaptista
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-19 00:00:00.000000000 Z
11
+ date: 2024-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -31,6 +31,9 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '2.9'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.9.2
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,6 +41,9 @@ dependencies:
38
41
  - - "~>"
39
42
  - !ruby/object:Gem::Version
40
43
  version: '2.9'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.9.2
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: faraday-typhoeus
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +100,7 @@ extensions: []
94
100
  extra_rdoc_files: []
95
101
  files:
96
102
  - ".gitignore"
103
+ - ".rspec"
97
104
  - ".rubocop.yml"
98
105
  - ".ruby-version"
99
106
  - Gemfile