omniai 0.0.8 → 0.1.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: b478ff0decf827e07f67c3c85973ad576f546980417f82ff5f71949c99157e22
4
- data.tar.gz: edcd0f60037d10fca5aec3ecc6cbd08c0c9b1c42aa9353506c232821cbd0fe0f
3
+ metadata.gz: 7f87f1b50c279a434ef7c23fb84f66321450897a62c9db0eda70659e3ac024a6
4
+ data.tar.gz: 334d3055c491bb582107b62ff555bba9bd175d9a23d7492cbfb07bdae4a1b876
5
5
  SHA512:
6
- metadata.gz: 69d8116cf9313da66fc89857332d8efb9e2005f007474dd377ec8c34ffb5c18e22155740390acb793f255447399f176605f0306e94c99c0e160346581fd1e597
7
- data.tar.gz: 5c7975664071ff8158d5fd9e33fac37f7874b856d319edb0555deeefb78ac70e1dd7c13ffde015a5b0029c4b1ad26554d34ba0ca294bbf8acc81365adef1705c
6
+ metadata.gz: c6ed29f063e505bf58ad6ca19f8d4dace1627f73fb3bee8b52fb5f9756d550f3ff322e7ef00d286044eaa9a0b48a16811e62efb70c130948302834e9206a362d
7
+ data.tar.gz: 512fa19f0b5cb1c67a081d413bdf8e9b964716f08151fc20b048f5c2b46edc4eb8ec891563eb510c98e7ae5749c63afbbb661546718ec219f1287592c1f351da
data/README.md CHANGED
@@ -22,44 +22,57 @@ gem install omniai-openai
22
22
 
23
23
  ## Usage
24
24
 
25
- ### Chat
25
+ OmniAI implements APIs for a number of popular clients by default. A client can be initialized using the specific gem (e.g. `omniai-openai` for `OmniAI::OpenAI`). Vendor specific docs can be found within each repo.
26
+
27
+ ### Client
26
28
 
27
- #### Anthropic (Claude)
29
+ #### [OmniAI::Anthropic](https://github.com/ksylvest/omniai-anthropic)
28
30
 
29
31
  ```ruby
30
32
  require 'omniai-anthropic'
31
33
 
32
34
  client = OmniAI::Anthropic::Client.new
33
- completion = client.chat.completion('Tell me a joke.')
34
- completion.choice.message.content # '...'
35
35
  ```
36
36
 
37
- #### Google (Gemini)
37
+ #### [OmniAI::Google](https://github.com/ksylvest/omniai-google)
38
38
 
39
39
  ```ruby
40
40
  require 'omniai-google'
41
41
 
42
42
  client = OmniAI::Google::Client.new
43
- completion = client.chat.completion('Tell me a joke.')
44
- completion.choice.message.content # '...'
45
43
  ```
46
44
 
47
- #### Mistral (LeChat)
45
+ #### [OmniAI::Mistral](https://github.com/ksylvest/omniai-mistral)
48
46
 
49
47
  ```ruby
50
48
  require 'omniai-mistral'
51
49
 
52
50
  client = OmniAI::Mistral::Client.new
53
- completion = client.chat.completion('Tell me a joke.')
54
- completion.choice.message.content # '...'
55
51
  ```
56
52
 
57
- #### OpenAI (ChatGPT)
53
+ #### [OmniAI::OpenAI](https://github.com/ksylvest/omniai-openai)
58
54
 
59
55
  ```ruby
60
56
  require 'omniai-openai'
61
57
 
62
58
  client = OmniAI::OpenAI::Client.new
59
+ ```
60
+
61
+ ### Chat
62
+
63
+ Clients that support chat (e.g. Anthropic w/ "Claude", Google w/ "Gemini", Mistral w/ "LeChat", OpenAI w/ "ChatGPT", etc) can generate completions using either a basic or streaming API:
64
+
65
+ #### Basic
66
+
67
+ ```ruby
63
68
  completion = client.chat.completion('Tell me a joke.')
64
- completion.choice.message.content # '...'
69
+ puts(completion.choice.message.content) # '...'
70
+ ```
71
+
72
+ #### Streaming
73
+
74
+ ```ruby
75
+ client.chat.completion 'Tell me a joke.', stream: proc do |chunk|
76
+ print(chunk.choice.delta.content) # '...'
77
+ end
65
78
  ```
@@ -4,14 +4,26 @@ module OmniAI
4
4
  class Chat
5
5
  # A choice returned by the API.
6
6
  class Choice
7
- attr_accessor :index, :message, :role
7
+ attr_accessor :data
8
8
 
9
- # @param index [Integer]
10
- # @param message [OmniAI::Chat::Message]
11
- # @param role [String]
12
- def initialize(index:, message:)
13
- @index = index
14
- @message = message
9
+ # @param data [Hash]
10
+ def initialize(data:)
11
+ @data = data
12
+ end
13
+
14
+ # @return [Integer]
15
+ def index
16
+ @data['index']
17
+ end
18
+
19
+ # @return [OmniAI::Chat::Delta]
20
+ def delta
21
+ Delta.new(data: @data['delta']) if @data['delta']
22
+ end
23
+
24
+ # @return [OmniAI::Chat::Message]
25
+ def message
26
+ Message.new(data: @data['message']) if @data['message']
15
27
  end
16
28
  end
17
29
  end
