omniai-openai 3.0.2 → 3.2.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: 312d839fba28d30d0475015f49a50482c2d852d2bbdd32e9e34b23c45f6b50fd
4
- data.tar.gz: 674840ca8ddcc80e245fab65548831df37336367c244be8ed37732fcfcc50ec9
3
+ metadata.gz: 231ff4c15352d0e13f870683f437e7e05b41b78e58c6d5b343f3bd827a7874aa
4
+ data.tar.gz: 06e2e6d651eb56384ede3176920abbd51f771ba17ea066ab6f1842fc3e6a06ca
5
5
  SHA512:
6
- metadata.gz: 9dadc45e46012fafb754d59fc695ac3e2b92ec7154c48a5fe00d04c7eaea7212092b9346193707c53e6a934f5960017ab8b49c28af962b0035e80ebc8df5fd82
7
- data.tar.gz: 3e3646c25af3ba4d23b7f6a6d2aaa1e302f2c874c71c71a846c23600598a55e19c3583c7c90ed5639beb0227037049f9d174bf0fe5eef7c7aa5d2a543a7b51b6
6
+ metadata.gz: c17675c981946547d0c19c0f885d347d13b44a11a4344fa6e08cd35e930f4ca97987e66c69eb600091bb7dc71139ceaf510cbf1cbf017dec848a3105fa3c084e
7
+ data.tar.gz: e68ce52cfbdc2230d7690a75d5e81f35682502e18284aaaa72622abe5ee93fb4cdd59aa042791e6c7e39ddfce2011ab2b5c1f07c5dfbe713d79efb2353dab1d2
data/README.md CHANGED
@@ -76,6 +76,18 @@ client = OmniAI::OpenAI::Client.new(
76
76
  api_prefix: '/api')
77
77
  ```
78
78
 
79
+ #### Usage with OpenAI-compatible gateways
80
+
81
+ Private gateways and governed AI control planes can also be configured with a
82
+ custom `host` while keeping application code on OmniAI's OpenAI provider:
83
+
84
+ ```ruby
85
+ client = OmniAI::OpenAI::Client.new(
86
+ host: ENV.fetch('OPENAI_HOST', 'https://api.openai.com'),
87
+ api_key: ENV.fetch('OPENAI_API_KEY')
88
+ )
89
+ ```
90
+
79
91
  ### Chat
80
92
 
81
93
  A chat completion is generated by passing in a simple text prompt:
@@ -97,13 +109,16 @@ completion.content # 'The capital of Canada is Ottawa.'
97
109
 
98
110
  #### Model
99
111
 
100
- `model` takes an optional string (default is `gpt-4o`):
112
+ `model` takes an optional string (default is `gpt-5.2`):
101
113
 
102
114
  ```ruby
103
- completion = client.chat('How fast is a cheetah?', model: OmniAI::OpenAI::Chat::Model::GPT_3_5_TURBO)
115
+ completion = client.chat('How fast is a cheetah?', model: OmniAI::OpenAI::Chat::Model::GPT_5_5)
104
116
  completion.content # 'A cheetah can reach speeds over 100 km/h.'
