omniai 1.6.6 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +55 -3
  4. data/lib/omniai/chat/choice.rb +68 -0
  5. data/lib/omniai/chat/content.rb +10 -2
  6. data/lib/omniai/chat/file.rb +3 -3
  7. data/lib/omniai/chat/function.rb +57 -0
  8. data/lib/omniai/chat/message/builder.rb +67 -0
  9. data/lib/omniai/chat/message.rb +64 -45
  10. data/lib/omniai/chat/payload.rb +85 -0
  11. data/lib/omniai/chat/prompt.rb +30 -16
  12. data/lib/omniai/chat/response.rb +70 -0
  13. data/lib/omniai/chat/stream.rb +61 -0
  14. data/lib/omniai/chat/text.rb +2 -2
  15. data/lib/omniai/chat/tool_call.rb +54 -0
  16. data/lib/omniai/chat/tool_call_message.rb +61 -0
  17. data/lib/omniai/chat/tool_call_result.rb +51 -0
  18. data/lib/omniai/chat/url.rb +2 -2
  19. data/lib/omniai/chat/usage.rb +60 -0
  20. data/lib/omniai/chat.rb +61 -34
  21. data/lib/omniai/cli/embed_handler.rb +58 -0
  22. data/lib/omniai/cli.rb +8 -4
  23. data/lib/omniai/client.rb +10 -0
  24. data/lib/omniai/context.rb +55 -0
  25. data/lib/omniai/embed/response.rb +59 -0
  26. data/lib/omniai/embed/usage.rb +26 -0
  27. data/lib/omniai/embed.rb +80 -0
  28. data/lib/omniai/tool.rb +6 -2
  29. data/lib/omniai/version.rb +1 -1
  30. metadata +17 -17
  31. data/lib/omniai/chat/context.rb +0 -42
  32. data/lib/omniai/chat/response/choice.rb +0 -35
  33. data/lib/omniai/chat/response/chunk.rb +0 -15
  34. data/lib/omniai/chat/response/completion.rb +0 -15
  35. data/lib/omniai/chat/response/delta.rb +0 -11
  36. data/lib/omniai/chat/response/delta_choice.rb +0 -25
  37. data/lib/omniai/chat/response/function.rb +0 -25
  38. data/lib/omniai/chat/response/message.rb +0 -11
  39. data/lib/omniai/chat/response/message_choice.rb +0 -25
  40. data/lib/omniai/chat/response/part.rb +0 -38
  41. data/lib/omniai/chat/response/payload.rb +0 -72
  42. data/lib/omniai/chat/response/resource.rb +0 -22
  43. data/lib/omniai/chat/response/stream.rb +0 -27
  44. data/lib/omniai/chat/response/tool_call.rb +0 -30
  45. data/lib/omniai/chat/response/usage.rb +0 -35
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ # Used to handle the setup of serializer / deserializer required per provide (e.g. Anthropic / Google / etc).
5
+ #
6
+ # Usage:
7
+ #
8
+ # OmniAI::Context.build do |context|
9
+ # context.serializers[:prompt] = (prompt, context:) -> { ... }
10
+ # context.serializers[:message] = (prompt, context:) -> { ... }
11
+ # context.serializers[:file] = (prompt, context:) -> { ... }
12
+ # context.serializers[:text] = (prompt, context:) -> { ... }
13
+ # context.serializers[:url] = (prompt, context:) -> { ... }
14
+ # context.deserializers[:prompt] = (data, context:) -> { Prompt.new(...) }
15
+ # context.deserializers[:message] = (data, context:) -> { Message.new(...) }
16
+ # context.deserializers[:file] = (data, context:) -> { File.new(...) }
17
+ # context.deserializers[:text] = (data, context:) -> { Text.new(...) }
18
+ # context.deserializers[:url] = (data, context:) -> { URL.new(...) }
19
+ # end
20
+ class Context
21
+ # @return [Hash]
22
+ attr_accessor :serializers
23
+
24
+ # @return [Hash]
25
+ attr_reader :deserializers
26
+
27
+ # @yield [context]
28
+ # @yieldparam context [Context]
29
+ #
30
+ # @return [Context]
31
+ def self.build(&block)
32
+ new.tap do |context|
33
+ block&.call(context)
34
+ end
35
+ end
36
+
37
+ # @return [Context]
38
+ def initialize
39
+ @serializers = {}
40
+ @deserializers = {}
41
+ end
42
+
43
+ # @param name [Symbol]
44
+ # @return [Proc, nil]
45
+ def serializer(name)
46
+ @serializers[name]
47
+ end
48
+
49
+ # @param name [Symbol]
50
+ # @return [Proc, nil]
51
+ def deserializer(name)
52
+ @deserializers[name]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Embed
5
+ # The response returned by the API.
6
+ class Response
7
+ # @return [Hash]
8
+ attr_accessor :data
9
+
10
+ # @param data [Hash]
11
+ # @param context [OmniAI::Context] optional
12
+ def initialize(data:, context: nil)
13
+ @data = data
14
+ @context = context
15
+ end
16
+
17
+ # @return [String]
18
+ def inspect
19
+ "#<#{self.class.name}>"
20
+ end
21
+
22
+ # @return [Usage]
23
+ def usage
24
+ @usage ||= begin
25
+ deserializer = @context&.deserializer(:usage)
26
+
27
+ if deserializer
28
+ deserializer.call(@data, context: @context)
29
+ else
30
+ prompt_tokens = @data.dig('usage', 'prompt_tokens')
31
+ total_tokens = @data.dig('usage', 'total_tokens')
32
+
33
+ Usage.new(prompt_tokens:, total_tokens:)
34
+ end
35
+ end
36
+ end
37
+
38
+ # @param index [Integer] optional
39
+ #
40
+ # @return [Array<Float>]
41
+ def embedding(index: 0)
42
+ embeddings[index]
43
+ end
44
+
45
+ # @return [Array<Array<Float>>]
46
+ def embeddings
47
+ @embeddings ||= begin
48
+ deserializer = @context&.deserializer(:embeddings)
49
+
50
+ if deserializer
51
+ deserializer.call(@data, context: @context)
52
+ else
53
+ @data['data'].map { |embedding| embedding['embedding'] }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Embed
5
+ # Token usage returned by the API.
6
+ class Usage
7
+ # @return [Integer]
8
+ attr_accessor :prompt_tokens
9
+
10
+ # @return [Integer]
11
+ attr_accessor :total_tokens
12
+
13
+ # @param prompt_tokens Integer
14
+ # @param total_tokens Integer
15
+ def initialize(prompt_tokens:, total_tokens:)
16
+ @prompt_tokens = prompt_tokens
17
+ @total_tokens = total_tokens
18
+ end
19
+
20
+ # @return [String]
21
+ def inspect
22
+ "#<#{self.class.name} prompt_tokens=#{@prompt_tokens} total_tokens=#{@total_tokens}>"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ # An abstract class that provides a consistent interface for processing embedding requests.
5
+ #
6
+ # Usage:
7
+ #
8
+ # class OmniAI::OpenAI::Embed < OmniAI::Embed
9
+ # module Model
10
+ # SMALL = "text-embedding-3-small"
11
+ # LARGE = "text-embedding-3-large"
12
+ # ADA = "text-embedding-3-002"
13
+ # end
14
+ #
15
+ # protected
16
+ #
17
+ # # @return [Hash]
18
+ # def payload
19
+ # { ... }
20
+ # end
21
+ #
22
+ # # @return [String]
23
+ # def path
24
+ # "..."
25
+ # end
26
+ # end
27
+ #
28
+ # client.embed(input, model: "...")
29
+ class Embed
30
+ def self.process!(...)
31
+ new(...).process!
32
+ end
33
+
34
+ # @param input [String] required
35
+ # @param client [Client] the client
36
+ # @param model [String] required
37
+ #
38
+ # @return [Response]
39
+ def initialize(input, client:, model:)
40
+ @input = input
41
+ @client = client
42
+ @model = model
43
+ end
44
+
45
+ # @raise [Error]
46
+ # @return [Response]
47
+ def process!
48
+ response = request!
49
+ raise HTTPError, response.flush unless response.status.ok?
50
+
51
+ parse!(response:)
52
+ end
53
+
54
+ protected
55
+
56
+ # @param response [HTTP::Response]
57
+ # @return [Response]
58
+ def parse!(response:)
59
+ Response.new(data: response.parse)
60
+ end
61
+
62
+ # @return [HTTP::Response]
63
+ def request!
64
+ @client
65
+ .connection
66
+ .accept(:json)
67
+ .post(path, json: payload)
68
+ end
69
+
70
+ # @return [Hash]
71
+ def payload
72
+ raise NotImplementedError, "#{self.class.name}#payload undefined"
73
+ end
74
+
75
+ # @return [String]
76
+ def path
77
+ raise NotImplementedError, "#{self.class.name}#path undefined"
78
+ end
79
+ end
80
+ end
data/lib/omniai/tool.rb CHANGED
@@ -44,7 +44,7 @@ module OmniAI
44
44
  end
