intelligence 0.6.0 → 0.8.0

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