105
117
  ```
106
118
 
119
+ Note that `temperature` is not supported by every model (e.g. `gpt-5`, `gpt-5.5` and the `o`-series). It is
120
+ omitted from the request for those models rather than sent and rejected — see `TEMPERATURE_UNSUPPORTED_MODELS`.
121
+
107
122
  [OpenAI API Reference `model`](https://platform.openai.com/docs/api-reference/chat/create#chat-create-model)
108
123
 
109
124
  #### Temperature
@@ -146,6 +161,61 @@ JSON.parse(completion.content) # { "name": "Ringo" }
146
161
 
147
162
  > When using JSON mode, you must also instruct the model to produce JSON yourself via a system or user message.
148
163
 
164
+ #### Reasoning
165
+
166
+ OpenAI o1 and o3 models support reasoning, which provides a summary of the model's thought process.
167
+
168
+ ```ruby
169
+ # Enable reasoning with unified thinking API
170
+ response = client.chat("What is 25 * 25?", model: "o3-mini", thinking: true)
171
+
172
+ # Or use OpenAI-specific reasoning options
173
+ response = client.chat("What is 25 * 25?", model: "o3-mini", reasoning: { effort: "high", summary: "auto" })
174
+ ```
175
+
176
+ **Reasoning Effort Levels:**
177
+ - `low` - Minimal reasoning
178
+ - `medium` - Balanced reasoning
179
+ - `high` - Maximum reasoning effort
180
+
181
+ #### Accessing Reasoning Content
182
+
183
+ ```ruby
184
+ response.choices.first.message.contents.each do |content|
185
+ case content
186
+ when OmniAI::Chat::Thinking
187
+ puts "Reasoning: #{content.thinking}"
188
+ when OmniAI::Chat::Text
189
+ puts "Response: #{content.text}"
190
+ end
191
+ end
192
+ ```
193
+
194
+ #### Streaming with Reasoning
195
+
196
+ ```ruby
197
+ client.chat("What are the prime factors of 1234567?", model: "o3-mini", thinking: true, stream: $stdout)
198
+ ```
199
+
200
+ [OpenAI API Reference `reasoning`](https://platform.openai.com/docs/guides/reasoning)
201
+
202
+ #### Other Responses API Options
203
+
204
+ Any option that is not modelled explicitly is forwarded to the Responses API verbatim, so parameters the gem
205
+ does not wrap are still reachable:
206
+
207
+ ```ruby
208
+ completion = client.chat('Summarize this.', model: 'gpt-5.5', max_output_tokens: 512, store: false)
209
+
210
+ # continue a stored conversation
211
+ completion = client.chat('And the next one?', previous_response_id: 'resp_123')
212
+ ```
213
+
214
+ This covers `max_output_tokens`, `previous_response_id`, `store`, `parallel_tool_calls`, `metadata`,
215
+ `truncation`, `top_p`, `service_tier`, and `prompt_cache_key`, among others.
216
+
217
+ [OpenAI API Reference `responses`](https://platform.openai.com/docs/api-reference/responses/create)
218
+
149
219
  ### Transcribe
150
220
 
151
221
  A transcription is generated by passing in a path to a file:
@@ -231,7 +301,7 @@ client.speak('She sells seashells by the seashore.', voice: OmniAI::OpenAI::Spea
231
301
 
232
302
  #### Model
233
303
 
234
- `model` is optional and must be either `tts-1` or `tts-1-hd` (default):
304
+ `model` is optional and is one of `tts-1`, `tts-1-hd`, or `gpt-4o-mini-tts` (default):
235
305
 
236
306
  ```ruby
237
307
  client.speak('I saw a kitten eating chicken in the kitchen.', format: OmniAI::OpenAI::Speak::Model::TTS_1)
@@ -8,10 +8,11 @@ module OmniAI
8
8
  # @param data [Hash]
9
9
  # @param context [Context]
10
10
  #
11
- # @return [OmniAI::Chat::Text, OmniAI::Chat::ToolCall]
11
+ # @return [OmniAI::Chat::Text, OmniAI::Chat::Thinking, OmniAI::Chat::ToolCall]
12
12
  def self.deserialize(data, context:)
13
13
  case data["type"]
14
14
  when /(input|output)_text/ then OmniAI::Chat::Text.deserialize(data, context:)
15
+ when "reasoning" then OmniAI::Chat::Thinking.deserialize(data, context:)
15
16
  end
16
17
  end
17
18
  end
@@ -30,11 +30,14 @@ module OmniAI
30
30
  # @param message [OmniAI::Chat::Message]
31
31
  # @param context [OmniAI::Context]
32
32
  #
33
- # @return [Hash]
33
+ # @return [Hash, nil]
34
34
  def self.serialize_for_content(message, context:)
35
35
  role = message.role
36
36
  direction = message.direction
37
- parts = arrayify(message.content)
37
+ # Filter out thinking content - OpenAI doesn't accept it in input messages
38
+ parts = arrayify(message.content).reject { |part| part.is_a?(OmniAI::Chat::Thinking) }
39
+
40
+ return if parts.empty?
38
41
 
39
42
  content = parts.map do |part|
40
43
  case part
@@ -54,9 +57,19 @@ module OmniAI
54
57
  case data["type"]
55
58
  when "message" then deserialize_for_content(data, context:)
