omniai 1.6.6 → 1.8.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.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bc92ec17e5ca87590eca2181453b6a3e54c2d15bb6dda0c80c604a443e59660
4
- data.tar.gz: e4eb4ad22437711fbac2c538edc644861107985662f24a236b317d229802f7b7
3
+ metadata.gz: 7325d58430db1dd52ecbbf7964ad9a02a3fe5ae89be51779582a672bbc8c8222
4
+ data.tar.gz: 2464d2ae08fc94fad891acae6eb1ec7fbe163414a3dfb6583fd89bca52dc92d1
5
5
  SHA512:
6
- metadata.gz: 75b7b695353ec0899b786ee057beaedcd8ab21ba15efca6f3a21c4e22730dc73b7676fbdfece11f624a1dce77638ad2d7cc4a6ff054c2180edb8f423b20ca6ee
7
- data.tar.gz: 9c7ab97db7e4d8085774c9fd76803b8abfe53905b6d9b6d141caed311d019347d3f0e880e957597b529de8e479d1b69415213e82ca1daba34bf00d7049eddbae
6
+ metadata.gz: 2d966778690e811bab403217378a748c778e043c48cdb946d9f2b3503ae3b48a165c63ab57bbba65ffa864cd9395388181b57b3c689efb548b864d58f931a757
7
+ data.tar.gz: a82e9b1b092c4df179a4d2b931f6d2c217ba1976504fd40179bc9c0549ab9c9fd349e3eedb79df2c3e30cde1a1ea3b236b99449074c10da381171e1d560314e8
data/Gemfile CHANGED
@@ -4,11 +4,13 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
+ gem 'factory_bot'
7
8
  gem 'logger'
8
9
  gem 'rake'
9
10
  gem 'rspec'
10
11
  gem 'rspec_junit_formatter'
11
12
  gem 'rubocop'
13
+ gem 'rubocop-factory_bot'
12
14
  gem 'rubocop-rake'
13
15
  gem 'rubocop-rspec'
14
16
  gem 'simplecov'
data/README.md CHANGED
@@ -128,7 +128,7 @@ Generating a completion is as simple as sending in the text:
128
128
 
129
129
  ```ruby
130
130
  completion = client.chat('Tell me a joke.')
131
- completion.choice.message.content # 'Why don't scientists trust atoms? They make up everything!'
131
+ completion.text # 'Why don't scientists trust atoms? They make up everything!'
132
132
  ```
133
133
 
134
134
  #### Completions using a Complex Prompt
@@ -145,7 +145,7 @@ completion = client.chat do |prompt|
145
145
  message.file('./hamster.jpeg', "image/jpeg")
146
146
  end
147
147
  end
148
- completion.choice.message.content # 'They are photos of a cat, a cat, and a hamster.'
148
+ completion.text # 'They are photos of a cat, a cat, and a hamster.'
149
149
  ```
150
150
 
151
151
  #### Completions using Streaming via Proc
@@ -154,7 +154,7 @@ A real-time stream of messages can be generated by passing in a proc:
154
154
 
155
155
  ```ruby
156
156
  stream = proc do |chunk|
157
- print(chunk.choice.delta.content) # '...'
157
+ print(chunk.text) # '...'
158
158
  end
159
159
  client.chat('Tell me a joke.', stream:)
160
160
  ```
@@ -229,6 +229,29 @@ tempfile.close
229
229
  tempfile.unlink
230
230
  ```
231
231
 
232
+ ### Embeddings
233
+
234
+ Clients that support generating embeddings (e.g. OpenAI, Mistral, etc.) convert text to embeddings via the following:
235
+
236
+ ```ruby
237
+ response = client.embed('The quick brown fox jumps over a lazy dog')
238
+ response.usage # <OmniAI::Embed::Usage prompt_tokens=5 total_tokens=5>
239
+ response.embedding # [0.1, 0.2, ...] >
240
+ ```
241
+
242
+ Batches of text can also be converted to embeddings via the following:
243
+
244
+ ```ruby
245
+ response = client.embed([
246
+ '',
247
+ '',
248
+ ])
249
+ response.usage # <OmniAI::Embed::Usage prompt_tokens=5 total_tokens=5>
250
+ response.embeddings.each do |embedding|
251
+ embedding # [0.1, 0.2, ...]
252
+ end
253
+ ```
254
+
232
255
  ## CLI
233
256
 
234
257
  OmniAI packages a basic command line interface (CLI) to allow for exploration of various APIs. A detailed CLI documentation can be found via help:
