intelligence 0.6.0 → 0.8.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +576 -0
  3. data/intelligence.gemspec +2 -1
  4. data/lib/intelligence/adapter/base.rb +13 -6
  5. data/lib/intelligence/adapter/class_methods.rb +15 -0
  6. data/lib/intelligence/adapter/module_methods.rb +41 -0
  7. data/lib/intelligence/adapter.rb +2 -2
  8. data/lib/intelligence/adapters/anthropic/adapter.rb +21 -19
  9. data/lib/intelligence/adapters/anthropic/chat_request_methods.rb +189 -0
  10. data/lib/intelligence/adapters/anthropic/{chat_methods.rb → chat_response_methods.rb} +13 -137
  11. data/lib/intelligence/adapters/cerebras.rb +19 -19
  12. data/lib/intelligence/adapters/generic/adapter.rb +4 -2
  13. data/lib/intelligence/adapters/generic/chat_request_methods.rb +221 -0
  14. data/lib/intelligence/adapters/generic/chat_response_methods.rb +234 -0
  15. data/lib/intelligence/adapters/generic.rb +1 -1
  16. data/lib/intelligence/adapters/google/adapter.rb +33 -22
  17. data/lib/intelligence/adapters/google/chat_request_methods.rb +234 -0
  18. data/lib/intelligence/adapters/google/chat_response_methods.rb +236 -0
  19. data/lib/intelligence/adapters/groq.rb +29 -49
  20. data/lib/intelligence/adapters/hyperbolic.rb +13 -39
  21. data/lib/intelligence/adapters/mistral.rb +21 -42
  22. data/lib/intelligence/adapters/open_ai/adapter.rb +39 -32
  23. data/lib/intelligence/adapters/open_ai/chat_request_methods.rb +186 -0
  24. data/lib/intelligence/adapters/open_ai/chat_response_methods.rb +239 -0
  25. data/lib/intelligence/adapters/open_ai.rb +1 -1
  26. data/lib/intelligence/adapters/open_router.rb +18 -18
  27. data/lib/intelligence/adapters/samba_nova.rb +16 -18
  28. data/lib/intelligence/adapters/together_ai.rb +25 -23
  29. data/lib/intelligence/conversation.rb +11 -10
  30. data/lib/intelligence/message.rb +45 -29
  31. data/lib/intelligence/message_content/base.rb +2 -9
  32. data/lib/intelligence/message_content/binary.rb +3 -3
  33. data/lib/intelligence/message_content/file.rb +3 -3
  34. data/lib/intelligence/message_content/text.rb +10 -2
  35. data/lib/intelligence/message_content/tool_call.rb +61 -5
  36. data/lib/intelligence/message_content/tool_result.rb +11 -6
  37. data/lib/intelligence/tool.rb +139 -0
  38. data/lib/intelligence/version.rb +1 -1
  39. data/lib/intelligence.rb +3 -1
  40. metadata +31 -13
  41. data/lib/intelligence/adapter/class_methods/construction.rb +0 -17
  42. data/lib/intelligence/adapter/module_methods/construction.rb +0 -43
  43. data/lib/intelligence/adapters/generic/chat_methods.rb +0 -355
  44. data/lib/intelligence/adapters/google/chat_methods.rb +0 -393
  45. data/lib/intelligence/adapters/legacy/adapter.rb +0 -11
  46. data/lib/intelligence/adapters/legacy/chat_methods.rb +0 -54
  47. data/lib/intelligence/adapters/open_ai/chat_methods.rb +0 -345
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intelligence
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kristoph Cichocki-Romanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-16 00:00:00.000000000 Z
11
+ date: 2024-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.7'
27
27
  - !ruby/object:Gem::Dependency
28
- name: adaptiveconfiguration
28
+ name: dynamicschema
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.0.beta08
33
+ version: 1.0.0.beta03
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.0.beta08
40
+ version: 1.0.0.beta03
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mime-types
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json-repair
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,31 +122,34 @@ extensions: []
108
122
  extra_rdoc_files: []
109
123
  files:
110
124
  - LICENSE
125
+ - README.md
111
126
  - intelligence.gemspec
112
127
  - lib/intelligence.rb