56
59
  when "function_call" then deserialize_for_tool_call(data, context:)
60
+ when "reasoning" then deserialize_for_reasoning(data, context:)
57
61
  end
58
62
  end
59
63
 
64
+ # @param data [Hash]
65
+ # @param context [OmniAI::Context]
66
+ #
67
+ # @return [OmniAI::Chat::Message]
68
+ def self.deserialize_for_reasoning(data, context:)
69
+ thinking = OmniAI::Chat::Thinking.deserialize(data, context:)
70
+ OmniAI::Chat::Message.new(role: OmniAI::Chat::Role::ASSISTANT, content: [thinking])
71
+ end
72
+
60
73
  # @param data [Hash]
61
74
  # @param context [OmniAI::Context]
62
75
  #
@@ -25,7 +25,7 @@ module OmniAI
25
25
  # @return [OmniAI::Chat::Response]
26
26
  def self.deserialize(data, context:)
27
27
  usage = OmniAI::Chat::Usage.deserialize(data["usage"], context:) if data["usage"]
28
- choices = data["output"].map { |choice_data| OmniAI::Chat::Choice.deserialize(choice_data, context:) }
28
+ choices = data["output"].filter_map { |choice_data| OmniAI::Chat::Choice.deserialize(choice_data, context:) }
29
29
 
30
30
  OmniAI::Chat::Response.new(data:, choices:, usage:)
31
31
  end
@@ -15,8 +15,10 @@ module OmniAI
15
15
  @chunks.each do |chunk|
16
16
  parser.feed(chunk.b) do |type, data, _id|
17
17
  case type
18
- when /response\.(.*)_text\.delta/
18
+ when "response.output_text.delta"
19
19
  block.call(OmniAI::Chat::Delta.new(text: JSON.parse(data)["delta"]))
20
+ when "response.reasoning_summary_text.delta"
21
+ block.call(OmniAI::Chat::Delta.new(thinking: JSON.parse(data)["delta"]))
20
22
  when "response.completed"
21
23
  response = JSON.parse(data)["response"]
22
24
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module OpenAI
5
+ class Chat
6
+ # Overrides thinking serialize / deserialize.
7
+ module ThinkingSerializer
8
+ # @param data [Hash]
9
+ # @param context [Context]
10
+ #
11
+ # @return [OmniAI::Chat::Thinking]
12
+ def self.deserialize(data, context: nil) # rubocop:disable Lint/UnusedMethodArgument
13
+ summary = data["summary"]
14
+
15
+ thinking = case summary
16
+ when Array
17
+ summary.filter_map { |item| item["text"] if item["type"] == "summary_text" }.join("\n")
18
+ when String
19
+ summary
20
+ else
21
+ data["thinking"]
22
+ end
23
+
24
+ OmniAI::Chat::Thinking.new(thinking)
25
+ end
26
+
27
+ # @param thinking [OmniAI::Chat::Thinking]
28
+ # @param context [Context]
29
+ #
30
+ # @return [Hash]
31
+ def self.serialize(thinking, context: nil) # rubocop:disable Lint/UnusedMethodArgument
32
+ { type: "reasoning", summary: thinking.thinking }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module OpenAI
5
+ class Chat
6
+ # Overrides usage deserialize to read OpenAI's reasoning breakdown.
7
+ module UsageSerializer
8
+ # OpenAI already counts reasoning inside `output_tokens` and reports the breakdown alongside it, so the
9
+ # breakdown is read into `thinking_tokens` and the output count is left exactly as reported. Adding the two
10
+ # together would double count.
11
+ #
12
+ # This gem targets the Responses API (`/responses`), whose usage object reports the breakdown at
13
+ # `output_tokens_details.reasoning_tokens`. The Chat Completions vocabulary from the previous API
14
+ # generation (`completion_tokens_details`) is deliberately not read — this gem never receives it.
15
+ #
16
+ # @param data [Hash]
17
+ # @return [OmniAI::Chat::Usage]
18
+ def self.deserialize(data, *)
19
+ # Deserialize without a context so the generic flat parse runs rather than recursing into this method.
20
+ usage = OmniAI::Chat::Usage.deserialize(data)
21
+
22
+ # Only overwrite when the vendor container is actually present. A payload produced by `Usage#serialize`
23
+ # carries base's own `thinking_tokens` key and no `output_tokens_details`, so assigning unconditionally
24
+ # would clobber a correctly-parsed value with nil and break the round-trip. `unless nil?` rather than
25
+ # `||=`, so a reported zero from the wire still wins over base's nil.
26
+ reasoning_tokens = data.dig("output_tokens_details", "reasoning_tokens")
27
+ usage.thinking_tokens = reasoning_tokens unless reasoning_tokens.nil?
28
+
29
+ usage
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -32,11 +32,23 @@ module OmniAI
32
32
  end
