omniai-google 3.21.0 → 3.23.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: ce2434a17917341984b99049670ef65083443c2c04b8937d2b8d4af688a5b631
4
- data.tar.gz: 6c22fa11d66efc7df6ca393bc7f167c5f682ac0398a934a6b25a8674920a6905
3
+ metadata.gz: 5b5542c78680371ff3d0e4268303377d97e71867bb1116811852ee7ef7e6c397
4
+ data.tar.gz: ea3ee571ac27dca8d87c75b314961e2ac23b5ef0bc860c3aab84c0f1d4680e87
5
5
  SHA512:
6
- metadata.gz: f935566b3cae177f1bd445f19e2a2b87ae32d9acc274d5a5a4cb470b867c36d3e2382d9ca945f5b8e5a00a617806a1c1d4399905b642c716b2c10e9bf6dfd568
7
- data.tar.gz: b183524e4139f0a2470749fde7bb0fb837dc32cd0ca9f810af1559d44a2fbef4e62a3e5fe98e8b385de4f66559b55026a643a04371477f909ffd9bb1d45adaca
6
+ metadata.gz: 777e0f53cd5943ef85795e2a141164bc3206db3ba9a340d0001bdc95a296b2ffa88ef5f786ee7c1f15c3500dab679f8878253956ad38879518aa82a9226fa54a
7
+ data.tar.gz: 341ab1bf0279b58e7ff94d62a36243598f86b3a5766cb3490913d7e1e6beb9509defe80f28540c9b2371a04d630a56324532b7d16e3dcb1ee6be3efe294b4d04
data/README.md CHANGED
@@ -58,6 +58,30 @@ OmniAI::Google.configure do |config|
58
58
  end