113
128
  - lib/intelligence/adapter.rb
114
129
  - lib/intelligence/adapter/base.rb
115
- - lib/intelligence/adapter/class_methods/construction.rb
116
- - lib/intelligence/adapter/module_methods/construction.rb
130
+ - lib/intelligence/adapter/class_methods.rb
131
+ - lib/intelligence/adapter/module_methods.rb
117
132
  - lib/intelligence/adapter_error.rb
118
133
  - lib/intelligence/adapters/anthropic.rb
119
134
  - lib/intelligence/adapters/anthropic/adapter.rb
120
- - lib/intelligence/adapters/anthropic/chat_methods.rb
135
+ - lib/intelligence/adapters/anthropic/chat_request_methods.rb
136
+ - lib/intelligence/adapters/anthropic/chat_response_methods.rb
121
137
  - lib/intelligence/adapters/cerebras.rb
122
138
  - lib/intelligence/adapters/generic.rb
123
139
  - lib/intelligence/adapters/generic/adapter.rb
124
- - lib/intelligence/adapters/generic/chat_methods.rb
140
+ - lib/intelligence/adapters/generic/chat_request_methods.rb
141
+ - lib/intelligence/adapters/generic/chat_response_methods.rb
125
142
  - lib/intelligence/adapters/google.rb
126
143
  - lib/intelligence/adapters/google/adapter.rb
127
- - lib/intelligence/adapters/google/chat_methods.rb
144
+ - lib/intelligence/adapters/google/chat_request_methods.rb
145
+ - lib/intelligence/adapters/google/chat_response_methods.rb
128
146
  - lib/intelligence/adapters/groq.rb
129
147
  - lib/intelligence/adapters/hyperbolic.rb
130
- - lib/intelligence/adapters/legacy/adapter.rb
131
- - lib/intelligence/adapters/legacy/chat_methods.rb
132
148
  - lib/intelligence/adapters/mistral.rb
133
149
  - lib/intelligence/adapters/open_ai.rb
134
150
  - lib/intelligence/adapters/open_ai/adapter.rb
135
- - lib/intelligence/adapters/open_ai/chat_methods.rb
151
+ - lib/intelligence/adapters/open_ai/chat_request_methods.rb
152
+ - lib/intelligence/adapters/open_ai/chat_response_methods.rb
136
153
  - lib/intelligence/adapters/open_router.rb
137
154
  - lib/intelligence/adapters/samba_nova.rb
138
155
  - lib/intelligence/adapters/together_ai.rb
@@ -153,6 +170,7 @@ files:
153
170
  - lib/intelligence/message_content/text.rb
154
171
  - lib/intelligence/message_content/tool_call.rb
155
172
  - lib/intelligence/message_content/tool_result.rb
173
+ - lib/intelligence/tool.rb
156
174
  - lib/intelligence/unsupported_content_error.rb
157
175
  - lib/intelligence/version.rb
158
176
  homepage: https://github.com/EndlessInternational/intelligence
