intelligence 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/intelligence.gemspec +1 -1
  3. data/lib/intelligence/adapter/base.rb +15 -2
  4. data/lib/intelligence/adapter/class_methods/construction.rb +17 -0
  5. data/lib/intelligence/adapter/module_methods/construction.rb +43 -0
  6. data/lib/intelligence/adapter.rb +2 -2
  7. data/lib/intelligence/adapters/anthropic/adapter.rb +4 -15
  8. data/lib/intelligence/adapters/anthropic/chat_methods.rb +9 -8
  9. data/lib/intelligence/adapters/cerebras.rb +3 -3
  10. data/lib/intelligence/adapters/generic/adapter.rb +1 -12
  11. data/lib/intelligence/adapters/generic/chat_methods.rb +34 -10
  12. data/lib/intelligence/adapters/google/adapter.rb +2 -15
  13. data/lib/intelligence/adapters/google/chat_methods.rb +68 -21
  14. data/lib/intelligence/adapters/groq.rb +20 -1
  15. data/lib/intelligence/adapters/hyperbolic.rb +1 -1
  16. data/lib/intelligence/adapters/legacy/adapter.rb +0 -2
  17. data/lib/intelligence/adapters/legacy/chat_methods.rb +23 -6
  18. data/lib/intelligence/adapters/mistral.rb +57 -0
  19. data/lib/intelligence/adapters/open_ai/adapter.rb +2 -16
  20. data/lib/intelligence/adapters/open_ai/chat_methods.rb +39 -8
  21. data/lib/intelligence/adapters/open_router.rb +62 -0
  22. data/lib/intelligence/adapters/samba_nova.rb +1 -1
  23. data/lib/intelligence/adapters/together_ai.rb +1 -1
  24. data/lib/intelligence/chat_request.rb +57 -7
  25. data/lib/intelligence/chat_result.rb +4 -0
  26. data/lib/intelligence/chat_result_choice.rb +4 -2
  27. data/lib/intelligence/conversation.rb +37 -9
  28. data/lib/intelligence/message.rb +90 -23
  29. data/lib/intelligence/message_content/base.rb +10 -0
  30. data/lib/intelligence/message_content/binary.rb +6 -0
  31. data/lib/intelligence/message_content/file.rb +35 -0
  32. data/lib/intelligence/message_content/text.rb +5 -0
  33. data/lib/intelligence/message_content/tool_call.rb +8 -1
  34. data/lib/intelligence/message_content/tool_result.rb +8 -1
  35. data/lib/intelligence/message_content.rb +12 -3
  36. data/lib/intelligence/version.rb +1 -1
  37. data/lib/intelligence.rb +4 -3
  38. metadata +9 -5
  39. 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, required: true
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, required: true
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 ] || self.key
15
- organization = options[ :organization ] || self.organization
16
- project = options[ :project ] || self.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
- result = self.chat_options.merge( options ).compact
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
- mime_type = MIME::Types[ content_type ].first
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
- 'only supports content of type image/*'
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
- end
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
@@ -10,7 +10,7 @@ module Intelligence
10
10
  configuration do
11
11
 
12
12
  # normalized properties for all endpoints
13
- parameter :key, String, required: true
13
+ parameter :key, String
14
14
 
15
15
  # properties for generative text endpoints
16
16
  group :chat_options do
@@ -8,7 +8,7 @@ module Intelligence
8
8
  chat_request_uri "https://api.together.xyz/v1/chat/completions"
9
9
 
10
10
  configuration do
11
- parameter :key, String, required: true
11
+ parameter :key, String
12
12
  group :chat_options do
13
13
  parameter :model, String
14
14
  parameter :temperature, Float
@@ -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. ChatResponseMethods
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
- # class. ChatRequest
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
@@ -27,6 +27,10 @@ module Intelligence
27
27
  @choices.first.message
28
28
  end
29
29
 
30
+ def text
31
+ return self.message&.text || ''
32
+ end
33
+
30
34
  end
31
35
 
32
36
  end
@@ -13,7 +13,9 @@ module Intelligence
13
13
  if chat_choice_attributes[ :message ]
14
14
  end
15
15
 
16
- private; def build_message( json_message )
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
- @system_message = attributes[ :system_message ]&.dup
14
- @messages = attributes[ :messages ]&.dup || []
15
- @tools = attributes[ :tools ]&.dup || []
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
@@ -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
- def initialize( role, content_type = nil, content_attributes = nil )
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.to_sym )
61
+ unless ROLES.include?( role&.to_sym )
12
62
 
13
- @role = role.to_sym
63
+ @role = role.to_sym
14
64
  @contents = []
15
65
 
16
- @contents << MessageContent.build( content_type, content_attributes ) unless content_type.nil?
17
- end
18
-
19
- def append_content( content )
20
- @contents.push( content ) unless content.nil?
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
- !@role.nil? && @contents.all?{ | contents | contents.valid? }
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}" )
@@ -2,6 +2,12 @@ module Intelligence
2
2
  module MessageContent
3
3
 
4
4
  class Binary < Base
5
+
6
+ configuration do
7
+ parameter :content_type, String, required: true
8
+ parameter :bytes, String, required: true
9
+ end
10
+
5
11
  attr_reader :content_type
6
12
  attr_reader :bytes
7
13
 
@@ -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,11 @@ module Intelligence
2
2
  module MessageContent
3
3
 
4
4
  class Text < Base
5
+
6
+ configuration do
7
+ parameter :text, String, required: true
8
+ end
9
+
5
10
  attr_reader :text
6
11
 
7
12
  def valid?
@@ -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