intelligence 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/intelligence.gemspec +47 -0
- data/lib/intelligence/adapter/base.rb +7 -0
- data/lib/intelligence/adapter/construction_methods.rb +37 -0
- data/lib/intelligence/adapter.rb +8 -0
- data/lib/intelligence/adapter_error.rb +8 -0
- data/lib/intelligence/adapters/anthropic/adapter.rb +59 -0
- data/lib/intelligence/adapters/anthropic/chat_methods.rb +358 -0
- data/lib/intelligence/adapters/anthropic.rb +2 -0
- data/lib/intelligence/adapters/cerebras.rb +35 -0
- data/lib/intelligence/adapters/generic/adapter.rb +21 -0
- data/lib/intelligence/adapters/generic/chat_methods.rb +331 -0
- data/lib/intelligence/adapters/generic.rb +2 -0
- data/lib/intelligence/adapters/google/adapter.rb +60 -0
- data/lib/intelligence/adapters/google/chat_methods.rb +346 -0
- data/lib/intelligence/adapters/google.rb +2 -0
- data/lib/intelligence/adapters/groq.rb +51 -0
- data/lib/intelligence/adapters/hyperbolic.rb +55 -0
- data/lib/intelligence/adapters/legacy/adapter.rb +13 -0
- data/lib/intelligence/adapters/legacy/chat_methods.rb +37 -0
- data/lib/intelligence/adapters/open_ai/adapter.rb +75 -0
- data/lib/intelligence/adapters/open_ai/chat_methods.rb +314 -0
- data/lib/intelligence/adapters/open_ai.rb +2 -0
- data/lib/intelligence/adapters/samba_nova.rb +64 -0
- data/lib/intelligence/adapters/together_ai.rb +46 -0
- data/lib/intelligence/chat_error_result.rb +11 -0
- data/lib/intelligence/chat_metrics.rb +32 -0
- data/lib/intelligence/chat_request.rb +117 -0
- data/lib/intelligence/chat_result.rb +32 -0
- data/lib/intelligence/chat_result_choice.rb +26 -0
- data/lib/intelligence/conversation.rb +48 -0
- data/lib/intelligence/error.rb +3 -0
- data/lib/intelligence/error_result.rb +24 -0
- data/lib/intelligence/invalid_content_error.rb +3 -0
- data/lib/intelligence/message.rb +53 -0
- data/lib/intelligence/message_content/base.rb +18 -0
- data/lib/intelligence/message_content/binary.rb +24 -0
- data/lib/intelligence/message_content/text.rb +17 -0
- data/lib/intelligence/message_content/tool_call.rb +20 -0
- data/lib/intelligence/message_content/tool_result.rb +20 -0
- data/lib/intelligence/message_content.rb +16 -0
- data/lib/intelligence/unsupported_content_error.rb +3 -0
- data/lib/intelligence/version.rb +3 -0
- data/lib/intelligence.rb +24 -0
- 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,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,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,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
|