33
33
 
34
34
  module Model
35
+ GPT_5_6_LUNA = "gpt-5.6-luna"
36
+ GPT_5_6_SOL = "gpt-5.6-sol"
37
+ GPT_5_6_TERRA = "gpt-5.6-terra"
38
+ GPT_5_5 = "gpt-5.5"
39
+ GPT_5_5_PRO = "gpt-5.5-pro"
40
+ GPT_5_4 = "gpt-5.4"
41
+ GPT_5_4_MINI = "gpt-5.4-mini"
42
+ GPT_5_4_NANO = "gpt-5.4-nano"
43
+ GPT_5_4_PRO = "gpt-5.4-pro"
44
+ GPT_5_3_CODEX = "gpt-5.3-codex"
35
45
  GPT_5_2 = "gpt-5.2"
46
+ GPT_5_2_PRO = "gpt-5.2-pro"
36
47
  GPT_5_1 = "gpt-5.1"
37
48
  GPT_5 = "gpt-5"
38
49
  GPT_5_MINI = "gpt-5-mini"
39
50
  GPT_5_NANO = "gpt-5-nano"
51
+ GPT_5_PRO = "gpt-5-pro"
40
52
  GPT_4_1 = "gpt-4.1"
41
53
  GPT_4_1_NANO = "gpt-4.1-nano"
42
54
  GPT_4_1_MINI = "gpt-4.1-mini"
@@ -54,6 +66,38 @@ module OmniAI
54
66
 
55
67
  DEFAULT_MODEL = Model::GPT_5_2
56
68
 
69
+ # Models that reject `temperature` on the Responses API.
70
+ #
71
+ # OpenAI's support here is not patterned by version, so this cannot be a prefix rule: `gpt-5` and its
72
+ # mini/nano/pro variants reject `temperature`, `gpt-5.1` through `gpt-5.4` accept it, and `gpt-5.5`
73
+ # onwards rejects it again. Every entry below was verified live against the API on 2026-08-16 by issuing
74
+ # a request with `temperature` and checking for `Unsupported parameter`.
75
+ #
76
+ # Models absent from this list pass `temperature` through unchanged. That is deliberate: an unlisted
77
+ # model that rejects it surfaces a loud API error the caller can act on, whereas over-applying the guard
78
+ # would silently discard a caller's temperature.
79
+ TEMPERATURE_UNSUPPORTED_MODELS = [
80
+ Model::GPT_5_6_LUNA,
81
+ Model::GPT_5_6_SOL,
82
+ Model::GPT_5_6_TERRA,
83
+ Model::GPT_5_5,
84
+ Model::GPT_5_5_PRO,
85
+ Model::GPT_5_4_PRO,
86
+ Model::GPT_5_2_PRO,
87
+ Model::GPT_5,
88
+ Model::GPT_5_MINI,
89
+ Model::GPT_5_NANO,
90
+ Model::GPT_5_PRO,
91
+ Model::O1_MINI,
92
+ Model::O1,
93
+ Model::O3,
94
+ Model::O3_MINI,
95
+ Model::O4_MINI,
96
+ ].freeze
97
+
98
+ # Keys in `@options` consumed by dedicated builders below rather than sent verbatim.
99
+ RESERVED_OPTIONS = %i[text reasoning thinking].freeze
100
+
57
101
  # @return [Context]
58
102
  CONTEXT = Context.build do |context|
59
103
  context.serializers[:choice] = ChoiceSerializer.method(:serialize)
@@ -65,6 +109,7 @@ module OmniAI
65
109
  context.serializers[:response] = ResponseSerializer.method(:serialize)
