intelligence 0.5.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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/intelligence.gemspec +47 -0
  4. data/lib/intelligence/adapter/base.rb +7 -0
  5. data/lib/intelligence/adapter/construction_methods.rb +37 -0
  6. data/lib/intelligence/adapter.rb +8 -0
  7. data/lib/intelligence/adapter_error.rb +8 -0
  8. data/lib/intelligence/adapters/anthropic/adapter.rb +59 -0
  9. data/lib/intelligence/adapters/anthropic/chat_methods.rb +358 -0
  10. data/lib/intelligence/adapters/anthropic.rb +2 -0
  11. data/lib/intelligence/adapters/cerebras.rb +35 -0
  12. data/lib/intelligence/adapters/generic/adapter.rb +21 -0
  13. data/lib/intelligence/adapters/generic/chat_methods.rb +331 -0
  14. data/lib/intelligence/adapters/generic.rb +2 -0
  15. data/lib/intelligence/adapters/google/adapter.rb +60 -0
  16. data/lib/intelligence/adapters/google/chat_methods.rb +346 -0
  17. data/lib/intelligence/adapters/google.rb +2 -0
  18. data/lib/intelligence/adapters/groq.rb +51 -0
  19. data/lib/intelligence/adapters/hyperbolic.rb +55 -0
  20. data/lib/intelligence/adapters/legacy/adapter.rb +13 -0
  21. data/lib/intelligence/adapters/legacy/chat_methods.rb +37 -0
  22. data/lib/intelligence/adapters/open_ai/adapter.rb +75 -0
  23. data/lib/intelligence/adapters/open_ai/chat_methods.rb +314 -0
  24. data/lib/intelligence/adapters/open_ai.rb +2 -0
  25. data/lib/intelligence/adapters/samba_nova.rb +64 -0
  26. data/lib/intelligence/adapters/together_ai.rb +46 -0
  27. data/lib/intelligence/chat_error_result.rb +11 -0
  28. data/lib/intelligence/chat_metrics.rb +32 -0
  29. data/lib/intelligence/chat_request.rb +117 -0
  30. data/lib/intelligence/chat_result.rb +32 -0
  31. data/lib/intelligence/chat_result_choice.rb +26 -0
  32. data/lib/intelligence/conversation.rb +48 -0
  33. data/lib/intelligence/error.rb +3 -0
  34. data/lib/intelligence/error_result.rb +24 -0
  35. data/lib/intelligence/invalid_content_error.rb +3 -0
  36. data/lib/intelligence/message.rb +53 -0
  37. data/lib/intelligence/message_content/base.rb +18 -0
  38. data/lib/intelligence/message_content/binary.rb +24 -0
  39. data/lib/intelligence/message_content/text.rb +17 -0
  40. data/lib/intelligence/message_content/tool_call.rb +20 -0
  41. data/lib/intelligence/message_content/tool_result.rb +20 -0
  42. data/lib/intelligence/message_content.rb +16 -0
  43. data/lib/intelligence/unsupported_content_error.rb +3 -0
  44. data/lib/intelligence/version.rb +3 -0
  45. data/lib/intelligence.rb +24 -0
  46. metadata +181 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 694776aeafc7552879d8b9d5358907e62c90a6fc5a1219081ee1d1a4cb5ddeef
