omniai-google 3.9.0 → 3.10.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: 5683bc29149bb31082f782ed460483773e2a88537290909d04adc3fb5285aad4
4
- data.tar.gz: dd2cdde8860b212f25a4c761e1e48eccb31aeb0cbaa58dc963d48386f2352c6c
3
+ metadata.gz: 2b8dca01238f0fabfede35ce895cb4ec7ae936d9f3296c7accd88aa0284a6bfc
4
+ data.tar.gz: aa5f4c994eaac71ce5a39e6c3c0e0765eee2da33aacf2753d82b994b8c19b328
5
5
  SHA512:
6
- metadata.gz: 1880ce5da42484fa6f28ba61d14f4ee1d11cb4f3bf2d2b7d772b663af9222743775618dfc1f02de7ac1f50ac952ca742374b039722930ae28c6b388d67c0eff8
7
- data.tar.gz: 6e8d16e2b1686ae61810ece5e5fb6c07fa69a4b0dcd25063261edc54e8765e228e3e40bd9fdde8b56fffc7b5c467f1a4e9c83708bc48b2bcd47ecfb144b52f31
6
+ metadata.gz: c5b8f8335542020564f81a03bb69236f3db4c2b0683353e84ca8866d69fc557b52ae353deebd272c480fe3a9f54b190a4c451550c8a1f93f5d4e2638731b48d6
7
+ data.tar.gz: f5a2f43feda13a60a4fcdf7ae9e4826ffae58f527f550d258a3ba8e520421ff1b601e2f59b6affcb6560a2e59aa58452be1ce74fcbdf48c433736d2e98fdc212
@@ -5,6 +5,20 @@ module OmniAI
5
5
  class Chat
6
6
  # Overrides choice serialize / deserialize.
7
7
  module ChoiceSerializer
8
+ # Maps Gemini candidate `finishReason` values onto the normalized OmniAI::Chat::FinishReason symbols. The
9
+ # entire content-policy / safety family maps to `:filter`; unrecognized values (e.g. OTHER,
10
+ # MALFORMED_FUNCTION_CALL, FINISH_REASON_UNSPECIFIED) fall through to `:other`.
11
+ FINISH_REASONS = {
12
+ "STOP" => OmniAI::Chat::FinishReason::STOP,
13
+ "MAX_TOKENS" => OmniAI::Chat::FinishReason::LENGTH,
14
+ "SAFETY" => OmniAI::Chat::FinishReason::FILTER,
15
+ "RECITATION" => OmniAI::Chat::FinishReason::FILTER,
16
+ "LANGUAGE" => OmniAI::Chat::FinishReason::FILTER,
17
+ "BLOCKLIST" => OmniAI::Chat::FinishReason::FILTER,
18
+ "PROHIBITED_CONTENT" => OmniAI::Chat::FinishReason::FILTER,
19
+ "SPII" => OmniAI::Chat::FinishReason::FILTER,
20
+ "IMAGE_SAFETY" => OmniAI::Chat::FinishReason::FILTER,
21
+ }.freeze
8
22
  # @param choice [OmniAI::Chat::Choice]
9
23
  # @param context [Context]
10
24
  # @return [Hash]
@@ -18,7 +32,8 @@ module OmniAI
18
32
  # @return [OmniAI::Chat::Choice]
19
33
  def self.deserialize(data, context:)
20
34
  message = OmniAI::Chat::Message.deserialize(data["content"], context:)
21
- OmniAI::Chat::Choice.new(message:)
35
+ finish_reason = OmniAI::Chat::FinishReason.deserialize(data["finishReason"], table: FINISH_REASONS)
36
+ OmniAI::Chat::Choice.new(message:, finish_reason:)
22
37
  end
23
38
  end
24
39
  end
@@ -55,9 +55,7 @@ module OmniAI
55
55
  # @param candidate [Hash]
56
56
  # @param index [Integer]
57
57
  def process_candidate!(candidate:, index:, &block)
58
- return unless candidate["content"]
59
-
60
- candidate["content"]["parts"]&.each do |part|
58
+ candidate.dig("content", "parts")&.each do |part|
61
59
  if part["thought"]
