intelligence 0.5.0 → 0.7.1
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/README.md +555 -0
- data/intelligence.gemspec +1 -1
- data/lib/intelligence/adapter/base.rb +23 -3
- data/lib/intelligence/adapter/class_methods.rb +15 -0
- data/lib/intelligence/adapter/{construction_methods.rb → module_methods.rb} +8 -4
- data/lib/intelligence/adapter.rb +2 -2
- data/lib/intelligence/adapters/anthropic/adapter.rb +21 -30
- data/lib/intelligence/adapters/anthropic/chat_request_methods.rb +189 -0
- data/lib/intelligence/adapters/anthropic/{chat_methods.rb → chat_response_methods.rb} +8 -124
- data/lib/intelligence/adapters/cerebras.rb +17 -17
- data/lib/intelligence/adapters/generic/adapter.rb +1 -12
- data/lib/intelligence/adapters/generic/chat_methods.rb +42 -11
- data/lib/intelligence/adapters/generic.rb +1 -1
- data/lib/intelligence/adapters/google/adapter.rb +33 -35
- data/lib/intelligence/adapters/google/chat_request_methods.rb +233 -0
- data/lib/intelligence/adapters/google/{chat_methods.rb → chat_response_methods.rb} +52 -162
- data/lib/intelligence/adapters/groq.rb +46 -28
- data/lib/intelligence/adapters/hyperbolic.rb +13 -13
- data/lib/intelligence/adapters/legacy/adapter.rb +0 -2
- data/lib/intelligence/adapters/legacy/chat_methods.rb +22 -6
- data/lib/intelligence/adapters/mistral.rb +57 -0
- data/lib/intelligence/adapters/open_ai/adapter.rb +38 -45
- data/lib/intelligence/adapters/open_ai/chat_request_methods.rb +186 -0
- data/lib/intelligence/adapters/open_ai/{chat_methods.rb → chat_response_methods.rb} +60 -131
- data/lib/intelligence/adapters/open_ai.rb +1 -1
- data/lib/intelligence/adapters/open_router.rb +62 -0
- data/lib/intelligence/adapters/samba_nova.rb +13 -13
- data/lib/intelligence/adapters/together_ai.rb +21 -19
- 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 +38 -9
- data/lib/intelligence/message.rb +103 -20
- data/lib/intelligence/message_content/base.rb +3 -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 +12 -1
- data/lib/intelligence/message_content/tool_result.rb +15 -3
- data/lib/intelligence/message_content.rb +12 -3
- data/lib/intelligence/tool.rb +139 -0
- data/lib/intelligence/version.rb +1 -1
- data/lib/intelligence.rb +6 -4
- metadata +18 -9
@@ -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
|
+
schema do
|
11
|
+
key String
|
12
|
+
chat_options do
|
13
|
+
model String
|
14
|
+
temperature Float
|
15
|
+
top_k Integer
|
16
|
+
top_p Float
|
17
|
+
max_tokens Integer
|
18
|
+
seed Integer
|
19
|
+
stop String, array: true
|
20
|
+
stream [ TrueClass, FalseClass ]
|
21
|
+
frequency_penalty Float
|
22
|
+
repetition_penalty Float
|
23
|
+
presence_penalty Float
|
24
|
+
|
25
|
+
provider do
|
26
|
+
order String, array: true
|
27
|
+
require_parameters [ TrueClass, FalseClass ]
|
28
|
+
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
|
@@ -7,27 +7,27 @@ module Intelligence
|
|
7
7
|
|
8
8
|
chat_request_uri "https://api.sambanova.ai/v1/chat/completions"
|
9
9
|
|
10
|
-
|
10
|
+
schema do
|
11
11
|
|
12
12
|
# normalized properties for all endpoints
|
13
|
-
|
13
|
+
key String
|
14
14
|
|
15
15
|
# properties for generative text endpoints
|
16
|
-
|
16
|
+
chat_options do
|
17
17
|
|
18
18
|
# normalized properties for samba nova generative text endpoint
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
model String
|
20
|
+
max_tokens Integer
|
21
|
+
temperature Float
|
22
|
+
top_p Float
|
23
|
+
top_k Float
|
24
|
+
stop String, array: true
|
25
|
+
stream [ TrueClass, FalseClass ]
|
26
26
|
|
27
27
|
# samba nova properties for samba nova generative text endpoint
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
repetition_penalty Float
|
29
|
+
stream_options do
|
30
|
+
include_usage [ TrueClass, FalseClass ]
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
@@ -7,32 +7,34 @@ module Intelligence
|
|
7
7
|
|
8
8
|
chat_request_uri "https://api.together.xyz/v1/chat/completions"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
schema do
|
11
|
+
key String
|
12
|
+
chat_options do
|
13
|
+
model String
|
14
|
+
temperature Float
|
15
|
+
top_p Float
|
16
|
+
top_k Integer
|
17
|
+
n Integer
|
18
|
+
max_tokens Float
|
19
|
+
stop String, array: true
|
20
|
+
stream [ TrueClass, FalseClass ]
|
21
|
+
frequency_penalty Float
|
22
|
+
presence_penalty Float
|
23
|
+
repetition_penalty Float
|
24
|
+
user String
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
def translate_end_result( end_result )
|
29
29
|
case end_result
|
30
|
-
when 'eos'
|
30
|
+
when 'eos', 'stop'
|
31
31
|
:ended
|
32
|
-
|
32
|
+
# unfortunatelly eos seems to only work with certain models while others always return
|
33
|
+
# stop so for now tomorrow ai will not support :end_sequence_encountered
|
34
|
+
# when 'stop'
|
35
|
+
# :end_sequence_encountered
|
36
|
+
when 'length'
|
33
37
|
:token_limit_exceeded
|
34
|
-
when 'stop'
|
35
|
-
:end_sequence_encountered
|
36
38
|
when 'function_call'
|
37
39
|
:tool_called
|
38
40
|
else
|
@@ -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,36 @@
|
|
1
1
|
module Intelligence
|
2
|
-
|
3
|
-
#
|
4
|
-
# class. Conversation
|
5
|
-
#
|
6
2
|
class Conversation
|
7
3
|
|
4
|
+
include DynamicSchema::Definable
|
5
|
+
include DynamicSchema::Buildable
|
6
|
+
|
7
|
+
schema do
|
8
|
+
system_message default: { role: :system }, &Message.schema
|
9
|
+
message as: :messages, array: true, &Message.schema
|
10
|
+
end
|
11
|
+
|
8
12
|
attr_reader :system_message
|
9
13
|
attr_reader :messages
|
10
14
|
attr_reader :tools
|
11
15
|
|
12
|
-
def initialize( attributes =
|
13
|
-
|
14
|
-
@messages =
|
15
|
-
@tools =
|
16
|
+
def initialize( attributes = nil )
|
17
|
+
|
18
|
+
@messages = []
|
19
|
+
@tools = []
|
20
|
+
if attributes
|
21
|
+
if attributes[ :system_message ]&.any?
|
22
|
+
system_message = Message.new(
|
23
|
+
attributes[ :system_message ][ :role ],
|
24
|
+
attributes[ :system_message ]
|
25
|
+
)
|
26
|
+
@system_message = system_message unless system_message.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
attributes[ :messages ]&.each do | message_attributes |
|
30
|
+
@messages << Message.new( message_attributes[ :role ], message_attributes )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
16
34
|
end
|
17
35
|
|
18
36
|
def has_system_message?
|
@@ -35,6 +53,18 @@ module Intelligence
|
|
35
53
|
@system_message = message
|
36
54
|
end
|
37
55
|
|
56
|
+
def append_message( *messages )
|
57
|
+
@messages.concat( messages.flatten )
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
alias :<< :append_message
|
62
|
+
|
63
|
+
def append_tool( *tools )
|
64
|
+
@tools.concat( tools.flatten )
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
38
68
|
def to_h
|
39
69
|
result = {}
|
40
70
|
result[ :system_message ] = @system_message.to_h if @system_message
|
@@ -44,5 +74,4 @@ module Intelligence
|
|
44
74
|
end
|
45
75
|
|
46
76
|
end
|
47
|
-
|
48
77
|
end
|
data/lib/intelligence/message.rb
CHANGED
@@ -1,46 +1,129 @@
|
|
1
1
|
module Intelligence
|
2
2
|
class Message
|
3
3
|
|
4
|
+
include DynamicSchema::Definable
|
5
|
+
|
4
6
|
ROLES = [ :system, :user, :assistant ]
|
7
|
+
schema do
|
8
|
+
role Symbol, required: true
|
9
|
+
content array: true, as: :contents do
|
10
|
+
type Symbol, default: :text
|
11
|
+
|
12
|
+
# note: we replicate these schema elements of the individual content types here to
|
13
|
+
# provide more semantic flexibility when building a message; we don't delegate to the
|
14
|
+
# individual content type schemas because, unlike for a specific content type, not all
|
15
|
+
# attributes are required
|
16
|
+
|
17
|
+
# text
|
18
|
+
text String
|
19
|
+
# binary and file
|
20
|
+
content_type String
|
21
|
+
bytes String
|
22
|
+
uri URI
|
23
|
+
# tool call and tool result
|
24
|
+
tool_call_id String
|
25
|
+
tool_name String
|
26
|
+
tool_parameters [ Hash, String ]
|
27
|
+
tool_result [ Hash, String ]
|
28
|
+
end
|
29
|
+
end
|
5
30
|
|
6
31
|
attr_reader :role
|
7
32
|
attr_reader :contents
|
8
33
|
|
9
|
-
|
10
|
-
|
11
|
-
|
34
|
+
##
|
35
|
+
# The +build!+ class method constructs and returns a new +Message+ instance. The +build!+
|
36
|
+
# method accepts message +attributes+ and a block, which may be combined when constructing
|
37
|
+
# a +Message+. The +role+ is required, either as an attribute or in the block. If the +role+
|
38
|
+
# is not present, an exception will be raised.
|
39
|
+
#
|
40
|
+
# The block offers the +role+ method, as well as a +content+ method permiting the caller to
|
41
|
+
# set the role and add content respectivelly.
|
42
|
+
#
|
43
|
+
# Note that there is no corresponding +build+ method because a +Message+ strictly requires a
|
44
|
+
# +role+. It cannot be constructed without one.
|
45
|
+
#
|
46
|
+
# === examples
|
47
|
+
#
|
48
|
+
# message = Message.build!( role: :user )
|
49
|
+
#
|
50
|
+
# message = Message.build! do
|
51
|
+
# role :user
|
52
|
+
# content do
|
53
|
+
# type :text
|
54
|
+
# text 'this is a user message'
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# message = Message.build!( role: :user ) do
|
59
|
+
# content text: 'this is a user message'
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# message = Message.build!( role: :user ) do
|
63
|
+
# content text: 'what do you see in this image?'
|
64
|
+
# content type: :binary do
|
65
|
+
# content_type 'image/png'
|
66
|
+
# bytes File.binread( '99_red_balloons.png' )
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
def self.build!( attributes = nil, &block )
|
71
|
+
attributes = self.builder.build!( attributes, &block )
|
72
|
+
self.new( attributes[ :role ], attributes )
|
73
|
+
end
|
12
74
|
|
13
|
-
|
75
|
+
def initialize( role, attributes = nil )
|
76
|
+
@role = role&.to_sym
|
14
77
|
@contents = []
|
15
78
|
|
16
|
-
|
79
|
+
raise ArgumentError.new( "The role is invalid. It must be one of #{ROLES.join( ', ' )}." ) \
|
80
|
+
unless ROLES.include?( @role )
|
81
|
+
|
82
|
+
if attributes && attributes[ :contents ]
|
83
|
+
attributes[ :contents ].each do | content |
|
84
|
+
@contents << MessageContent.build!( content[ :type ], content )
|
85
|
+
end
|
86
|
+
end
|
17
87
|
end
|
18
88
|
|
19
|
-
|
20
|
-
|
21
|
-
|
89
|
+
##
|
90
|
+
# The empty? method return true if the message has no content.
|
91
|
+
#
|
92
|
+
def empty?
|
93
|
+
@contents.empty?
|
22
94
|
end
|
23
95
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
96
|
+
##
|
97
|
+
# The valid? method returns true if the message has a valid role, has content, and the content
|
98
|
+
# is valid.
|
99
|
+
#
|
100
|
+
def valid?
|
101
|
+
ROLES.include?( @role ) && !@contents.empty? && @contents.all?{ | contents | contents.valid? }
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# The text method is a convenience that returns all text content in the message joined with
|
106
|
+
# a newline. Any non-text content is skipped. If there is no text content an empty string is
|
107
|
+
# returned.
|
108
|
+
#
|
109
|
+
def text
|
110
|
+
result = []
|
111
|
+
each_content do | content |
|
112
|
+
result << content.text if content.is_a?( MessageContent::Text )
|
113
|
+
end
|
114
|
+
result.join( "\n" )
|
29
115
|
end
|
30
116
|
|
31
|
-
alias :<< :append_content
|
32
|
-
|
33
117
|
def each_content( &block )
|
34
118
|
@contents.each( &block )
|
35
119
|
end
|
36
120
|
|
37
|
-
def
|
38
|
-
@contents.
|
121
|
+
def append_content( content )
|
122
|
+
@contents.push( content ) unless content.nil?
|
123
|
+
self
|
39
124
|
end
|
40
125
|
|
41
|
-
|
42
|
-
!@role.nil? && @contents.all?{ | contents | contents.valid? }
|
43
|
-
end
|
126
|
+
alias :<< :append_content
|
44
127
|
|
45
128
|
def to_h
|
46
129
|
{
|
@@ -2,6 +2,9 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class Base
|
5
|
+
include DynamicSchema::Definable
|
6
|
+
include DynamicSchema::Buildable
|
7
|
+
|
5
8
|
def initialize( attributes = {} )
|
6
9
|
attributes.each do | key, value |
|
7
10
|
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
|
+
schema do
|
7
|
+
content_type String
|
8
|
+
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,10 +2,21 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class ToolCall < Base
|
5
|
+
|
6
|
+
schema do
|
7
|
+
tool_call_id String
|
8
|
+
tool_name String, required: true
|
9
|
+
tool_parameters [ Hash, String ]
|
10
|
+
end
|
11
|
+
|
5
12
|
attr_reader :tool_call_id
|
6
13
|
attr_reader :tool_name
|
7
14
|
attr_reader :tool_parameters
|
8
15
|
|
16
|
+
def valid?
|
17
|
+
tool_name && !tool_name.empty?
|
18
|
+
end
|
19
|
+
|
9
20
|
def to_h
|
10
21
|
{
|
11
22
|
type: :tool_call,
|
@@ -17,4 +28,4 @@ module Intelligence
|
|
17
28
|
end
|
18
29
|
|
19
30
|
end
|
20
|
-
end
|
31
|
+
end
|
@@ -2,19 +2,31 @@ module Intelligence
|
|
2
2
|
module MessageContent
|
3
3
|
|
4
4
|
class ToolResult < Base
|
5
|
+
|
6
|
+
schema do
|
7
|
+
tool_call_id
|
8
|
+
tool_name String, required: true
|
9
|
+
tool_result [ Hash, String ]
|
10
|
+
end
|
11
|
+
|
5
12
|
attr_reader :tool_call_id
|
6
13
|
attr_reader :tool_name
|
7
14
|
attr_reader :tool_result
|
8
15
|
|
16
|
+
def valid?
|
17
|
+
tool_call_id && !tool_call_id.empty? &&
|
18
|
+
tool_name && !tool_name.empty?
|
19
|
+
end
|
20
|
+
|
9
21
|
def to_h
|
10
22
|
{
|
11
|
-
type: :
|
23
|
+
type: :tool_result,
|
12
24
|
tool_call_id: tool_call_id,
|
13
25
|
tool_name: tool_name,
|
14
|
-
|
26
|
+
tool_result: tool_result
|
15
27
|
}.compact
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
19
31
|
end
|
20
|
-
end
|
32
|
+
end
|
@@ -4,11 +4,20 @@ end
|
|
4
4
|
|
5
5
|
module Intelligence
|
6
6
|
module MessageContent
|
7
|
-
|
8
|
-
def self.
|
7
|
+
|
8
|
+
def self.[]( type )
|
9
9
|
type_name = type.to_s.split( '_' ).map { | word | word.capitalize }.join
|
10
10
|
klass = Intelligence.const_get( "MessageContent::#{type_name}" ) rescue nil
|
11
|
-
|
11
|
+
raise TypeError, "An unknown content type '#{type}' was given." unless klass
|
12
|
+
klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build!( type, attributes = nil, &block )
|
16
|
+
self[ type ].build!( attributes, &block )
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build( type, attributes = nil, &block )
|
20
|
+
self[ type ].build( attributes, &block )
|
12
21
|
end
|
13
22
|
|
14
23
|
end
|