gemini-ai 3.2.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  "](#"