omniai 3.6.0 → 3.7.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 +13 -0
- data/lib/omniai/chat/choice.rb +9 -2
- data/lib/omniai/chat/finish_reason.rb +102 -0
- data/lib/omniai/chat/message.rb +2 -2
- data/lib/omniai/chat/response.rb +18 -1
- data/lib/omniai/client.rb +2 -2
- data/lib/omniai/version.rb +1 -1
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7464f0580c8f8392155996aa7b4675491bec1fe62b48ff9b12e53264952a50e5
|
|
4
|
+
data.tar.gz: 3ef5570bd7eac6ed72a9826c90313565dc0fd00b6ec52745fe02a04ce2855264
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e5ab80ba78ab316ca74e46dffb0bdea26ed13b83f4ec4ff7c5e985580c5460d926366d71bf547ac905273968db44cf1949eb57c56b0560077f1b805f52a4c93d
|
|
7
|
+
data.tar.gz: bd453f4fd6355066833db225b771b1078fd8d916138213a1f0d4d18f37316cae7b60f47a215c46d7a76fb096eeece2e6d887a8dc9a4819c8c54dd65f3ece731f
|
data/README.md
CHANGED
|
@@ -398,6 +398,19 @@ require 'omniai/openai'
|
|
|
398
398
|
client = OmniAI::OpenAI::Client.new
|
|
399
399
|
```
|
|
400
400
|
|
|
401
|
+
OpenAI-compatible gateways can be configured by passing a custom host. This keeps
|
|
402
|
+
normal OpenAI usage unchanged while allowing private gateways, local model
|
|
403
|
+
servers, or governed endpoints such as Tuning Engines:
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
require 'omniai/openai'
|
|
407
|
+
|
|
408
|
+
client = OmniAI::OpenAI::Client.new(
|
|
409
|
+
api_key: ENV.fetch('OPENAI_API_KEY'),
|
|
410
|
+
host: ENV.fetch('OPENAI_HOST', 'https://api.openai.com')
|
|
411
|
+
)
|
|
412
|
+
```
|
|
413
|
+
|
|
401
414
|
#### Usage with LocalAI
|
|
402
415
|
|
|
403
416
|
LocalAI support is offered through [OmniAI::OpenAI](https://github.com/ksylvest/omniai-openai):
|
data/lib/omniai/chat/choice.rb
CHANGED
|
@@ -16,11 +16,18 @@ module OmniAI
|
|
|
16
16
|
# @return [Message]
|
|
17
17
|
attr_accessor :message
|
|
18
18
|
|
|
19
|
+
# @!attribute [rw] finish_reason
|
|
20
|
+
# @return [FinishReason, nil] the normalized reason generation stopped (carrying both `#reason` and the
|
|
21
|
+
# verbatim provider `#value`), or `nil` when absent.
|
|
22
|
+
attr_accessor :finish_reason
|
|
23
|
+
|
|
19
24
|
# @param message [Message]
|
|
20
25
|
# @param index [Integer]
|
|
21
|
-
|
|
26
|
+
# @param finish_reason [FinishReason, nil]
|
|
27
|
+
def initialize(message:, index: DEFAULT_INDEX, finish_reason: nil)
|
|
22
28
|
@message = message
|
|
23
29
|
@index = index
|
|
30
|
+
@finish_reason = finish_reason
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
# @return [String]
|
|
@@ -39,7 +46,7 @@ module OmniAI
|
|
|
39
46
|
index = data["index"] || DEFAULT_INDEX
|
|
40
47
|
message = Message.deserialize(data["message"] || data["delta"], context:)
|
|
41
48
|
|
|
42
|
-
new(message:, index:)
|
|
49
|
+
new(message:, index:, finish_reason: FinishReason.deserialize(data["finish_reason"]))
|
|
43
50
|
end
|
|
44
51
|
|
|
45
52
|
# @param context [OmniAI::Context] optional
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OmniAI
|
|
4
|
+
class Chat
|
|
5
|
+
# A normalized, provider-agnostic reason a generation stopped, paired with the verbatim provider value.
|
|
6
|
+
#
|
|
7
|
+
# `#reason` is one of the canonical symbols ({REASONS}) for branching/alerting; `#value` is the raw provider
|
|
8
|
+
# token (e.g. "RECITATION", "end_turn", "stop"), preserved verbatim — including when the reason normalizes to
|
|
9
|
+
# `:other`. Each provider maps its own vocabulary onto a reason in its deserializer; the verbatim value is never
|
|
10
|
+
# discarded, so consumers keep provider granularity without digging the provider-specific response `data`.
|
|
11
|
+
class FinishReason
|
|
12
|
+
# A natural stopping point was reached.
|
|
13
|
+
STOP = :stop
|
|
14
|
+
|
|
15
|
+
# The token budget (requested max tokens or the model's context window) was reached.
|
|
16
|
+
LENGTH = :length
|
|
17
|
+
|
|
18
|
+
# The model is requesting a tool / function call.
|
|
19
|
+
TOOL_CALL = :tool_call
|
|
20
|
+
|
|
21
|
+
# The provider deliberately suppressed or blocked output (safety, policy, recitation, unsupported language).
|
|
22
|
+
FILTER = :filter
|
|
23
|
+
|
|
24
|
+
# A value was present but does not map to a known category (forward-compatible fallback).
|
|
25
|
+
OTHER = :other
|
|
26
|
+
|
|
27
|
+
# The canonical normalized reasons.
|
|
28
|
+
REASONS = %i[stop length tool_call filter other].freeze
|
|
29
|
+
|
|
30
|
+
# The Chat Completions `finish_reason` vocabulary (originated by OpenAI; also emitted by Mistral and
|
|
31
|
+
# OpenAI-compatible gateways). Applied by the base `Choice.deserialize` path, which models that schema.
|
|
32
|
+
CHAT_COMPLETIONS = {
|
|
33
|
+
"stop" => STOP,
|
|
34
|
+
"length" => LENGTH,
|
|
35
|
+
"tool_calls" => TOOL_CALL,
|
|
36
|
+
"function_call" => TOOL_CALL,
|
|
37
|
+
"content_filter" => FILTER,
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
# @!attribute [r] reason
|
|
41
|
+
# @return [Symbol] one of {REASONS}
|
|
42
|
+
attr_reader :reason
|
|
43
|
+
|
|
44
|
+
# @!attribute [r] value
|
|
45
|
+
# @return [String] the verbatim provider token
|
|
46
|
+
attr_reader :value
|
|
47
|
+
|
|
48
|
+
# Normalizes a raw provider value through a mapping table into a FinishReason.
|
|
49
|
+
#
|
|
50
|
+
# - `nil` in → `nil` out (absence is not the same as unrecognized).
|
|
51
|
+
# - otherwise → a FinishReason whose `reason` is the table's mapping (or `:other` when unmapped) and whose
|
|
52
|
+
# `value` is the raw token, preserved verbatim — always, including on `:other`.
|
|
53
|
+
#
|
|
54
|
+
# @param value [String, nil] the raw provider value
|
|
55
|
+
# @param table [Hash{String => Symbol}] the provider's mapping table (defaults to the Chat Completions vocabulary)
|
|
56
|
+
#
|
|
57
|
+
# @return [FinishReason, nil]
|
|
58
|
+
def self.deserialize(value, table: CHAT_COMPLETIONS)
|
|
59
|
+
return if value.nil?
|
|
60
|
+
|
|
61
|
+
new(reason: table.fetch(value, OTHER), value:)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param reason [Symbol] one of {REASONS}
|
|
65
|
+
# @param value [String] the verbatim provider token
|
|
66
|
+
def initialize(reason:, value:)
|
|
67
|
+
@reason = reason
|
|
68
|
+
@value = value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [String]
|
|
72
|
+
def inspect
|
|
73
|
+
"#<#{self.class.name} reason=#{reason.inspect} value=#{value.inspect}>"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Boolean]
|
|
77
|
+
def stop?
|
|
78
|
+
reason == STOP
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Boolean]
|
|
82
|
+
def length?
|
|
83
|
+
reason == LENGTH
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Boolean]
|
|
87
|
+
def tool_call?
|
|
88
|
+
reason == TOOL_CALL
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
def filter?
|
|
93
|
+
reason == FILTER
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [Boolean]
|
|
97
|
+
def other?
|
|
98
|
+
reason == OTHER
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/omniai/chat/message.rb
CHANGED
|
@@ -145,7 +145,7 @@ module OmniAI
|
|
|
145
145
|
return if @content.nil?
|
|
146
146
|
return @content if @content.is_a?(String)
|
|
147
147
|
|
|
148
|
-
parts = arrayify(@content).
|
|
148
|
+
parts = arrayify(@content).grep(Text)
|
|
149
149
|
parts.map(&:text).join("\n") unless parts.empty?
|
|
150
150
|
end
|
|
151
151
|
|
|
@@ -158,7 +158,7 @@ module OmniAI
|
|
|
158
158
|
def thinking
|
|
159
159
|
return if @content.nil?
|
|
160
160
|
|
|
161
|
-
parts = arrayify(@content).
|
|
161
|
+
parts = arrayify(@content).grep(Thinking)
|
|
162
162
|
parts.map(&:thinking).join("\n") unless parts.empty?
|
|
163
163
|
end
|
|
164
164
|
|
data/lib/omniai/chat/response.rb
CHANGED
|
@@ -23,12 +23,29 @@ module OmniAI
|
|
|
23
23
|
# @param data [Hash]
|
|
24
24
|
# @param choices [Array<Choice>]
|
|
25
25
|
# @param usage [Usage, nil]
|
|
26
|
-
|
|
26
|
+
# @param finish_reason [FinishReason, nil] an optional response-level finish reason (used by providers that
|
|
27
|
+
# expose it at the response level, e.g. OpenAI's Responses API); when omitted, `#finish_reason` falls back to
|
|
28
|
+
# the first choice's finish reason.
|
|
29
|
+
def initialize(data:, choices: [], usage: nil, finish_reason: nil)
|
|
27
30
|
@data = data
|
|
28
31
|
@choices = choices
|
|
29
32
|
@usage = usage
|
|
33
|
+
@finish_reason = finish_reason
|
|
30
34
|
end
|
|
31
35
|
|
|
36
|
+
# The normalized {FinishReason} for the final turn (carrying both `#reason` and the verbatim provider `#value`),
|
|
37
|
+
# or `nil` when absent. Some providers (e.g. OpenAI's Responses API) expose this at the response level; most
|
|
38
|
+
# expose it per-choice. Prefers an explicit response-level value, then falls back to the first choice. Reflects
|
|
39
|
+
# this response only (the final turn) — it is not aggregated across the parent chain, unlike {#total_usage}.
|
|
40
|
+
#
|
|
41
|
+
# @return [FinishReason, nil]
|
|
42
|
+
def finish_reason
|
|
43
|
+
@finish_reason || @choices.first&.finish_reason
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @!attribute [w] finish_reason
|
|
47
|
+
attr_writer :finish_reason
|
|
48
|
+
|
|
32
49
|
# @return [String]
|
|
33
50
|
def inspect
|
|
34
51
|
"#<#{self.class.name} choices=#{@choices.inspect} usage=#{@usage.inspect}>"
|
data/lib/omniai/client.rb
CHANGED
|
@@ -10,7 +10,7 @@ module OmniAI
|
|
|
10
10
|
# super
|
|
11
11
|
# end
|
|
12
12
|
#
|
|
13
|
-
# # @return [HTTP::Client]
|
|
13
|
+
# # @return [HTTP::Client, HTTP::Session]
|
|
14
14
|
# def connection
|
|
15
15
|
# @connection ||= super.auth("Bearer: #{@api_key}")
|
|
16
16
|
# end
|
|
@@ -175,7 +175,7 @@ module OmniAI
|
|
|
175
175
|
"#{api_key[..2]}***" if api_key
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
-
# @return [HTTP::Client]
|
|
178
|
+
# @return [HTTP::Client, HTTP::Session] an `HTTP::Client` on http 5, an `HTTP::Session` on http 6
|
|
179
179
|
def connection
|
|
180
180
|
http = HTTP.persistent(@host)
|
|
181
181
|
http = http.use(instrumentation: { instrumenter: Instrumentation.new(logger: @logger) }) if @logger
|
data/lib/omniai/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kevin Sylvestre
|
|
@@ -41,16 +41,22 @@ dependencies:
|
|
|
41
41
|
name: http
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '5'
|
|
47
|
+
- - "<"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '7'
|
|
47
50
|
type: :runtime
|
|
48
51
|
prerelease: false
|
|
49
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
53
|
requirements:
|
|
51
|
-
- - "
|
|
54
|
+
- - ">="
|
|
52
55
|
- !ruby/object:Gem::Version
|
|
53
56
|
version: '5'
|
|
57
|
+
- - "<"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '7'
|
|
54
60
|
- !ruby/object:Gem::Dependency
|
|
55
61
|
name: logger
|
|
56
62
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -99,6 +105,7 @@ files:
|
|
|
99
105
|
- lib/omniai/chat/content.rb
|
|
100
106
|
- lib/omniai/chat/delta.rb
|
|
101
107
|
- lib/omniai/chat/file.rb
|
|
108
|
+
- lib/omniai/chat/finish_reason.rb
|
|
102
109
|
- lib/omniai/chat/function.rb
|
|
103
110
|
- lib/omniai/chat/media.rb
|
|
104
111
|
- lib/omniai/chat/message.rb
|