retell-sdk-unofficial 0.1.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.
@@ -0,0 +1,285 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ require_relative 'api/agent'
5
+ require_relative 'api/call'
6
+ require_relative 'api/retell_llm'
7
+ require_relative 'api/phone_number'
8
+ require_relative 'api/voice'
9
+ require_relative 'api/concurrency'
10
+
11
+ module Retell
12
+ module SDK
13
+ module Unofficial
14
+ class Client
15
+ DEFAULT_BASE_URL = 'https://api.retellai.com'.freeze
16
+ DEFAULT_TIMEOUT = 60
17
+
18
+ def initialize(api_key:, base_url: nil, timeout: DEFAULT_TIMEOUT)
19
+ @api_key = api_key
20
+ @base_url = base_url || DEFAULT_BASE_URL
21
+ @timeout = timeout
22
+ end
23
+
24
+ def agent
25
+ @agent ||= Retell::SDK::Unofficial::API::Agent.new(self)
26
+ end
27
+
28
+ def call
29
+ @call ||= Retell::SDK::Unofficial::API::Call.new(self)
30
+ end
31
+
32
+ def retell_llm
33
+ @retell_llm ||= Retell::SDK::Unofficial::API::RetellLLM.new(self)
34
+ end
35
+
36
+ def phone_number
37
+ @phone_number ||= Retell::SDK::Unofficial::API::PhoneNumber.new(self)
38
+ end
39
+
40
+ def voice
41
+ @voice ||= Retell::SDK::Unofficial::API::Voice.new(self)
42
+ end
43
+
44
+ def concurrency
45
+ @concurrency ||= Retell::SDK::Unofficial::API::Concurrency.new(self)
46
+ end
47
+
48
+ def agents
49
+ agent.list
50
+ end
51
+
52
+ def calls(**params)
53
+ call.list(**params)
54
+ end
55
+
56
+ def retell_llms
57
+ retell_llm.list
58
+ end
59
+
60
+ def phone_numbers
61
+ phone_number.list
62
+ end
63
+
64
+ def voices
65
+ voice.list
66
+ end
67
+
68
+ def request(method, path, params = {}, **options)
69
+ url = "#{@base_url}#{path}"
70
+
71
+ http_options = {
72
+ headers: headers.merge(options[:headers] || {}),
73
+ format: :json,
74
+ timeout: options[:timeout] || @timeout
75
+ }
76
+
77
+ body_params = options[:body] || {}
78
+
79
+ if [:get, :delete].include?(method)
80
+ options[:query] = options[:query].merge(params)
81
+ else
82
+ options[:body] = options[:body].merge(params)
83
+ end
84
+
85
+ http_options[:query] = options[:query] unless options[:query].empty?
86
+
87
+ if [:post, :patch].include?(method)
88
+ http_options[:body] = options[:body].to_json
89
+ elsif !body_params.empty?
90
+ http_options[:body] = body_params.to_json
91
+ end
92
+
93
+ begin
94
+ response = HTTParty.send(
95
+ method,
96
+ url,
97
+ http_options
98
+ )
99
+ rescue Net::OpenTimeout, Net::ReadTimeout, Timeout::Error, Errno::ETIMEDOUT => e
100
+ raise APITimeoutError.new(http_options)
101
+ end
102
+
103
+ handle_response(response)
104
+ end
105
+
106
+ def get(path, params = {}, **options)
107
+ request(:get, path, params, **options)
108
+ end
109
+
110
+ def post(path, params = {}, **options)
111
+ request(:post, path, params, **options)
112
+ end
113
+
114
+ def patch(path, params = {}, **options)
115
+ request(:patch, path, params, **options)
116
+ end
117
+
118
+ def delete(path, params = {}, **options)
119
+ request(:delete, path, params, **options)
120
+ nil
121
+ end
122
+
123
+ def make_request_options(extra_headers: nil, extra_query: nil, extra_body: nil, timeout: nil)
124
+ options = {}
125
+ options[:headers] = extra_headers || {}
126
+ options[:query] = extra_query || {}
127
+ options[:body] = extra_body || {}
128
+ options[:timeout] = timeout if timeout
129
+ options
130
+ end
131
+
132
+ private
133
+
134
+ def headers
135
+ {
136
+ 'Authorization' => "Bearer #{@api_key}",
137
+ 'Content-Type' => 'application/json',
138
+ 'Accept' => 'application/json'
139
+ }
140
+ end
141
+
142
+ def handle_response(response)
143
+ case response.code
144
+ when 204
145
+ return nil
146
+ when 200..299
147
+ parse_response(response)
148
+ when 400..499
149
+ raise Retell::SDK::Unofficial::Error.new(response.code, "Client error: #{response.body}")
150
+ when 500..599
151
+ raise Retell::SDK::Unofficial::Error.new(response.code, "Server error: #{response.body}")
152
+ else
153
+ raise Retell::SDK::Unofficial::Error.new(response.code, "Unknown error: #{response.body}")
154
+ end
155
+ end
156
+
157
+ def parse_response(response)
158
+ data = response.parsed_response
159
+ if data.is_a?(Array)
160
+ parse_array_response(data)
161
+ elsif data.is_a?(Hash)
162
+ parse_single_response(data)
163
+ else
164
+ raise Retell::SDK::Unofficial::Error, "Unexpected data format: #{data.class}"
165
+ end
166
+ end
167
+
168
+ def parse_array_response(data)
169
+ type_key = data.first&.keys&.find { |key|
170
+ key.end_with?('_id') || key == 'phone_number'
171
+ }&.gsub('_id', '')
172
+
173
+ return unknown_response_error('array') unless type_key
174
+
175
+ list_class = "Retell::SDK::Unofficial::#{process_type_key(type_key)}List"
176
+ item_class = "Retell::SDK::Unofficial::#{process_type_key(type_key)}"
177
+
178
+ items = data.map { |item_data| constantize_item_class(item_class).new(
179
+ self,
180
+ **process_data(item_data, type_key)
181
+ )}
182
+ raw_response = { "#{type_key}s".to_sym => items }
183
+
184
+ constantize_list_class(list_class).new(**raw_response)
185
+ end
186
+
187
+ def parse_single_response(data)
188
+ type_key = data.keys.find { |key|
189
+ key.end_with?('_id') || key == 'phone_number' || key == 'concurrency_limit'
190
+ }&.gsub('_id', '')
191
+
192
+ return unknown_response_error('single object') unless type_key
193
+
194
+ response_class = "Retell::SDK::Unofficial::#{process_type_key(type_key)}"
195
+ processed_data = process_data(data, type_key)
196
+
197
+ constantize_item_class(response_class).new(self, **processed_data)
198
+ end
199
+
200
+ # yes, same as constantize_list_class - to circumvent type checking issues
201
+ def constantize_item_class(class_name)
202
+ class_name.split('::').inject(Object) { |mod, class_name| mod.const_get(class_name) }
203
+ end
204
+
205
+ # yes, same as constantize_item_class - Ruby type checking isn't where it should be
206
+ def constantize_list_class(class_name)
207
+ class_name.split('::').inject(Object) { |mod, class_name| mod.const_get(class_name) }
208
+ end
209
+
210
+ def process_type_key(type_key)
211
+ case type_key
212
+ when 'llm'
213
+ 'RetellLLM'
214
+ when 'concurrency_limit'
215
+ 'Concurrency'
216
+ when 'phone_number'
217
+ 'PhoneNumber'
218
+ else
219
+ type_key.capitalize
220
+ end
221
+ end
222
+
223
+ def process_data(data, type_key)
224
+ case data
225
+ when Hash
226
+ data.map do |key, value|
227
+ processed_value = process_data(value, type_key)
228
+ [
229
+ key.to_sym,
230
+ convert_to_float(processed_value, type_key, key.to_sym)
231
+ ]
232
+ end.to_h
233
+ when Array
234
+ data.map { |item| process_data(item, type_key) }
235
+ else
236
+ data
237
+ end
238
+ end
239
+
240
+ def convert_to_float(value, type_key, field_name)
241
+ if value.is_a?(Integer) && float_fields(type_key).include?(field_name)
242
+ value.to_f
243
+ else
244
+ value
245
+ end
246
+ end
247
+
248
+ def float_fields(type_key)
249
+ case type_key
250
+ when 'agent'
251
+ [:ambient_sound_volume, :backchannel_frequency, :interruption_sensitivity, :responsiveness, :voice_speed, :voice_temperature, :volume]
252
+ when 'llm'
253
+ [:model_temperature]
254
+ else
255
+ []
256
+ end
257
+ end
258
+
259
+ def unknown_response_error(type)
260
+ raise Retell::SDK::Unofficial::Error, "Unknown response type for #{type} data"
261
+ end
262
+ end
263
+
264
+ class Error < StandardError
265
+ attr_reader :code, :message
266
+
267
+ def initialize(code, message)
268
+ @code = code
269
+ @message = message
270
+ super("#{code}: #{message}")
271
+ end
272
+ end
273
+
274
+
275
+ class APITimeoutError < Error
276
+ attr_reader :request
277
+
278
+ def initialize(request)
279
+ @request = request
280
+ super(500, "API request timed out")
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,17 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class Concurrency < Base
5
+ @attributes = [
6
+ :current_concurrency, :concurrency_limit
7
+ ]
8
+
9
+ attr_reader *@attributes
10
+
11
+ def retrieve
12
+ @client.concurrency.retrieve()
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class PhoneCall < BaseCall
5
+ @attributes = [:from_number, :to_number, :direction] + BaseCall.attributes
6
+
7
+ attr_reader *@attributes
8
+
9
+ def initialize(client, raw_response)
10
+ super(client, raw_response)
11
+ @from_number = raw_response[:from_number]
12
+ @to_number = raw_response[:to_number]
13
+ @direction = raw_response[:direction]
14
+ end
15
+
16
+ def retrieve
17
+ @client.call.retrieve(self)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,62 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class PhoneNumber < Base
5
+ @attributes = [
6
+ :phone_number, :phone_number_pretty, :inbound_agent_id, :outbound_agent_id,
7
+ :area_code, :nickname, :last_modification_timestamp
8
+ ]
9
+
10
+ @writeable_attributes = [
11
+ :inbound_agent_id, :outbound_agent_id, :nickname
12
+ ]
13
+
14
+ attr_reader *@attributes
15
+
16
+ @writeable_attributes.each do |attr|
17
+ define_method("#{attr}=") do |value|
18
+ self[attr] = value
19
+ end
20
+ end
21
+
22
+ def inbound_agent
23
+ inbound_agent_id
24
+ end
25
+
26
+ def inbound_agent=(value)
27
+ self[:inbound_agent_id] = value
28
+ end
29
+
30
+ def outbound_agent
31
+ outbound_agent_id
32
+ end
33
+
34
+ def outbound_agent=(value)
35
+ self[:outbound_agent_id] = value
36
+ end
37
+
38
+ def retrieve
39
+ @client.phone_number.retrieve(self)
40
+ end
41
+
42
+ def update(**params)
43
+ update_params = @changed_attributes.merge(params)
44
+
45
+ if update_params.any?
46
+ updated_phone_number = @client.phone_number.update(self, **update_params)
47
+ self.class.attributes.each do |attr|
48
+ instance_variable_set("@#{attr}", updated_phone_number.send(attr))
49
+ end
50
+ @changed_attributes.clear
51
+ end
52
+
53
+ self
54
+ end
55
+
56
+ def delete
57
+ @client.phone_number.delete(self)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class PhoneNumberList < BaseList
5
+ def initialize(raw_response)
6
+ super(:phone_numbers, raw_response)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class RetellLLM < Base
5
+ @attributes = [
6
+ :last_modification_timestamp, :llm_id, :llm_websocket_url, :begin_message,
7
+ :general_prompt, :general_tools, :inbound_dynamic_variables_webhook_url,
8
+ :model, :model_temperature, :starting_state, :states
9
+ ]
10
+
11
+ @writeable_attributes = [
12
+ :begin_message,:general_prompt, :general_tools, :inbound_dynamic_variables_webhook_url, :model, :model_temperature, :states, :starting_state
13
+ ]
14
+
15
+ attr_reader *@attributes
16
+
17
+ def id
18
+ llm_id
19
+ end
20
+
21
+ def websocket_url
22
+ llm_websocket_url
23
+ end
24
+
25
+ def url
26
+ llm_websocket_url
27
+ end
28
+
29
+ @writeable_attributes.each do |attr|
30
+ define_method("#{attr}=") do |value|
31
+ self[attr] = value
32
+ end
33
+ end
34
+
35
+ def retrieve
36
+ @client.retell_llm.retrieve(self)
37
+ end
38
+
39
+ def update(**params)
40
+ update_params = @changed_attributes.merge(params)
41
+
42
+ if update_params.any?
43
+ updated_retell_llm = @client.retell_llm.update(self, **update_params)
44
+ self.class.attributes.each do |attr|
45
+ instance_variable_set("@#{attr}", updated_retell_llm.send(attr))
46
+ end
47
+ @changed_attributes.clear
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ def delete
54
+ @client.retell_llm.delete(self)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,11 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class RetellLLMList < BaseList
5
+ def initialize(raw_response)
6
+ super(:llms, raw_response)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class Voice < Base
5
+ @attributes = [
6
+ :voice_id, :voice_name, :provider, :accent, :gender, :age, :preview_audio_url
7
+ ]
8
+
9
+ attr_reader *@attributes
10
+
11
+ def retrieve
12
+ @client.voice.retrieve(self)
13
+ end
14
+
15
+ def id
16
+ voice_id
17
+ end
18
+
19
+ def name
20
+ voice_name
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class VoiceList < BaseList
5
+ def initialize(raw_response)
6
+ super(:voices, raw_response)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class WebCall < BaseCall
5
+ @attributes = [:access_token] + BaseCall.attributes
6
+
7
+ attr_reader *@attributes
8
+
9
+ def initialize(client, raw_response)
10
+ super(client, raw_response)
11
+ @access_token = raw_response[:access_token]
12
+ end
13
+
14
+ def retrieve
15
+ @client.call.retrieve(self)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'unofficial/client'
2
+ require_relative 'unofficial/api/agent'
3
+ require_relative 'unofficial/api/call'
4
+ require_relative 'unofficial/api/retell_llm'
5
+ require_relative 'unofficial/api/phone_number'
6
+ require_relative 'unofficial/api/voice'
7
+ require_relative 'unofficial/api/concurrency'
8
+ require_relative 'unofficial/base'
9
+ require_relative 'unofficial/base_list'
10
+ require_relative 'unofficial/agent'
11
+ require_relative 'unofficial/agent_list'
12
+ require_relative 'unofficial/call'
13
+ require_relative 'unofficial/base_call'
14
+ require_relative 'unofficial/web_call'
15
+ require_relative 'unofficial/phone_call'
16
+ require_relative 'unofficial/call_list'
17
+ require_relative 'unofficial/retell_llm'
18
+ require_relative 'unofficial/retell_llm_list'
19
+ require_relative 'unofficial/phone_number'
20
+ require_relative 'unofficial/phone_number_list'
21
+ require_relative 'unofficial/voice'
22
+ require_relative 'unofficial/voice_list'
23
+ require_relative 'unofficial/concurrency'
24
+
25
+ module Retell
26
+ module SDK
27
+ module Unofficial
28
+ def self.new(api_key:, base_url: nil)
29
+ Client.new(base_url: base_url, api_key: api_key)
30
+ end
31
+ end
32
+ end
33
+ end