gemini-ai 3.2.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.
@@ -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
@@ -46,12 +54,19 @@ module Gemini
46
54
 
47
55
  @service_version = config.dig(:credentials, :version) || DEFAULT_SERVICE_VERSION
48
56
 
49
- @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]}/publishers/google/models/#{config[:options][:model]}"
52
- when 'generative-language-api'
53
- "https://generativelanguage.googleapis.com/#{@service_version}/models/#{config[:options][:model]}"
54
- end
57
+ @base_address = case @service
58
+ when 'vertex-ai-api'
59
+ "https://#{config[:credentials][:region]}-aiplatform.googleapis.com/#{@service_version}/projects/#{@project_id}/locations/#{config[:credentials][:region]}"
60
+ when 'generative-language-api'
61
+ "https://generativelanguage.googleapis.com/#{@service_version}"
62
+ end
63
+
64
+ @model_address = case @service
65
+ when 'vertex-ai-api'
66
+ "publishers/google/models/#{config[:options][:model]}"
67
+ when 'generative-language-api'
68
+ "models/#{config[:options][:model]}"
69
+ end
55
70
 
56
71
  @server_sent_events = config.dig(:options, :server_sent_events)
57
72
 
@@ -68,21 +83,74 @@ module Gemini
68
83
  end
69
84
  end
70
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
+
101
+ def predict(payload, server_sent_events: nil, &callback)
102
+ result = request(
103
+ "#{@model_address}:predict", payload,
104
+ server_sent_events:, &callback
105
+ )
106
+
107
+ return result.first if result.is_a?(Array) && result.size == 1
108
+
109
+ result
110
+ end
111
+
112
+ def embed_content(payload, server_sent_events: nil, &callback)
113
+ result = request(
114
+ "#{@model_address}:embedContent", payload,
115
+ server_sent_events:, &callback
116
+ )
117
+
118
+ return result.first if result.is_a?(Array) && result.size == 1
119
+
120
+ result
121
+ end
122
+
71
123
  def stream_generate_content(payload, server_sent_events: nil, &callback)
72
- request('streamGenerateContent', payload, server_sent_events:, &callback)
124
+ request("#{@model_address}:streamGenerateContent", payload, server_sent_events:, &callback)
125
+ end
126
+
127
+ def models(_server_sent_events: nil, &callback)
128
+ result = request(
129
+ 'models',
130
+ nil, server_sent_events: false, request_method: 'GET', &callback
131
+ )
132
+
133
+ return result.first if result.is_a?(Array) && result.size == 1
134
+
135
+ result
73
136
  end
74
137
 
75
138
  def generate_content(payload, server_sent_events: nil, &callback)
76
- result = request('generateContent', payload, server_sent_events:, &callback)
139
+ result = request(
140
+ "#{@model_address}:generateContent", payload,
141
+ server_sent_events:, &callback
142
+ )
77
143
 
78
144
  return result.first if result.is_a?(Array) && result.size == 1
79
145
 
80
146
  result
81
147
  end
82
148
 
83
- def request(path, payload, server_sent_events: nil, &callback)
149
+ def request(path, payload, server_sent_events: nil, request_method: 'POST', &callback)
84
150
  server_sent_events_enabled = server_sent_events.nil? ? @server_sent_events : server_sent_events
85
- url = "#{@address}:#{path}"
151
+
152
+ url = "#{@base_address}/#{path}"
153
+
86
154
  params = []
87
155
 
88
156
  params << 'alt=sse' if server_sent_events_enabled
@@ -97,20 +165,21 @@ module Gemini
97
165
 
98
166
  results = []
99
167
 
168
+ method_to_call = request_method.to_s.strip.downcase.to_sym
169
+
100
170
  response = Faraday.new(request: @request_options) do |faraday|
101
171
  faraday.adapter @faraday_adapter
102
172
  faraday.response :raise_error
103
- end.post do |request|
173
+ end.send(method_to_call) do |request|
104
174
  request.url url
105
175
  request.headers['Content-Type'] = 'application/json'
106
176
  if @authentication == :service_account || @authentication == :default_credentials
107
177
  request.headers['Authorization'] = "Bearer #{@authorizer.fetch_access_token!['access_token']}"
108
178
  end
109
179
 
110
- request.body = payload.to_json
180
+ request.body = payload.to_json unless payload.nil?
111
181
 
112
182
  if server_sent_events_enabled
113
-
114
183
  partial_json = ''
115
184
 
116
185
  parser = EventStreamParser::Parser.new
data/gemini-ai.gemspec CHANGED
@@ -30,9 +30,13 @@ 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
+
36
+ # Before upgrading, check this:
37
+ # https://github.com/gbaptista/gemini-ai/pull/10
35
38
  spec.add_dependency 'googleauth', '~> 1.8'
39
+
36
40
  spec.add_dependency 'typhoeus', '~> 1.4', '>= 1.4.1'
37
41
 
38
42
  spec.metadata['rubygems_mfa_required'] = 'true'
data/static/gem.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Gemini
4
4
  GEM = {
5
5
  name: 'gemini-ai',
6
- version: '3.2.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.",
@@ -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
  "](#"