@@ -0,0 +1,41 @@
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
+ # @param [index] [Integer]
35
+ # @return [OmniAI::Chat::Delta]
36
+ def choice(index: 0)
37
+ choices[index]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,51 @@
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
+ @usage ||= Usage.new(data: @data['usage']) if @data['usage']
37
+ end
38
+
39
+ # @return [Array<OmniAI::Chat::Choice>]
40
+ def choices
41
+ @choices ||= @data['choices'].map { |choice| Choice.new(data: choice) }
42
+ end
43
+
44
+ # @param [index] [Integer] optional - default is 0
45
+ # @return [OmniAI::Chat::Choice]
46
+ def choice(index: 0)
47
+ choices[index]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,26 @@
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 :data
8
+
9
+ # @param content [Integer]
10
+ # @param role [String]
11
+ def initialize(data:)
12
+ @data = data
13
+ end
14
+
15
+ # @return [String, nil]
16
+ def role
17
+ @data['role']
18
+ end
19
+
20
+ # @return [String, nil]
21
+ def content
22
+ @data['content']
23
+ end
24
+ end
25
+ end
26
+ end
@@ -4,13 +4,22 @@ module OmniAI
4
4
  class Chat
5
5
  # A message returned by the API.
6
6
  class Message
7
- attr_accessor :content, :role
7
+ attr_accessor :data
8
8
 
9
9
  # @param content [Integer]
10
10
  # @param role [String]
11
- def initialize(content:, role:)
12
- @content = content
13
- @role = role
11
+ def initialize(data:)
12
+ @data = data
13
+ end
14
+
15
+ # @return [String]
16
+ def role
17
+ @data['role']
18
+ end
19
+
20
+ # @return [String]
21
+ def content
22
+ @data['content']
14
23
  end
15
24
  end
16
25
  end
@@ -46,7 +46,26 @@ module OmniAI
46
46
  # @param response [HTTP::Response]
47
47
  # @return [OmniAI::Chat::Completion::Response]
48
48
  def parse!(response:)
49
- raise NotImplementedError, "#{self.class.name}#parse! undefined"
49
+ if @stream
50
+ stream!(response:)
51
+ else
52
+ OmniAI::OpenAI::Chat::Completion.new(data: response.parse)
53
+ end
54
+ end
55
+
56
+ # @param response [HTTP::Response]
57
+ # @return [OmniAI::Chat::Chunk]
58
+ def stream!(response:)
59
+ parser = EventStreamParser::Parser.new
60
+
61
+ response.body.each do |chunk|
62
+ parser.feed(chunk) do |_, data|
63
+ break if data.eql?('[DONE]')
64
+
65
+ chunk = OmniAI::OpenAI::Chat::Chunk.new(data: data.parse)
66
+ @stream.call(chunk)
67
+ end
68
+ end
50
69
  end
51
70
 
52
71
  # @return [Hash]
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Chat
5
+ # A usage 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 [Integer]
15
+ def completion_tokens
16
+ @data['completion_tokens']
17
+ end
18
+
19
+ # @return [Integer]
20
+ def prompt_tokens
21
+ @data['prompt_tokens']
22
+ end
23
+
24
+ # @return [Integer]
25
+ def total_tokens
26
+ @data['total_tokens']
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OmniAI
4
- VERSION = '0.0.8'
4
+ VERSION = '0.1.0'
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.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-11 00:00:00.000000000 Z
11
+ date: 2024-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: event_stream_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: http
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,10 +67,12 @@ files:
53
67
  - lib/omniai.rb
54
68
  - lib/omniai/chat.rb
55
69
  - lib/omniai/chat/choice.rb
70
+ - lib/omniai/chat/chunk.rb
71
+ - lib/omniai/chat/completion.rb
72
+ - lib/omniai/chat/delta.rb
56
73
  - lib/omniai/chat/message.rb
57
74
  - lib/omniai/chat/request.rb
58
- - lib/omniai/chat/response.rb
59
- - lib/omniai/chat/stream.rb
75
+ - lib/omniai/chat/usage.rb
60
76
  - lib/omniai/client.rb
61
77
  - lib/omniai/version.rb
62
78
  homepage: https://github.com/ksylvest/omniai
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- # An abstract class that provides a consistent interface for processing chat responses.
6
- #
7
- # Usage:
8
- #
9
- # class OmniAI::OpenAI::Chat::Response < OmniAI::Chat::Response
10
- # def choices
11
- # # TODO: implement
12
- # end
13
- # end
14
- class Response
15
- attr_accessor :data
16
-
17
- # @param data [Hash]
18
- def initialize(data:)
19
- @data = data
20
- end
21
-
22
- # @param [index] [Integer]
23
- # @return [OmniAI::Chat::Choice]
24
- def choice(index: 0)
25
- choices[index]
26
- end
27
-
28
- # @return [Array<OmniAI::Chat::Choice>]
29
- def choices
30
- raise NotImplementedError, "#{self.class.name}#choices undefined"
31
- end
32
- end
33
- end
34
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- # A processor for streaming back events in chunks.
6
- class Stream
7
- LINE_REGEX = /data:\s*(?<data>.*)\n\n/
8
-
9
- def initialize
10
- @buffer = String.new
11
- end
12
-
13
- # @yield [data] a parsed hash
14
- # @param [String] chunk
15
- def process!(chunk)
16
- @buffer << chunk
17
-
18
- while (line = @buffer.slice!(LINE_REGEX))
19
- match = LINE_REGEX.match(line)
20
- data = match[:data]
21
- break if data.eql?('[DONE]')
22
-
23
- yield JSON.parse(data)
24
- end
25
- end
26
- end
27
- end
28
- end