@@ -263,3 +286,32 @@ Type 'exit' or 'quit' to abort.
263
286
  ```
264
287
  The warmest place on earth is Africa.
265
288
  ```
289
+
290
+ ### Embed
291
+
292
+ #### w/ input
293
+
294
+ ```bash
295
+ omniai embed "The quick brown fox jumps over a lazy dog."
296
+ ```
297
+
298
+ ```
299
+ 0.0
300
+ ...
301
+ ```
302
+
303
+ #### w/o input
304
+
305
+ ```bash
306
+ omniai embed --provider="openai" --model="text-embedding-ada-002"
307
+ ```
308
+
309
+ ```
310
+ Type 'exit' or 'quit' to abort.
311
+ # Whe quick brown fox jumps over a lazy dog.
312
+ ```
313
+
314
+ ```
315
+ 0.0
316
+ ...
317
+ ```
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Chat
5
+ # For for.
6
+ class Choice
7
+ # @return [Integer]
8
+ attr_accessor :index
9
+
10
+ # @return [Message]
11
+ attr_accessor :message
12
+
13
+ # @param message [Message]
14
+ # @param index [Integer]
15
+ # @param tool_call_list [Array<ToolCall>]
16
+ def initialize(message:, index: 0)
17
+ @message = message
18
+ @index = index
19
+ end
20
+
21
+ # @return [String]
22
+ def inspect
23
+ "#<#{self.class.name} index=#{@index} message=#{@message.inspect}>"
24
+ end
25
+
26
+ # @param data [Hash]
27
+ # @param context [OmniAI::Context] optional
28
+ #
29
+ # @return [Choice]
30
+ def self.deserialize(data, context: nil)
31
+ deserialize = context&.deserializer(:choice)
32
+ return deserialize.call(data, context:) if deserialize
33
+
34
+ index = data['index']
35
+ message = Message.deserialize(data['message'] || data['delta'], context:)
36
+
37
+ new(message:, index:)
38
+ end
39
+
40
+ # @param context [OmniAI::Context] optional
41
+ # @return [Hash]
42
+ def serialize(context: nil)
43
+ serialize = context&.serializer(:choice)
44
+ return serialize.call(self, context:) if serialize
45
+
46
+ {
47
+ index:,
48
+ message: message.serialize(context:),
49
+ }
50
+ end
51
+
52
+ # @return [Message]
53
+ def delta
54
+ message
55
+ end
56
+
57
+ # @return [Array<Content>, String]
58
+ def content
59
+ message.content
60
+ end
61
+
62
+ # @return [Array<ToolCall, nil]
63
+ def tool_call_list
64
+ message.tool_call_list
65
+ end
66
+ end
67
+ end
68
+ end
@@ -16,14 +16,22 @@ module OmniAI
16
16
  #
17
17
  # @return [String]
18
18
  def serialize(context: nil)
19
- raise NotImplementedError, ' # {self.class}#serialize undefined'
19
+ raise NotImplementedError, "#{self.class}#serialize undefined"
20
20
  end
21
21
 
22
- # @param data [hash]
22
+ # @param data [Hash, Array, String]
23
23
  # @param context [Context] optional
24
24
  #
25
25
  # @return [Content]
26
26
  def self.deserialize(data, context: nil)
27
+ return data if data.nil?
28
+ return data.map { |data| deserialize(data, context:) } if data.is_a?(Array)
29
+
30
+ deserialize = context&.deserializer(:content)
31
+ return deserialize.call(data, context:) if deserialize
32
+
33
+ return data if data.is_a?(String)
34
+
27
35
  raise ArgumentError, "untyped data=#{data.inspect}" unless data.key?('type')
28
36
 
29
37
  case data['type']
@@ -21,8 +21,8 @@ module OmniAI
21
21
  # @return [String]
22
22
  def fetch!
23
23
  case @io
24
- when IO then @io.read
25
- else ::File.binread(@io)
24
+ when String then ::File.binread(@io)
25
+ else @io.read
26
26
  end
27
27
  end
28
28
 
@@ -33,7 +33,7 @@ module OmniAI
33
33
  content = fetch!
34
34
  Text.new("<file>#{filename}: #{content}</file>").serialize(context:)
35
35
  else
36
- serializer = context&.serializers&.[](:file)
36
+ serializer = context&.serializer(:file)
37
37
  return serializer.call(self, context:) if serializer
38
38
 
