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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +55 -3
- data/lib/omniai/chat/choice.rb +68 -0
- data/lib/omniai/chat/content.rb +10 -2
- data/lib/omniai/chat/file.rb +3 -3
- data/lib/omniai/chat/function.rb +57 -0
- data/lib/omniai/chat/message/builder.rb +67 -0
- data/lib/omniai/chat/message.rb +64 -45
- data/lib/omniai/chat/payload.rb +85 -0
- data/lib/omniai/chat/prompt.rb +30 -16
- data/lib/omniai/chat/response.rb +70 -0
- data/lib/omniai/chat/stream.rb +61 -0
- data/lib/omniai/chat/text.rb +2 -2
- data/lib/omniai/chat/tool_call.rb +54 -0
- data/lib/omniai/chat/tool_call_message.rb +61 -0
- data/lib/omniai/chat/tool_call_result.rb +51 -0
- data/lib/omniai/chat/url.rb +2 -2
- data/lib/omniai/chat/usage.rb +60 -0
- data/lib/omniai/chat.rb +61 -34
- data/lib/omniai/cli/embed_handler.rb +58 -0
- data/lib/omniai/cli.rb +8 -4
- data/lib/omniai/client.rb +10 -0
- data/lib/omniai/context.rb +55 -0
- data/lib/omniai/embed/response.rb +59 -0
- data/lib/omniai/embed/usage.rb +26 -0
- data/lib/omniai/embed.rb +80 -0
- data/lib/omniai/tool.rb +6 -2
- data/lib/omniai/version.rb +1 -1
- metadata +17 -17
- data/lib/omniai/chat/context.rb +0 -42
- data/lib/omniai/chat/response/choice.rb +0 -35
- data/lib/omniai/chat/response/chunk.rb +0 -15
- data/lib/omniai/chat/response/completion.rb +0 -15
- data/lib/omniai/chat/response/delta.rb +0 -11
- data/lib/omniai/chat/response/delta_choice.rb +0 -25
- data/lib/omniai/chat/response/function.rb +0 -25
- data/lib/omniai/chat/response/message.rb +0 -11
- data/lib/omniai/chat/response/message_choice.rb +0 -25
- data/lib/omniai/chat/response/part.rb +0 -38
- data/lib/omniai/chat/response/payload.rb +0 -72
- data/lib/omniai/chat/response/resource.rb +0 -22
- data/lib/omniai/chat/response/stream.rb +0 -27
- data/lib/omniai/chat/response/tool_call.rb +0 -30
- data/lib/omniai/chat/response/usage.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7325d58430db1dd52ecbbf7964ad9a02a3fe5ae89be51779582a672bbc8c8222
|
4
|
+
data.tar.gz: 2464d2ae08fc94fad891acae6eb1ec7fbe163414a3dfb6583fd89bca52dc92d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d966778690e811bab403217378a748c778e043c48cdb946d9f2b3503ae3b48a165c63ab57bbba65ffa864cd9395388181b57b3c689efb548b864d58f931a757
|
7
|
+
data.tar.gz: a82e9b1b092c4df179a4d2b931f6d2c217ba1976504fd40179bc9c0549ab9c9fd349e3eedb79df2c3e30cde1a1ea3b236b99449074c10da381171e1d560314e8
|
data/Gemfile
CHANGED
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.
|
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.
|
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.
|
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
|
data/lib/omniai/chat/content.rb
CHANGED
@@ -16,14 +16,22 @@ module OmniAI
|
|
16
16
|
#
|
17
17
|
# @return [String]
|
18
18
|
def serialize(context: nil)
|
19
|
-
raise NotImplementedError,
|
19
|
+
raise NotImplementedError, "#{self.class}#serialize undefined"
|
20
20
|
end
|
21
21
|
|
22
|
-
# @param data [
|
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']
|
data/lib/omniai/chat/file.rb
CHANGED
@@ -21,8 +21,8 @@ module OmniAI
|
|
21
21
|
# @return [String]
|
22
22
|
def fetch!
|
23
23
|
case @io
|
24
|
-
when
|
25
|
-
else
|
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&.
|
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
|
data/lib/omniai/chat/message.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
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&.
|
81
|
+
deserialize = context&.deserializer(:message)
|
52
82
|
return deserialize.call(data, context:) if deserialize
|
53
83
|
|
54
|
-
|
55
|
-
|
56
|
-
|
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&.
|
100
|
+
serializer = context&.serializer(:message)
|
70
101
|
return serializer.call(self, context:) if serializer
|
71
102
|
|
72
|
-
content = @content.is_a?(
|
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
|
-
#
|
93
|
-
|
94
|
-
|
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
|
-
#
|
106
|
-
|
107
|
-
|
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
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
data/lib/omniai/chat/prompt.rb
CHANGED
@@ -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&.
|
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
|
-
#
|
89
|
+
# @example
|
90
|
+
# prompt.message 'What is the capital of Canada?', role: '...'
|
85
91
|
#
|
86
|
-
#
|
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 [
|
100
|
+
# @yield [builder]
|
101
|
+
# @yieldparam builder [Message::Builder]
|
102
|
+
#
|
92
103
|
# @return [Message]
|
93
|
-
def message(content = nil, role:
|
94
|
-
|
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
|
-
#
|
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 [
|
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
|
-
#
|
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 [
|
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, &)
|