66
110
  context.deserializers[:response] = ResponseSerializer.method(:deserialize)
67
111
  context.deserializers[:content] = ContentSerializer.method(:deserialize)
112
+ context.deserializers[:usage] = UsageSerializer.method(:deserialize)
68
113
  context.serializers[:file] = FileSerializer.method(:serialize)
69
114
  context.serializers[:url] = URLSerializer.method(:serialize)
70
115
  context.serializers[:tool] = ToolSerializer.method(:serialize)
@@ -74,6 +119,8 @@ module OmniAI
74
119
  context.deserializers[:tool_call_result] = ToolCallResultSerializer.method(:deserialize)
75
120
  context.serializers[:tool_call_message] = ToolCallMessageSerializer.method(:serialize)
76
121
  context.deserializers[:tool_call_message] = ToolCallMessageSerializer.method(:deserialize)
122
+ context.serializers[:thinking] = ThinkingSerializer.method(:serialize)
123
+ context.deserializers[:thinking] = ThinkingSerializer.method(:deserialize)
77
124
  end
78
125
 
79
126
  protected
@@ -88,7 +135,7 @@ module OmniAI
88
135
  @prompt
89
136
  .messages
90
137
  .reject(&:system?)
91
- .map { |message| message.serialize(context:) }
138
+ .filter_map { |message| message.serialize(context:) }
92
139
  end
93
140
 
94
141
  # @return [String, nil]
@@ -104,36 +151,38 @@ module OmniAI
104
151
  parts.join("\n\n")
105
152
  end
106
153
 
154
+ # @return [String]
155
+ def model
156
+ @model || DEFAULT_MODEL
157
+ end
158
+
107
159
  # @return [Float, nil]
108
160
  def temperature
109
161
  return if @temperature.nil?
110
-
111
- return if [
112
- Model::GPT_5,
113
- Model::GPT_5_MINI,
114
- Model::GPT_5_NANO,
115
- Model::GPT_5_1,
116
- Model::GPT_5_2,
117
- Model::O1_MINI,
118
- Model::O1,
119
- Model::O3_MINI,
120
- ].include?(@model)
162
+ return if TEMPERATURE_UNSUPPORTED_MODELS.include?(model)
121
163
 
122
164
  @temperature
123
165
  end
124
166
 
167
+ # Unrecognized options are passed through verbatim so callers can reach Responses API parameters the gem
168
+ # does not model explicitly (e.g. `max_output_tokens`, `previous_response_id`, `store`,
169
+ # `parallel_tool_calls`, `metadata`, `truncation`, `top_p`). Keys built by dedicated methods are excluded
170
+ # so they are not sent twice, and the explicit keys below win over any same-named passthrough.
171
+ #
125
172
  # @return [Hash]
126
173
  def payload
127
- OmniAI::OpenAI.config.chat_options.merge({
128
- instructions:,
129
- input:,
130
- model: @model || DEFAULT_MODEL,
131
- stream: stream? || nil,
132
- temperature:,
133
- tools:,
134
- text:,
135
- reasoning:,
136
- }).compact
174
+ OmniAI::OpenAI.config.chat_options
175
+ .merge(@options.except(*RESERVED_OPTIONS))
176
+ .merge({
177
+ instructions:,
178
+ input:,
179
+ model:,
180
+ stream: stream? || nil,
181
+ temperature:,
182
+ tools:,
183
+ text:,
184
+ reasoning:,
185
+ }).compact
137
186
  end
138
187
 
139
188
  # @return [Array<Hash>]
@@ -155,9 +204,24 @@ module OmniAI
155
204
  end
156
205
 
157
206
  # @return [Hash]
207
+ # Accepts unified `thinking:` option and translates to OpenAI's `reasoning:` format.
208
+ # Example: `thinking: { effort: "high" }` becomes `reasoning: { effort: "high", summary: "auto" }`
158
209
  def reasoning
