omniai 1.2.2 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 239eb058ae53afd3f28fe00a18af282a856ca842a4f81bcdf22413a02d8d44fc
4
- data.tar.gz: e74ac69ab6435fd9da3d1602c9cce5e052f53bebf609d8815868b58be0897884
3
+ metadata.gz: 341ce12cf0950b167a43c298f3ccb1df821bbdc469bc6824b14219bef4e38ab5
4
+ data.tar.gz: 9badfe7e48d20fe84054d9206e8572a333f382407c3236a3715e1d85cf6b6a84
5
5
  SHA512:
6
- metadata.gz: a6b18cae8b5aab610bf255860ccd514b794eda3021b2f9b3aa86020968e0f449b683b4db74291e71583698b61d7612a7e49b3ee0310c806a826f7b27c50c7d92
7
- data.tar.gz: bd12b38f3c9a150fe09644aaf187aa05aa1063ee136196e11fcaa356f4fe5bdc2c93f6292a79025f301c6d35a1202186f6ad787787828dfd5af91f229a34b550
6
+ metadata.gz: f81164e402f5e1e4b2ccc2e627675d42631cfda985bf91469eb32735a789eecbe69c7f10bd9d2d5b4959f2d0f2484ae1bf7ced58f607a7cae822846c831ce96c
7
+ data.tar.gz: '0182828dad6fb6f6d98550608c3bd57ee980ff4acbcc6dfb3ee7d61a16200566f1c8965424537583d0b73225298ca5eba6ddafd3c51791ca847f53264a7654fd'
data/Gemfile CHANGED
@@ -4,8 +4,8 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
+ gem 'logger'
7
8
  gem 'rake'
8
-
9
9
  gem 'rspec'
10
10
  gem 'rspec_junit_formatter'
11
11
  gem 'rubocop'