4
+ data.tar.gz: 380f5cd13200750f65938fd4e92c21218af8c2b57af0614bfe16f714ba296e68
5
+ SHA512:
6
+ metadata.gz: f2ca65b2a4e3a202bc1b331f7965da9e76a8ccd2ec83f7c5c273c94137fb1e0131226ecf3bf2aed6d50e262933d26c0024b3a5052edda28c39768c766b508e29
7
+ data.tar.gz: c6694ffa62ddd9eb766f42fb9cc073195a01e11e0d34cd346588b3923266c14f794d1711e1766888369874e27ce4ac696de0513055af8e27149a778611ce0cb2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Endless International
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,47 @@
1
+ require_relative 'lib/intelligence/version'
2
+
3
+ Gem::Specification.new do | spec |
4
+
5
+ spec.name = 'intelligence'
6
+ spec.version = Intelligence::VERSION
7
+ spec.authors = [ 'Kristoph Cichocki-Romanov' ]
8
+ spec.email = [ 'rubygems.org@kristoph.net' ]
9
+
10
+ spec.summary = <<~TEXT.gsub( "\n", " " ).strip
11
+ A Ruby gem for seamlessly and uniformly interacting with large languge and vision model (LLM)
12
+ API's served by numerous services, including those of OpenAI, Anthropic, Google and others.
13
+ TEXT
14
+ spec.description = <<~TEXT.gsub( "\n", " " ).strip
15
+ Intelligence is a lightweight yet powerful Ruby gem that allows you to seamlessly and uniformly
16
+ interact with large language and vision models (LLM) API's of numerous vendors, including
17
+ OpenAI, Anthropic, Google, Cerebras, Groq, Hyperbolic, Samba Nova and Together AI. It can be
18
+ trivially expanded to other OpenAI conformant API providers as well as self hosted models.
19
+
20
+ Intelligence supports text models in streaming and non-streaming mode, vision models, and
21
+ tool use.
22
+
23
+ Intelligence has minimal dependencies and does not require the vendors ( often bloated )
24
+ SDK's.
25
+ TEXT
26
+
27
+ spec.license = 'MIT'
28
+ spec.homepage = 'https://github.com/EndlessInternational/intelligence'
29
+ spec.metadata = {
30
+ 'source_code_uri' => 'https://github.com/EndlessInternational/intelligence',
31
+ 'bug_tracker_uri' => 'https://github.com/EndlessInternational/intelligence/issues',
32
+ # 'documentation_uri' => 'https://github.com/EndlessInternational/intelligence/wiki'
33
+ }
34
+
35
+ spec.required_ruby_version = '>= 3.0'
36
+ spec.files = Dir[ "lib/**/*.rb", "LICENSE", "README.md", "intelligence.gemspec" ]
37
+ spec.require_paths = [ "lib" ]
38
+
39
+ spec.add_runtime_dependency 'faraday', '~> 2.7'
40
+ spec.add_runtime_dependency 'adaptiveconfiguration', '~> 1.0.0.beta01'
41
+ spec.add_runtime_dependency 'mime-types', '~> 3.6'
42
+
43
+ spec.add_development_dependency 'rspec', '~> 3.4'
44
+ spec.add_development_dependency 'debug', '~> 1.9'
45
+ spec.add_development_dependency 'vcr', '~> 6.3'
46
+
47
+ end
@@ -0,0 +1,7 @@
1
+ module Intelligence
2
+ module Adapter
3
+ class Base
4
+ extend AdaptiveConfiguration::Configurable
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ module Intelligence
2
+ module Adapter
3
+ module ConstructionMethods
4
+
5
+ def []( adapter_type )
6
+
7
+ raise ArgumentError.new( "An adapter type is required but nil was given." ) \
8
+ if adapter_type.nil?
9
+
10
+ class_name = adapter_type.to_s.split( '_' ).map( &:capitalize ).join
11
+ class_name += "::Adapter"
12
+
13
+ adapter_class = Intelligence.const_get( class_name ) rescue nil
14
+ if adapter_class.nil?
15
+ adapter_file = File.expand_path( "../../adapters/#{adapter_type}", __FILE__ )
16
+ unless require adapter_file
17
+ raise ArgumentError.new(
18
+ "The Intelligence adapter file #{adapter_file} is missing or does not define #{class_name}."
19
+ )
20
+ end
21
+ adapter_class = Intelligence.const_get( class_name ) rescue nil
22
+ end
23
+
24
+ raise ArgumentError.new( "An unknown Intelligence adapter #{adapter_type} was configured." ) \
25
+ if adapter_class.nil?
26
+
27
+ adapter_class
28
+
29
+ end
30
+
31
+ def build( adapter_type, attributes, &block )
32
+ self.[]( adapter_type ).new( attributes, &block )
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'adapter/construction_methods'
2
+ require_relative 'adapter/base'
3
+
4
+ module Intelligence
5
+ module Adapter
6
+ extend ConstructionMethods
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Intelligence
2
+ class AdapterError < Error;
3
+ def initialize( adapter_type, text )
4
+ adapter_class_name = adapter_type.to_s.split( '_' ).map( &:capitalize ).join
5
+ super( "The #{adapter_class_name} #{text}." )
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'chat_methods'
2
+
3
+ module Intelligence
4
+ module Anthropic
5
+ class Adapter < Adapter::Base
6
+
7
+ configuration do
8
+
9
+ # normalized properties for all endpoints
10
+ parameter :key, required: true
11
+
12
+ # anthropic specific properties for all endpoints
13
+ parameter :version, String, required: true, default: '2023-06-01'
14
+
15
+ group :chat_options do
16
+
17
+ # normalized properties for anthropic generative text endpoint
18
+ parameter :model, String, required: true
19
+ parameter :max_tokens, Integer, required: true
20
+ parameter :temperature, Float
21
+ parameter :top_k, Integer
22
+ parameter :top_p, Float
23
+ parameter :stop, String, array: true, as: :stop_sequences
24
+ parameter :stream, [ TrueClass, FalseClass ]
25
+
26
+ # anthropic variant of normalized properties for anthropic generative text endpoints
27
+ parameter :stop_sequences, String, array: true
28
+
29
+ # anthropic specific properties for anthropic generative text endpoints
30
+ parameter :tool_choice do
31
+ parameter :type, String
32
+ # the name parameter should only be set if type = 'tool'
33
+ parameter :name, String
34
+ end
35
+ group :metadata do
36
+ parameter :user_id, String
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ attr_reader :key
44
+ attr_reader :version
45
+ attr_reader :chat_options
46
+
47
+ def initialize( attributes = nil, &block )
48
+ configuration = self.class.configure( attributes, &block )
49
+ @key = configuration[ :key ]
50
+ @version = configuration[ :version ]
51
+ @chat_options = configuration[ :chat_options ] || {}
52
+ end
53
+
54
+ include ChatMethods
55
+
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,358 @@
1
+ require 'base64'
2
+
3
+ module Intelligence
4
+ module Anthropic
5
+ module ChatMethods
6
+
7
+ CHAT_REQUEST_URI = "https://api.anthropic.com/v1/messages"
8
+
9
+ def chat_request_uri( options )
10
+ CHAT_REQUEST_URI
11
+ end
12
+
13
+ def chat_request_headers( options = {} )
14
+ result = {}
15
+
16
+ key = options[ :key ] || self.key
17
+ version = options[ :version ] || self.version || "2023-06-01"
18
+
19
+ raise ArgumentError.new(
20
+ "An Anthropic key is required to build an Anthropic chat request."
21
+ ) if key.nil?
22
+
23
+ result[ 'content-type' ] = 'application/json'
24
+ result[ 'x-api-key' ] = "#{key}"
25
+ result[ 'anthropic-version' ] = version unless version.nil?
26
+
27
+ result
28
+ end
29
+
30
+ def chat_request_body( conversation, options = {} )
31
+ options = self.class.configure( options ) unless options.empty?
32
+ result = self.chat_options.merge( options ).compact
33
+
34
+ system_message = translate_system_message( conversation[ :system_message ] )
35
+ result[ :system ] = system_message unless system_message.nil?
36
+ result[ :messages ] = []
37
+
38
+ messages = conversation[ :messages ]
39
+ length = messages&.length || 0
40
+ index = 0; while index < length
41
+
42
+ message = messages[ index ]
43
+ unless message.nil?
44
+
45
+ # The Anthropic API will not accept a sequence of messages where the role of two
46
+ # sequentian messages is the same.
47
+ #
48
+ # The purpose of this code is to identify such occurences and coalece them such
49
+ # that the first message in the sequence aggregates the contents of all subsequent
50
+ # messages with the same role.
51
+ look_ahead_index = index + 1; while look_ahead_index < length
52
+ ahead_message = messages[ look_ahead_index ]
53
+ unless ahead_message.nil?
54
+ if ahead_message[ :role ] == message[ :role ]
55
+ message[ :contents ] =
56
+ ( message[ :contents ] || [] ) +
57
+ ( ahead_message[ :contents ] || [] )
58
+ messages[ look_ahead_index ] = nil
59
+ look_ahead_index += 1
60
+ else
61
+ break
62
+ end
63
+ end
64
+ end
65
+
66
+ result_message = { role: message[ :role ] }
67
+ result_message_content = []
68
+
69
+ message[ :contents ]&.each do | content |
70
+ case content[ :type ]
71
+ when :text
72
+ result_message_content << { type: 'text', text: content[ :text ] }
73
+ when :binary
74
+ content_type = content[ :content_type ]
75
+ bytes = content[ :bytes ]
76
+ if content_type && bytes
77
+ mime_type = MIME::Types[ content_type ].first
78
+ if mime_type&.media_type == 'image'
79
+ result_message_content << {
80
+ type: 'image',
81
+ source: {
82
+ type: 'base64',
83
+ media_type: content_type,
84
+ data: Base64.strict_encode64( bytes )
85
+ }
86
+ }
87
+ else
88
+ raise UnsupportedContentError.new(
89
+ :anthropic,
90
+ 'only support content of type image/*'
91
+ )
92
+ end
93
+ else
94
+ raise InvalidContentError.new( :anthropic )
95
+ end
96
+ end
97
+ end
98
+
99
+ result_message[ :content ] = result_message_content
100
+ result[ :messages ] << result_message
101
+
102
+ end
103
+
104
+ index += 1
105
+
106
+ end
107
+
108
+ JSON.generate( result )
109
+ end
110
+
111
+ def chat_result_attributes( response )
112
+ return nil unless response.success?
113
+
114
+ response_json = JSON.parse( response.body, symbolize_names: true ) rescue nil
115
+ return nil if response_json.nil?
116
+
117
+ result = {}
118
+
119
+ result_choice = {
120
+ end_reason: translate_end_result( response_json[ :stop_reason ] ),
121
+ end_sequence: response_json[ :stop_sequence ],
122
+ }
123
+
124
+ if response_json[ :content ] &&
125
+ response_json[ :content ].is_a?( Array ) &&
126
+ !response_json[ :content ].empty?
127
+
128
+ result_content = []
129
+ response_json[ :content ].each do | content |
130
+ if content[ :type ] == 'text'
131
+ result_content.push( { type: 'text', text: content[ :text ] } )
132
+ end
133
+ end
134
+
135
+ unless result_content.empty?
136
+ result_choice[ :message ] = {
137
+ role: response_json[ :role ] || 'assistant',
138
+ contents: result_content
139
+ }
140
+ end
141
+
142
+ end
143
+
144
+ result[ :choices ] = [ result_choice ]
145
+
146
+ metrics_json = response_json[ :usage ]
147
+ unless metrics_json.nil?
148
+
149
+ result_metrics = {}
150
+ result_metrics[ :input_tokens ] = metrics_json[ :input_tokens ]
151
+ result_metrics[ :output_tokens ] = metrics_json[ :output_tokens ]
152
+ result_metrics = result_metrics.compact
153
+
154
+ result[ :metrics ] = result_metrics unless result_metrics.empty?
155
+
156
+ end
157
+
158
+ result
159
+ end
160
+
161
+ def chat_result_error_attributes( response )
162
+ error_type, error_description = translate_response_status( response.status )
163
+ parsed_body = JSON.parse( response.body, symbolize_names: true ) rescue nil
164
+ if parsed_body && parsed_body.respond_to?( :include? ) && parsed_body.include?( :error )
165
+ result = {
166
+ error_type: error_type.to_s,
167
+ error: parsed_body[ :error ][ :type ],
168
+ error_description: parsed_body[ :error ][ :message ] || error_description
169
+ }
170
+ elsif
171
+ result = {
172
+ error_type: error_type.to_s,
173
+ error_description: error_description
174
+ }
175
+ end
176
+ result
177
+ end
178
+
179
+ def stream_result_chunk_attributes( context, chunk )
180
+ context ||= {}
181
+
182
+ buffer = context[ :buffer ] || ''
183
+ contents = context[ :contents ] || []
184
+ end_reason = context[ :end_reason ]
185
+ end_sequence = context[ :end_sequence ]
186
+ metrics = context[ :metrics ] || {
187
+ input_tokens: 0,
188
+ output_tokens: 0
189
+ }
190
+
191
+ contents.each do | content |
192
+ case content[ :type ]
193
+ when :text
194
+ content[ :text ] = ''
195
+ when :tool_call
196
+ content[ :tool_parameters ] = ''
197
+ else
198
+ content.clear
199
+ end
200
+ end
201
+
202
+ buffer += chunk
203
+ while ( eol_index = buffer.index( "\n" ) )
204
+
205
+ line = buffer.slice!( 0..eol_index )
206
+ line = line.strip
207
+ next if line.empty? || !line.start_with?( 'data:' )
208
+
209
+ line = line[ 6..-1 ]
210
+ data = JSON.parse( line )
211
+
212
+ case data[ 'type' ]
213
+ when 'message_start'
214
+ metrics[ :input_tokens ] += data[ 'message' ]&.[]( 'usage' )&.[]( 'input_tokens' ) || 0
215
+ metrics[ :output_tokens ] += data[ 'message' ]&.[]( 'usage' )&.[]( 'output_tokens' ) || 0
216
+ when 'content_block_start'
217
+ index = data[ 'index' ]
218
+ contents.fill( {}, contents.size, index + 1 ) if contents.size <= index
219
+ if content_block = data[ 'content_block' ]
220
+ if content_block[ 'type' ] == 'text'
221
+ contents[ index ] = {
222
+ type: :text,
223
+ text: content_block[ 'text' ] || ''
224
+ }
225
+ elsif content_block[ 'type' ] == 'tool_use'
226
+ contents[ index ] = {
227
+ type: :tool_call,
228
+ tool_name: content_block[ 'name' ],
229
+ tool_call_id: content_block[ 'id' ],
230
+ tool_parameters: ''
231
+ }
232
+ end
233
+ end
234
+ when 'content_block_delta'
235
+ index = data[ 'index' ]
236
+ contents.fill( {}, contents.size, index + 1 ) if contents.size <= index
237
+ if delta = data[ 'delta' ]
238
+ if delta[ 'type' ] == 'text_delta'
239
+ contents[ index ][ :type ] = :text
240
+ contents[ index ][ :text ] = ( contents[ index ][ :text ] || '' ) + delta[ 'text' ]
241
+ elsif delta[ 'type' ] == 'input_json_delta'
242
+ contents[ index ][ :type ] = :tool_call
243
+ contents[ index ][ :tool_parameters ] =
244
+ ( contents[ index ][ :tool_parameters ] || '' ) + delta[ 'input_json_delta' ]
245
+ end
246
+ end
247
+ when 'message_delta'
248
+ if delta = data[ 'delta' ]
249
+ end_reason = delta[ 'stop_reason' ]
250
+ end_sequence = delta[ 'stop_sequence' ]
251
+ end
252
+ metrics[ :output_tokens ] += data[ 'usage' ]&.[]( 'output_tokens' ) || 0
253
+ when 'message_stop'
254
+
255
+ end
256
+ end
257
+
258
+ context = {
259
+ buffer: buffer,
260
+ contents: contents,
261
+ end_reason: end_reason,
262
+ end_sequence: end_sequence,
263
+ metrics: metrics
264
+ }
265
+ choices = [ {
266
+ end_reason: translate_end_result( end_reason ),
267
+ end_sequence: end_sequence,
268
+ message: {
269
+ contents: contents.dup
270
+ }
271
+ }
272
+ ]
273
+
274
+ [ context, ( choices.empty? ? nil : { choices: choices } ) ]
275
+ end
276
+
277
+ def stream_result_attributes( context )
278
+ {
279
+ choices: [ {
280
+ end_reason: translate_end_result( context[ :end_reason ] ),
281
+ end_sequence: context[ :end_sequence ],
282
+ } ],
283
+ metrics: context[ :metrics ]
284
+ }
285
+ end
286
+
287
+ alias_method :stream_result_error_attributes, :chat_result_error_attributes
288
+
289
+ private
290
+
291
+ def translate_system_message( system_message )
292
+
293
+ return nil if system_message.nil?
294
+
295
+ # note: the current version of the anthropic api simply takes a string as the
296
+ # system message but the beta version requires an array of objects akin
297
+ # to message contents.
298
+
299
+ result = ''
300
+ system_message[ :contents ].each do | content |
301
+ result += content[ :text ] if content[ :type ] == :text
302
+ end
303
+
304
+ result.empty? ? nil : result
305
+
306
+ end
307
+
308
+ def translate_end_result( end_result )
309
+ case end_result
310
+ when 'end_turn'
311
+ :ended
312
+ when 'max_tokens'
313
+ :token_limit_exceeded
314
+ when 'stop_sequence'
315
+ :end_sequence_encountered
316
+ when 'tool_use'
317
+ :tool_called
318
+ else
319
+ # if the result has already been translated, this simply returns it
320
+ end_result
321
+ end
322
+ end
323
+
324
+ def translate_response_status( status )
325
+ case status
326
+ when 400
327
+ [ :invalid_request_error,
328
+ "There was an issue with the format or content of your request." ]
329
+ when 401
330
+ [ :authentication_error,
331
+ "There's an issue with your API key." ]
332
+ when 403
333
+ [ :permission_error,
334
+ "Your API key does not have permission to use the specified resource." ]
335
+ when 404
336
+ [ :not_found_error,
337
+ "The requested resource was not found." ]
338
+ when 413
339
+ [ :request_too_large,
340
+ "Request exceeds the maximum allowed number of bytes." ]
341
+ when 429
342
+ [ :rate_limit_error,
343
+ "Your account has hit a rate limit." ]
344
+ when 500
345
+ [ :api_error,
346
+ "An unexpected error has occurred internal to Anthropic's systems." ]
347
+ when 529
348
+ [ :overloaded_error,
349
+ "Anthropic's API is temporarily overloaded." ]
350
+ else
351
+ [ :unknown_error, "
352
+ An unknown error occurred." ]
353
+ end
354
+ end
355
+
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,2 @@
1
+ require_relative '../adapter'
2
+ require_relative 'anthropic/adapter'
@@ -0,0 +1,35 @@
1
+ require_relative 'legacy/adapter'
2
+
3
+ module Intelligence
4
+ module Cerebras
5
+
6
+ class Adapter < Legacy::Adapter
7
+
8
+ chat_request_uri "https://api.cerebras.ai/v1/chat/completions"
9
+
10
+ configuration do
11
+ parameter :key, required: true
12
+ group :chat_options do
13
+ parameter :model, String, required: true
14
+ parameter :max_tokens, Integer, required: true
15
+ parameter :response_format do
16
+ parameter :type, String, default: 'json_schema'
17
+ parameter :json_schema
18
+ end
19
+ parameter :seed, Integer
20
+ parameter :stop, array: true
21
+ parameter :stream, [ TrueClass, FalseClass ]
22
+ parameter :temperature, Float
23
+ parameter :top_p, Float
24
+ parameter :tool_choice do
25
+ parameter :type, String
26
+ parameter :mame, String
27
+ end
28
+ parameter :user
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require_relative '../../adapter'
2
+ require_relative 'chat_methods'
3
+
4
+ module Intelligence
5
+ module Generic
6
+ class Adapter < Adapter::Base
7
+
8
+ attr_reader :key
9
+ attr_reader :chat_options
10
+
11
+ def initialize( attributes = nil, &block )
12
+ configuration = self.class.configure( attributes, &block )
13
+ @key = configuration[ :key ]
14
+ @chat_options = configuration[ :chat_options ] || {}
15
+ end
16
+
17
+ include ChatMethods
18
+
19
+ end
20
+ end
21
+ end