59
59
  ```
60
60
 
61
+ #### Vertex Locations per Model
62
+
63
+ No single Vertex location serves every model. To route each model to its own location, set `locations`; the host is
64
+ derived from the location the way Google's SDKs do it (`global`, the `us` / `eu` multi-regions, or a single region):
65
+
66
+ ```ruby
67
+ OmniAI::Google.configure do |config|
68
+ config.credentials = File.open("./credentials.json")
69
+ config.project_id = '...'
70
+ config.locations = {
71
+ 'gemini-3.8-flash' => 'us', # https://aiplatform.us.rep.googleapis.com
72
+ 'gemini-2.5-flash' => 'us-central1', # https://us-central1-aiplatform.googleapis.com
73
+ 'gemini-embedding-001' => 'us-central1',
74
+ 'chirp_3' => 'us',
75
+ }
76
+ end
77
+ ```
78
+
79
+ - Applies to chat (including streaming), embed, transcribe and live. A mapped location is used as given.
80
+ - A model missing from the map raises `OmniAI::Google::UnknownLocationError` (with `#model`) before any request.
81
+ List every model you call, including any default you rely on (`Chat::DEFAULT_MODEL`, `Embed::DEFAULT_MODEL`,
82
+ `Transcribe::DEFAULT_MODEL`).
83
+ - A client built with an explicit `host:` or `location_id:` ignores the map.
84
+
61
85
  **Note for Transcription**: When using transcription features, ensure your service account has the necessary permissions for Google Cloud Speech-to-Text API and Google Cloud Storage (for automatic file uploads). See the [GCS Setup](#gcs-setup-for-transcription) section below for detailed configuration.
62
86
 
63
87
  Credentials may be configured using:
@@ -76,6 +76,11 @@ module OmniAI
76
76
  context.deserializers[:thinking] = ThinkingSerializer.method(:deserialize)
77
77
  end
78
78
 
79
+ # `client` is swapped for `client.for_model(model)`, so `locations` routes the request.
80
+ def initialize(prompt = nil, client:, model:, **, &)
81
+ super(prompt, client: client.for_model(model), model:, **, &)
82
+ end
83
+
79
84
  protected
80
85
 
81
86
  # @return [Context]
@@ -20,6 +20,10 @@ module OmniAI
20
20
  #
21
21
  # client = OmniAI::Google::Client.new
22
22
  class Client < OmniAI::Client
23
+ # Marks `host` / `location_id` as not passed, so an explicit value can take precedence over `locations`.
24
+ UNSET = Object.new.freeze
25
+ private_constant :UNSET
26
+
23
27
  # @!attribute [rw] version
24
28
  # @return [String, nil]
25
29
  attr_accessor :version
@@ -32,28 +36,56 @@ module OmniAI
32
36
  # @param version [String] default is `OmniAI::Google.config.version`
33
37
  # @param logger [Logger] default is `OmniAI::Google.config.logger`
34
38
  # @param timeout [Integer] default is `OmniAI::Google.config.timeout`
39
+ # @param locations [Hash{String => String}, nil] default is `OmniAI::Google.config.locations`; ignored when
40
+ # `host` or `location_id` is passed
35
41
  def initialize(
36
42
  api_key: OmniAI::Google.config.api_key,
37
43
  project_id: OmniAI::Google.config.project_id,
38
- location_id: OmniAI::Google.config.location_id,
44
+ location_id: UNSET,
39
45
  credentials: OmniAI::Google.config.credentials,
40
46
  logger: OmniAI::Google.config.logger,
41
- host: OmniAI::Google.config.host,
47
+ host: UNSET,
42
48
  version: nil,
43
- timeout: OmniAI::Google.config.timeout
49
+ timeout: OmniAI::Google.config.timeout,
50
+ locations: OmniAI::Google.config.locations
44
51
  )
45
52
  if api_key.nil? && credentials.nil?
46
53
  raise(ArgumentError, "either an `api_key` or `credentials` must be provided")
47
54
  end
48
55
 
56
+ pinned = !(UNSET.equal?(host) && UNSET.equal?(location_id))
57
+ @locations = locations unless pinned
58
+ host = OmniAI::Google.config.host if UNSET.equal?(host)
59
+ location_id = OmniAI::Google.config.location_id if UNSET.equal?(location_id)
60
+
49
61
  super(api_key:, host:, logger:, timeout:)
50
62
 
51
63
  @project_id = project_id
52
64
  @location_id = location_id
53
65
  @credentials = Credentials.parse(credentials)
66
+ @version_override = version
54
67
  @version = version || default_version
55
68
  end
56
69
 
70
+ # A copy of this client pointed at the model's location in `locations`, or this client when no map applies.
71
+ #
72
+ # @param model [String]
73
+ #
74
+ # @raise [UnknownLocationError] when the map has no entry for the model
75
+ #
76
+ # @return [Client]
77
+ def for_model(model)
78
+ return self if @locations.nil?
79
+
80
+ location_id = @locations.fetch(model) { raise UnknownLocationError, model }
81
+ dup.tap { |client| client.locate!(location_id) }
82
+ end
83
+
84
+ # @return [Boolean] true for a client returned by `#for_model`
85
+ def located?
86
+ @located || false
87
+ end
88
+
57
89
  # @raise [OmniAI::Error]
58
90
  #
59
91
  # @param messages [String] optional
@@ -155,6 +187,16 @@ module OmniAI
155
187
  false
156
188
  end
157
189
 
190
+ protected
191
+
192
+ # @param location_id [String]
193
+ def locate!(location_id)
194
+ @location_id = location_id
195
+ @host = Vertex.host(location_id)
196
+ @version = @version_override || Config::Version::STABLE
197
+ @located = true
198
+ end
199
+
158
200
  private
159
201
 
160
202
  # Vertex AI serves `v1`; the Gemini API serves `v1beta`. `OmniAI::Google.config.version` derives from the
@@ -19,6 +19,12 @@ module OmniAI
19
19
  # @return [String, nil]
20
20
  attr_accessor :location_id
21
21
 
22
+ # @!attribute [rw] locations
23
+ # Vertex location per model, e.g. `{ "gemini-3.8-flash" => "us" }`. When set, each request derives its host
24
+ # from the model's location, and a model missing from the map raises `UnknownLocationError`.
25
+ # @return [Hash{String => String}, nil]
26
+ attr_accessor :locations
27
+
22
28
  # @param api_key [String, nil] optional - defaults to `ENV['GOOGLE_API_KEY']`
23
29
  # @param project_id [String, nil] optional - defaults to `ENV['GOOGLE_PROJECT_ID']`
24
30
  # @param location_id [String, nil] optional - defaults to `ENV['GOOGLE_LOCATION_ID']`
@@ -15,7 +15,10 @@ module OmniAI
15
15
  TEXT_EMBEDDING_005 = "text-embedding-005"
16
16
  TEXT_MULTILINGUAL_EMBEDDING_002 = "text-multilingual-embedding-002"
17
17
  GEMINI_EMBEDDING_001 = "gemini-embedding-001"
18
+ # Preview of GEMINI_EMBEDDING_2.
18
19
  GEMINI_EMBEDDING_2_PREVIEW = "gemini-embedding-2-preview"
20
+ # Vertex serves it at global, us and eu only.
21
+ GEMINI_EMBEDDING_2 = "gemini-embedding-2"
19
22
  EMBEDDING = TEXT_EMBEDDING_004
20
23
  MULTILINGUAL_EMBEDDING = TEXT_MULTILINGUAL_EMBEDDING_002
21
24
  end
@@ -72,10 +75,15 @@ module OmniAI
72
75
  context.deserializers[:usage] = USAGE_METADATA_DESERIALIZER
73
76
  end
74
77
 
78
+ # `client` is swapped for `client.for_model(model)`, so `locations` routes the request.
79
+ def initialize(input, client:, model:, **)
80
+ super(input, client: client.for_model(model), model:, **)
81
+ end
82
+
75
83
  protected
76
84
 
77
85
  # Determines which endpoint to use based on client and model configuration.
78
- # Routes gemini-embedding-2-* models to embedContent on Vertex, as Google's
86
+ # Routes gemini-embedding-2* models to embedContent on Vertex, as Google's
79
87
  # Vertex AI requires this endpoint for newer multimodal embedding models.
80
88
  #
81
89
  # @return [Symbol] :embed_content, :predict, or :batch_embed_contents
@@ -108,7 +116,7 @@ module OmniAI
108
116
  end
109
117
  end
110
118
 
111
- # Builds payload for the Vertex embedContent endpoint (gemini-embedding-2-* models).
119
+ # Builds payload for the Vertex embedContent endpoint (gemini-embedding-2* models).
112
120
  # @return [Hash]
113
121
  def embed_content_payload
114
122
  raise ArgumentError, "embedContent does not support batch input" if @input.is_a?(Array) && @input.length > 1
@@ -76,7 +76,7 @@ module OmniAI
76
76
  # @yieldparam event [Live::Event::*]
77
77
  def initialize(client:, model: DEFAULT_MODEL, system: nil, tools: nil, tool_behavior: :blocking, voice: nil,
78
78
  transcription: true, vad: nil, compression: false, resumption: nil, write_timeout: WRITE_TIMEOUT, &handler)
79
- @client = client
79
+ @client = client.for_model(model)
80
80
  @model = model
81
81
  @system = system
82
82
  @tools = tools || []
@@ -54,7 +54,7 @@ module OmniAI
54
54
  def initialize(io, client:, model:, language: nil, format: nil, diarization: false, word_timestamps: false,
55
55
  vocabulary: nil)
56
56
  @io = io
57
- @client = client
57
+ @client = client.for_model(model)
58
58
  @model = model
59
59
  @language = language
60
60
  @format = format || OmniAI::Transcribe::Format::JSON
@@ -92,8 +92,12 @@ module OmniAI
92
92
  # Served from the global endpoint and the us/eu multi-regions, never a single region, so a zonal Vertex
93
93
  # location_id maps to its multi-region (same rule as Chirp 3); `us` is the default.
94
94
  #
95
- # @return [String] "global", "us" or "eu"
95
+ # A location from `locations` is used as given.
96
+ #
97
+ # @return [String] "global", "us" or "eu", unless mapped
96
98
  def gemini_location_id
99
+ return @client.instance_variable_get(:@location_id) if @client.located?
100
+
97
101
  case @client.instance_variable_get(:@location_id)
98
102
  when "global" then "global"
99
103
  when /\A(eu|europe)/i then "eu"
@@ -103,8 +107,7 @@ module OmniAI
103
107
 
104
108
  # @return [String]
105
109
  def host
106
- location = gemini_location_id
107
- location == "global" ? "https://aiplatform.googleapis.com" : "https://aiplatform.#{location}.rep.googleapis.com"
110
+ Vertex.host(gemini_location_id)
108
111
  end
109
112
 
110
113
  # @return [String]
@@ -49,6 +49,11 @@ module OmniAI
49
49
  super
50
50
  end
51
51
 
52
+ # `client` is swapped for `client.for_model(model)`, so `locations` routes the request.
53
+ def initialize(io, client:, model:, **)
54
+ super(io, client: client.for_model(model), model:, **)
55
+ end
56
+
52
57
  # @raise [HTTPError]
53
58
  #
54
59
  # @return [OmniAI::Transcribe::Transcription]
@@ -12,8 +12,12 @@ module OmniAI
12
12
  raise(ArgumentError, "project_id is required for transcription")
13
13
  end
14
14
 
15
+ # A location from `locations` is used as given; otherwise it is derived per model.
16
+ #
15
17
  # @return [String]
16
18
  def location_id
19
+ return @client.instance_variable_get(:@location_id) if @client.located?
20
+
17
21
  case @model
18
22
  when "chirp_2"
19
23
  "us-central1"
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Google
5
+ # Raised before any request when `locations` is configured but has no entry for the model.
6
+ class UnknownLocationError < OmniAI::Error
7
+ # @return [String]
8
+ attr_reader :model
9
+
10
+ # @param model [String]
11
+ def initialize(model)
12
+ @model = model
13
+ super("no location configured for model #{model.inspect}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAI
4
4
  module Google
5
- VERSION = "3.21.0"
5
+ VERSION = "3.23.0"
6
6
  end
7
7
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Google
5
+ # Vertex AI endpoints.
6
+ module Vertex
7
+ MULTI_REGIONS = %w[us eu].freeze
8
+
9
+ # The host serving a location, derived the way Google's own SDKs do (google-genai, vertexai).
10
+ #
11
+ # @param location_id [String] e.g. "global", "us" or "us-central1"
12
+ #
13
+ # @return [String]
14
+ def self.host(location_id)
15
+ if location_id.eql?("global")
16
+ "https://aiplatform.googleapis.com"
17
+ elsif MULTI_REGIONS.include?(location_id)
18
+ "https://aiplatform.#{location_id}.rep.googleapis.com"
19
+ else
20
+ "https://#{location_id}-aiplatform.googleapis.com"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniai-google
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.21.0
4
+ version: 3.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
@@ -144,9 +144,11 @@ files:
144
144
  - lib/omniai/google/transcribe.rb
145
145
  - lib/omniai/google/transcribe/gemini.rb
146
146
  - lib/omniai/google/transcribe_helpers.rb
147
+ - lib/omniai/google/unknown_location_error.rb
147
148
  - lib/omniai/google/upload.rb
148
149
  - lib/omniai/google/upload/file.rb
149
150
  - lib/omniai/google/version.rb
151
+ - lib/omniai/google/vertex.rb
150
152
  homepage: https://github.com/ksylvest/omniai-google
151
153
  licenses:
152
154
  - MIT