intelligence 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|