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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +576 -0
  3. data/intelligence.gemspec +2 -1
  4. data/lib/intelligence/adapter/base.rb +13 -6
  5. data/lib/intelligence/adapter/class_methods.rb +15 -0
  6. data/lib/intelligence/adapter/module_methods.rb +41 -0
  7. data/lib/intelligence/adapter.rb +2 -2
  8. data/lib/intelligence/adapters/anthropic/adapter.rb +21 -19
  9. data/lib/intelligence/adapters/anthropic/chat_request_methods.rb +189 -0
  10. data/lib/intelligence/adapters/anthropic/{chat_methods.rb → chat_response_methods.rb} +13 -137
  11. data/lib/intelligence/adapters/cerebras.rb +19 -19
  12. data/lib/intelligence/adapters/generic/adapter.rb +4 -2
  13. data/lib/intelligence/adapters/generic/chat_request_methods.rb +221 -0
  14. data/lib/intelligence/adapters/generic/chat_response_methods.rb +234 -0
  15. data/lib/intelligence/adapters/generic.rb +1 -1
  16. data/lib/intelligence/adapters/google/adapter.rb +33 -22
  17. data/lib/intelligence/adapters/google/chat_request_methods.rb +234 -0
  18. data/lib/intelligence/adapters/google/chat_response_methods.rb +236 -0
  19. data/lib/intelligence/adapters/groq.rb +29 -49
  20. data/lib/intelligence/adapters/hyperbolic.rb +13 -39
  21. data/lib/intelligence/adapters/mistral.rb +21 -42
  22. data/lib/intelligence/adapters/open_ai/adapter.rb +39 -32
  23. data/lib/intelligence/adapters/open_ai/chat_request_methods.rb +186 -0
  24. data/lib/intelligence/adapters/open_ai/chat_response_methods.rb +239 -0
  25. data/lib/intelligence/adapters/open_ai.rb +1 -1
  26. data/lib/intelligence/adapters/open_router.rb +18 -18
  27. data/lib/intelligence/adapters/samba_nova.rb +16 -18
  28. data/lib/intelligence/adapters/together_ai.rb +25 -23
  29. data/lib/intelligence/conversation.rb +11 -10
  30. data/lib/intelligence/message.rb +45 -29
  31. data/lib/intelligence/message_content/base.rb +2 -9
  32. data/lib/intelligence/message_content/binary.rb +3 -3
  33. data/lib/intelligence/message_content/file.rb +3 -3
  34. data/lib/intelligence/message_content/text.rb +10 -2
  35. data/lib/intelligence/message_content/tool_call.rb +61 -5
  36. data/lib/intelligence/message_content/tool_result.rb +11 -6
  37. data/lib/intelligence/tool.rb +139 -0
  38. data/lib/intelligence/version.rb +1 -1
  39. data/lib/intelligence.rb +3 -1
  40. metadata +31 -13
  41. data/lib/intelligence/adapter/class_methods/construction.rb +0 -17
  42. data/lib/intelligence/adapter/module_methods/construction.rb +0 -43
  43. data/lib/intelligence/adapters/generic/chat_methods.rb +0 -355
  44. data/lib/intelligence/adapters/google/chat_methods.rb +0 -393
  45. data/lib/intelligence/adapters/legacy/adapter.rb +0 -11
  46. data/lib/intelligence/adapters/legacy/chat_methods.rb +0 -54
  47. data/lib/intelligence/adapters/open_ai/chat_methods.rb +0 -345
@@ -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 'legacy/adapter'
1
+ require_relative 'generic/adapter'
2
2
 
3
3
  module Intelligence
4
4
  module Groq
5
5
 
6
- class Adapter < Legacy::Adapter
6
+ class Adapter < Generic::Adapter
7
7
 
8
8
  chat_request_uri 'https://api.groq.com/openai/v1/chat/completions'
9
9
 
10
- configuration do
11
- parameter :key, String
12
- group :chat_options do
13
- parameter :frequency_penalty, Float
14
- parameter :logit_bias
15
- parameter :logprobs, [ TrueClass, FalseClass ]
16
- parameter :max_tokens, Integer
17
- parameter :model, String
18
- parameter :n, Integer
19
- # the parallel_tool_calls parameter is only allowed when 'tools' are specified
20
- parameter :parallel_tool_calls, [ TrueClass, FalseClass ]
21
- parameter :presence_penalty, Float
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
- parameter :type, String
24
+ type Symbol, in: [ :text, :json_object ]
26
25
  end
27
- parameter :seed, Integer
28
- parameter :stop, String, array: true
29
- parameter :stream, [ TrueClass, FalseClass ]
30
- group :stream_options do
31
- parameter :include_usage, [ TrueClass, FalseClass ]
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
- parameter :temperature, Float
34
- group :tool_choice do
32
+ temperature Float
33
+ tool_choice do
35
34
  # one of 'auto', 'none' or 'function'
36
- parameter :type, String
37
- # the function group is required if you specify a type of 'function'
38
- group :function do
39
- parameter :name, String
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
- parameter :top_logprobs, Integer
43
- parameter :top_p, Float
44
- parameter :user, String
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
- configuration do
11
- parameter :key, String
12
- group :chat_options do
13
- parameter :model, String
14
- parameter :temperature, Float
15
- parameter :top_p, Float
16
- parameter :n, Integer
17
- parameter :max_tokens, Integer
18
- parameter :stop, String, array: true
19
- parameter :stream, [ TrueClass, FalseClass ]
20
- parameter :frequency_penalty, Float
21
- parameter :presence_penalty, Float
22
- parameter :user, String
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