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.
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