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.
- checksums.yaml +4 -4
- data/README.md +576 -0
- data/intelligence.gemspec +2 -1
- data/lib/intelligence/adapter/base.rb +13 -6
- data/lib/intelligence/adapter/class_methods.rb +15 -0
- data/lib/intelligence/adapter/module_methods.rb +41 -0
- data/lib/intelligence/adapter.rb +2 -2
- data/lib/intelligence/adapters/anthropic/adapter.rb +21 -19
- data/lib/intelligence/adapters/anthropic/chat_request_methods.rb +189 -0
- data/lib/intelligence/adapters/anthropic/{chat_methods.rb → chat_response_methods.rb} +13 -137
- data/lib/intelligence/adapters/cerebras.rb +19 -19
- data/lib/intelligence/adapters/generic/adapter.rb +4 -2
- data/lib/intelligence/adapters/generic/chat_request_methods.rb +221 -0
- data/lib/intelligence/adapters/generic/chat_response_methods.rb +234 -0
- data/lib/intelligence/adapters/generic.rb +1 -1
- data/lib/intelligence/adapters/google/adapter.rb +33 -22
- data/lib/intelligence/adapters/google/chat_request_methods.rb +234 -0
- data/lib/intelligence/adapters/google/chat_response_methods.rb +236 -0
- data/lib/intelligence/adapters/groq.rb +29 -49
- data/lib/intelligence/adapters/hyperbolic.rb +13 -39
- data/lib/intelligence/adapters/mistral.rb +21 -42
- data/lib/intelligence/adapters/open_ai/adapter.rb +39 -32
- data/lib/intelligence/adapters/open_ai/chat_request_methods.rb +186 -0
- data/lib/intelligence/adapters/open_ai/chat_response_methods.rb +239 -0
- data/lib/intelligence/adapters/open_ai.rb +1 -1
- data/lib/intelligence/adapters/open_router.rb +18 -18
- data/lib/intelligence/adapters/samba_nova.rb +16 -18
- data/lib/intelligence/adapters/together_ai.rb +25 -23
- data/lib/intelligence/conversation.rb +11 -10
- data/lib/intelligence/message.rb +45 -29
- data/lib/intelligence/message_content/base.rb +2 -9
- data/lib/intelligence/message_content/binary.rb +3 -3
- data/lib/intelligence/message_content/file.rb +3 -3
- data/lib/intelligence/message_content/text.rb +10 -2
- data/lib/intelligence/message_content/tool_call.rb +61 -5
- data/lib/intelligence/message_content/tool_result.rb +11 -6
- data/lib/intelligence/tool.rb +139 -0
- data/lib/intelligence/version.rb +1 -1
- data/lib/intelligence.rb +3 -1
- metadata +31 -13
- data/lib/intelligence/adapter/class_methods/construction.rb +0 -17
- data/lib/intelligence/adapter/module_methods/construction.rb +0 -43
- data/lib/intelligence/adapters/generic/chat_methods.rb +0 -355
- data/lib/intelligence/adapters/google/chat_methods.rb +0 -393
- data/lib/intelligence/adapters/legacy/adapter.rb +0 -11
- data/lib/intelligence/adapters/legacy/chat_methods.rb +0 -54
- data/lib/intelligence/adapters/open_ai/chat_methods.rb +0 -345
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Intelligence
|
4
|
+
module Google
|
5
|
+
module ChatRequestMethods
|
6
|
+
|
7
|
+
GENERATIVE_LANGUAGE_URI = "https://generativelanguage.googleapis.com/v1beta/models/"
|
8
|
+
|
9
|
+
SUPPORTED_BINARY_MEDIA_TYPES = %w[ text ]
|
10
|
+
|
11
|
+
SUPPORTED_BINARY_CONTENT_TYPES = %w[
|
12
|
+
image/png image/jpeg image/webp image/heic image/heif
|
13
|
+
audio/aac audio/flac audio/mp3 audio/m4a audio/mpeg audio/mpga audio/mp4 audio/opus
|
14
|
+
audio/pcm audio/wav audio/webm
|
15
|
+
application/pdf
|
16
|
+
]
|
17
|
+
|
18
|
+
SUPPORTED_FILE_MEDIA_TYPES = %w[ text ]
|
19
|
+
|
20
|
+
SUPPORTED_CONTENT_TYPES = %w[
|
21
|
+
image/png image/jpeg image/webp image/heic image/heif
|
22
|
+
video/x-flv video/quicktime video/mpeg video/mpegps video/mpg video/mp4 video/webm
|
23
|
+
video/wmv video/3gpp
|
24
|
+
audio/aac audio/flac audio/mp3 audio/m4a audio/mpeg audio/mpga audio/mp4 audio/opus
|
25
|
+
audio/pcm audio/wav audio/webm
|
26
|
+
application/pdf
|
27
|
+
]
|
28
|
+
|
29
|
+
def chat_request_uri( options )
|
30
|
+
options = @options.merge( build_options( options ) )
|
31
|
+
|
32
|
+
key = options[ :key ]
|
33
|
+
gc = options[ :generationConfig ] || {}
|
34
|
+
model = gc[ :model ]
|
35
|
+
stream = gc.key?( :stream ) ? gc[ :stream ] : false
|
36
|
+
|
37
|
+
raise ArgumentError.new( "A Google API key is required to build a Google chat request." ) \
|
38
|
+
if key.nil?
|
39
|
+
raise ArgumentError.new( "A Google model is required to build a Google chat request." ) \
|
40
|
+
if model.nil?
|
41
|
+
|
42
|
+
uri = URI( GENERATIVE_LANGUAGE_URI )
|
43
|
+
path = File.join( uri.path, model )
|
44
|
+
path += stream ? ':streamGenerateContent' : ':generateContent'
|
45
|
+
uri.path = path
|
46
|
+
query = { key: key }
|
47
|
+
query[ :alt ] = 'sse' if stream
|
48
|
+
uri.query = URI.encode_www_form( query )
|
49
|
+
|
50
|
+
uri.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def chat_request_headers( options = {} )
|
54
|
+
{ 'Content-Type' => 'application/json' }
|
55
|
+
end
|
56
|
+
|
57
|
+
def chat_request_body( conversation, options = {} )
|
58
|
+
options = @options.merge( build_options( options ) )
|
59
|
+
|
60
|
+
gc = options[ :generationConfig ]
|
61
|
+
# discard properties not part of the google generationConfig schema
|
62
|
+
gc.delete( :model )
|
63
|
+
gc.delete( :stream )
|
64
|
+
|
65
|
+
# googlify tool configuration
|
66
|
+
if tool_config = gc.delete( :tool_config )
|
67
|
+
mode = tool_config[ :function_calling_config ]&.[]( :mode )
|
68
|
+
tool_config[ :function_calling_config ][ :mode ] = mode.to_s.upcase if mode
|
69
|
+
end
|
70
|
+
|
71
|
+
result = {}
|
72
|
+
result[ :generationConfig ] = gc
|
73
|
+
result[ :tool_config ] = tool_config if tool_config
|
74
|
+
|
75
|
+
# construct the system prompt in the form of the google schema
|
76
|
+
system_instructions = to_google_system_message( conversation[ :system_message ] )
|
77
|
+
result[ :systemInstruction ] = system_instructions if system_instructions
|
78
|
+
|
79
|
+
result[ :contents ] = []
|
80
|
+
conversation[ :messages ]&.each do | message |
|
81
|
+
|
82
|
+
result_message = { role: message[ :role ] == :user ? 'user' : 'model' }
|
83
|
+
result_message_parts = []
|
84
|
+
|
85
|
+
message[ :contents ]&.each do | content |
|
86
|
+
case content[ :type ]
|
87
|
+
when :text
|
88
|
+
result_message_parts << { text: content[ :text ] }
|
89
|
+
when :binary
|
90
|
+
content_type = content[ :content_type ]
|
91
|
+
bytes = content[ :bytes ]
|
92
|
+
if content_type && bytes
|
93
|
+
mime_type = MIME::Types[ content_type ].first
|
94
|
+
if SUPPORTED_BINARY_MEDIA_TYPES.include?( mime_type&.media_type ) ||
|
95
|
+
SUPPORTED_BINARY_CONTENT_TYPES.include?( content_type )
|
96
|
+
result_message_parts << {
|
97
|
+
inline_data: {
|
98
|
+
mime_type: content_type,
|
99
|
+
data: Base64.strict_encode64( bytes )
|
100
|
+
}
|
101
|
+
}
|
102
|
+
else
|
103
|
+
raise UnsupportedContentError.new(
|
104
|
+
:google,
|
105
|
+
"does not support #{content_type} content type"
|
106
|
+
)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise UnsupportedContentError.new(
|
110
|
+
:google,
|
111
|
+
'requires binary content to include content type and ( packed ) bytes'
|
112
|
+
)
|
113
|
+
end
|
114
|
+
when :file
|
115
|
+
content_type = content[ :content_type ]
|
116
|
+
uri = content[ :uri ]
|
117
|
+
if content_type && uri
|
118
|
+
mime_type = MIME::Types[ content_type ].first
|
119
|
+
if SUPPORTED_FILE_MEDIA_TYPES.include?( mime_type&.media_type ) ||
|
120
|
+
SUPPORTED_FILE_CONTENT_TYPES.include?( content_type )
|
121
|
+
result_message_parts << {
|
122
|
+
file_data: {
|
123
|
+
mime_type: content_type,
|
124
|
+
file_uri: uri
|
125
|
+
}
|
126
|
+
}
|
127
|
+
else
|
128
|
+
raise UnsupportedContentError.new(
|
129
|
+
:google,
|
130
|
+
"does not support #{content_type} content type"
|
131
|
+
)
|
132
|
+
end
|
133
|
+
else
|
134
|
+
raise UnsupportedContentError.new(
|
135
|
+
:google,
|
136
|
+
'requires file content to include content type and uri'
|
137
|
+
)
|
138
|
+
end
|
139
|
+
when :tool_call
|
140
|
+
result_message_parts << {
|
141
|
+
functionCall: {
|
142
|
+
name: content[ :tool_name ],
|
143
|
+
args: content[ :tool_parameters ]
|
144
|
+
}
|
145
|
+
}
|
146
|
+
when :tool_result
|
147
|
+
result_message_parts << {
|
148
|
+
functionResponse: {
|
149
|
+
name: content[ :tool_name ],
|
150
|
+
response: {
|
151
|
+
name: content[ :tool_name ],
|
152
|
+
content: content[ :tool_result ]
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
else
|
157
|
+
raise InvalidContentError.new( :google )
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
result_message[ :parts ] = result_message_parts
|
162
|
+
result[ :contents ] << result_message
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
tools_attributes = to_google_tools( conversation[ :tools ] )
|
167
|
+
result[ :tools ] = [ { function_declarations: tools_attributes } ] \
|
168
|
+
if tools_attributes&.any?
|
169
|
+
|
170
|
+
JSON.generate( result )
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def to_google_system_message( system_message )
|
176
|
+
return nil if system_message.nil?
|
177
|
+
|
178
|
+
text = ''
|
179
|
+
system_message[ :contents ].each do | content |
|
180
|
+
text += content[ :text ] if content[ :type ] == :text
|
181
|
+
end
|
182
|
+
|
183
|
+
return nil if text.empty?
|
184
|
+
|
185
|
+
{
|
186
|
+
role: 'user',
|
187
|
+
parts: [
|
188
|
+
{ text: text }
|
189
|
+
]
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_google_tools( tools )
|
194
|
+
properties_array_to_object = lambda do | properties |
|
195
|
+
return nil unless properties&.any?
|
196
|
+
object = {}
|
197
|
+
required = []
|
198
|
+
properties.each do | property |
|
199
|
+
name = property.delete( :name )
|
200
|
+
required << name if property.delete( :required )
|
201
|
+
if property[ :properties ]&.any?
|
202
|
+
property_properties, property_required =
|
203
|
+
properties_array_to_object.call( property[ :properties ] )
|
204
|
+
property[ :properties ] = property_properties
|
205
|
+
property[ :required ] = property_required if property_required.any?
|
206
|
+
end
|
207
|
+
object[ name ] = property
|
208
|
+
end
|
209
|
+
[ object, required.compact ]
|
210
|
+
end
|
211
|
+
|
212
|
+
return tools&.map { | tool |
|
213
|
+
function = {
|
214
|
+
name: tool[ :name ],
|
215
|
+
description: tool[ :description ],
|
216
|
+
}
|
217
|
+
if tool[ :properties ]&.any?
|
218
|
+
properties_object, properties_required =
|
219
|
+
properties_array_to_object.call( tool[ :properties ] )
|
220
|
+
function[ :parameters ] = {
|
221
|
+
type: 'object',
|
222
|
+
properties: properties_object
|
223
|
+
}
|
224
|
+
function[ :parameters ][ :required ] = properties_required if properties_required.any?
|
225
|
+
end
|
226
|
+
function
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Intelligence
|
4
|
+
module Google
|
5
|
+
module ChatResponseMethods
|
6
|
+
|
7
|
+
def chat_result_attributes( response )
|
8
|
+
|
9
|
+
return nil unless response.success?
|
10
|
+
|
11
|
+
response_json = JSON.parse( response.body, symbolize_names: true ) rescue nil
|
12
|
+
return nil if response_json.nil? || response_json[ :candidates ].nil?
|
13
|
+
|
14
|
+
result = {}
|
15
|
+
result[ :choices ] = []
|
16
|
+
|
17
|
+
response_json[ :candidates ]&.each do | response_choice |
|
18
|
+
|
19
|
+
end_reason = translate_finish_reason( response_choice[ :finishReason ] )
|
20
|
+
|
21
|
+
role = nil
|
22
|
+
contents = []
|
23
|
+
|
24
|
+
response_content = response_choice[ :content ]
|
25
|
+
if response_content
|
26
|
+
role = ( response_content[ :role ] == 'model' ) ? 'assistant' : 'user'
|
27
|
+
contents = []
|
28
|
+
response_content[ :parts ]&.each do | response_content_part |
|
29
|
+
if response_content_part.key?( :text )
|
30
|
+
contents.push( {
|
31
|
+
type: 'text', text: response_content_part[ :text ]
|
32
|
+
} )
|
33
|
+
elsif function_call = response_content_part[ :functionCall ]
|
34
|
+
contents.push( {
|
35
|
+
type: :tool_call,
|
36
|
+
tool_name: function_call[ :name ],
|
37
|
+
tool_parameters: function_call[ :args ]
|
38
|
+
} )
|
39
|
+
# google does not indicate there is tool call in the stop reason so
|
40
|
+
# we will synthesize this end reason
|
41
|
+
end_reason = :tool_called if end_reason == :ended
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
result_message = nil
|
47
|
+
if role
|
48
|
+
result_message = { role: role }
|
49
|
+
result_message[ :contents ] = contents
|
50
|
+
end
|
51
|
+
|
52
|
+
result[ :choices ].push( { end_reason: end_reason, message: result_message } )
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
metrics_json = response_json[ :usageMetadata ]
|
57
|
+
unless metrics_json.nil?
|
58
|
+
|
59
|
+
metrics = {}
|
60
|
+
metrics[ :input_tokens ] = metrics_json[ :promptTokenCount ]
|
61
|
+
metrics[ :output_tokens ] = metrics_json[ :candidatesTokenCount ]
|
62
|
+
metrics = metrics.compact
|
63
|
+
|
64
|
+
result[ :metrics ] = metrics unless metrics.empty?
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
result
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def chat_result_error_attributes( response )
|
73
|
+
|
74
|
+
error_type, error_description = translate_error_response_status( response.status )
|
75
|
+
result = { error_type: error_type.to_s, error_description: error_description }
|
76
|
+
|
77
|
+
response_body = JSON.parse( response.body, symbolize_names: true ) rescue nil
|
78
|
+
if response_body && response_body[ :error ]
|
79
|
+
error_details_reason = response_body[ :error ][ :details ]&.first&.[]( :reason )
|
80
|
+
# a special case for authentication
|
81
|
+
error_type = :authentication_error if error_details_reason == 'API_KEY_INVALID'
|
82
|
+
result = {
|
83
|
+
error_type: error_type.to_s,
|
84
|
+
error: error_details_reason || response_body[ :error ][ :status ] || error_type,
|
85
|
+
error_description: response_body[ :error ][ :message ]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
result
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def stream_result_chunk_attributes( context, chunk )
|
93
|
+
|
94
|
+
context ||= {}
|
95
|
+
buffer = context[ :buffer ] || ''
|
96
|
+
metrics = context[ :metrics ] || {
|
97
|
+
input_tokens: 0,
|
98
|
+
output_tokens: 0
|
99
|
+
}
|
100
|
+
choices = context[ :choices ] || Array.new( 1 , { message: {} } )
|
101
|
+
|
102
|
+
choices.each do | choice |
|
103
|
+
choice[ :message ][ :contents ] = choice[ :message ][ :contents ]&.map do | content |
|
104
|
+
case content[ :type ]
|
105
|
+
when :text
|
106
|
+
content[ :text ] = ''
|
107
|
+
else
|
108
|
+
content.clear
|
109
|
+
end
|
110
|
+
content
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
buffer += chunk
|
115
|
+
while ( eol_index = buffer.index( "\n" ) )
|
116
|
+
|
117
|
+
line = buffer.slice!( 0..eol_index )
|
118
|
+
line = line.strip
|
119
|
+
next if line.empty? || !line.start_with?( 'data:' )
|
120
|
+
line = line[ 6..-1 ]
|
121
|
+
|
122
|
+
data = JSON.parse( line, symbolize_names: true )
|
123
|
+
if data.is_a?( Hash )
|
124
|
+
|
125
|
+
data[ :candidates ]&.each do | data_candidate |
|
126
|
+
|
127
|
+
data_candidate_index = data_candidate[ :index ] || 0
|
128
|
+
data_candidate_content = data_candidate[ :content ]
|
129
|
+
data_candidate_finish_reason = data_candidate[ :finishReason ]
|
130
|
+
choices.fill( { message: { role: 'assistant' } }, choices.size, data_candidate_index + 1 ) \
|
131
|
+
if choices.size <= data_candidate_index
|
132
|
+
contents = choices[ data_candidate_index ][ :message ][ :contents ] || []
|
133
|
+
last_content = contents&.last
|
134
|
+
|
135
|
+
if data_candidate_content&.include?( :parts )
|
136
|
+
data_candidate_content_parts = data_candidate_content[ :parts ]
|
137
|
+
data_candidate_content_parts&.each do | data_candidate_content_part |
|
138
|
+
if data_candidate_content_part.key?( :text )
|
139
|
+
if last_content.nil? || last_content[ :type ] != :text
|
140
|
+
contents.push( { type: :text, text: data_candidate_content_part[ :text ] } )
|
141
|
+
else
|
142
|
+
last_content[ :text ] =
|
143
|
+
( last_content[ :text ] || '' ) + data_candidate_content_part[ :text ]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
choices[ data_candidate_index ][ :message ][ :contents ] = contents
|
149
|
+
choices[ data_candidate_index ][ :end_reason ] =
|
150
|
+
translate_finish_reason( data_candidate_finish_reason )
|
151
|
+
end
|
152
|
+
|
153
|
+
if usage = data[ :usageMetadata ]
|
154
|
+
metrics[ :input_tokens ] = usage[ :promptTokenCount ]
|
155
|
+
metrics[ :output_tokens ] = usage[ :candidatesTokenCount ]
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
context[ :buffer ] = buffer
|
163
|
+
context[ :metrics ] = metrics
|
164
|
+
context[ :choices ] = choices
|
165
|
+
|
166
|
+
[ context, choices.empty? ? nil : { choices: choices.dup } ]
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
def stream_result_attributes( context )
|
171
|
+
|
172
|
+
choices = context[ :choices ]
|
173
|
+
metrics = context[ :metrics ]
|
174
|
+
|
175
|
+
choices = choices.map do | choice |
|
176
|
+
{ end_reason: choice[ :end_reason ] }
|
177
|
+
end
|
178
|
+
|
179
|
+
{ choices: choices, metrics: context[ :metrics ] }
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
alias_method :stream_result_error_attributes, :chat_result_error_attributes
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def translate_finish_reason( finish_reason )
|
188
|
+
case finish_reason
|
189
|
+
when 'STOP'
|
190
|
+
:ended
|
191
|
+
when 'MAX_TOKENS'
|
192
|
+
:token_limit_exceeded
|
193
|
+
when 'SAFETY', 'RECITATION', 'BLOCKLIST', 'PROHIBITED_CONTENT', 'SPII'
|
194
|
+
:filtered
|
195
|
+
else
|
196
|
+
nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def translate_error_response_status( status )
|
201
|
+
case status
|
202
|
+
when 400
|
203
|
+
[ :invalid_request_error,
|
204
|
+
"There was an issue with the format or content of your request." ]
|
205
|
+
when 403
|
206
|
+
[ :permission_error,
|
207
|
+
"Your API key does not have permission to use the specified resource." ]
|
208
|
+
when 404
|
209
|
+
[ :not_found_error,
|
210
|
+
"The requested resource was not found." ]
|
211
|
+
when 413
|
212
|
+
[ :request_too_large,
|
213
|
+
"Request exceeds the maximum allowed number of bytes." ]
|
214
|
+
when 422
|
215
|
+
[ :invalid_request_error,
|
216
|
+
"There was an issue with the format or content of your request." ]
|
217
|
+
when 429
|
218
|
+
[ :rate_limit_error,
|
219
|
+
"Your account has hit a rate limit." ]
|
220
|
+
when 500, 502, 503
|
221
|
+
[ :api_error,
|
222
|
+
"An unexpected error has occurred internal to the providers systems." ]
|
223
|
+
when 529
|
224
|
+
[ :overloaded_error,
|
225
|
+
"The providers server is temporarily overloaded." ]
|
226
|
+
else
|
227
|
+
[ :unknown_error, "
|
228
|
+
An unknown error occurred." ]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
@@ -1,66 +1,46 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'generic/adapter'
|
2
2
|
|
3
3
|
module Intelligence
|
4
4
|
module Groq
|
5
5
|
|
6
|
-
class Adapter <
|
6
|
+
class Adapter < Generic::Adapter
|
7
7
|
|
8
8
|
chat_request_uri 'https://api.groq.com/openai/v1/chat/completions'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
group :response_format do
|
10
|
+
schema do
|
11
|
+
key String
|
12
|
+
chat_options do
|
13
|
+
frequency_penalty Float
|
14
|
+
logit_bias
|
15
|
+
logprobs [ TrueClass, FalseClass ]
|
16
|
+
max_tokens Integer
|
17
|
+
model String
|
18
|
+
# the parallel_tool_calls is only allowed when 'tools' are specified
|
19
|
+
parallel_tool_calls [ TrueClass, FalseClass ]
|
20
|
+
presence_penalty Float
|
21
|
+
response_format do
|
23
22
|
# 'text' and 'json_object' are the only supported types; you must also instruct
|
24
23
|
# the model to output json
|
25
|
-
|
24
|
+
type Symbol, in: [ :text, :json_object ]
|
26
25
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
seed Integer
|
27
|
+
stop String, array: true
|
28
|
+
stream [ TrueClass, FalseClass ]
|
29
|
+
stream_options do
|
30
|
+
include_usage [ TrueClass, FalseClass ]
|
32
31
|
end
|
33
|
-
|
34
|
-
|
32
|
+
temperature Float
|
33
|
+
tool_choice do
|
35
34
|
# one of 'auto', 'none' or 'function'
|
36
|
-
|
37
|
-
# the function
|
38
|
-
|
39
|
-
|
35
|
+
type Symbol, in: [ :auto, :none, :function ]
|
36
|
+
# the function parameters is required if you specify a type of 'function'
|
37
|
+
function do
|
38
|
+
name String
|
40
39
|
end
|
41
40
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
alias chat_request_generic_message_attributes chat_request_message_attributes
|
49
|
-
|
50
|
-
# groq models only support the legacy Open AI message schema for the assistant
|
51
|
-
# messages while supporting the modern message schema for user messages
|
52
|
-
def chat_request_message_attributes( message )
|
53
|
-
role = message[ :role ]&.to_sym
|
54
|
-
case role
|
55
|
-
when :user
|
56
|
-
chat_request_generic_message_attributes( message )
|
57
|
-
when :assistant
|
58
|
-
chat_request_legacy_message_attributes( message )
|
59
|
-
else
|
60
|
-
raise UnsupportedContentError.new(
|
61
|
-
:mistral,
|
62
|
-
'only supports user and assistant message roles'
|
63
|
-
)
|
41
|
+
top_logprobs Integer
|
42
|
+
top_p Float
|
43
|
+
user String
|
64
44
|
end
|
65
45
|
end
|
66
46
|
|
@@ -7,48 +7,22 @@ module Intelligence
|
|
7
7
|
|
8
8
|
chat_request_uri "https://api.hyperbolic.xyz/v1/chat/completions"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
schema do
|
11
|
+
key String
|
12
|
+
chat_options do
|
13
|
+
model String
|
14
|
+
temperature Float
|
15
|
+
top_p Float
|
16
|
+
n Integer
|
17
|
+
max_tokens Integer
|
18
|
+
stop String, array: true
|
19
|
+
stream [ TrueClass, FalseClass ]
|
20
|
+
frequency_penalty Float
|
21
|
+
presence_penalty Float
|
22
|
+
user String
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def chat_result_error_attributes( response )
|
27
|
-
|
28
|
-
error_type, error_description = translate_error_response_status( response.status )
|
29
|
-
result = {
|
30
|
-
error_type: error_type.to_s,
|
31
|
-
error_description: error_description
|
32
|
-
}
|
33
|
-
parsed_body = JSON.parse( response.body, symbolize_names: true ) rescue nil
|
34
|
-
if parsed_body && parsed_body.respond_to?( :include? )
|
35
|
-
if parsed_body.include?( :error )
|
36
|
-
result = {
|
37
|
-
error_type: error_type.to_s,
|
38
|
-
error: parsed_body[ :error ][ :code ] || error_type.to_s,
|
39
|
-
error_description: parsed_body[ :error ][ :message ] || error_description
|
40
|
-
}
|
41
|
-
elsif parsed_body.include?( :detail )
|
42
|
-
result[ :error_description ] = parsed_body[ :detail ]
|
43
|
-
elsif parsed_body[ :object ] == 'error'
|
44
|
-
result[ :error_description ] = parsed_body[ :message ]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
result
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
26
|
end
|
53
27
|
|
54
28
|
end
|