62
60
  block&.call(OmniAI::Chat::Delta.new(thinking: part["text"]))
63
61
  elsif part["text"]
@@ -73,16 +71,29 @@ module OmniAI
73
71
  def merge_candidate!(candidate:, index:)
74
72
  if @data["candidates"][index].nil?
75
73
  @data["candidates"][index] = candidate
76
- else
77
- (candidate["content"]["parts"] || []).each do |part|
78
- merge_part!(part:, candidate: @data["candidates"][index])
79
- end
74
+ return
75
+ end
76
+
77
+ existing = @data["candidates"][index]
78
+
79
+ candidate.dig("content", "parts")&.each do |part|
80
+ merge_part!(part:, candidate: existing)
81
+ end
82
+
83
+ # Preserve top-level candidate keys (most importantly `finishReason`) that arrive on a later — usually
84
+ # terminal — chunk after the content has already streamed. Without this, the reason a generation stopped
85
+ # (e.g. MAX_TOKENS / SAFETY) is silently dropped from the assembled response.
86
+ candidate.each do |key, value|
87
+ next if key.eql?("content")
88
+
89
+ existing[key] = value
80
90
  end
81
91
  end
82
92
 
83
93
  # @param part [Hash]
84
94
  # @param candidate [Hash]
85
95
  def merge_part!(part:, candidate:)
96
+ candidate["content"] ||= {}
86
97
  parts = candidate["content"]["parts"] ||= []
87
98
  last_part = parts.last
88
99
 
@@ -113,7 +113,7 @@ module OmniAI
113
113
  # Speech-to-Text API uses different endpoints for regional vs global
114
114
  endpoint = speech_endpoint
115
115
  speech_connection = HTTP.persistent(endpoint)
116
- .timeout(**http_timeout_options)
116
+ .timeout(http_timeout_options)
117
117
  .accept(:json)
118
118
 
119
119
  # Add authentication if using credentials
@@ -45,12 +45,14 @@ module OmniAI
45
45
  # Normalizes the client timeout into keyword args for HTTP.rb's `.timeout`. The speech
46
46
  # endpoints build their own connections, so (unlike the base client, which passes the
47
47
  # value straight through) they must accept both a scalar and a per-operation Hash. A Hash
48
- # is passed through untouched; a scalar (or nil) is wrapped per-operation as before.
48
+ # is passed through untouched; a scalar is wrapped per-operation. When no timeout is set, returns `:null`
49
+ # (the http "no timeout" sentinel) — http 6 rejects nil per-operation values, http 5 accepts `:null` too.
49
50
  #
50
- # @return [Hash]
51
+ # @return [Hash, Symbol]
51
52
  def http_timeout_options
52
53
  timeout = @client.timeout
53
54
  return timeout if timeout.is_a?(Hash)
55
+ return :null if timeout.nil?
54
56
 
55
57
  { connect: timeout, write: timeout, read: timeout }
56
58
  end
@@ -212,7 +214,7 @@ module OmniAI
212
214
  def poll_operation!(operation_name)
213
215
  endpoint = speech_endpoint
214
216
  connection = HTTP.persistent(endpoint)
215
- .timeout(**http_timeout_options)
217
+ .timeout(http_timeout_options)
216
218
  .accept(:json)
217
219
 
218
220
  # Add authentication if using credentials
@@ -250,7 +252,7 @@ module OmniAI
250
252
  def request_batch!
251
253
  endpoint = speech_endpoint
252
254
  connection = HTTP.persistent(endpoint)
253
- .timeout(**http_timeout_options)
255
+ .timeout(http_timeout_options)
254
256
  .accept(:json)
255
257
 
256
258
  # Add authentication if using credentials
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAI
4
4
  module Google
5
- VERSION = "3.9.0"
5
+ VERSION = "3.10.0"
6
6
  end
7
7
  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.9.0
4
+ version: 3.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '3.2'
60
+ version: '3.7'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '3.2'
67
+ version: '3.7'
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: openssl
70
70
  requirement: !ruby/object:Gem::Requirement