data/README.md CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  [![CircleCI](https://circleci.com/gh/ksylvest/omniai.svg?style=svg)](https://circleci.com/gh/ksylvest/omniai)
4
4
 
5
- OmniAI is a flexible AI library that standardizes the APIs for multiple AI providers:
5
+ OmniAI is a flexible AI library that standardizes the APIs of many different AIs:
6
6
 
7
7
  - [OmniAI::Anthropic](https://github.com/ksylvest/omniai-anthropic)
8
8
  - [OmniAI::Google](https://github.com/ksylvest/omniai-google)
9
9
  - [OmniAI::Mistral](https://github.com/ksylvest/omniai-mistral)
10
10
  - [OmniAI::OpenAI](https://github.com/ksylvest/omniai-openai)
11
11
 
12
+ All libraries are community maintained.
13
+
12
14
  ## Installation
13
15
 
14
16
  ```sh
@@ -72,6 +74,79 @@ Ollama support is offered through [OmniAI::OpenAI](https://github.com/ksylvest/o
72
74
 
73
75
  [Usage with Ollama](https://github.com/ksylvest/omniai-openai#usage-with-ollama)
74
76
 
77
+ #### Logging
78
+
79
+ Logging the **request** / **response** is configurable by passing a logger into any client:
80
+
81
+ ```ruby
82
+ require 'omniai/openai'
83
+ require 'logger'
84
+
85
+ logger = Logger.new(STDOUT)
86
+ client = OmniAI::Example::Client.new(logger:)
87
+ ```
88
+
89
+ ```
90
+ I, [...] INFO -- : > POST https://...
91
+ D, [...] DEBUG -- : Authorization: Bearer ...
92
+ ...
93
+ {"messages":[{"role":"user","content":"Tell me a joke!"}],"model":"..."}
94
+ I, [...] INFO -- : < 200 OK
95
+ D, [...] DEBUG -- : Date: ...
96
+ ...
97
+ {
98
+ "id": "...",
99
+ "object": "...",
100
+ ...
101
+ }
102
+ ```
103
+
104
+ The level of the logger can be configured to either `INFO` and `DEBUG`:
105
+
106
+ **INFO**:
107
+
108
+ ```ruby
109
+ logger.level = Logger::INFO
110
+ ```
111
+
112
+ - Request: verb / URI
113
+ - Response: status
114
+
115
+ **DEBUG**:
116
+
117
+ ```ruby
118
+ logger.level = Logger::DEBUG
119
+ ```
120
+
121
+ - Request: verb / URI / headers / body
122
+ - Response: status / headers / body
123
+
124
+ #### Timeouts
125
+
126
+ Timeouts are configurable by passing a `timeout` an integer duration for the request / response of any APIs using:
127
+
128
+ ```ruby
129
+ require 'omniai/openai'
130
+ require 'logger'
131
+
132
+ logger = Logger.new(STDOUT)
133
+ client = OmniAI::OpenAI::Client.new(timeout: 8) # i.e. 8 seconds
134
+ ```
135
+
136
+ Timeouts are also be configurable by passing a `timeout` hash with `timeout` / `read` / `write` / `keys using:
137
+
138
+ ```ruby
139
+ require 'omniai/openai'
140
+ require 'logger'
141
+
142
+ logger = Logger.new(STDOUT)
143
+ client = OmniAI::OpenAI::Client.new(timeout: {
144
+ read: 2, # i.e. 2 seconds
145
+ write: 3, # i.e. 3 seconds
146
+ connect: 4, # i.e. 4 seconds
147
+ })
148
+ ```
149
+
75
150
  ### Chat
76
151
 
77
152
  Clients that support chat (e.g. Anthropic w/ "Claude", Google w/ "Gemini", Mistral w/ "LeChat", OpenAI w/ "ChatGPT", etc) generate completions using the following calls:
@@ -31,9 +31,9 @@ module OmniAI
31
31
  @data['model']
32
32
  end
33
33
 
34
- # @return [Array<OmniAI::Chat::Choice>]
34
+ # @return [Array<OmniAI::Chat::DeltaChoice>]
35
35
  def choices
36
- @choices ||= @data['choices'].map { |data| Choice.for(data:) }
36
+ @choices ||= @data['choices'].map { |data| DeltaChoice.for(data:) }
37
37
  end
38
38
 
39
39
  # @param index [Integer]
@@ -38,13 +38,13 @@ module OmniAI
38
38
  @usage ||= Usage.for(data: @data['usage'])
39
39
  end
40
40
 
41
- # @return [Array<OmniAI::Chat::Choice>]
41
+ # @return [Array<OmniAI::Chat::MessageChoice>]
42
42
  def choices
43
- @choices ||= @data['choices'].map { |data| Choice.for(data:) }
43
+ @choices ||= @data['choices'].map { |data| MessageChoice.for(data:) }
44
44
  end
45
45
 
46
46
  # @param index [Integer] optional - default is 0
47
- # @return [OmniAI::Chat::Choice]
47
+ # @return [OmniAI::Chat::MessageChoice]
48
48
  def choice(index: 0)
49
49
  choices[index]
50
50
  end
@@ -0,0 +1,26 @@
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
@@ -0,0 +1,26 @@
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/client.rb CHANGED
@@ -9,19 +9,36 @@ module OmniAI
9
9
  # def initialize(api_key: ENV.fetch('OPENAI_API_KEY'), logger: nil)
10
10
  # super
11
11
  # end
12
+ #
13
+ # # @return [HTTP::Client]
14
+ # def connection
15
+ # @connection ||= super.auth("Bearer: #{@api_key}")
16
+ # end
12
17
  # end
13
18
  class Client
14
19
  class Error < StandardError; end
15
20
 
16
- attr_accessor :api_key, :logger, :host
21
+ # @return [String, nil]
22
+ attr_accessor :api_key
23
+
24
+ # @return [Logger, nil]
25
+ attr_accessor :logger
26
+
27
+ # @return [String, nil]
28
+ attr_accessor :host
29
+
30
+ # @return [Integer, nil]
31
+ attr_accessor :timeout
17
32
 
18
- # @param api_key [String] optional
19
- # @param host [String] optional - supports for customzing the host of the client (e.g. 'http://localhost:8080')
20
- # @param logger [Logger] optional
21
- def initialize(api_key: nil, logger: nil, host: nil)
33
+ # @param api_key [String, nil] optional
34
+ # @param host [String, nil] optional - supports for customzing the host of the client (e.g. 'http://localhost:8080')
35
+ # @param logger [Logger, nil] optional
36
+ # @param timeout [Integer, nil] optional
37
+ def initialize(api_key: nil, logger: nil, host: nil, timeout: nil)
22
38
  @api_key = api_key
23
39
  @host = host
24
40
  @logger = logger
41
+ @timeout = timeout
25
42
  end
26
43
 
27
44
  # @return [String]
@@ -39,7 +56,10 @@ module OmniAI
39
56
 
40
57
  # @return [HTTP::Client]
41
58
  def connection
42
- raise NotImplementedError, "#{self.class.name}#connection undefined"
59
+ http = HTTP.persistent(@host)
60
+ http = http.use(logging: { logger: @logger }) if @logger
61
+ http = http.timeout(@timeout) if @timeout
62
+ http
43
63
  end
44
64
 
45
65
  # @raise [OmniAI::Error]
data/lib/omniai/config.rb CHANGED
@@ -1,9 +1,56 @@
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`. Usage:
5
+ #
6
+ # OmniAI::OpenAI.config do |config|
7
+ # config.api_key = '...'
8
+ # config.host = 'http://localhost:8080'
9
+ # config.logger = Logger.new(STDOUT)
10
+ # config.timeout = 15
11
+ # config.chat_options = { ... }
12
+ # config.transcribe_options = { ... }
13
+ # config.speak_options = { ... }
14
+ # end
5
15
  class Config
6
- attr_accessor :api_key, :host, :logger
16
+ # @return [String, nil]
17
+ attr_accessor :api_key
18
+
19
+ # @return [String, nil]
20
+ attr_accessor :host
21
+
22
+ # @return [Logger, nil]
23
+ attr_accessor :logger
24
+
25
+ # @return [Integer, Hash{Symbol => Integer}, nil]
26
+ # @option timeout [Integer] :read
27
+ # @option timeout [Integer] :write
28
+ # @option timeout [Integer] :connect
29
+ attr_accessor :timeout
30
+
31
+ # @return [Hash]
32
+ attr_accessor :chat_options
33
+
34
+ # @return [Hash]
35
+ attr_accessor :transcribe_options
36
+
37
+ # @return [Hash]
38
+ attr_accessor :speak_options
39
+
40
+ # @param api_key [String] optional
41
+ # @param host [String] optional
42
+ # @param logger [Logger] optional
43
+ # @param timeout [Integer] optional
44
+ def initialize(api_key: nil, host: nil, logger: nil, timeout: nil)
45
+ @api_key = api_key
46
+ @host = host
47
+ @logger = logger
48
+ @timeout = timeout
49
+
50
+ @chat_options = {}
51
+ @transcribe_options = {}
52
+ @speak_options = {}
53
+ end
7
54
 
8
55
  # @return [String]
9
56
  def inspect
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OmniAI
4
- VERSION = '1.2.2'
4
+ VERSION = '1.3.0'
5
5
  end
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.2.2
4
+ version: 1.3.0
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-06-20 00:00:00.000000000 Z
11
+ date: 2024-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -66,7 +66,6 @@ files:
66
66
  - bin/setup
67
67
  - lib/omniai.rb
68
68
  - lib/omniai/chat.rb
69
- - lib/omniai/chat/choice.rb
70
69
  - lib/omniai/chat/chunk.rb
71
70
  - lib/omniai/chat/completion.rb
72
71
  - lib/omniai/chat/content/file.rb
@@ -74,7 +73,9 @@ files:
74
73
  - lib/omniai/chat/content/text.rb
75
74
  - lib/omniai/chat/content/url.rb
76
75
  - lib/omniai/chat/delta.rb
76
+ - lib/omniai/chat/delta_choice.rb
77
77
  - lib/omniai/chat/message.rb
78
+ - lib/omniai/chat/message_choice.rb
78
79
  - lib/omniai/chat/stream.rb
79
80
  - lib/omniai/chat/usage.rb
80
81
  - lib/omniai/client.rb
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- # A choice returned by the API.
6
- class Choice
7
- attr_accessor :index, :delta, :message
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']) if data['delta']
14
- message = Message.for(data: data['message']) if data['message']
15
-
16
- new(index:, delta:, message:)
17
- end
18
-
19
- # @param index [Integer]
20
- # @param delta [OmniAI::Chat::Delta] optional
21
- # @param message [OmniAI::Chat::Message] optional
22
- def initialize(index:, delta:, message:)
23
- @index = index
24
- @delta = delta
25
- @message = message
26
- end
27
- end
28
- end
29
- end