intelligence 0.5.0 → 0.6.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/intelligence.gemspec +1 -1
- data/lib/intelligence/adapter/base.rb +15 -2
- data/lib/intelligence/adapter/class_methods/construction.rb +17 -0
- data/lib/intelligence/adapter/module_methods/construction.rb +43 -0
- data/lib/intelligence/adapter.rb +2 -2
- data/lib/intelligence/adapters/anthropic/adapter.rb +4 -15
- data/lib/intelligence/adapters/anthropic/chat_methods.rb +9 -8
- data/lib/intelligence/adapters/cerebras.rb +3 -3
- data/lib/intelligence/adapters/generic/adapter.rb +1 -12
- data/lib/intelligence/adapters/generic/chat_methods.rb +34 -10
- data/lib/intelligence/adapters/google/adapter.rb +2 -15
- data/lib/intelligence/adapters/google/chat_methods.rb +68 -21
- data/lib/intelligence/adapters/groq.rb +20 -1
- data/lib/intelligence/adapters/hyperbolic.rb +1 -1
- data/lib/intelligence/adapters/legacy/adapter.rb +0 -2
- data/lib/intelligence/adapters/legacy/chat_methods.rb +23 -6
- data/lib/intelligence/adapters/mistral.rb +57 -0
- data/lib/intelligence/adapters/open_ai/adapter.rb +2 -16
- data/lib/intelligence/adapters/open_ai/chat_methods.rb +39 -8
- data/lib/intelligence/adapters/open_router.rb +62 -0
- data/lib/intelligence/adapters/samba_nova.rb +1 -1
- data/lib/intelligence/adapters/together_ai.rb +1 -1
- data/lib/intelligence/chat_request.rb +57 -7
- data/lib/intelligence/chat_result.rb +4 -0
- data/lib/intelligence/chat_result_choice.rb +4 -2
- data/lib/intelligence/conversation.rb +37 -9
- data/lib/intelligence/message.rb +90 -23
- data/lib/intelligence/message_content/base.rb +10 -0
- data/lib/intelligence/message_content/binary.rb +6 -0
- data/lib/intelligence/message_content/file.rb +35 -0
- data/lib/intelligence/message_content/text.rb +5 -0
- data/lib/intelligence/message_content/tool_call.rb +8 -1
- data/lib/intelligence/message_content/tool_result.rb +8 -1
- data/lib/intelligence/message_content.rb +12 -3
- data/lib/intelligence/version.rb +1 -1
- data/lib/intelligence.rb +4 -3
- metadata +9 -5
- data/lib/intelligence/adapter/construction_methods.rb +0 -37
@@ -7,7 +7,7 @@ module Intelligence
|
|
7
7
|
configuration do
|
8
8
|
|
9
9
|
# normalized properties for all endpoints
|
10
|
-
parameter :key, String
|
10
|
+
parameter :key, String
|
11
11
|
|
12
12
|
# openai properties for all endpoints
|
13
13
|
parameter :organization
|
@@ -17,7 +17,7 @@ module Intelligence
|
|
17
17
|
group :chat_options do
|
18
18
|
|
19
19
|
# normalized properties for openai generative text endpoint
|
20
|
-
parameter :model, String
|
20
|
+
parameter :model, String
|
21
21
|
parameter :n, Integer
|
22
22
|
parameter :max_tokens, Integer, as: :max_completion_tokens
|
23
23
|
parameter :temperature, Float
|
@@ -54,20 +54,6 @@ module Intelligence
|
|
54
54
|
|
55
55
|
end
|
56
56
|
|
57
|
-
attr_reader :key
|
58
|
-
attr_reader :organization
|
59
|
-
attr_reader :project
|
60
|
-
attr_reader :chat_options
|
61
|
-
|
62
|
-
|
63
|
-
def initialize( attributes = nil, &block )
|
64
|
-
configuration = self.class.configure( attributes, &block )
|
65
|
-
@key = configuration[ :key ]
|
66
|
-
@organization = configuration[ :organization ]
|
67
|
-
@project = configuration[ :project ]
|
68
|
-
@chat_options = configuration[ :chat_options ] || {}
|
69
|
-
end
|
70
|
-
|
71
57
|
include ChatMethods
|
72
58
|
|
73
59
|
end
|
@@ -4,16 +4,20 @@ module Intelligence
|
|
4
4
|
|
5
5
|
CHAT_REQUEST_URI = "https://api.openai.com/v1/chat/completions"
|
6
6
|
|
7
|
+
SUPPORTED_CONTENT_TYPES = [ 'image/jpeg', 'image/png' ]
|
8
|
+
|
7
9
|
def chat_request_uri( options )
|
8
10
|
CHAT_REQUEST_URI
|
9
11
|
end
|
10
12
|
|
11
13
|
def chat_request_headers( options = {} )
|
14
|
+
options = options ? self.class.configure( options ) : {}
|
15
|
+
options = @options.merge( options )
|
12
16
|
result = {}
|
13
17
|
|
14
|
-
key = options[ :key ]
|
15
|
-
organization = options[ :organization ]
|
16
|
-
project = options[ :project ]
|
18
|
+
key = options[ :key ]
|
19
|
+
organization = options[ :organization ]
|
20
|
+
project = options[ :project ]
|
17
21
|
|
18
22
|
raise ArgumentError.new( "An OpenAI key is required to build an OpenAI chat request." ) \
|
19
23
|
if key.nil?
|
@@ -27,7 +31,9 @@ module Intelligence
|
|
27
31
|
end
|
28
32
|
|
29
33
|
def chat_request_body( conversation, options = {} )
|
30
|
-
|
34
|
+
options = options ? self.class.configure( options ) : {}
|
35
|
+
options = @options.merge( options )
|
36
|
+
result = options[ :chat_options ]&.compact || {}
|
31
37
|
result[ :messages ] = []
|
32
38
|
|
33
39
|
system_message = translate_system_message( conversation[ :system_message ] )
|
@@ -46,8 +52,7 @@ module Intelligence
|
|
46
52
|
content_type = content[ :content_type ]
|
47
53
|
bytes = content[ :bytes ]
|
48
54
|
if content_type && bytes
|
49
|
-
|
50
|
-
if mime_type&.media_type == 'image'
|
55
|
+
if SUPPORTED_CONTENT_TYPES.include?( content_type )
|
51
56
|
result_message_content << {
|
52
57
|
type: 'image_url',
|
53
58
|
image_url: {
|
@@ -57,7 +62,7 @@ module Intelligence
|
|
57
62
|
else
|
58
63
|
raise UnsupportedContentError.new(
|
59
64
|
:open_ai,
|
60
|
-
|
65
|
+
"only supports content of type #{SUPPORTED_CONTENT_TYPES.join( ', ' )}"
|
61
66
|
)
|
62
67
|
end
|
63
68
|
else
|
@@ -66,7 +71,33 @@ module Intelligence
|
|
66
71
|
'requires binary content to include content type and ( packed ) bytes'
|
67
72
|
)
|
68
73
|
end
|
69
|
-
|
74
|
+
when :file
|
75
|
+
content_type = content[ :content_type ]
|
76
|
+
uri = content[ :uri ]
|
77
|
+
if content_type && uri
|
78
|
+
if SUPPORTED_CONTENT_TYPES.include?( content_type )
|
79
|
+
result_message_content << {
|
80
|
+
type: 'image_url',
|
81
|
+
image_url: {
|
82
|
+
url: uri
|
83
|
+
}
|
84
|
+
}
|
85
|
+
else
|
86
|
+
raise UnsupportedContentError.new(
|
87
|
+
:open_ai,
|
88
|
+
"only supports content of type #{SUPPORTED_CONTENT_TYPES.join( ', ' )}"
|
89
|
+
)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
raise UnsupportedContentError.new(
|
93
|
+
:open_ai,
|
94
|
+
'requires file content to include content type and uri'
|
95
|
+
)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
raise InvalidContentError.new( :open_ai )
|
99
|
+
end
|
100
|
+
|
70
101
|
end
|
71
102
|
|
72
103
|
result_message[ :content ] = result_message_content
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'generic/adapter'
|
2
|
+
|
3
|
+
module Intelligence
|
4
|
+
module OpenRouter
|
5
|
+
|
6
|
+
class Adapter < Generic::Adapter
|
7
|
+
|
8
|
+
chat_request_uri "https://openrouter.ai/api/v1/chat/completions"
|
9
|
+
|
10
|
+
configuration do
|
11
|
+
parameter :key, String
|
12
|
+
group :chat_options do
|
13
|
+
parameter :model, String
|
14
|
+
parameter :temperature, Float
|
15
|
+
parameter :top_k, Integer
|
16
|
+
parameter :top_p, Float
|
17
|
+
parameter :max_tokens, Integer
|
18
|
+
parameter :seed, Integer
|
19
|
+
parameter :stop, String, array: true
|
20
|
+
parameter :stream, [ TrueClass, FalseClass ]
|
21
|
+
parameter :frequency_penalty, Float
|
22
|
+
parameter :repetition_penalty, Float
|
23
|
+
parameter :presence_penalty, Float
|
24
|
+
|
25
|
+
group :provider do
|
26
|
+
parameter :order, String, array: true
|
27
|
+
parameter :require_parameters, [ TrueClass, FalseClass ]
|
28
|
+
parameter :allow_fallbacks, [ TrueClass, FalseClass ]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# def chat_result_error_attributes( response )
|
34
|
+
#
|
35
|
+
# error_type, error_description = translate_error_response_status( response.status )
|
36
|
+
# result = {
|
37
|
+
# error_type: error_type.to_s,
|
38
|
+
# error_description: error_description
|
39
|
+
# }
|
40
|
+
# parsed_body = JSON.parse( response.body, symbolize_names: true ) rescue nil
|
41
|
+
# if parsed_body && parsed_body.respond_to?( :include? )
|
42
|
+
# if parsed_body.include?( :error )
|
43
|
+
# result = {
|
44
|
+
# error_type: error_type.to_s,
|
45
|
+
# error: parsed_body[ :error ][ :code ] || error_type.to_s,
|
46
|
+
# error_description: parsed_body[ :error ][ :message ] || error_description
|
47
|
+
# }
|
48
|
+
# elsif parsed_body.include?( :detail )
|
49
|
+
# result[ :error_description ] = parsed_body[ :detail ]
|
50
|
+
# elsif parsed_body[ :object ] == 'error'
|
51
|
+
# result[ :error_description ] = parsed_body[ :message ]
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# result
|
56
|
+
#
|
57
|
+
# end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Intelligence
|
2
2
|
|
3
|
-
#
|
4
|
-
# module. ChatRequestMethods
|
5
3
|
#
|
6
4
|
# The ChatRequestMethods module extends a Faraday request, adding the +receive_result+ method.
|
7
5
|
#
|
@@ -14,9 +12,7 @@ module Intelligence
|
|
14
12
|
end
|
15
13
|
|
16
14
|
#
|
17
|
-
# module.
|
18
|
-
#
|
19
|
-
# The ChatResponseMethods module extends a Farada reponse, adding the +result+ method.
|
15
|
+
# The ChatResponseMethods module extends a Faraday reponse, adding the +result+ method.
|
20
16
|
#
|
21
17
|
module ChatResponseMethods
|
22
18
|
|
@@ -26,13 +22,38 @@ module Intelligence
|
|
26
22
|
|
27
23
|
end
|
28
24
|
|
25
|
+
##
|
26
|
+
# The +ChatRequest+ class encapsulates a request to an LLM. After creating a new +ChatRequest+
|
27
|
+
# instance you can make the actual request by calling the +chat+ or +stream+ methods. In order
|
28
|
+
# to construct a +ChatRequest+ you must first construct and configure an adapter.
|
29
|
+
#
|
30
|
+
# === example
|
31
|
+
#
|
32
|
+
# adapter = Intelligence::Adapter.build( :open_ai ) do
|
33
|
+
# key ENV[ 'OPENAI_API_KEY' ]
|
34
|
+
# chat_options do
|
35
|
+
# model 'gpt-4o'
|
36
|
+
# max_tokens 512
|
37
|
+
# end
|
38
|
+
# end
|
29
39
|
#
|
30
|
-
#
|
40
|
+
# request = Intelligence::ChatRequest.new( adapter: adapter )
|
41
|
+
# response = request.chat( 'Hello!' )
|
31
42
|
#
|
43
|
+
# if response.success?
|
44
|
+
# puts response.result.text
|
45
|
+
# else
|
46
|
+
# puts response.result.error_description
|
47
|
+
# end
|
48
|
+
#
|
32
49
|
class ChatRequest
|
33
50
|
|
34
51
|
DEFAULT_CONNECTION = Faraday.new { | builder | builder.adapter Faraday.default_adapter }
|
35
52
|
|
53
|
+
##
|
54
|
+
# The +initialize+ method initializes the +ChatRequest+ instance. You MUST pass a previously
|
55
|
+
# constructed and configured +adapter+ and optionally a (Faraday) +connection+.
|
56
|
+
#
|
36
57
|
def initialize( connection: nil, adapter: , **options )
|
37
58
|
@connection = connection || DEFAULT_CONNECTION
|
38
59
|
@adapter = adapter
|
@@ -42,8 +63,25 @@ module Intelligence
|
|
42
63
|
if @adapter.nil?
|
43
64
|
end
|
44
65
|
|
66
|
+
##
|
67
|
+
# The +chat+ method leverages the adapter associated with this +ChatRequest+ instance to
|
68
|
+
# construct and make an HTTP request - through Faraday - to an LLM service. The +chat+ method
|
69
|
+
# always returns a +Faraday::Respose+ which is augmented with a +result+ method.
|
70
|
+
#
|
71
|
+
# If the response is successful ( if +response.success?+ returns true ) the +result+ method
|
72
|
+
# returns a +ChatResponse+ instance. If the response is not successful a +ChatErrorResult+
|
73
|
+
# instance is returned.
|
74
|
+
#
|
75
|
+
# === arguments
|
76
|
+
# * +conversation+ - an instance of +Intelligence::Conversation+ or String; this encapsulates
|
77
|
+
# the content to be sent to the LLM
|
78
|
+
# * +options+ - a Hash with options; these options overide any of the configuration
|
79
|
+
# options used to configure the adapter; you can, for example, pass
|
80
|
+
# +{ chat_options: { max_tokens: 1024 }+ to limit the response to 1024
|
81
|
+
# tokens.
|
45
82
|
def chat( conversation, options = {} )
|
46
83
|
|
84
|
+
conversation = build_quick_conversation( conversation ) if conversation.is_a?( String )
|
47
85
|
options = @options.merge( options )
|
48
86
|
|
49
87
|
uri = @adapter.chat_request_uri( options )
|
@@ -77,6 +115,7 @@ module Intelligence
|
|
77
115
|
|
78
116
|
def stream( conversation, options = {} )
|
79
117
|
|
118
|
+
conversation = build_quick_conversation( conversation ) if conversation.is_a?( String )
|
80
119
|
options = @options.merge( options )
|
81
120
|
|
82
121
|
uri = @adapter.chat_request_uri( options )
|
@@ -112,6 +151,17 @@ module Intelligence
|
|
112
151
|
|
113
152
|
end
|
114
153
|
|
154
|
+
private
|
155
|
+
|
156
|
+
def build_quick_conversation( text )
|
157
|
+
conversation = Conversation.new()
|
158
|
+
conversation.messages << Message.build! do
|
159
|
+
role :user
|
160
|
+
content text: text
|
161
|
+
end
|
162
|
+
conversation
|
163
|
+
end
|
164
|
+
|
115
165
|
end
|
116
166
|
|
117
|
-
end
|
167
|
+
end
|
@@ -13,7 +13,9 @@ module Intelligence
|
|
13
13
|
if chat_choice_attributes[ :message ]
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_message( json_message )
|
17
19
|
message = Message.new( json_message[ :role ]&.to_sym || :assistant )
|
18
20
|
json_message[ :contents ]&.each do | json_content |
|
19
21
|
message << MessageContent.build( json_content[ :type ], json_content )
|
@@ -23,4 +25,4 @@ module Intelligence
|
|
23
25
|
|
24
26
|
end
|
25
27
|
|
26
|
-
end
|
28
|
+
end
|
@@ -1,18 +1,40 @@
|
|
1
1
|
module Intelligence
|
2
|
-
|
3
|
-
#
|
4
|
-
# class. Conversation
|
5
|
-
#
|
6
2
|
class Conversation
|
7
3
|
|
4
|
+
extend AdaptiveConfiguration::Configurable
|
5
|
+
|
6
|
+
configuration do
|
7
|
+
group :system_message, default: { role: :system }, &Message::CONFIGURATION
|
8
|
+
group :message, as: :messages, array: true, &Message::CONFIGURATION
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build( attributes = nil, &block )
|
12
|
+
configuration = self.configure( attributes, &block )
|
13
|
+
self.new( configuration.to_h )
|
14
|
+
end
|
15
|
+
|
8
16
|
attr_reader :system_message
|
9
17
|
attr_reader :messages
|
10
18
|
attr_reader :tools
|
11
19
|
|
12
|
-
def initialize( attributes =
|
13
|
-
|
14
|
-
@messages =
|
15
|
-
@tools =
|
20
|
+
def initialize( attributes = nil )
|
21
|
+
|
22
|
+
@messages = []
|
23
|
+
@tools = []
|
24
|
+
if attributes
|
25
|
+
if attributes[ :system_message ]
|
26
|
+
system_message = Message.new(
|
27
|
+
attributes[ :system_message ][ :role ],
|
28
|
+
attributes[ :system_message ]
|
29
|
+
)
|
30
|
+
@system_message = system_message unless system_message.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
attributes[ :messages ]&.each do | message_attributes |
|
34
|
+
@messages << Message.new( message_attributes[ :role ], message_attributes )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
16
38
|
end
|
17
39
|
|
18
40
|
def has_system_message?
|
@@ -35,6 +57,13 @@ module Intelligence
|
|
35
57
|
@system_message = message
|
36
58
|
end
|
37
59
|
|
60
|
+
def append_message( *messages )
|
61
|
+
@messages.concat( messages.flatten )
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
alias :<< :append_message
|
66
|
+
|
38
67
|
def to_h
|
39
68
|
result = {}
|
40
69
|
result[ :system_message ] = @system_message.to_h if @system_message
|
@@ -44,5 +73,4 @@ module Intelligence
|
|
44
73
|
end
|
45
74
|
|
46
75
|
end
|
47
|
-
|
48
76
|
end
|
data/lib/intelligence/message.rb
CHANGED
@@ -1,45 +1,101 @@
|
|
1
1
|
module Intelligence
|
2
2
|
class Message
|
3
3
|
|
4
|
+
extend AdaptiveConfiguration::Configurable
|
5
|
+
|
4
6
|
ROLES = [ :system, :user, :assistant ]
|
7
|
+
CONFIGURATION = Proc.new do
|
8
|
+
parameter :role, Symbol, required: true
|
9
|
+
group :content, array: true, as: :contents do
|
10
|
+
parameter :type, Symbol, default: :text
|
11
|
+
parameter :text, String
|
12
|
+
parameter :content_type, String
|
13
|
+
parameter :bytes, String
|
14
|
+
parameter :uri, URI
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
configuration( &CONFIGURATION )
|
5
19
|
|
6
20
|
attr_reader :role
|
7
21
|
attr_reader :contents
|
8
22
|
|
9
|
-
|
23
|
+
##
|
24
|
+
# The build! class method constructs and returns a new +Message+ instance. The build! method
|
25
|
+
# accepts message +attributes+ and a block, which may be combined when constructing a Message.
|
26
|
+
# The +role+ is required, either as a paramter or in the block. If the role is not present,
|
27
|
+
# an exception will be raised.
|
28
|
+
#
|
29
|
+
# The block offers the +role+ method, as well as a +content+ method permiting the caller to
|
30
|
+
# set the role and add content respectivelly.
|
31
|
+
#
|
32
|
+
# === examples
|
33
|
+
#
|
34
|
+
# message = Message.build!( role: :user )
|
35
|
+
#
|
36
|
+
# message = Message.build! do
|
37
|
+
# role :user
|
38
|
+
# content do
|
39
|
+
# type :text
|
40
|
+
# text 'this is a user message'
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# message = Message.build!( role: :user ) do
|
45
|
+
# content text: 'this is a user message'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# message = Message.build!( role: :user ) do
|
49
|
+
# content text: 'what do you see in this image?'
|
50
|
+
# content type: :binary do
|
51
|
+
# content_type 'image/png'
|
52
|
+
# bytes File.binread( '99_red_balloons.png' )
|
53
|
+
#
|
54
|
+
def self.build!( attributes = nil, &block )
|
55
|
+
configuration = self.configure!( attributes, &block ).to_h
|
56
|
+
self.new( configuration[ :role ], configuration )
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize( role, attributes = nil )
|
10
60
|
raise ArgumentError.new( "The role is invalid. It must be one of #{ROLES.join( ', ' )}." ) \
|
11
|
-
unless ROLES.include?( role
|
61
|
+
unless ROLES.include?( role&.to_sym )
|
12
62
|
|
13
|
-
|
63
|
+
@role = role.to_sym
|
14
64
|
@contents = []
|
15
65
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def build_and_append_content( content_type = nil, content_attributes = nil )
|
25
|
-
append_content(
|
26
|
-
MessageContent.build( content_type, content_attributes )
|
27
|
-
)
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
|
-
alias :<< :append_content
|
32
|
-
|
33
|
-
def each_content( &block )
|
34
|
-
@contents.each( &block )
|
66
|
+
if attributes && attributes[ :contents ]
|
67
|
+
attributes[ :contents ].each do | content |
|
68
|
+
@contents << MessageContent.build!( content[ :type ], content )
|
69
|
+
end
|
70
|
+
end
|
35
71
|
end
|
36
72
|
|
73
|
+
##
|
74
|
+
# The empty? method return true if the message has no content.
|
75
|
+
#
|
37
76
|
def empty?
|
38
77
|
@contents.empty?
|
39
78
|
end
|
40
79
|
|
80
|
+
##
|
81
|
+
# The valid? method returns true if the message has a valid role, has content, and the content
|
82
|
+
# is valid.
|
83
|
+
#
|
41
84
|
def valid?
|
42
|
-
!@
|
85
|
+
ROLES.include?( @role ) && !@contents.empty? && @contents.all?{ | contents | contents.valid? }
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# The text method is a convenience that returns all text content in the message joined with
|
90
|
+
# a newline. Any non-text content is skipped. If there is no text content an empty string is
|
91
|
+
# returned.
|
92
|
+
#
|
93
|
+
def text
|
94
|
+
result = []
|
95
|
+
each_content do | content |
|
96
|
+
result << content.text if content.is_a?( MessageContent::Text )
|
97
|
+
end
|
98
|
+
result.join( "\n" )
|
43
99
|
end
|
44
100
|
|
45
101
|
def to_h
|
@@ -49,5 +105,16 @@ module Intelligence
|
|
49
105
|
}
|
50
106
|
end
|
51
107
|
|
108
|
+
def each_content( &block )
|
109
|
+
@contents.each( &block )
|
110
|
+
end
|
111
|
+
|
112
|
+
def append_content( content )
|
113
|
+
@contents.push( content ) unless content.nil?
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
alias :<< :append_content
|
118
|
+
|
52
119
|
end
|
53
120
|
end
|
@@ -2,6 +2,16 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class Base
|
5
|
+
extend AdaptiveConfiguration::Configurable
|
6
|
+
|
7
|
+
def self.build( attributes = nil, &block )
|
8
|
+
self.new( self.configure( attributes, &block ) )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build!( attributes = nil, &block )
|
12
|
+
self.new( self.configure!( attributes, &block ) )
|
13
|
+
end
|
14
|
+
|
5
15
|
def initialize( attributes = {} )
|
6
16
|
attributes.each do | key, value |
|
7
17
|
instance_variable_set( "@#{key}", value.freeze ) if self.respond_to?( "#{key}" )
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Intelligence
|
2
|
+
module MessageContent
|
3
|
+
|
4
|
+
class File < Base
|
5
|
+
|
6
|
+
configuration do
|
7
|
+
parameter :content_type, String
|
8
|
+
parameter :uri, URI, required: true
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize( attributes )
|
12
|
+
@uri = URI( attributes[ :uri ] ) if attributes[ :uri ]
|
13
|
+
@content_type = attributes[ :content_type ]
|
14
|
+
end
|
15
|
+
|
16
|
+
def content_type
|
17
|
+
@content_type ||= valid_uri? ? MIME::Types.type_for( @uri.path )&.first&.content_type : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid_uri?( schemes = [ 'http', 'https' ] )
|
21
|
+
!!( @uri && schemes.include?( @uri.scheme ) && @uri.path && !@uri.path.empty? )
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid?
|
25
|
+
valid_uri? && !MIME::Types[ content_type ].empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
{ type: :file, content_type: content_type, uri: @uri.to_s }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -2,6 +2,13 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class ToolCall < Base
|
5
|
+
|
6
|
+
configuration do
|
7
|
+
parameter :tool_call_id, String
|
8
|
+
parameter :tool_name, String, required: true
|
9
|
+
parameter :tool_parameters, String, array: true
|
10
|
+
end
|
11
|
+
|
5
12
|
attr_reader :tool_call_id
|
6
13
|
attr_reader :tool_name
|
7
14
|
attr_reader :tool_parameters
|
@@ -17,4 +24,4 @@ module Intelligence
|
|
17
24
|
end
|
18
25
|
|
19
26
|
end
|
20
|
-
end
|
27
|
+
end
|
@@ -2,6 +2,13 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class ToolResult < Base
|
5
|
+
|
6
|
+
configuration do
|
7
|
+
parameter :tool_call_id
|
8
|
+
parameter :tool_name, String, required: true
|
9
|
+
parameter :tool_result, String, required: true
|
10
|
+
end
|
11
|
+
|
5
12
|
attr_reader :tool_call_id
|
6
13
|
attr_reader :tool_name
|
7
14
|
attr_reader :tool_result
|
@@ -17,4 +24,4 @@ module Intelligence
|
|
17
24
|
end
|
18
25
|
|
19
26
|
end
|
20
|
-
end
|
27
|
+
end
|