omniai-openai 3.1.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 +4 -4
- data/README.md +35 -3
- data/lib/omniai/openai/chat/usage_serializer.rb +34 -0
- data/lib/omniai/openai/chat.rb +68 -21
- data/lib/omniai/openai/speak.rb +4 -1
- data/lib/omniai/openai/transcribe.rb +4 -1
- data/lib/omniai/openai/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 231ff4c15352d0e13f870683f437e7e05b41b78e58c6d5b343f3bd827a7874aa
|
|
4
|
+
data.tar.gz: 06e2e6d651eb56384ede3176920abbd51f771ba17ea066ab6f1842fc3e6a06ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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-
|
|
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::
|
|
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
|
|
@@ -184,6 +199,23 @@ client.chat("What are the prime factors of 1234567?", model: "o3-mini", thinking
|
|
|
184
199
|
|
|
185
200
|
[OpenAI API Reference `reasoning`](https://platform.openai.com/docs/guides/reasoning)
|
|
186
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
|
+
|
|
187
219
|
### Transcribe
|
|
188
220
|
|
|
189
221
|
A transcription is generated by passing in a path to a file:
|
|
@@ -269,7 +301,7 @@ client.speak('She sells seashells by the seashore.', voice: OmniAI::OpenAI::Spea
|
|
|
269
301
|
|
|
270
302
|
#### Model
|
|
271
303
|
|
|
272
|
-
`model` is optional and
|
|
304
|
+
`model` is optional and is one of `tts-1`, `tts-1-hd`, or `gpt-4o-mini-tts` (default):
|
|
273
305
|
|
|
274
306
|
```ruby
|
|
275
307
|
client.speak('I saw a kitten eating chicken in the kitchen.', format: OmniAI::OpenAI::Speak::Model::TTS_1)
|
|
@@ -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
|
data/lib/omniai/openai/chat.rb
CHANGED
|
@@ -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)
|
|
@@ -106,36 +151,38 @@ module OmniAI
|
|
|
106
151
|
parts.join("\n\n")
|
|
107
152
|
end
|
|
108
153
|
|
|
154
|
+
# @return [String]
|
|
155
|
+
def model
|
|
156
|
+
@model || DEFAULT_MODEL
|
|
157
|
+
end
|
|
158
|
+
|
|
109
159
|
# @return [Float, nil]
|
|
110
160
|
def temperature
|
|
111
161
|
return if @temperature.nil?
|
|
112
|
-
|
|
113
|
-
return if [
|
|
114
|
-
Model::GPT_5,
|
|
115
|
-
Model::GPT_5_MINI,
|
|
116
|
-
Model::GPT_5_NANO,
|
|
117
|
-
Model::GPT_5_1,
|
|
118
|
-
Model::GPT_5_2,
|
|
119
|
-
Model::O1_MINI,
|
|
120
|
-
Model::O1,
|
|
121
|
-
Model::O3_MINI,
|
|
122
|
-
].include?(@model)
|
|
162
|
+
return if TEMPERATURE_UNSUPPORTED_MODELS.include?(model)
|
|
123
163
|
|
|
124
164
|
@temperature
|
|
125
165
|
end
|
|
126
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
|
+
#
|
|
127
172
|
# @return [Hash]
|
|
128
173
|
def payload
|
|
129
|
-
OmniAI::OpenAI.config.chat_options
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
|
139
186
|
end
|
|
140
187
|
|
|
141
188
|
# @return [Array<Hash>]
|
data/lib/omniai/openai/speak.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
39
|
+
version: '3.8'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: openssl
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -89,6 +89,7 @@ files:
|
|
|
89
89
|
- lib/omniai/openai/chat/tool_call_serializer.rb
|
|
90
90
|
- lib/omniai/openai/chat/tool_serializer.rb
|
|
91
91
|
- lib/omniai/openai/chat/url_serializer.rb
|
|
92
|
+
- lib/omniai/openai/chat/usage_serializer.rb
|
|
92
93
|
- lib/omniai/openai/client.rb
|
|
93
94
|
- lib/omniai/openai/config.rb
|
|
94
95
|
- lib/omniai/openai/embed.rb
|