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