@@ -1,17 +0,0 @@
1
- module Intelligence
2
- module Adapter
3
- module ClassMethods
4
- module Construction
5
-
6
- def build( options = nil, &block )
7
- self.new( configuration: self.configure( options, &block ) )
8
- end
9
-
10
- def build!( options = nil, &block )
11
- self.new( configuration: self.configure!( options, &block ) )
12
- end
13
-
14
- end
15
- end
16
- end
17
- end
@@ -1,43 +0,0 @@
1
- module Intelligence
2
- module Adapter
3
- module ModuleMethods
4
- module Construction
5
-
6
- def []( adapter_type )
7
-
8
- raise ArgumentError.new( "An adapter type is required but nil was given." ) \
9
- if adapter_type.nil?
10
-
11
- class_name = adapter_type.to_s.split( '_' ).map( &:capitalize ).join
12
- class_name += "::Adapter"
13
-
14
- adapter_class = Intelligence.const_get( class_name ) rescue nil
15
- if adapter_class.nil?
16
- adapter_file = File.expand_path( "../../../adapters/#{adapter_type}", __FILE__ )
17
- unless require adapter_file
18
- raise ArgumentError.new(
19
- "The Intelligence adapter file #{adapter_file} is missing or does not define #{class_name}."
20
- )
21
- end
22
- adapter_class = Intelligence.const_get( class_name ) rescue nil
23
- end
24
-
25
- raise ArgumentError.new( "An unknown Intelligence adapter #{adapter_type} was requested." ) \
26
- if adapter_class.nil?
27
-
28
- adapter_class
29
-
30
- end
31
-
32
- def build( adapter_type, attributes = nil, &block )
33
- self.[]( adapter_type ).build( attributes, &block )
34
- end
35
-
36
- def build!( adapter_type, attributes = nil, &block )
37
- self.[]( adapter_type ).build!( attributes, &block )
38
- end
39
-
40
- end
41
- end
42
- end
43
- end
@@ -1,355 +0,0 @@
1
- module Intelligence
2
- module Generic
3
- module ChatMethods
4
-
5
- module ClassMethods
6
- def chat_request_uri( uri = nil )
7
- if uri
8
- @chat_request_uri = uri
9
- else
10
- @chat_request_uri
11
- end
12
- end
13
- end
14
-
15
- def self.included( base )
16
- base.extend( ClassMethods )
17
- end
18
-
19
- def chat_request_uri( options )
20
- self.class.chat_request_uri
21
- end
22
-
23
- def chat_request_headers( options = nil )
24
- options = options ? self.class.configure( options ) : {}
25
- options = @options.merge( options )
26
- result = {}
27
-
28
- key = options[ :key ]
29
-
30
- raise ArgumentError.new( "An API key is required to build a chat request." ) \
31
- if key.nil?
32
-
33
- result[ 'Content-Type' ] = 'application/json'
34
- result[ 'Authorization' ] = "Bearer #{key}"
35
-
36
- result
37
- end
38
-
39
- def chat_request_body( conversation, options = nil )
40
- options = options ? self.class.configure( options ) : {}
41
- options = @options.merge( options )
42
-
43
- result = options[ :chat_options ]
44
- result[ :messages ] = []
45
-
46
- system_message = system_message_to_s( conversation[ :system_message ] )
47
- result[ :messages ] << { role: 'system', content: system_message } if system_message
48
-
49
- conversation[ :messages ]&.each do | message |
50
- result[ :messages ] << chat_request_message_attributes( message )
51
- end
52
-
53
- JSON.generate( result )
54
- end
55
-
56
- def chat_request_message_attributes( message )
57
- result_message = { role: message[ :role ] }
58
- result_message_content = []
59
- message[ :contents ]&.each do | content |
60
- case content[ :type ]
61
- when :text
62
- result_message_content << { type: 'text', text: content[ :text ] }
63
- when :binary
64
- content_type = content[ :content_type ]
65
- bytes = content[ :bytes ]
66
- if content_type && bytes
67
- mime_type = MIME::Types[ content_type ].first
68
- if mime_type&.media_type == 'image'
69
- result_message_content << {
70
- type: 'image_url',
71
- image_url: {
72
- url: "data:#{content_type};base64,#{Base64.strict_encode64( bytes )}".freeze
73
- }
74
- }
75
- else
76
- raise UnsupportedContentError.new(
77
- :generic,
78
- 'only support content of type image/*'
79
- )
80
- end
81
- else
82
- raise UnsupportedContentError.new(
83
- :generic,
84
- 'requires binary content to include content type and ( packed ) bytes'
85
- )
86
- end
87
- when :file
88
- content_type = content[ :content_type ]
89
- uri = content[ :uri ]
90
- if content_type && uri
91
- mime_type = MIME::Types[ content_type ].first
92
- if mime_type&.media_type == 'image'
93
- result_message_content << {
94
- type: 'image_url',
95
- image_url: { url: uri }
96
- }
97
- else
98
- raise UnsupportedContentError.new(
99
- :generic,
100
- 'only support content of type image/*'
101
- )
102
- end
103
- else
104
- raise UnsupportedContentError.new(
105
- :generic,
106
- 'requires binary content to include content type and ( packed ) bytes'
107
- )
108
- end
109
- end
110
- end
111
- result_message[ :content ] = result_message_content
112
- result_message
113
- end
114
-
115
- def chat_result_attributes( response )
116
-
117
- return nil unless response.success?
118
- response_json = JSON.parse( response.body, symbolize_names: true ) rescue nil
119
- return nil if response_json.nil? || response_json[ :choices ].nil?
120
-
121
- result = {}
122
- result[ :choices ] = []
123
-
124
- ( response_json[ :choices ] || [] ).each do | json_choice |
125
- json_message = json_choice[ :message ]
126
- result[ :choices ].push(
127
- {
128
- end_reason: translate_end_result( json_choice[ :finish_reason ] ),
129
- message: {
130
- role: json_message[ :role ],
131
- contents: [ { type: 'text', text: json_message[ :content ] } ]
132
- }
133
- }
134
- )
135
- end
136
-
137
- metrics_json = response_json[ :usage ]
138
- unless metrics_json.nil?
139
-
140
- metrics = {}
141
- metrics[ :input_tokens ] = metrics_json[ :prompt_tokens ]
142
- metrics[ :output_tokens ] = metrics_json[ :completion_tokens ]
143
- metrics = metrics.compact
144
-
145
- result[ :metrics ] = metrics unless metrics.empty?
146
-
147
- end
148
-
149
- result
150
-
151
- end
152
-
153
- def chat_result_error_attributes( response )
154
-
155
- error_type, error_description = translate_error_response_status( response.status )
156
- result = {
157
- error_type: error_type.to_s,
158
- error_description: error_description
159
- }
160
-
161
- parsed_body = JSON.parse( response.body, symbolize_names: true ) rescue nil
162
- if parsed_body && parsed_body.respond_to?( :include? ) && parsed_body.include?( :error )
163
- result = {
164
- error_type: error_type.to_s,
165
- error: parsed_body[ :error ][ :code ] || error_type.to_s,
166
- error_description: parsed_body[ :error ][ :message ] || error_description
167
- }
168
- end
169
-
170
- result
171
-
172
- end
173
-
174
- def stream_result_chunk_attributes( context, chunk )
175
-
176
- context ||= {}
177
- buffer = context[ :buffer ] || ''
178
- metrics = context[ :metrics ] || {
179
- input_tokens: 0,
180
- output_tokens: 0
181
- }
182
- choices = context[ :choices ] || Array.new( 1 , { message: {} } )
183
-
184
- choices.each do | choice |
185
- choice[ :message ][ :contents ] = choice[ :message ][ :contents ]&.map do | content |
186
- case content[ :type ]
187
- when :text
188
- content[ :text ] = ''
189
- when :tool_call
190
- content[ :tool_parameters ] = ''
191
- else
192
- content.clear
193
- end
194
- content
195
- end
196
- end
197
-
198
- buffer += chunk
199
- while ( eol_index = buffer.index( "\n" ) )
200
-
201
- line = buffer.slice!( 0..eol_index )
202
- line = line.strip
203
- next if line.empty? || !line.start_with?( 'data:' )
204
- line = line[ 6..-1 ]
205
-
206
- next if line.end_with?( '[DONE]' )
207
- data = JSON.parse( line )
208
- if data.is_a?( Hash )
209
-
210
- data[ 'choices' ]&.each do | data_choice |
211
-
212
- data_choice_index = data_choice[ 'index' ]
213
- data_choice_delta = data_choice[ 'delta' ]
214
- data_choice_finish_reason = data_choice[ 'finish_reason' ]
215
-
216
- choices.fill( { message: {} }, choices.size, data_choice_index + 1 ) \
217
- if choices.size <= data_choice_index
218
- contents = choices[ data_choice_index ][ :message ][ :contents ] || []
219
- last_content = contents&.last
220
-
221
- if data_choice_delta.include?( 'content' )
222
- data_choice_content = data_choice_delta[ 'content' ] || ''
223
- if last_content.nil? || last_content[ :type ] == :tool_call
224
- contents.push( { type: :text, text: data_choice_content } )
225
- elsif last_content[ :type ] == :text || last_content[ :type ].nil?
226
- last_content[ :type ] = :text
227
- last_content[ :text ] = ( last_content[ :text ] || '' ) + data_choice_content
228
- end
229
- elsif data_choice_delta.include?( 'function_call' )
230
- data_choice_tool_call = data_choice_delta[ 'function_call' ]
231
- data_choice_tool_name = data_choice_tool_call[ 'name' ]
232
- data_choice_tool_parameters = data_choice_tool_call[ 'arguments' ]
233
- if last_content.nil? || last_content[ :type ] == :text
234
- contents.push( {
235
- type: :tool_call,
236
- tool_name: data_choice_tool_name,
237
- tool_parameters: data_choice_tool_parameters
238
- } )
239
- elsif last_content[ :type ].nil?
240
- last_content[ :type ] = :tool_call
241
- last_content[ :tool_name ] = data_choice_tool_name \
242
- if data_choice_tool_name.present?
243
- last_content[ :tool_parameters ] = tool_parameters
244
- elsif last_content[ :type ] == :tool_call
245
- last_content[ :tool_parameters ] =
246
- ( last_content[ :tool_parameters ] || '' ) + data_choice_tool_parameters
247
- end
248
- end
249
- choices[ data_choice_index ][ :message ][ :contents ] = contents
250
- choices[ data_choice_index ][ :end_reason ] ||=
251
- translate_end_result( data_choice_finish_reason )
252
- end
253
-
254
- if usage = data[ 'usage' ]
255
- # note: A number of providers will resend the input tokens as part of their usage
256
- # payload.
257
- metrics[ :input_tokens ] = usage[ 'prompt_tokens' ] \
258
- if usage.include?( 'prompt_tokens' )
259
- metrics[ :output_tokens ] += usage[ 'completion_tokens' ] \
260
- if usage.include?( 'completion_tokens' )
261
- end
262
-
263
- end
264
-
265
- end
266
-
267
- context[ :buffer ] = buffer
268
- context[ :metrics ] = metrics
269
- context[ :choices ] = choices
270
-
271
- [ context, choices.empty? ? nil : { choices: choices.dup } ]
272
-
273
- end
274
-
275
- def stream_result_attributes( context )
276
-
277
- choices = context[ :choices ]
278
- metrics = context[ :metrics ]
279
-
280
- choices = choices.map do | choice |
281
- { end_reason: choice[ :end_reason ] }
282
- end
283
-
284
- { choices: choices, metrics: context[ :metrics ] }
285
-
286
- end
287
-
288
- alias_method :stream_result_error_attributes, :chat_result_error_attributes
289
-
290
- def translate_end_result( end_result )
291
- case end_result
292
- when 'stop'
293
- :ended
294
- when 'length'
295
- :token_limit_exceeded
296
- when 'function_call'
297
- :tool_called
298
- when 'content_filter'
299
- :filtered
300
- else
301
- nil
302
- end
303
- end
304
-
305
- def translate_error_response_status( status )
306
- case status
307
- when 400
308
- [ :invalid_request_error,
309
- "There was an issue with the format or content of your request." ]
310
- when 401
311
- [ :authentication_error,
312
- "There's an issue with your API key." ]
313
- when 403
314
- [ :permission_error,
315
- "Your API key does not have permission to use the specified resource." ]
316
- when 404
317
- [ :not_found_error,
318
- "The requested resource was not found." ]
319
- when 413
320
- [ :request_too_large,
321
- "Request exceeds the maximum allowed number of bytes." ]
322
- when 422
323
- [ :invalid_request_error,
324
- "There was an issue with the format or content of your request." ]
325
- when 429
326
- [ :rate_limit_error,
327
- "Your account has hit a rate limit." ]
328
- when 500, 502, 503
329
- [ :api_error,
330
- "An unexpected error has occurred internal to the providers systems." ]
331
- when 529
332
- [ :overloaded_error,
333
- "The providers server is temporarily overloaded." ]
334
- else
335
- [ :unknown_error, "
336
- An unknown error occurred." ]
337
- end
338
- end
339
-
340
- private; def system_message_to_s( system_message )
341
-
342
- return nil if system_message.nil?
343
-
344
- result = ''
345
- system_message[ :contents ].each do | content |
346
- result += content[ :text ] if content[ :type ] == :text
347
- end
348
-
349
- result.empty? ? nil : result
350
-
351
- end
352
-
353
- end
354
- end
355
- end