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 +4 -4
- data/README.md +73 -3
- data/lib/omniai/openai/chat/content_serializer.rb +2 -1
- data/lib/omniai/openai/chat/message_serializer.rb +15 -2
- data/lib/omniai/openai/chat/response_serializer.rb +1 -1
- data/lib/omniai/openai/chat/stream.rb +3 -1
- data/lib/omniai/openai/chat/thinking_serializer.rb +37 -0
- data/lib/omniai/openai/chat/usage_serializer.rb +34 -0
- data/lib/omniai/openai/chat.rb +88 -24
- 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 +5 -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
|
|
@@ -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
|
|
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
|
-
|
|
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"].
|
|
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
|
|
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
|
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)
|
|
@@ -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
|
-
.
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
160
|
-
options
|
|
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]
|
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.0
|
|
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
|
|
@@ -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
|