39
39
  { type: "#{kind}_url", "#{kind}_url": { url: data_uri } }
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Chat
5
+ # A function that includes a name / arguments.
6
+ class Function
7
+ # @return [String]
8
+ attr_accessor :name
9
+
10
+ # @return [Hash]
11
+ attr_accessor :arguments
12
+
13
+ # @param name [String]
14
+ # @param arguments [Hash]
15
+ def initialize(name:, arguments:)
16
+ @name = name
17
+ @arguments = arguments
18
+ end
19
+
20
+ # @return [String]
21
+ def inspect
22
+ "#<#{self.class.name} name=#{name.inspect} arguments=#{arguments.inspect}>"
23
+ end
24
+
25
+ # @param data [Hash]
26
+ # @param context [Context] optional
27
+ #
28
+ # @return [Function]
29
+ def self.deserialize(data, context: nil)
30
+ deserialize = context&.deserializer(:function)
31
+ return deserialize.call(data, context:) if deserialize
32
+
33
+ name = data['name']
34
+ arguments = begin
35
+ JSON.parse(data['arguments']) if data['arguments']
36
+ rescue JSON::ParserError
37
+ data['arguments']
38
+ end
39
+
40
+ new(name:, arguments:)
41
+ end
42
+
43
+ # @param context [Context] optional
44
+ #
45
+ # @return [Hash]
46
+ def serialize(context: nil)
47
+ serializer = context&.serializer(:function)
48
+ return serializer.call(self, context:) if serializer
49
+
50
+ {
51
+ name: @name,
52
+ arguments: (JSON.generate(@arguments) if @arguments),
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ class Chat
5
+ class Message
6
+ # Used to build a message.
7
+ class Builder
8
+ # @param role [String]
9
+ # @return [Message]
10
+ def self.build(role:)
11
+ builder = new(role:)
12
+ yield(builder)
13
+ builder.build
14
+ end
15
+
16
+ # @param role [String]
17
+ def initialize(role: Role::USER)
18
+ @role = role
19
+ @content = []
20
+ end
21
+
22
+ # @return [Message]
23
+ def build
24
+ Message.new(content: @content, role: @role)
25
+ end
26
+
27
+ # @example
28
+ # message.text('What are these photos of?')
29
+ #
30
+ # @param value [String]
31
+ #
32
+ # @return [Text]
33
+ def text(value)
34
+ Text.new(value).tap do |text|
35
+ @content << text
36
+ end
37
+ end
38
+
39
+ # @example
40
+ # message.url('https://example.com/hamster.jpg', type: "image/jpeg")
41
+ #
42
+ # @param uri [String]
43
+ # @param type [String]
44
+ #
45
+ # @return [URL]
46
+ def url(uri, type)
47
+ URL.new(uri, type).tap do |url|
48
+ @content << url
49
+ end
50
+ end
51
+
52
+ # @example
53
+ # message.file(File.open('hamster.jpg'), type: "image/jpeg")
54
+ #
55
+ # @param io [IO]
56
+ # @param type [String]
57
+ #
58
+ # @return [File]
59
+ def file(io, type)
60
+ File.new(io, type).tap do |file|
61
+ @content << file
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -10,20 +10,50 @@ module OmniAI
10
10
  # message.url 'https://example.com/cat.jpg', type: "image/jpeg"
11
11
  # message.url 'https://example.com/dog.jpg', type: "image/jpeg"
12
12
  # message.file File.open('hamster.jpg'), type: "image/jpeg"
13
+ # message.
13
14
  # end
14
15
  # end
15
16
  class Message
17
+ # @example
18
+ # prompt.build('What is the capital of Canada?')
19
+ #
20
+ # @example
21
+ # prompt.build(role: 'user') do |message|
22
+ # message.text 'What is the capital of Canada?'
23
+ # end
24
+ #
25
+ # @param content [String, nil]
26
+ # @param role [Symbol]
27
+ #
28
+ # @yield [builder]
29
+ # @yieldparam builder [Message::Builder]
30
+ #
31
+ # @return [Message]
32
+ def self.build(content = nil, role: Role::USER, &block)
33
+ raise ArgumentError, 'content or block is required' if content.nil? && block.nil?
34
+
35
+ Builder.build(role:) do |builder|
36
+ builder.text(content) if content
37
+ block&.call(builder)
38
+ end
39
+ end
40
+
16
41
  # @return [Array<Content>, String]
17
42
  attr_accessor :content
18
43
 
19
44
  # @return [String]
20
45
  attr_accessor :role
21
46
 
47
+ # @return [Array<ToolCall>, nil]
48
+ attr_accessor :tool_call_list
49
+
22
50
  # @param content [String, nil]
23
51
  # @param role [String]
24
- def initialize(content: nil, role: Role::USER)
25
- @content = content || []
52
+ # @param tool_call_list [Array<ToolCall>, nil]
53
+ def initialize(content:, role: Role::USER, tool_call_list: nil)
54
+ @content = content
26
55
  @role = role
56
+ @tool_call_list = tool_call_list
27
57
  end
28
58
 
29
59
  # @return [String]
@@ -48,13 +78,14 @@ module OmniAI
48
78
  #
49
79
  # @return [Message]
50
80
  def self.deserialize(data, context: nil)
51
- deserialize = context&.deserializers&.[](:message)
81
+ deserialize = context&.deserializer(:message)
52
82
  return deserialize.call(data, context:) if deserialize
53
83
 
54
- new(
55
- content: data['content'].map { |content| Content.deserialize(content, context:) },
56
- role: data['role']
57
- )
84
+ role = data['role']
85
+ content = Content.deserialize(data['content'], context:)
86
+ tool_call_list = data['tool_calls']&.map { |subdata| ToolCall.deserialize(subdata, context:) }
87
+
88
+ new(content:, role:, tool_call_list:)
58
89
  end
59
90
 
60
91
  # Usage:
@@ -66,12 +97,13 @@ module OmniAI
66
97
  #
67
98
  # @return [Hash]
68
99
  def serialize(context: nil)
69
- serializer = context&.serializers&.[](:message)
100
+ serializer = context&.serializer(:message)
70
101
  return serializer.call(self, context:) if serializer
71
102
 
72
- content = @content.is_a?(String) ? @content : @content.map { |content| content.serialize(context:) }
103
+ content = @content.is_a?(Array) ? @content.map { |content| content.serialize(context:) } : @content
104
+ tool_calls = @tool_call_list&.map { |tool_call| tool_call.serialize(context:) }
73
105
 
74
- { role: @role, content: }
106
+ { role: @role, content:, tool_calls: }.compact
75
107
  end
76
108
 
77
109
  # @return [Boolean]
@@ -89,45 +121,32 @@ module OmniAI
89
121
  role?(Role::USER)
90
122
  end
91
123
 
92
- # Usage:
93
- #
94
- # message.text('What are these photos of?')
95
- #
96
- # @param value [String]
97
- #
98
- # @return [Text]
99
- def text(value)
100
- Text.new(value).tap do |text|
101
- @content << text
102
- end
124
+ # @return [Boolean]
125
+ def tool?
126
+ role?(Role::TOOL)
103
127
  end
104
128
 
105
- # Usage:
106
- #
107
- # message.url('https://example.com/hamster.jpg', type: "image/jpeg")
108
- #
109
- # @param uri [String]
110
- # @param type [String]
111
- #
112
- # @return [URL]
113
- def url(uri, type)
114
- URL.new(uri, type).tap do |url|
115
- @content << url
116
- end
129
+ # @return [Boolean]
130
+ def text?
131
+ !text.nil?
117
132
  end
118
133
 
119
- # Usage:
120
- #
121
- # message.file(File.open('hamster.jpg'), type: "image/jpeg")
122
- #
123
- # @param io [IO]
124
- # @param type [String]
125
- #
126
- # @return [File]
127
- def file(io, type)
128
- File.new(io, type).tap do |file|
129
- @content << file
130
- end
134
+ # @return [String, nil]
135
+ def text
136
+ return if @content.nil?
137
+ return @content if @content.is_a?(String)
138
+
139
+ parts = arrayify(@content).filter { |content| content.is_a?(Text) }
140
+ parts.map(&:text).join("\n") unless parts.empty?
141
+ end
142
+
143
+ # @param object [Object]
144
+ # @return [Array]
145
+ def arrayify(object)
146
+ return if object.nil?
147
+ return object if object.is_a?(Array)
148
+
149
+ [object]
131
150
  end
132
151
  end
133
152
  end
@@ -0,0 +1,85 @@
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
+ @choices = choices
17
+ @usage = usage
18
+ end
19
+
20
+ # @return [String]
21
+ def inspect
22
+ "#<#{self.class.name} choices=#{choices.inspect} usage=#{usage.inspect}>"
23
+ end
24
+
25
+ # @param data [Hash]
26
+ # @param context [OmniAI::Context] optional
27
+ def self.deserialize(data, context: nil)
28
+ deserialize = context&.deserializer(:payload)
29
+ return deserialize.call(data, context:) if deserialize
30
+
31
+ choices = data['choices'].map { |choice_data| Choice.deserialize(choice_data, context:) }
32
+ usage = Usage.deserialize(data['usage'], context:) if data['usage']
33
+
34
+ new(choices:, usage:)
35
+ end
36
+
37
+ # @param context [OmniAI::Context] optional
38
+ # @return [Hash]
39
+ def serialize(context:)
40
+ serialize = context&.serializer(:payload)
41
+ return serialize.call(self, context:) if serialize
42
+
43
+ {
44
+ choices: choices.map { |choice| choice.serialize(context:) },
45
+ usage: usage&.serialize(context:),
46
+ }
47
+ end
48
+
49
+ # @param index [Integer]
50
+ # @return [Choice]
51
+ def choice(index: 0)
52
+ @choices[index]
53
+ end
54
+
55
+ # @param index [Integer]
56
+ # @return [Message]
57
+ def message(index: 0)
58
+ choice(index:).message
59
+ end
60
+
61
+ # @return [Array<Message>]
62
+ def messages
63
+ @choices.map(&:message)
64
+ end
65
+
66
+ # @param index [Integer]
67
+ # @return [String, nil]
68
+ def text(index: 0)
69
+ message(index:).text
70
+ end
71
+
72
+ # @param index [Integer]
73
+ # @return [Boolean]
74
+ def text?(index: 0)
75
+ message(index:).text?
76
+ end
77
+
78
+ # @param index [Integer]
79
+ # @return [Array<ToolCall>]
80
+ def tool_call_list(index:)
81
+ message(index:).tool_call_list
82
+ end
83
+ end
84
+ end
85
+ end
@@ -57,6 +57,11 @@ module OmniAI
57
57
  @messages = messages
58
58
  end
59
59
 
60
+ # @return [Prompt]
61
+ def dup
62
+ self.class.new(messages: @messages.dup)
63
+ end
64
+
60
65
  # @return [String]
61
66
  def inspect
62
67
  "#<#{self.class.name} messages=#{@messages.inspect}>"
@@ -75,57 +80,66 @@ module OmniAI
75
80
  #
76
81
  # @return [Array<Hash>]
77
82
  def serialize(context: nil)
78
- serializer = context&.serializers&.[](:prompt)
83
+ serializer = context&.serializer(:prompt)
79
84
  return serializer.call(self, context:) if serializer
80
85
 
81
86
  @messages.map { |message| message.serialize(context:) }
82
87
  end
83
88
 
84
- # Usage:
89
+ # @example
90
+ # prompt.message 'What is the capital of Canada?', role: '...'
85
91
  #
86
- # prompt.message('What is the capital of Canada?')
92
+ # @example
93
+ # prompt.message role: '...' do |message|
94
+ # message.text 'What is the capital of Canada?'
95
+ # end
87
96
  #
88
97
  # @param content [String, nil]
89
98
  # @param role [Symbol]
90
99
  #
91
- # @yield [Message]
100
+ # @yield [builder]
101
+ # @yieldparam builder [Message::Builder]
102
+ #
92
103
  # @return [Message]
93
- def message(content = nil, role: :user, &block)
94
- raise ArgumentError, 'content or block is required' if content.nil? && block.nil?
95
-
96
- Message.new(content:, role:).tap do |message|
97
- block&.call(message)
104
+ def message(content = nil, role: Role::USER, &)
105
+ Message.build(content, role:, &).tap do |message|
98
106
  @messages << message
99
107
  end
100
108
  end
101
109
 
102
- # Usage:
103
- #
104
- # prompt.system('You are a helpful assistant.')
110
+ # @example
111
+ # prompt.system 'You are a helpful assistant.'
105
112
  #
113
+ # @example
106
114
  # prompt.system do |message|
107
115
  # message.text 'You are a helpful assistant.'
108
116
  # end
109
117
  #
110
118
  # @param content [String, nil]
111
119
  #
112
- # @yield [Message]
120
+ # @yield [builder]
121
+ # @yieldparam builder [Message::Builder]
122
+ #
113
123
  # @return [Message]
114
124
  def system(content = nil, &)
115
125
  message(content, role: Role::SYSTEM, &)
116
126
  end
117
127
 
118
- # Usage:
119
- #
128
+ # @example
120
129
  # prompt.user('What is the capital of Canada?')
121
130
  #
131
+ # @example
122
132
  # prompt.user do |message|
123
133
  # message.text 'What is the capital of Canada?'
134
+ # message.url 'https://...', type: "image/gif"
135
+ # message.file File.open('...'), type: "image/gif"
124
136
  # end
125
137
  #
126
138
  # @param content [String, nil]
127
139
  #
128
- # @yield [Message]
140
+ # @yield [builder]
141
+ # @yieldparam builder [Message::Builder]
142
+ #
129
143
  # @return [Message]
130
144
  def user(content = nil, &)
131
145
  message(content, role: Role::USER, &)