45
45
 
46
46
  # @example
47
- # tool.prepare
47
+ # tool.serialize
48
48
  # # {
49
49
  # # type: 'function',
50
50
  # # function: {
@@ -60,8 +60,12 @@ module OmniAI
60
60
  # # }
61
61
  # # }
62
62
  #
63
+ # @param context [Context] optional
63
64
  # @return [Hash]
64
- def prepare
65
+ def serialize(context: nil)
66
+ serialize = context&.serializer(:tool)
67
+ return serialize.call(self, context:) if serialize
68
+
65
69
  {
66
70
  type: 'function',
67
71
  function: {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OmniAI
4
- VERSION = '1.6.6'
4
+ VERSION = '1.8.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.6.6
4
+ version: 1.8.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-07-19 00:00:00.000000000 Z
11
+ date: 2024-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -68,33 +68,33 @@ files:
68
68
  - exe/omniai
69
69
  - lib/omniai.rb
70
70
  - lib/omniai/chat.rb
71
+ - lib/omniai/chat/choice.rb
71
72
  - lib/omniai/chat/content.rb
72
- - lib/omniai/chat/context.rb
73
73
  - lib/omniai/chat/file.rb
74
+ - lib/omniai/chat/function.rb
74
75
  - lib/omniai/chat/media.rb
75
76
  - lib/omniai/chat/message.rb
77
+ - lib/omniai/chat/message/builder.rb
78
+ - lib/omniai/chat/payload.rb
76
79
  - lib/omniai/chat/prompt.rb
77
- - lib/omniai/chat/response/choice.rb
78
- - lib/omniai/chat/response/chunk.rb
79
- - lib/omniai/chat/response/completion.rb
80
- - lib/omniai/chat/response/delta.rb
81
- - lib/omniai/chat/response/delta_choice.rb
82
- - lib/omniai/chat/response/function.rb
83
- - lib/omniai/chat/response/message.rb
84
- - lib/omniai/chat/response/message_choice.rb
85
- - lib/omniai/chat/response/part.rb
86
- - lib/omniai/chat/response/payload.rb
87
- - lib/omniai/chat/response/resource.rb
88
- - lib/omniai/chat/response/stream.rb
89
- - lib/omniai/chat/response/tool_call.rb
90
- - lib/omniai/chat/response/usage.rb
80
+ - lib/omniai/chat/response.rb
81
+ - lib/omniai/chat/stream.rb
91
82
  - lib/omniai/chat/text.rb
83
+ - lib/omniai/chat/tool_call.rb
84
+ - lib/omniai/chat/tool_call_message.rb
85
+ - lib/omniai/chat/tool_call_result.rb
92
86
  - lib/omniai/chat/url.rb
87
+ - lib/omniai/chat/usage.rb
93
88
  - lib/omniai/cli.rb
94
89
  - lib/omniai/cli/base_handler.rb
95
90
  - lib/omniai/cli/chat_handler.rb
91
+ - lib/omniai/cli/embed_handler.rb
96
92
  - lib/omniai/client.rb
97
93
  - lib/omniai/config.rb
94
+ - lib/omniai/context.rb
95
+ - lib/omniai/embed.rb
96
+ - lib/omniai/embed/response.rb
97
+ - lib/omniai/embed/usage.rb
98
98
  - lib/omniai/instrumentation.rb
99
99
  - lib/omniai/speak.rb
100
100
  - lib/omniai/tool.rb
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- # Used to handle the setup of serializer / deserializer methods for each type.
6
- #
7
- # Usage:
8
- #
9
- # OmniAI::Chat::Context.build do |context|
10
- # context.serializers[:prompt] = (prompt, context:) -> { ... }
11
- # context.serializers[:message] = (prompt, context:) -> { ... }
12
- # context.serializers[:file] = (prompt, context:) -> { ... }
13
- # context.serializers[:text] = (prompt, context:) -> { ... }
14
- # context.serializers[:url] = (prompt, context:) -> { ... }
15
- # context.deserializers[:prompt] = (data, context:) -> { Prompt.new(...) }
16
- # context.deserializers[:message] = (data, context:) -> { Message.new(...) }
17
- # context.deserializers[:file] = (data, context:) -> { File.new(...) }
18
- # context.deserializers[:text] = (data, context:) -> { Text.new(...) }
19
- # context.deserializers[:url] = (data, context:) -> { URL.new(...) }
20
- # end
21
- class Context
22
- # @return [Hash]
23
- attr_accessor :serializers
24
-
25
- # @return [Hash]
26
- attr_reader :deserializers
27
-
28
- # @return [Context]
29
- def self.build(&block)
30
- new.tap do |context|
31
- block&.call(context)
32
- end
33
- end
34
-
35
- # @return [Context]
36
- def initialize
37
- @serializers = {}
38
- @deserializers = {}
39
- end
40
- end
41
- end
42
- end
@@ -1,35 +0,0 @@
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 [Part]
14
- def part
15
- raise NotImplementedError, "#{self.class.name}#part undefined"
16
- end
17
-
18
- # @return [ToolCallList]
19
- def tool_call_list
20
- part.tool_call_list
21
- end
22
-
23
- # @return [String, nil]
24
- def content
25
- part.content
26
- end
27
-
28
- # @return [Boolean]
29
- def content?
30
- !content.nil?
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,15 +0,0 @@
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<DeltaChoice>]
9
- def choices
10
- @choices ||= @data['choices'].map { |data| DeltaChoice.new(data:) }
11
- end
12
- end
13
- end
14
- end
15
- end
@@ -1,15 +0,0 @@
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<MessageChoice>]
9
- def choices
10
- @choices ||= @data['choices'].map { |data| MessageChoice.new(data:) }
11
- end
12
- end
13
- end
14
- end
15
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- module Response
6
- # A delta returned by the API.
7
- class Delta < Part
8
- end
9
- end
10
- end
11
- end
@@ -1,25 +0,0 @@
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 [Delta]
14
- def delta
15
- @delta ||= Delta.new(data: @data['delta'])
16
- end
17
-
18
- # @return [Delta]
19
- def part
20
- delta
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,25 +0,0 @@
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
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OmniAI
4
- class Chat
5
- module Response
6
- # A message returned by the API.
7
- class Message < Part
8
- end
9
- end
10
- end
11
- end
@@ -1,25 +0,0 @@
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 [Message]
14
- def message
15
- @message ||= Message.new(data: @data['message'])
16
- end
17
-
18
- # @return [Message]
19
- def part
20
- message
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,38 +0,0 @@
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<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 [ToolCall, nil]
32
- def tool_call(index: 0)
33
- tool_call_list[index]
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,72 +0,0 @@
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<Choice>]
34
- def choices
35
- raise NotImplementedError, "#{self.class.name}#choices undefined"
36
- end
37
-
38
- # @param index [Integer]
39
- # @return [DeltaChoice]
40
- def choice(index: 0)
41
- choices[index]
42
- end
43
-
44
- # @param index [Integer]
45
- # @return [Part]
46
- def part(index: 0)
47
- choice(index:).part
48
- end
49
-
50
- # @return [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<ToolCall>]
66
- def tool_call_list
67
- choice.tool_call_list
68
- end
69
- end
70
- end
71
- end
72
- end
@@ -1,22 +0,0 @@
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
@@ -1,27 +0,0 @@
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