omniai 1.4.2 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +32 -1
- data/lib/omniai/chat/response/choice.rb +40 -0
- data/lib/omniai/chat/response/chunk.rb +15 -0
- data/lib/omniai/chat/response/completion.rb +15 -0
- data/lib/omniai/chat/response/delta.rb +11 -0
- data/lib/omniai/chat/response/delta_choice.rb +25 -0
- data/lib/omniai/chat/response/function.rb +25 -0
- data/lib/omniai/chat/response/message.rb +11 -0
- data/lib/omniai/chat/response/message_choice.rb +25 -0
- data/lib/omniai/chat/response/part.rb +38 -0
- data/lib/omniai/chat/response/payload.rb +77 -0
- data/lib/omniai/chat/response/resource.rb +22 -0
- data/lib/omniai/chat/response/stream.rb +27 -0
- data/lib/omniai/chat/response/tool_call.rb +30 -0
- data/lib/omniai/chat/response/usage.rb +35 -0
- data/lib/omniai/chat.rb +56 -10
- data/lib/omniai/cli/chat_handler.rb +1 -0
- data/lib/omniai/cli.rb +3 -2
- data/lib/omniai/client.rb +3 -2
- data/lib/omniai/config.rb +3 -1
- data/lib/omniai/instrumentation.rb +0 -2
- data/lib/omniai/tool/parameters.rb +47 -0
- data/lib/omniai/tool/property.rb +90 -0
- data/lib/omniai/tool.rb +85 -0
- data/lib/omniai/version.rb +1 -1
- metadata +20 -11
- data/lib/omniai/chat/chunk.rb +0 -46
- data/lib/omniai/chat/completion.rb +0 -53
- data/lib/omniai/chat/delta.rb +0 -31
- data/lib/omniai/chat/delta_choice.rb +0 -26
- data/lib/omniai/chat/message.rb +0 -31
- data/lib/omniai/chat/message_choice.rb +0 -26
- data/lib/omniai/chat/stream.rb +0 -25
- data/lib/omniai/chat/usage.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a989cab03afca85385b23f4684d377b8d1a09533a73899fa79eebcce4f4a3260
|
4
|
+
data.tar.gz: bb39575e251b0dd6cf6e0e3b24ba86765fd550feeb3a222ce5f785ca654e8741
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e20f0ee5ab32ceb95e33e38888c81e28f936294cfcd9a3dad80a8d89a9fd3241477455e706d70fb949502b01cdf968547104e55931138512b4e617dd9688fe02
|
7
|
+
data.tar.gz: 58aa9a8a43b2e8a7580beac909b7ddd34fcaaa3d9c60c9105d3dba0cd18923f7361473fdb863890fbfa17013a1339b71008381e105bd961de44dec67db8eac8b
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -143,7 +143,9 @@ completion = client.chat(messages, model: '...', temperature: 0.7, format: :json
|
|
143
143
|
completion.choice.message.content # '...'
|
144
144
|
```
|
145
145
|
|
146
|
-
#### Completions using
|
146
|
+
#### Completions using Streaming via Proc
|
147
|
+
|
148
|
+
A real-time stream of messages can be generated by passing in a proc:
|
147
149
|
|
148
150
|
```ruby
|
149
151
|
stream = proc do |chunk|
|
@@ -152,6 +154,35 @@ end
|
|
152
154
|
client.chat('Tell me a joke.', stream:)
|
153
155
|
```
|
154
156
|
|
157
|
+
#### Completion using Streaming via IO
|
158
|
+
|
159
|
+
The above code can also be supplied any IO (e.g. `File`, `$stdout`, `$stdin`, etc):
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
client.chat('Tell me a story', stream: $stdout)
|
163
|
+
```
|
164
|
+
|
165
|
+
#### Completion with Tools
|
166
|
+
|
167
|
+
A chat can also be initialized with tools:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
client.chat('What is the weather in "London, England" and "Madrid, Spain"?', tools: [
|
171
|
+
OmniAI::Tool.new(
|
172
|
+
proc { |location:, unit: 'celsius'| "It is #{rand(20..50)}° #{unit} in #{location}" },
|
173
|
+
name: 'Weather',
|
174
|
+
description: 'Lookup the weather in a location',
|
175
|
+
parameters: OmniAI::Tool::Parameters.new(
|
176
|
+
properties: {
|
177
|
+
location: OmniAI::Tool::Property.string(description: 'The city and country (e.g. Toronto, Canada).'),
|
178
|
+
unit: OmniAI::Tool::Property.string(enum: %w[celcius farenheit]),
|
179
|
+
},
|
180
|
+
required: %i[location]
|
181
|
+
)
|
182
|
+
)
|
183
|
+
])
|
184
|
+
```
|
185
|
+
|
155
186
|
### Transcribe
|
156
187
|
|
157
188
|
Clients that support transcribe (e.g. OpenAI w/ "Whisper") convert recordings to text via the following calls:
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# For use with MessageChoice or DeltaChoice.
|
7
|
+
class Choice < Resource
|
8
|
+
# @return [Integer]
|
9
|
+
def index
|
10
|
+
@data['index']
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [OmniAI::Chat::Response::Part]
|
14
|
+
def part
|
15
|
+
raise NotImplementedError, "#{self.class.name}#part undefined"
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [OmniAI::Chat::Response::ToolCallList]
|
19
|
+
def tool_call_list
|
20
|
+
part.tool_call_list
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean]
|
24
|
+
def tool_call_list?
|
25
|
+
tool_call_list.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String, nil]
|
29
|
+
def content
|
30
|
+
part.content
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean]
|
34
|
+
def content?
|
35
|
+
!content.nil?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A chunk returned by the API.
|
7
|
+
class Chunk < Payload
|
8
|
+
# @return [Array<OmniAI::Chat::Response::DeltaChoice>]
|
9
|
+
def choices
|
10
|
+
@choices ||= @data['choices'].map { |data| DeltaChoice.new(data:) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A completion returned by the API.
|
7
|
+
class Completion < Payload
|
8
|
+
# @return [Array<OmniAI::Chat:Response:::MessageChoice>]
|
9
|
+
def choices
|
10
|
+
@choices ||= @data['choices'].map { |data| MessageChoice.new(data:) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A delta choice returned by the API.
|
7
|
+
class DeltaChoice < Choice
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} index=#{index} delta=#{delta.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [OmniAI::Chat::Response::Delta]
|
14
|
+
def delta
|
15
|
+
@delta ||= Delta.new(data: @data['delta'])
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [OmniAI::Chat::Response::Delta]
|
19
|
+
def part
|
20
|
+
delta
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A function returned by the API.
|
7
|
+
class Function < Resource
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} name=#{name.inspect} arguments=#{arguments.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def name
|
15
|
+
@data['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Hash, nil]
|
19
|
+
def arguments
|
20
|
+
JSON.parse(@data['arguments']) if @data['arguments']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A choice returned by the API.
|
7
|
+
class MessageChoice < Choice
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} index=#{index} message=#{message.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [OmniAI::Chat::Response::Message]
|
14
|
+
def message
|
15
|
+
@message ||= Message.new(data: @data['message'])
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [OmniAI::Chat::Response::Message]
|
19
|
+
def part
|
20
|
+
message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# Either a delta or message.
|
7
|
+
class Part < Resource
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} role=#{role.inspect} content=#{content.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def role
|
15
|
+
@data['role'] || Role::USER
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String, nil]
|
19
|
+
def content
|
20
|
+
@data['content']
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<OmniAI::Chat::Response::ToolCall>]
|
24
|
+
def tool_call_list
|
25
|
+
return [] unless @data['tool_calls']
|
26
|
+
|
27
|
+
@tool_call_list ||= @data['tool_calls'].map { |tool_call_data| ToolCall.new(data: tool_call_data) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param index [Integer]
|
31
|
+
# @return [OmniAI::Chat::Response::ToolCall, nil]
|
32
|
+
def tool_call(index: 0)
|
33
|
+
tool_call_list[index]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A chunk or completion.
|
7
|
+
class Payload < Resource
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} id=#{id.inspect} choices=#{choices.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def id
|
15
|
+
@data['id']
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Time]
|
19
|
+
def created
|
20
|
+
Time.at(@data['created']) if @data['created']
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Time]
|
24
|
+
def updated
|
25
|
+
Time.at(@data['updated']) if @data['updated']
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
def model
|
30
|
+
@data['model']
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array<OmniAI::Chat::Response::Choice>]
|
34
|
+
def choices
|
35
|
+
raise NotImplementedError, "#{self.class.name}#choices undefined"
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param index [Integer]
|
39
|
+
# @return [OmniAI::Chat::Response::DeltaChoice]
|
40
|
+
def choice(index: 0)
|
41
|
+
choices[index]
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param index [Integer]
|
45
|
+
# @return [OmniAI::Chat::Response::Part]
|
46
|
+
def part(index: 0)
|
47
|
+
choice(index:).part
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [OmniAI::Chat::Response::Usage]
|
51
|
+
def usage
|
52
|
+
@usage ||= Usage.new(data: @data['usage']) if @data['usage']
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String, nil]
|
56
|
+
def content
|
57
|
+
choice.content
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Boolean]
|
61
|
+
def content?
|
62
|
+
choice.content?
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<OmniAI::Chat::Response:ToolCall>]
|
66
|
+
def tool_call_list
|
67
|
+
choice.tool_call_list
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Boolean]
|
71
|
+
def tool_call_list?
|
72
|
+
choice.tool_call_list?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A generic data to handle response.
|
7
|
+
class Resource
|
8
|
+
attr_accessor :data
|
9
|
+
|
10
|
+
# @param data [Hash]
|
11
|
+
def initialize(data:)
|
12
|
+
@data = data
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def inspect
|
17
|
+
"#<#{self.class.name} data=#{@data.inspect}>"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A stream given when streaming.
|
7
|
+
class Stream
|
8
|
+
# @param response [HTTP::Response]
|
9
|
+
def initialize(response:)
|
10
|
+
@response = response
|
11
|
+
@parser = EventStreamParser::Parser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# @yield [OmniAI::Chat::Chunk]
|
15
|
+
def stream!
|
16
|
+
@response.body.each do |chunk|
|
17
|
+
@parser.feed(chunk) do |_, data|
|
18
|
+
next if data.eql?('[DONE]')
|
19
|
+
|
20
|
+
yield(Chunk.new(data: JSON.parse(data)))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A tool-call returned by the API.
|
7
|
+
class ToolCall < Resource
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
"#<#{self.class.name} id=#{id.inspect} type=#{type.inspect}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def id
|
15
|
+
@data['id']
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
def type
|
20
|
+
@data['type']
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [OmniAI::Chat::Response::Function]
|
24
|
+
def function
|
25
|
+
@function ||= Function.new(data: @data['function']) if @data['function']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Chat
|
5
|
+
module Response
|
6
|
+
# A usage returned by the API.
|
7
|
+
class Usage < Resource
|
8
|
+
# @return [String]
|
9
|
+
def inspect
|
10
|
+
properties = [
|
11
|
+
"input_tokens=#{input_tokens}",
|
12
|
+
"output_tokens=#{output_tokens}",
|
13
|
+
"total_tokens=#{total_tokens}",
|
14
|
+
]
|
15
|
+
"#<#{self.class.name} #{properties.join(' ')}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Integer]
|
19
|
+
def input_tokens
|
20
|
+
@data['input_tokens'] || @data['prompt_tokens']
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Integer]
|
24
|
+
def output_tokens
|
25
|
+
@data['output_tokens'] || @data['completion_tokens']
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Integer]
|
29
|
+
def total_tokens
|
30
|
+
@data['total_tokens'] || (input_tokens + output_tokens)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/omniai/chat.rb
CHANGED
@@ -27,10 +27,19 @@ module OmniAI
|
|
27
27
|
class Chat
|
28
28
|
JSON_PROMPT = 'Respond with valid JSON. Do not include any non-JSON in the response.'
|
29
29
|
|
30
|
+
# An error raised when a chat makes a tool-call for a tool that cannot be found.
|
31
|
+
class ToolCallLookupError < Error
|
32
|
+
def initialize(tool_call)
|
33
|
+
super("missing tool for tool_call=#{tool_call.inspect}")
|
34
|
+
@tool_call = tool_call
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
30
38
|
module Role
|
31
39
|
ASSISTANT = 'assistant'
|
32
40
|
USER = 'user'
|
33
41
|
SYSTEM = 'system'
|
42
|
+
TOOL = 'tool'
|
34
43
|
end
|
35
44
|
|
36
45
|
module Format
|
@@ -46,13 +55,15 @@ module OmniAI
|
|
46
55
|
# @param model [String] required
|
47
56
|
# @param temperature [Float, nil] optional
|
48
57
|
# @param stream [Proc, IO, nil] optional
|
58
|
+
# @param tools [Array<OmniAI::Tool>] optional
|
49
59
|
# @param format [Symbol, nil] optional - :json
|
50
|
-
def initialize(messages, client:, model:, temperature: nil, stream: nil, format: nil)
|
51
|
-
@messages = messages
|
60
|
+
def initialize(messages, client:, model:, temperature: nil, stream: nil, tools: nil, format: nil)
|
61
|
+
@messages = arrayify(messages)
|
52
62
|
@client = client
|
53
63
|
@model = model
|
54
64
|
@temperature = temperature
|
55
65
|
@stream = stream
|
66
|
+
@tools = tools
|
56
67
|
@format = format
|
57
68
|
end
|
58
69
|
|
@@ -87,9 +98,21 @@ module OmniAI
|
|
87
98
|
end
|
88
99
|
end
|
89
100
|
|
90
|
-
# @param response [
|
101
|
+
# @param response [HTTP::Response]
|
102
|
+
# @return [OmniAI::Chat::Response::Completion]
|
91
103
|
def complete!(response:)
|
92
|
-
self.class::Completion.new(data: response.parse)
|
104
|
+
completion = self.class::Response::Completion.new(data: response.parse)
|
105
|
+
|
106
|
+
if @tools && completion.tool_call_required?
|
107
|
+
@messages = [
|
108
|
+
*@messages,
|
109
|
+
*completion.choices.map(&:message).map(&:data),
|
110
|
+
*(completion.tool_call_list.map { |tool_call| execute_tool_call(tool_call) }),
|
111
|
+
]
|
112
|
+
process!
|
113
|
+
else
|
114
|
+
completion
|
115
|
+
end
|
93
116
|
end
|
94
117
|
|
95
118
|
# @param response [HTTP::Response]
|
@@ -97,21 +120,23 @@ module OmniAI
|
|
97
120
|
def stream!(response:)
|
98
121
|
raise Error, "#{self.class.name}#stream! unstreamable" unless @stream
|
99
122
|
|
100
|
-
self.class::Stream.new(response:).stream! do |chunk|
|
123
|
+
self.class::Response::Stream.new(response:).stream! do |chunk|
|
101
124
|
case @stream
|
102
|
-
when IO
|
103
|
-
|
104
|
-
|
125
|
+
when IO, StringIO
|
126
|
+
if chunk.content?
|
127
|
+
@stream << chunk.content
|
128
|
+
@stream.flush
|
129
|
+
end
|
105
130
|
else @stream.call(chunk)
|
106
131
|
end
|
107
132
|
end
|
108
133
|
|
109
|
-
@stream.puts if @stream.is_a?(IO)
|
134
|
+
@stream.puts if @stream.is_a?(IO) || @stream.is_a?(StringIO)
|
110
135
|
end
|
111
136
|
|
112
137
|
# @return [Array<Hash>]
|
113
138
|
def messages
|
114
|
-
|
139
|
+
@messages.map do |content|
|
115
140
|
case content
|
116
141
|
when String then { role: Role::USER, content: }
|
117
142
|
when Hash then content
|
@@ -133,5 +158,26 @@ module OmniAI
|
|
133
158
|
.accept(:json)
|
134
159
|
.post(path, json: payload)
|
135
160
|
end
|
161
|
+
|
162
|
+
# @param tool_call [OmniAI::Chat::ToolCall]
|
163
|
+
def execute_tool_call(tool_call)
|
164
|
+
function = tool_call.function
|
165
|
+
|
166
|
+
tool = @tools.find { |entry| function.name == entry.name } || raise(ToolCallLookupError, tool_call)
|
167
|
+
result = tool.call(function.arguments)
|
168
|
+
|
169
|
+
prepare_tool_call_message(tool_call:, content: result)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @param tool_call [OmniAI::Chat::ToolCall]
|
173
|
+
# @param content [String]
|
174
|
+
def prepare_tool_call_message(tool_call:, content:)
|
175
|
+
{
|
176
|
+
role: Role::TOOL,
|
177
|
+
name: tool_call.function.name,
|
178
|
+
tool_call_id: tool_call.id,
|
179
|
+
content:,
|
180
|
+
}
|
181
|
+
end
|
136
182
|
end
|
137
183
|
end
|
data/lib/omniai/cli.rb
CHANGED
@@ -12,8 +12,8 @@ module OmniAI
|
|
12
12
|
class CLI
|
13
13
|
ChatArgs = Struct.new(:provider, :model, :temperature)
|
14
14
|
|
15
|
-
# @param
|
16
|
-
# @param
|
15
|
+
# @param stdin [IO] a stream
|
16
|
+
# @param stdout [IO] a stream
|
17
17
|
# @param provider [String] a provider
|
18
18
|
def initialize(stdin: $stdin, stdout: $stdout, provider: 'openai')
|
19
19
|
@stdin = stdin
|
@@ -22,6 +22,7 @@ module OmniAI
|
|
22
22
|
@args = {}
|
23
23
|
end
|
24
24
|
|
25
|
+
# @param argv [Array<String>]
|
25
26
|
def parse(argv = ARGV)
|
26
27
|
parser.order!(argv)
|
27
28
|
command = argv.shift
|
data/lib/omniai/client.rb
CHANGED
@@ -129,9 +129,10 @@ module OmniAI
|
|
129
129
|
# @param format [Symbol] optional :text or :json
|
130
130
|
# @param temperature [Float, nil] optional
|
131
131
|
# @param stream [Proc, nil] optional
|
132
|
+
# @param tools [Array<OmniAI::Tool>] optional
|
132
133
|
#
|
133
134
|
# @return [OmniAI::Chat::Completion]
|
134
|
-
def chat(messages, model:, temperature: nil, format: nil, stream: nil)
|
135
|
+
def chat(messages, model:, temperature: nil, format: nil, stream: nil, tools: nil)
|
135
136
|
raise NotImplementedError, "#{self.class.name}#chat undefined"
|
136
137
|
end
|
137
138
|
|
@@ -144,7 +145,7 @@ module OmniAI
|
|
144
145
|
# @param temperature [Float, nil] optional
|
145
146
|
# @param format [Symbol] :text, :srt, :vtt, or :json (default)
|
146
147
|
#
|
147
|
-
# @return
|
148
|
+
# @return [OmniAI::Transcribe::Transcription]
|
148
149
|
def transcribe(io, model:, language: nil, prompt: nil, temperature: nil, format: nil)
|
149
150
|
raise NotImplementedError, "#{self.class.name}#speak undefined"
|
150
151
|
end
|
data/lib/omniai/config.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module OmniAI
|
4
|
-
# A configuration for each agent w/ `api_key` / `host` / `logger`.
|
4
|
+
# A configuration for each agent w/ `api_key` / `host` / `logger`.
|
5
|
+
#
|
6
|
+
# Usage:
|
5
7
|
#
|
6
8
|
# OmniAI::OpenAI.config do |config|
|
7
9
|
# config.api_key = '...'
|
@@ -18,7 +18,6 @@ module OmniAI
|
|
18
18
|
@logger.error("#{name}: #{error.message}")
|
19
19
|
end
|
20
20
|
|
21
|
-
# @param name [String]
|
22
21
|
# @param payload [Hash]
|
23
22
|
# @option payload [HTTP::Request] :request
|
24
23
|
def start(_, payload)
|
@@ -26,7 +25,6 @@ module OmniAI
|
|
26
25
|
@logger.info("#{request.verb.upcase} #{request.uri}")
|
27
26
|
end
|
28
27
|
|
29
|
-
# @param name [String]
|
30
28
|
# @param payload [Hash]
|
31
29
|
# @option payload [HTTP::Response] :response
|
32
30
|
def finish(_, payload)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Tool
|
5
|
+
# Usage:
|
6
|
+
#
|
7
|
+
# parameters = OmniAI::Tool::Parameters.new(properties: {
|
8
|
+
# n: OmniAI::Tool::Parameters.integer(description: 'The nth number to calculate.')
|
9
|
+
# required: %i[n]
|
10
|
+
# })
|
11
|
+
class Parameters
|
12
|
+
module Type
|
13
|
+
OBJECT = 'object'
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param type [String]
|
17
|
+
# @param properties [Hash]
|
18
|
+
# @param required [Array<String>]
|
19
|
+
# @return [OmniAI::Tool::Parameters]
|
20
|
+
def initialize(type: Type::OBJECT, properties: {}, required: [])
|
21
|
+
@type = type
|
22
|
+
@properties = properties
|
23
|
+
@required = required
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Hash]
|
27
|
+
def prepare
|
28
|
+
{
|
29
|
+
type: @type,
|
30
|
+
properties: @properties.transform_values(&:prepare),
|
31
|
+
required: @required,
|
32
|
+
}.compact
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param args [Hash]
|
36
|
+
# @return [Hash]
|
37
|
+
def parse(args)
|
38
|
+
result = {}
|
39
|
+
@properties.each do |name, property|
|
40
|
+
value = args[String(name)]
|
41
|
+
result[name.intern] = property.parse(value) if value
|
42
|
+
end
|
43
|
+
result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
class Tool
|
5
|
+
# Usage:
|
6
|
+
#
|
7
|
+
# property = OmniAI::Tool::Property.new(type: 'string', description: 'The nth number to calculate.')
|
8
|
+
class Property
|
9
|
+
module Type
|
10
|
+
BOOLEAN = 'boolean'
|
11
|
+
INTEGER = 'integer'
|
12
|
+
STRING = 'string'
|
13
|
+
NUMBER = 'number'
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
# @return [String, nil]
|
20
|
+
attr_reader :description
|
21
|
+
|
22
|
+
# @return [Array<String>, nil]
|
23
|
+
attr_reader :enum
|
24
|
+
|
25
|
+
# @param description [String]
|
26
|
+
# @param enum [Array<String>]
|
27
|
+
# @return [OmniAI::Tool::Property]
|
28
|
+
def self.boolean(description: nil, enum: nil)
|
29
|
+
new(type: Type::BOOLEAN, description:, enum:)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param description [String]
|
33
|
+
# @param enum [Array<String>]
|
34
|
+
# @return [OmniAI::Tool::Property]
|
35
|
+
def self.integer(description: nil, enum: nil)
|
36
|
+
new(type: Type::INTEGER, description:, enum:)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param description [String]
|
40
|
+
# @param enum [Array<String>]
|
41
|
+
# @return [OmniAI::Tool::Property]
|
42
|
+
def self.string(description: nil, enum: nil)
|
43
|
+
new(type: Type::STRING, description:, enum:)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param description [String]
|
47
|
+
# @param enum [Array<String>]
|
48
|
+
# @return [OmniAI::Tool::Property]
|
49
|
+
def self.number(description: nil, enum: nil)
|
50
|
+
new(type: Type::NUMBER, description:, enum:)
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param description [String]
|
54
|
+
# @param enum [Array<String>]
|
55
|
+
# @return [OmniAI::Tool::Property]
|
56
|
+
def initialize(type:, description: nil, enum: nil)
|
57
|
+
@type = type
|
58
|
+
@description = description
|
59
|
+
@enum = enum
|
60
|
+
end
|
61
|
+
|
62
|
+
# @example
|
63
|
+
# property.prepare
|
64
|
+
# # {
|
65
|
+
# # type: 'string',
|
66
|
+
# # description: 'The unit (e.g. "fahrenheit" or "celsius").'
|
67
|
+
# # enum: %w[fahrenheit celsius]
|
68
|
+
# # }
|
69
|
+
#
|
70
|
+
# @return [Hash]
|
71
|
+
def prepare
|
72
|
+
{
|
73
|
+
type: @type,
|
74
|
+
description: @description,
|
75
|
+
enum: @enum,
|
76
|
+
}.compact
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [String, Integer, Float, Boolean, Object]
|
80
|
+
def parse(value)
|
81
|
+
case @type
|
82
|
+
when Type::INTEGER then Integer(value)
|
83
|
+
when Type::STRING then String(value)
|
84
|
+
when Type::NUMBER then Float(value)
|
85
|
+
else value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/omniai/tool.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAI
|
4
|
+
# Usage:
|
5
|
+
#
|
6
|
+
# fibonacci = proc do |n:|
|
7
|
+
# next(0) if n == 0
|
8
|
+
# next(1) if n == 1
|
9
|
+
# fibonacci.call(n: n - 1) + fibonacci.call(n: n - 2)
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# tool = OmniAI::Tool.new(fibonacci,
|
13
|
+
# name: 'Fibonacci',
|
14
|
+
# description: 'Cacluate the nth Fibonacci',
|
15
|
+
# parameters: OmniAI::Tool::Parameters.new(
|
16
|
+
# properties: {
|
17
|
+
# n: OmniAI::Tool::Property.integer(description: 'The nth Fibonacci number to calculate')
|
18
|
+
# },
|
19
|
+
# required: %i[n],
|
20
|
+
# )
|
21
|
+
# )
|
22
|
+
class Tool
|
23
|
+
# @return [Proc]
|
24
|
+
attr_accessor :function
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
attr_accessor :name
|
28
|
+
|
29
|
+
# @return [String, nil]
|
30
|
+
attr_accessor :description
|
31
|
+
|
32
|
+
# @return [Hash, nil]
|
33
|
+
attr_accessor :parameters
|
34
|
+
|
35
|
+
# @param function [Proc]
|
36
|
+
# @param name [String]
|
37
|
+
# @param description [String]
|
38
|
+
# @param parameters [Hash]
|
39
|
+
def initialize(function, name:, description: nil, parameters: nil)
|
40
|
+
@function = function
|
41
|
+
@name = name
|
42
|
+
@description = description
|
43
|
+
@parameters = parameters
|
44
|
+
end
|
45
|
+
|
46
|
+
# @example
|
47
|
+
# tool.prepare
|
48
|
+
# # {
|
49
|
+
# # type: 'function',
|
50
|
+
# # function: {
|
51
|
+
# # name: 'Fibonacci',
|
52
|
+
# # description: 'Calculate the nth Fibonacci number',
|
53
|
+
# # parameters: {
|
54
|
+
# # type: 'object',
|
55
|
+
# # properties: {
|
56
|
+
# # n: OmniAI::Tool::Property.integer(description: 'The nth Fibonacci number to calculate')
|
57
|
+
# # },
|
58
|
+
# # required: ['n']
|
59
|
+
# # }
|
60
|
+
# # }
|
61
|
+
# # }
|
62
|
+
#
|
63
|
+
# @return [Hash]
|
64
|
+
def prepare
|
65
|
+
{
|
66
|
+
type: 'function',
|
67
|
+
function: {
|
68
|
+
name: @name,
|
69
|
+
description: @description,
|
70
|
+
parameters: @parameters&.prepare,
|
71
|
+
}.compact,
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# @example
|
76
|
+
# tool.call({ "n" => 6 })
|
77
|
+
# #=> 8
|
78
|
+
#
|
79
|
+
# @param args [Hash]
|
80
|
+
# @return [String]
|
81
|
+
def call(args = {})
|
82
|
+
@function.call(**(@parameters ? @parameters.parse(args) : args))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/omniai/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|
@@ -68,18 +68,24 @@ files:
|
|
68
68
|
- exe/omniai
|
69
69
|
- lib/omniai.rb
|
70
70
|
- lib/omniai/chat.rb
|
71
|
-
- lib/omniai/chat/chunk.rb
|
72
|
-
- lib/omniai/chat/completion.rb
|
73
71
|
- lib/omniai/chat/content/file.rb
|
74
72
|
- lib/omniai/chat/content/media.rb
|
75
73
|
- lib/omniai/chat/content/text.rb
|
76
74
|
- lib/omniai/chat/content/url.rb
|
77
|
-
- lib/omniai/chat/
|
78
|
-
- lib/omniai/chat/
|
79
|
-
- lib/omniai/chat/
|
80
|
-
- lib/omniai/chat/
|
81
|
-
- lib/omniai/chat/
|
82
|
-
- lib/omniai/chat/
|
75
|
+
- lib/omniai/chat/response/choice.rb
|
76
|
+
- lib/omniai/chat/response/chunk.rb
|
77
|
+
- lib/omniai/chat/response/completion.rb
|
78
|
+
- lib/omniai/chat/response/delta.rb
|
79
|
+
- lib/omniai/chat/response/delta_choice.rb
|
80
|
+
- lib/omniai/chat/response/function.rb
|
81
|
+
- lib/omniai/chat/response/message.rb
|
82
|
+
- lib/omniai/chat/response/message_choice.rb
|
83
|
+
- lib/omniai/chat/response/part.rb
|
84
|
+
- lib/omniai/chat/response/payload.rb
|
85
|
+
- lib/omniai/chat/response/resource.rb
|
86
|
+
- lib/omniai/chat/response/stream.rb
|
87
|
+
- lib/omniai/chat/response/tool_call.rb
|
88
|
+
- lib/omniai/chat/response/usage.rb
|
83
89
|
- lib/omniai/cli.rb
|
84
90
|
- lib/omniai/cli/base_handler.rb
|
85
91
|
- lib/omniai/cli/chat_handler.rb
|
@@ -87,6 +93,9 @@ files:
|
|
87
93
|
- lib/omniai/config.rb
|
88
94
|
- lib/omniai/instrumentation.rb
|
89
95
|
- lib/omniai/speak.rb
|
96
|
+
- lib/omniai/tool.rb
|
97
|
+
- lib/omniai/tool/parameters.rb
|
98
|
+
- lib/omniai/tool/property.rb
|
90
99
|
- lib/omniai/transcribe.rb
|
91
100
|
- lib/omniai/transcribe/transcription.rb
|
92
101
|
- lib/omniai/version.rb
|
@@ -112,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
121
|
- !ruby/object:Gem::Version
|
113
122
|
version: '0'
|
114
123
|
requirements: []
|
115
|
-
rubygems_version: 3.5.
|
124
|
+
rubygems_version: 3.5.14
|
116
125
|
signing_key:
|
117
126
|
specification_version: 4
|
118
127
|
summary: A generalized framework for interacting with many AI services
|
data/lib/omniai/chat/chunk.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A chunk returned by the API.
|
6
|
-
class Chunk
|
7
|
-
attr_accessor :data
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
def initialize(data:)
|
11
|
-
@data = data
|
12
|
-
end
|
13
|
-
|
14
|
-
# @return [String]
|
15
|
-
def id
|
16
|
-
@data['id']
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [Time]
|
20
|
-
def created
|
21
|
-
Time.at(@data['created']) if @data['created']
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Time]
|
25
|
-
def updated
|
26
|
-
Time.at(@data['updated']) if @data['updated']
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [String]
|
30
|
-
def model
|
31
|
-
@data['model']
|
32
|
-
end
|
33
|
-
|
34
|
-
# @return [Array<OmniAI::Chat::DeltaChoice>]
|
35
|
-
def choices
|
36
|
-
@choices ||= @data['choices'].map { |data| DeltaChoice.for(data:) }
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param index [Integer]
|
40
|
-
# @return [OmniAI::Chat::Delta]
|
41
|
-
def choice(index: 0)
|
42
|
-
choices[index]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A completion returned by the API.
|
6
|
-
class Completion
|
7
|
-
attr_accessor :data
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
def initialize(data:)
|
11
|
-
@data = data
|
12
|
-
end
|
13
|
-
|
14
|
-
# @return [String]
|
15
|
-
def id
|
16
|
-
@data['id']
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [Time]
|
20
|
-
def created
|
21
|
-
Time.at(@data['created']) if @data['created']
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Time]
|
25
|
-
def updated
|
26
|
-
Time.at(@data['updated']) if @data['updated']
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [String]
|
30
|
-
def model
|
31
|
-
@data['model']
|
32
|
-
end
|
33
|
-
|
34
|
-
# @return [OmniAI::Chat::Usage]
|
35
|
-
def usage
|
36
|
-
return unless @data['usage']
|
37
|
-
|
38
|
-
@usage ||= Usage.for(data: @data['usage'])
|
39
|
-
end
|
40
|
-
|
41
|
-
# @return [Array<OmniAI::Chat::MessageChoice>]
|
42
|
-
def choices
|
43
|
-
@choices ||= @data['choices'].map { |data| MessageChoice.for(data:) }
|
44
|
-
end
|
45
|
-
|
46
|
-
# @param index [Integer] optional - default is 0
|
47
|
-
# @return [OmniAI::Chat::MessageChoice]
|
48
|
-
def choice(index: 0)
|
49
|
-
choices[index]
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/lib/omniai/chat/delta.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A delta returned by the API.
|
6
|
-
class Delta
|
7
|
-
attr_accessor :role, :content
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
# @return [OmniAI::Chat::Message]
|
11
|
-
def self.for(data:)
|
12
|
-
content = data['content'] || data[:content]
|
13
|
-
role = data['role'] || data[:role]
|
14
|
-
|
15
|
-
new(content:, role: role || Role::USER)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param content [String]
|
19
|
-
# @param role [String]
|
20
|
-
def initialize(content:, role: nil)
|
21
|
-
@content = content
|
22
|
-
@role = role
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [String]
|
26
|
-
def inspect
|
27
|
-
"#<#{self.class.name} role=#{role.inspect} content=#{content.inspect}>"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A delta choice returned by the API.
|
6
|
-
class DeltaChoice
|
7
|
-
attr_accessor :index, :delta
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
# @return [OmniAI::Chat::Choice]
|
11
|
-
def self.for(data:)
|
12
|
-
index = data['index']
|
13
|
-
delta = Delta.for(data: data['delta'])
|
14
|
-
|
15
|
-
new(index:, delta:)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param index [Integer]
|
19
|
-
# @param delta [Delta]
|
20
|
-
def initialize(index:, delta:)
|
21
|
-
@index = index
|
22
|
-
@delta = delta
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/omniai/chat/message.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A message returned by the API.
|
6
|
-
class Message
|
7
|
-
attr_accessor :role, :content
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
# @return [OmniAI::Chat::Message]
|
11
|
-
def self.for(data:)
|
12
|
-
content = data['content'] || data[:content]
|
13
|
-
role = data['role'] || data[:role]
|
14
|
-
|
15
|
-
new(content:, role: role || Role::USER)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param content [String]
|
19
|
-
# @param role [String] optional (default to "user") e.g. "assistant" / "user" / "system"
|
20
|
-
def initialize(content:, role: OmniAI::Chat::Role::USER)
|
21
|
-
@role = role
|
22
|
-
@content = content
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [String]
|
26
|
-
def inspect
|
27
|
-
"#<#{self.class.name} role=#{role.inspect} content=#{content.inspect}>"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A choice returned by the API.
|
6
|
-
class MessageChoice
|
7
|
-
attr_accessor :index, :message
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
# @return [OmniAI::Chat::Choice]
|
11
|
-
def self.for(data:)
|
12
|
-
index = data['index']
|
13
|
-
message = Message.for(data: data['message'])
|
14
|
-
|
15
|
-
new(index:, message:)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param index [Integer]
|
19
|
-
# @param message [OmniAI::Chat::Message]
|
20
|
-
def initialize(index:, message:)
|
21
|
-
@index = index
|
22
|
-
@message = message
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/omniai/chat/stream.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A stream given when streaming.
|
6
|
-
class Stream
|
7
|
-
# @param response [HTTP::Response]
|
8
|
-
def initialize(response:)
|
9
|
-
@response = response
|
10
|
-
@parser = EventStreamParser::Parser.new
|
11
|
-
end
|
12
|
-
|
13
|
-
# @yield [OmniAI::Chat::Chunk]
|
14
|
-
def stream!
|
15
|
-
@response.body.each do |chunk|
|
16
|
-
@parser.feed(chunk) do |_, data|
|
17
|
-
next if data.eql?('[DONE]')
|
18
|
-
|
19
|
-
yield(Chunk.new(data: JSON.parse(data)))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/omniai/chat/usage.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OmniAI
|
4
|
-
class Chat
|
5
|
-
# A usage returned by the API.
|
6
|
-
class Usage
|
7
|
-
attr_accessor :input_tokens, :output_tokens, :total_tokens
|
8
|
-
|
9
|
-
# @param data [Hash]
|
10
|
-
# @return [OmniAI::Chat::Usage]
|
11
|
-
def self.for(data:)
|
12
|
-
input_tokens = data['input_tokens'] || data['prompt_tokens']
|
13
|
-
output_tokens = data['output_tokens'] || data['completion_tokens']
|
14
|
-
total_tokens = data['total_tokens'] || (input_tokens + output_tokens)
|
15
|
-
|
16
|
-
new(
|
17
|
-
input_tokens:,
|
18
|
-
output_tokens:,
|
19
|
-
total_tokens:
|
20
|
-
)
|
21
|
-
end
|
22
|
-
|
23
|
-
# @param input_tokens [Integer]
|
24
|
-
# @param output_tokens [Integer]
|
25
|
-
# @param total_tokens [Integer]
|
26
|
-
def initialize(input_tokens:, output_tokens:, total_tokens:)
|
27
|
-
@input_tokens = input_tokens
|
28
|
-
@output_tokens = output_tokens
|
29
|
-
@total_tokens = total_tokens
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Integer]
|
33
|
-
def completion_tokens
|
34
|
-
@output_tokens
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [Integer]
|
38
|
-
def prompt_tokens
|
39
|
-
@input_tokens
|
40
|
-
end
|
41
|
-
|
42
|
-
# @return [String]
|
43
|
-
def inspect
|
44
|
-
"#<#{self.class.name} input_tokens=#{input_tokens} output_tokens=#{output_tokens} total_tokens=#{total_tokens}>"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|