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