omniai 1.9.6 → 2.0.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/lib/omniai/chat/choice.rb +0 -7
- data/lib/omniai/chat/delta.rb +23 -0
- data/lib/omniai/chat/function.rb +4 -2
- data/lib/omniai/chat/message.rb +4 -25
- data/lib/omniai/chat/response.rb +79 -31
- data/lib/omniai/chat/stream.rb +79 -18
- data/lib/omniai/chat/tool_call.rb +5 -3
- data/lib/omniai/chat/tool_call_list.rb +52 -0
- data/lib/omniai/chat/tool_call_message.rb +2 -2
- data/lib/omniai/chat.rb +24 -18
- data/lib/omniai/client.rb +1 -1
- data/lib/omniai/version.rb +1 -1
- metadata +4 -3
- data/lib/omniai/chat/payload.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 116095cbb73cb4e826fe28a4c644e37ea1ecb887d601b0ca4cbdc3a70e0ca81e
|
4
|
+
data.tar.gz: d17b5078d80e7cf6858b0e77e22d5949c157841672aa5bbe54a2782e8e10df31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b3a610780db624592fab65b2437ac552de7dd9f7d5c4f6ec62aff546720db832a40f4a7e24e68d64f42ce84dff24ed73a3105a08c37c61f00faa83d65fd55ca
|
7
|
+
data.tar.gz: 5000a20feca11ee1e29b03d71885d4e75aa6792a39ac828ae78b3fcae5783430f65fe93b209350aa3399dfc7cce1ccbadc0a844518e0ccb772d0b8704f304cc3
|
data/lib/omniai/chat/choice.rb
CHANGED
@@ -54,13 +54,6 @@ module OmniAI
|
|
54
54
|
}
|
55
55
|
end
|
56
56
|
|
57
|
-
# @param other [OmniAI::Chat::Choice]
|
58
|
-
#
|
59
|
-
# @return [OmniAI::Chat::Choice]
|
60
|
-
def merge(other)
|
61
|
-
self.class.new(index: @index, message: @message.merge(other.message))
|
62
|
-
end
|
63
|
-
|
64
57
|
# @return [Message]
|
65
58
|
def delta
|
66
59
|
message
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
# An `OmniAI::Chat::Delta` is used to assist with streaming. It represents a chunk of a conversation that is yielded
|
6
|
+
# back to the caller.
|
7
|
+
class Delta
|
8
|
+
# @!attribute [rw] text
|
9
|
+
#
|
10
|
+
attr_accessor :text
|
11
|
+
|
12
|
+
# @param text [String]
|
13
|
+
def initialize(text:)
|
14
|
+
@text = text
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Boolean]
|
18
|
+
def text?
|
19
|
+
!text.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/omniai/chat/function.rb
CHANGED
@@ -4,10 +4,12 @@ module OmniAI
|
|
4
4
|
class Chat
|
5
5
|
# A function that includes a name / arguments.
|
6
6
|
class Function
|
7
|
-
#
|
7
|
+
# @!attribute [rw] name
|
8
|
+
# @return [String]
|
8
9
|
attr_accessor :name
|
9
10
|
|
10
|
-
#
|
11
|
+
# @!attribute [rw] arguments
|
12
|
+
# @return [Hash]
|
11
13
|
attr_accessor :arguments
|
12
14
|
|
13
15
|
# @param name [String]
|
data/lib/omniai/chat/message.rb
CHANGED
@@ -47,12 +47,12 @@ module OmniAI
|
|
47
47
|
attr_accessor :role
|
48
48
|
|
49
49
|
# @!attribute [rw] tool_call_list
|
50
|
-
# @return [
|
50
|
+
# @return [ToolCallList, nil]
|
51
51
|
attr_accessor :tool_call_list
|
52
52
|
|
53
53
|
# @param content [String, nil]
|
54
54
|
# @param role [String]
|
55
|
-
# @param tool_call_list [
|
55
|
+
# @param tool_call_list [ToolCallList, nil]
|
56
56
|
def initialize(content:, role: Role::USER, tool_call_list: nil)
|
57
57
|
@content = content
|
58
58
|
@role = role
|
@@ -86,7 +86,7 @@ module OmniAI
|
|
86
86
|
|
87
87
|
role = data["role"]
|
88
88
|
content = Content.deserialize(data["content"], context:)
|
89
|
-
tool_call_list = data["tool_calls"]
|
89
|
+
tool_call_list = ToolCallList.deserialize(data["tool_calls"], context:)
|
90
90
|
|
91
91
|
new(content:, role:, tool_call_list:)
|
92
92
|
end
|
@@ -104,32 +104,11 @@ module OmniAI
|
|
104
104
|
return serializer.call(self, context:) if serializer
|
105
105
|
|
106
106
|
content = @content.is_a?(Array) ? @content.map { |content| content.serialize(context:) } : @content
|
107
|
-
tool_calls = @tool_call_list&.
|
107
|
+
tool_calls = @tool_call_list&.serialize(context:)
|
108
108
|
|
109
109
|
{ role: @role, content:, tool_calls: }.compact
|
110
110
|
end
|
111
111
|
|
112
|
-
# @param other [OmniAI::Chat::Message]
|
113
|
-
#
|
114
|
-
# @return [OmniAI::Chat::Message]
|
115
|
-
def merge(other)
|
116
|
-
content =
|
117
|
-
if @content && other.content
|
118
|
-
@content + other.content
|
119
|
-
else
|
120
|
-
@content || other.content
|
121
|
-
end
|
122
|
-
|
123
|
-
tool_call_list =
|
124
|
-
if @tool_call_list && other.tool_call_list
|
125
|
-
@tool_call_list + other.tool_call_list
|
126
|
-
else
|
127
|
-
@tool_call_list || other.tool_call_list
|
128
|
-
end
|
129
|
-
|
130
|
-
self.class.new(content:, role:, tool_call_list:)
|
131
|
-
end
|
132
|
-
|
133
112
|
# @return [Boolean]
|
134
113
|
def role?(role)
|
135
114
|
String(@role).eql?(String(role))
|
data/lib/omniai/chat/response.rb
CHANGED
@@ -2,68 +2,116 @@
|
|
2
2
|
|
3
3
|
module OmniAI
|
4
4
|
class Chat
|
5
|
-
#
|
5
|
+
# An `OmniAI::Chat::Response` encapsulates the result of generating a chat completion.
|
6
6
|
class Response
|
7
7
|
# @return [Hash]
|
8
8
|
attr_accessor :data
|
9
9
|
|
10
|
+
# @return [Array<Choice>]
|
11
|
+
attr_accessor :choices
|
12
|
+
|
13
|
+
# @return [Usage, nil]
|
14
|
+
attr_accessor :usage
|
15
|
+
|
10
16
|
# @param data [Hash]
|
11
|
-
# @param
|
12
|
-
|
17
|
+
# @param choices [Array<Choice>]
|
18
|
+
# @param usage [Usage, nil]
|
19
|
+
def initialize(data:, choices: [], usage: nil)
|
13
20
|
@data = data
|
14
|
-
@
|
21
|
+
@choices = choices
|
22
|
+
@usage = usage
|
15
23
|
end
|
16
24
|
|
17
|
-
# @return [
|
18
|
-
def
|
19
|
-
|
25
|
+
# @return [String]
|
26
|
+
def inspect
|
27
|
+
"#<#{self.class.name} choices=#{choices.inspect} usage=#{usage.inspect}>"
|
20
28
|
end
|
21
29
|
|
22
|
-
# @
|
23
|
-
|
24
|
-
|
25
|
-
|
30
|
+
# @param data [Hash]
|
31
|
+
# @param context [OmniAI::Context] optional
|
32
|
+
def self.deserialize(data, context: nil)
|
33
|
+
deserialize = context&.deserializer(:response)
|
34
|
+
return deserialize.call(data, context:) if deserialize
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
36
|
+
choices = data["choices"].map { |choice_data| Choice.deserialize(choice_data, context:) }
|
37
|
+
usage = Usage.deserialize(data["usage"], context:) if data["usage"]
|
38
|
+
|
39
|
+
new(data:, choices:, usage:)
|
30
40
|
end
|
31
41
|
|
32
|
-
# @
|
33
|
-
|
34
|
-
|
42
|
+
# @param context [OmniAI::Context] optional
|
43
|
+
# @return [Hash]
|
44
|
+
def serialize(context:)
|
45
|
+
serialize = context&.serializer(:response)
|
46
|
+
return serialize.call(self, context:) if serialize
|
47
|
+
|
48
|
+
{
|
49
|
+
choices: choices.map { |choice| choice.serialize(context:) },
|
50
|
+
usage: usage&.serialize(context:),
|
51
|
+
}
|
35
52
|
end
|
36
53
|
|
37
54
|
# @param index [Integer]
|
38
|
-
#
|
55
|
+
#
|
56
|
+
# @return [Choice, nil]
|
39
57
|
def choice(index: 0)
|
40
|
-
|
58
|
+
@choices[index]
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param index [Integer]
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
def choice?(index: 0)
|
65
|
+
!choice(index:).nil?
|
41
66
|
end
|
42
67
|
|
43
68
|
# @param index [Integer]
|
44
|
-
#
|
69
|
+
#
|
70
|
+
# @return [Message, nil]
|
45
71
|
def message(index: 0)
|
46
|
-
|
72
|
+
choice(index:)&.message
|
47
73
|
end
|
48
74
|
|
49
|
-
# @
|
50
|
-
|
51
|
-
|
75
|
+
# @param index [Integer]
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
def message?
|
79
|
+
!message(index:).nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Array<Message>]
|
83
|
+
def messages
|
84
|
+
@choices.map(&:message)
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param index [Integer]
|
88
|
+
#
|
89
|
+
# @return [String, nil]
|
90
|
+
def text(index: 0)
|
91
|
+
message(index:)&.text
|
52
92
|
end
|
53
93
|
|
94
|
+
# @param index [Integer]
|
95
|
+
#
|
54
96
|
# @return [Boolean]
|
55
|
-
def text?
|
56
|
-
message
|
97
|
+
def text?(index: 0)
|
98
|
+
message = message(index:)
|
99
|
+
|
100
|
+
!message.nil? && message.text?
|
57
101
|
end
|
58
102
|
|
59
|
-
# @
|
60
|
-
|
61
|
-
|
103
|
+
# @param index [Integer]
|
104
|
+
#
|
105
|
+
# @return [ToolCallList]
|
106
|
+
def tool_call_list(index: 0)
|
107
|
+
message(index:)&.tool_call_list
|
62
108
|
end
|
63
109
|
|
64
110
|
# @return [Boolean]
|
65
|
-
def tool_call_list?
|
66
|
-
tool_call_list
|
111
|
+
def tool_call_list?(index: 0)
|
112
|
+
tool_call_list = tool_call_list(index:)
|
113
|
+
|
114
|
+
!tool_call_list.nil? && tool_call_list.any?
|
67
115
|
end
|
68
116
|
end
|
69
117
|
end
|
data/lib/omniai/chat/stream.rb
CHANGED
@@ -13,30 +13,32 @@ module OmniAI
|
|
13
13
|
class Stream
|
14
14
|
# @param logger [OmniAI::Client]
|
15
15
|
# @param chunks [Enumerable<String>]
|
16
|
-
|
17
|
-
def initialize(chunks:, logger: nil, context: nil)
|
16
|
+
def initialize(chunks:, logger: nil)
|
18
17
|
@chunks = chunks
|
19
18
|
@logger = logger
|
20
|
-
@context = context
|
21
19
|
end
|
22
20
|
|
23
|
-
# @yield [
|
24
|
-
# @yieldparam
|
21
|
+
# @yield [delta]
|
22
|
+
# @yieldparam delta [OmniAI::Chat::Delta]
|
25
23
|
#
|
26
|
-
# @return [
|
24
|
+
# @return [Hash]
|
27
25
|
def stream!(&block)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
26
|
+
@data = { "choices" => [] }
|
27
|
+
|
28
|
+
@chunks.map do |chunk|
|
29
|
+
parser.feed(chunk) do |type, data, id|
|
30
|
+
process!(type, data, id, &block)
|
34
31
|
end
|
35
32
|
end
|
33
|
+
|
34
|
+
@data
|
36
35
|
end
|
37
36
|
|
38
37
|
protected
|
39
38
|
|
39
|
+
# @yield [delta]
|
40
|
+
# @yieldparam delta [OmniAI::Chat::Delta]
|
41
|
+
#
|
40
42
|
# @param type [String]
|
41
43
|
# @param data [String]
|
42
44
|
# @param id [String]
|
@@ -50,19 +52,78 @@ module OmniAI
|
|
50
52
|
@logger&.debug("Stream#process! #{arguments.join(' ')}")
|
51
53
|
end
|
52
54
|
|
55
|
+
# @yield [delta]
|
56
|
+
# @yieldparam delta [OmniAI::Chat::Delta]
|
57
|
+
#
|
53
58
|
# @param type [String]
|
54
59
|
# @param data [String]
|
55
60
|
# @param id [String]
|
56
|
-
|
57
|
-
# @return [OmniAI::Chat::Payload, nil]
|
58
|
-
def process!(type, data, id, &block)
|
61
|
+
def process!(type, data, id, &)
|
59
62
|
log(type, data, id)
|
60
63
|
|
61
64
|
return if data.eql?("[DONE]")
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
process_data!(data: JSON.parse(data), &)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @yield [delta]
|
70
|
+
# @yieldparam delta [OmniAI::Chat::Delta]
|
71
|
+
#
|
72
|
+
# @param data [Hash]
|
73
|
+
def process_data!(data:, &block)
|
74
|
+
data.each do |key, value|
|
75
|
+
@data[key] = value unless key.eql?("choices") || key.eql?("object")
|
76
|
+
end
|
77
|
+
|
78
|
+
data["choices"].each do |choice|
|
79
|
+
merge_choice!(choice:)
|
80
|
+
|
81
|
+
text = choice["delta"]["content"]
|
82
|
+
block&.call(Delta.new(text:)) if text
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param choice [Hash]
|
87
|
+
def merge_choice!(choice:)
|
88
|
+
index = choice["index"]
|
89
|
+
delta = choice["delta"]
|
90
|
+
|
91
|
+
if @data["choices"][index].nil?
|
92
|
+
@data["choices"][index] = {
|
93
|
+
"index" => index,
|
94
|
+
"message" => delta,
|
95
|
+
}
|
96
|
+
else
|
97
|
+
message = @data["choices"][index]["message"]
|
98
|
+
|
99
|
+
message["content"] += delta["content"] if delta["content"]
|
100
|
+
|
101
|
+
merge_tool_call_list!(tool_call_list: delta["tool_calls"], message:)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param tool_call_list [Array<Hash>, nil]
|
106
|
+
# @param message [Hash]
|
107
|
+
def merge_tool_call_list!(tool_call_list:, message:)
|
108
|
+
return unless tool_call_list
|
109
|
+
|
110
|
+
message["tool_calls"] ||= []
|
111
|
+
|
112
|
+
tool_call_list.each do |tool_call|
|
113
|
+
merge_tool_call_data!(tool_call:, message:)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param tool_call [Hash]
|
118
|
+
# @param message [Hash]
|
119
|
+
def merge_tool_call_data!(tool_call:, message:)
|
120
|
+
tool_call_index = tool_call["index"]
|
121
|
+
|
122
|
+
if message["tool_calls"][tool_call_index].nil?
|
123
|
+
message["tool_calls"][tool_call_index] = tool_call
|
124
|
+
else
|
125
|
+
message["tool_calls"][tool_call_index]["function"]["arguments"] += tool_call["function"]["arguments"]
|
126
|
+
end
|
66
127
|
end
|
67
128
|
|
68
129
|
# @return [EventStreamParser::Parser]
|
@@ -4,10 +4,12 @@ module OmniAI
|
|
4
4
|
class Chat
|
5
5
|
# A tool-call that includes an ID / function.
|
6
6
|
class ToolCall
|
7
|
-
#
|
7
|
+
# @!attribute [rw] id
|
8
|
+
# @return [String]
|
8
9
|
attr_accessor :id
|
9
10
|
|
10
|
-
#
|
11
|
+
# @!attribute [rw] function
|
12
|
+
# @return [Function]
|
11
13
|
attr_accessor :function
|
12
14
|
|
13
15
|
# @param id [String]
|
@@ -25,7 +27,7 @@ module OmniAI
|
|
25
27
|
# @param data [Hash]
|
26
28
|
# @param context [Context] optional
|
27
29
|
#
|
28
|
-
# @return [
|
30
|
+
# @return [ToolCall]
|
29
31
|
def self.deserialize(data, context: nil)
|
30
32
|
deserialize = context&.deserializer(:tool_call)
|
31
33
|
return deserialize.call(data, context:) if deserialize
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
# An `OmniAI::Chat::ToolCallList` is a collection designed to handle the merging of multiple tool call arrays. LLMs
|
6
|
+
# provide a subset of tool call items when chunking, so merging requires the use of the tool call index to combine.
|
7
|
+
class ToolCallList
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
# @param entries [Array<ToolCall>]
|
11
|
+
def initialize(entries: [])
|
12
|
+
@entries = entries
|
13
|
+
end
|
14
|
+
|
15
|
+
# Usage:
|
16
|
+
#
|
17
|
+
# ToolCallList.deserialize([]) # => #<ToolCallList ...>
|
18
|
+
#
|
19
|
+
# @param data [Array]
|
20
|
+
# @param context [Context] optional
|
21
|
+
#
|
22
|
+
# @return [ToolCallList]
|
23
|
+
def self.deserialize(data, context: nil)
|
24
|
+
return unless data
|
25
|
+
|
26
|
+
new(entries: data.map { |subdata| ToolCall.deserialize(subdata, context:) })
|
27
|
+
end
|
28
|
+
|
29
|
+
# Usage:
|
30
|
+
#
|
31
|
+
# tool_call_list.serialize # => [...]
|
32
|
+
#
|
33
|
+
# @param context [Context] optional
|
34
|
+
#
|
35
|
+
# @return [Array<Hash>]
|
36
|
+
def serialize(context: nil)
|
37
|
+
@entries.compact.map { |tool_call| tool_call.serialize(context:) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [String]
|
41
|
+
def inspect
|
42
|
+
"#<#{self.class.name} entries=#{@entries.inspect}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
# @yield toolcall
|
46
|
+
# @yieldparam toolcall [ToolCall]
|
47
|
+
def each(&)
|
48
|
+
@entries.each(&)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -28,7 +28,7 @@ module OmniAI
|
|
28
28
|
#
|
29
29
|
# @return [ToolMessage]
|
30
30
|
def self.deserialize(data, context: nil)
|
31
|
-
deserialize = context&.deserializer(:
|
31
|
+
deserialize = context&.deserializer(:tool_call_message)
|
32
32
|
return deserialize.call(data, context:) if deserialize
|
33
33
|
|
34
34
|
role = data["role"]
|
@@ -47,7 +47,7 @@ module OmniAI
|
|
47
47
|
#
|
48
48
|
# @return [Hash]
|
49
49
|
def serialize(context: nil)
|
50
|
-
serializer = context&.serializer(:
|
50
|
+
serializer = context&.serializer(:tool_call_message)
|
51
51
|
return serializer.call(self, context:) if serializer
|
52
52
|
|
53
53
|
role = @role
|
data/lib/omniai/chat.rb
CHANGED
@@ -86,12 +86,25 @@ module OmniAI
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# @raise [HTTPError]
|
89
|
+
#
|
90
|
+
# @return [OmniAI::Chat::Response]
|
89
91
|
def process!
|
90
92
|
response = request!
|
91
93
|
|
92
94
|
raise HTTPError, response.flush unless response.status.ok?
|
93
95
|
|
94
|
-
parse!(response:)
|
96
|
+
completion = parse!(response:)
|
97
|
+
|
98
|
+
if @tools && completion.tool_call_list?
|
99
|
+
spawn!(
|
100
|
+
@prompt.dup.tap do |prompt|
|
101
|
+
prompt.messages += completion.messages
|
102
|
+
prompt.messages += build_tool_call_messages(completion.tool_call_list)
|
103
|
+
end
|
104
|
+
).process!
|
105
|
+
else
|
106
|
+
completion
|
107
|
+
end
|
95
108
|
end
|
96
109
|
|
97
110
|
protected
|
@@ -135,6 +148,7 @@ module OmniAI
|
|
135
148
|
end
|
136
149
|
|
137
150
|
# @param response [HTTP::Response]
|
151
|
+
#
|
138
152
|
# @return [OmniAI::Chat::Response]
|
139
153
|
def parse!(response:)
|
140
154
|
if @stream
|
@@ -145,32 +159,22 @@ module OmniAI
|
|
145
159
|
end
|
146
160
|
|
147
161
|
# @param response [HTTP::Response]
|
162
|
+
#
|
148
163
|
# @return [OmniAI::Chat::Response]
|
149
164
|
def complete!(response:)
|
150
|
-
|
151
|
-
|
152
|
-
if @tools && completion.tool_call_list?
|
153
|
-
spawn!(
|
154
|
-
@prompt.dup.tap do |prompt|
|
155
|
-
prompt.messages += completion.messages
|
156
|
-
prompt.messages += build_tool_call_messages(completion.tool_call_list)
|
157
|
-
end
|
158
|
-
).process!
|
159
|
-
else
|
160
|
-
completion
|
161
|
-
end
|
165
|
+
OmniAI::Chat::Response.deserialize(response.parse, context:)
|
162
166
|
end
|
163
167
|
|
164
168
|
# @param response [HTTP::Response]
|
165
169
|
#
|
166
|
-
# @return [OmniAI::Chat::
|
170
|
+
# @return [OmniAI::Chat::Response]
|
167
171
|
def stream!(response:)
|
168
172
|
raise Error, "#{self.class.name}#stream! unstreamable" unless @stream
|
169
173
|
|
170
|
-
|
174
|
+
data = self.class::Stream.new(chunks: response.body, logger:).stream! do |chunk|
|
171
175
|
case @stream
|
172
176
|
when IO, StringIO
|
173
|
-
if chunk.text
|
177
|
+
if chunk.text?
|
174
178
|
@stream << chunk.text
|
175
179
|
@stream.flush
|
176
180
|
end
|
@@ -178,9 +182,11 @@ module OmniAI
|
|
178
182
|
end
|
179
183
|
end
|
180
184
|
|
181
|
-
|
185
|
+
response = OmniAI::Chat::Response.deserialize(data, context:)
|
186
|
+
|
187
|
+
@stream.puts if response.text? && (@stream.is_a?(IO) || @stream.is_a?(StringIO))
|
182
188
|
|
183
|
-
|
189
|
+
response
|
184
190
|
end
|
185
191
|
|
186
192
|
# @return [HTTP::Response]
|
data/lib/omniai/client.rb
CHANGED
@@ -134,7 +134,7 @@ module OmniAI
|
|
134
134
|
# @yield [prompt] optional
|
135
135
|
# @yieldparam prompt [OmniAI::Chat::Prompt]
|
136
136
|
#
|
137
|
-
# @return [OmniAI::Chat::
|
137
|
+
# @return [OmniAI::Chat::Response]
|
138
138
|
def chat(messages = nil, model:, temperature: nil, format: nil, stream: nil, tools: nil, &)
|
139
139
|
raise NotImplementedError, "#{self.class.name}#chat undefined"
|
140
140
|
end
|
data/lib/omniai/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-03 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: event_stream_parser
|
@@ -83,17 +83,18 @@ files:
|
|
83
83
|
- lib/omniai/chat.rb
|
84
84
|
- lib/omniai/chat/choice.rb
|
85
85
|
- lib/omniai/chat/content.rb
|
86
|
+
- lib/omniai/chat/delta.rb
|
86
87
|
- lib/omniai/chat/file.rb
|
87
88
|
- lib/omniai/chat/function.rb
|
88
89
|
- lib/omniai/chat/media.rb
|
89
90
|
- lib/omniai/chat/message.rb
|
90
91
|
- lib/omniai/chat/message/builder.rb
|
91
|
-
- lib/omniai/chat/payload.rb
|
92
92
|
- lib/omniai/chat/prompt.rb
|
93
93
|
- lib/omniai/chat/response.rb
|
94
94
|
- lib/omniai/chat/stream.rb
|
95
95
|
- lib/omniai/chat/text.rb
|
96
96
|
- lib/omniai/chat/tool_call.rb
|
97
|
+
- lib/omniai/chat/tool_call_list.rb
|
97
98
|
- lib/omniai/chat/tool_call_message.rb
|
98
99
|
- lib/omniai/chat/tool_call_result.rb
|
99
100
|
- lib/omniai/chat/url.rb
|
data/lib/omniai/chat/payload.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A chunk or completion.
|
6
|
-
class Payload
|
7
|
-
# @return [Array<Choice>]
|
8
|
-
attr_accessor :choices
|
9
|
-
|
10
|
-
# @return [Usage, nil]
|
11
|
-
attr_accessor :usage
|
12
|
-
|
13
|
-
# @param choices [Array<Choice>]
|
14
|
-
# @param usage [Usage, nil]
|
15
|
-
def initialize(choices: [], usage: nil)
|
16
|
-
@id = SecureRandom.alphanumeric
|
17
|
-
@choices = choices
|
18
|
-
@usage = usage
|
19
|
-
end
|
20
|
-
|
21
|
-
# @return [String]
|
22
|
-
def inspect
|
23
|
-
"#<#{self.class.name} choices=#{choices.inspect} usage=#{usage.inspect}>"
|
24
|
-
end
|
25
|
-
|
26
|
-
# @param data [Hash]
|
27
|
-
# @param context [OmniAI::Context] optional
|
28
|
-
def self.deserialize(data, context: nil)
|
29
|
-
deserialize = context&.deserializer(:payload)
|
30
|
-
return deserialize.call(data, context:) if deserialize
|
31
|
-
|
32
|
-
choices = data["choices"].map { |choice_data| Choice.deserialize(choice_data, context:) }
|
33
|
-
usage = Usage.deserialize(data["usage"], context:) if data["usage"]
|
34
|
-
|
35
|
-
new(choices:, usage:)
|
36
|
-
end
|
37
|
-
|
38
|
-
# @param context [OmniAI::Context] optional
|
39
|
-
# @return [Hash]
|
40
|
-
def serialize(context:)
|
41
|
-
serialize = context&.serializer(:payload)
|
42
|
-
return serialize.call(self, context:) if serialize
|
43
|
-
|
44
|
-
{
|
45
|
-
choices: choices.map { |choice| choice.serialize(context:) },
|
46
|
-
usage: usage&.serialize(context:),
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
# @param other [OmniAI::Chat::Payload]
|
51
|
-
def merge!(other)
|
52
|
-
return unless other
|
53
|
-
|
54
|
-
@usage = other.usage if other.usage
|
55
|
-
|
56
|
-
other.choices.each do |choice|
|
57
|
-
if choice?(index: choice.index)
|
58
|
-
choices[choice.index] = choices[choice.index].merge(choice)
|
59
|
-
else
|
60
|
-
@choices[choice.index] = choice
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param index [Integer]
|
66
|
-
# @return [Choice]
|
67
|
-
def choice(index: 0)
|
68
|
-
@choices[index]
|
69
|
-
end
|
70
|
-
|
71
|
-
# @param index [Integer]
|
72
|
-
# @return [Boolean]
|
73
|
-
def choice?(index: 0)
|
74
|
-
!choice(index:).nil?
|
75
|
-
end
|
76
|
-
|
77
|
-
# @param index [Integer]
|
78
|
-
# @return [Message]
|
79
|
-
def message(index: 0)
|
80
|
-
choice(index:).message
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return [Array<Message>]
|
84
|
-
def messages
|
85
|
-
@choices.map(&:message)
|
86
|
-
end
|
87
|
-
|
88
|
-
# @param index [Integer]
|
89
|
-
# @return [String, nil]
|
90
|
-
def text(index: 0)
|
91
|
-
message(index:).text
|
92
|
-
end
|
93
|
-
|
94
|
-
# @param index [Integer]
|
95
|
-
# @return [Boolean]
|
96
|
-
def text?(index: 0)
|
97
|
-
message(index:).text?
|
98
|
-
end
|
99
|
-
|
100
|
-
# @param index [Integer]
|
101
|
-
# @return [Array<ToolCall>]
|
102
|
-
def tool_call_list(index:)
|
103
|
-
message(index:).tool_call_list
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|