159
- options = @options.fetch(:reasoning, {})
160
- options unless options.empty?
210
+ # Support both native `reasoning:` and unified `thinking:` options
211
+ options = @options[:reasoning] || translate_thinking_to_reasoning
212
+ options unless options.nil? || options.empty?
213
+ end
214
+
215
+ # Translates unified thinking option to OpenAI reasoning format
216
+ # @return [Hash, nil]
217
+ def translate_thinking_to_reasoning
218
+ thinking = @options[:thinking]
219
+ return unless thinking
220
+
221
+ case thinking
222
+ when true then { effort: ReasoningEffort::HIGH, summary: "auto" }
223
+ when Hash then { summary: "auto" }.merge(thinking)
224
+ end
161
225
  end
162
226
 
163
227
  # @raise [ArgumentError]
@@ -13,14 +13,17 @@ module OmniAI
13
13
  module Voice
14
14
  ALLOY = "alloy" # https://platform.openai.com/docs/guides/text-to-speech/alloy
15
15
  ASH = "ash" # https://platform.openai.com/docs/guides/text-to-speech/ash
16
- BALLARD = "ballard" # https://platform.openai.com/docs/guides/text-to-speech/ballard
16
+ BALLAD = "ballad" # https://platform.openai.com/docs/guides/text-to-speech/ballad
17
+ CEDAR = "cedar" # https://platform.openai.com/docs/guides/text-to-speech/cedar
17
18
  CORAL = "coral" # https://platform.openai.com/docs/guides/text-to-speech/coral
18
19
  ECHO = "echo" # https://platform.openai.com/docs/guides/text-to-speech/echo
19
20
  FABLE = "fable" # https://platform.openai.com/docs/guides/text-to-speech/fable
21
+ MARIN = "marin" # https://platform.openai.com/docs/guides/text-to-speech/marin
20
22
  NOVA = "nova" # https://platform.openai.com/docs/guides/text-to-speech/nova
21
23
  ONYX = "onyx" # https://platform.openai.com/docs/guides/text-to-speech/onyx
22
24
  SAGE = "sage" # https://platform.openai.com/docs/guides/text-to-speech/sage
23
25
  SHIMMER = "shimmer" # https://platform.openai.com/docs/guides/text-to-speech/shimmer
26
+ VERSE = "verse" # https://platform.openai.com/docs/guides/text-to-speech/verse
24
27
  end
25
28
 
26
29
  DEFAULT_MODEL = Model::GPT_4O_MINI_TTS
@@ -7,7 +7,10 @@ module OmniAI
7
7
  module Model
8
8
  WHISPER_1 = "whisper-1"
9
9
  GPT_4O_TRANSCRIBE = "gpt-4o-transcribe"
10
- GPT_4O_MINI_TRANSCRIBE = "gpt-4-0-mini-transcribe"
10
+ GPT_4O_TRANSCRIBE_DIARIZE = "gpt-4o-transcribe-diarize"
11
+ GPT_4O_MINI_TRANSCRIBE = "gpt-4o-mini-transcribe"
12
+ GPT_TRANSCRIBE = "gpt-transcribe"
13
+ GPT_LIVE_TRANSCRIBE = "gpt-live-transcribe"
11
14
  WHISPER = WHISPER_1
12
15
  end
13
16
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAI
4
4
  module OpenAI
5
- VERSION = "3.0.2"
5
+ VERSION = "3.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniai-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '3.0'
32
+ version: '3.8'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '3.0'
39
+ version: '3.8'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: openssl
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -83,11 +83,13 @@ files:
83
83
  - lib/omniai/openai/chat/response_serializer.rb
84
84
  - lib/omniai/openai/chat/stream.rb
85
85
  - lib/omniai/openai/chat/text_serializer.rb
86
+ - lib/omniai/openai/chat/thinking_serializer.rb
86
87
  - lib/omniai/openai/chat/tool_call_message_serializer.rb
87
88
  - lib/omniai/openai/chat/tool_call_result_serializer.rb
88
89
  - lib/omniai/openai/chat/tool_call_serializer.rb
89
90
  - lib/omniai/openai/chat/tool_serializer.rb
90
91
  - lib/omniai/openai/chat/url_serializer.rb
92
+ - lib/omniai/openai/chat/usage_serializer.rb
91
93
  - lib/omniai/openai/client.rb
92
94
  - lib/omniai/openai/config.rb
93
95
  - lib/omniai/openai/embed.rb