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,179 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ module API
5
+ class PhoneNumber
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def create(
11
+ area_code: nil,
12
+ inbound_agent_id: nil,
13
+ inbound_agent: nil,
14
+ nickname: nil,
15
+ outbound_agent_id: nil,
16
+ outbound_agent: nil,
17
+ extra_headers: nil,
18
+ extra_query: nil,
19
+ extra_body: nil,
20
+ timeout: nil
21
+ )
22
+ inbound_agent_id = inbound_agent_id || inbound_agent
23
+ outbound_agent_id = outbound_agent_id || outbound_agent
24
+
25
+ inbound_agent_id = inbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? inbound_agent_id.agent_id : inbound_agent_id
26
+ outbound_agent_id = outbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? outbound_agent_id.agent_id : outbound_agent_id
27
+
28
+ payload = {
29
+ area_code: area_code,
30
+ inbound_agent_id: inbound_agent_id,
31
+ nickname: nickname,
32
+ outbound_agent_id: outbound_agent_id
33
+ }.compact
34
+
35
+ options = @client.make_request_options(
36
+ extra_headers: extra_headers,
37
+ extra_query: extra_query,
38
+ extra_body: extra_body,
39
+ timeout: timeout
40
+ )
41
+
42
+ @client.post('/create-phone-number', payload, **options)
43
+ end
44
+
45
+ def import(
46
+ phone_number,
47
+ termination_uri:,
48
+ inbound_agent_id: nil,
49
+ inbound_agent: nil,
50
+ nickname: nil,
51
+ outbound_agent_id: nil,
52
+ outbound_agent: nil,
53
+ sip_trunk_auth_password: nil,
54
+ sip_trunk_auth_username: nil,
55
+ extra_headers: nil,
56
+ extra_query: nil,
57
+ extra_body: nil,
58
+ timeout: nil
59
+ )
60
+ inbound_agent_id = inbound_agent_id || inbound_agent
61
+ outbound_agent_id = outbound_agent_id || outbound_agent
62
+
63
+ inbound_agent_id = inbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? inbound_agent_id.agent_id : inbound_agent_id
64
+ outbound_agent_id = outbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? outbound_agent_id.agent_id : outbound_agent_id
65
+
66
+ payload = {
67
+ phone_number: phone_number,
68
+ termination_uri: termination_uri,
69
+ inbound_agent_id: inbound_agent_id,
70
+ nickname: nickname,
71
+ outbound_agent_id: outbound_agent_id,
72
+ sip_trunk_auth_password: sip_trunk_auth_password,
73
+ sip_trunk_auth_username: sip_trunk_auth_username
74
+ }.compact
75
+
76
+ options = @client.make_request_options(
77
+ extra_headers: extra_headers,
78
+ extra_query: extra_query,
79
+ extra_body: extra_body,
80
+ timeout: timeout
81
+ )
82
+
83
+ @client.post('/import-phone-number', payload, **options)
84
+ end
85
+
86
+ def retrieve(
87
+ phone_number,
88
+ extra_headers: nil,
89
+ extra_query: nil,
90
+ extra_body: nil,
91
+ timeout: nil
92
+ )
93
+ phone_number = phone_number.is_a?(Retell::SDK::Unofficial::PhoneNumber) ? phone_number.phone_number : phone_number
94
+
95
+ options = @client.make_request_options(
96
+ extra_headers: extra_headers,
97
+ extra_query: extra_query,
98
+ extra_body: extra_body,
99
+ timeout: timeout
100
+ )
101
+
102
+ @client.get("/get-phone-number/#{phone_number}", **options)
103
+ end
104
+
105
+ def list(extra_headers: nil, extra_query: nil, extra_body: nil, timeout: nil)
106
+ options = @client.make_request_options(
107
+ extra_headers: extra_headers,
108
+ extra_query: extra_query,
109
+ extra_body: extra_body,
110
+ timeout: timeout
111
+ )
112
+
113
+ @client.get('/list-phone-numbers', **options)
114
+ end
115
+
116
+ def update(
117
+ phone_number,
118
+ inbound_agent_id: nil,
119
+ inbound_agent: nil,
120
+ nickname: nil,
121
+ outbound_agent_id: nil,
122
+ outbound_agent: nil,
123
+ extra_headers: nil,
124
+ extra_query: nil,
125
+ extra_body: nil,
126
+ timeout: nil
127
+ )
128
+ inbound_agent_id = inbound_agent_id || inbound_agent
129
+ outbound_agent_id = outbound_agent_id || outbound_agent
130
+
131
+ phone_number = phone_number.is_a?(Retell::SDK::Unofficial::PhoneNumber) ? phone_number.phone_number : phone_number
132
+ inbound_agent_id = inbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? inbound_agent_id.agent_id : inbound_agent_id
133
+ outbound_agent_id = outbound_agent_id.is_a?(Retell::SDK::Unofficial::Agent) ? outbound_agent_id.agent_id : outbound_agent_id
134
+
135
+ payload = {
136
+ inbound_agent_id: inbound_agent_id,
137
+ nickname: nickname,
138
+ outbound_agent_id: outbound_agent_id
139
+ }.compact
140
+
141
+ options = @client.make_request_options(
142
+ extra_headers: extra_headers,
143
+ extra_query: extra_query,
144
+ extra_body: extra_body,
145
+ timeout: timeout
146
+ )
147
+
148
+ @client.patch("/update-phone-number/#{phone_number}", payload, **options)
149
+ end
150
+
151
+ def delete(
152
+ phone_number,
153
+ extra_headers: nil,
154
+ extra_query: nil,
155
+ extra_body: nil,
156
+ timeout: nil
157
+ )
158
+ phone_number = phone_number.is_a?(Retell::SDK::Unofficial::PhoneNumber) ? phone_number.phone_number : phone_number
159
+
160
+ options = @client.make_request_options(
161
+ extra_headers: extra_headers,
162
+ extra_query: extra_query,
163
+ extra_body: extra_body,
164
+ timeout: timeout
165
+ )
166
+
167
+ @client.delete("/delete-phone-number/#{phone_number}", **options)
168
+ end
169
+
170
+ private
171
+
172
+ def validate_phone_number(value, param_name)
173
+ raise ArgumentError, "#{param_name} must be a valid phone number in E.164 format" unless value.match?(/^\+[1-9]\d{1,14}$/)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,254 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ module API
5
+ class RetellLLM
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def create(
11
+ begin_message: nil,
12
+ general_prompt: nil,
13
+ general_tools: nil,
14
+ inbound_dynamic_variables_webhook_url: nil,
15
+ model: nil,
16
+ model_temperature: nil,
17
+ starting_state: nil,
18
+ states: nil,
19
+ extra_headers: nil,
20
+ extra_query: nil,
21
+ extra_body: nil,
22
+ timeout: nil
23
+ )
24
+ validate_model(model) if model
25
+ validate_model_temperature(model_temperature) if model_temperature
26
+ validate_general_tools(general_tools) if general_tools
27
+ validate_states(states) if states
28
+
29
+ payload = {
30
+ begin_message: begin_message,
31
+ general_prompt: general_prompt,
32
+ general_tools: general_tools,
33
+ inbound_dynamic_variables_webhook_url: inbound_dynamic_variables_webhook_url,
34
+ model: model,
35
+ model_temperature: model_temperature,
36
+ starting_state: starting_state,
37
+ states: states
38
+ }.compact
39
+
40
+ options = @client.make_request_options(
41
+ extra_headers: extra_headers,
42
+ extra_query: extra_query,
43
+ extra_body: extra_body,
44
+ timeout: timeout
45
+ )
46
+
47
+ @client.post('/create-retell-llm', payload, **options)
48
+ end
49
+
50
+ def retrieve(
51
+ llm_or_id,
52
+ extra_headers: nil,
53
+ extra_query: nil,
54
+ extra_body: nil,
55
+ timeout: nil
56
+ )
57
+ llm_id = llm_or_id.is_a?(Retell::SDK::Unofficial::RetellLLM) ? llm_or_id.llm_id : llm_or_id
58
+ validate_non_empty_string(llm_id, 'llm_id')
59
+
60
+ options = @client.make_request_options(
61
+ extra_headers: extra_headers,
62
+ extra_query: extra_query,
63
+ extra_body: extra_body,
64
+ timeout: timeout
65
+ )
66
+
67
+ @client.get("/get-retell-llm/#{llm_id}", **options)
68
+ end
69
+
70
+ def update(
71
+ llm_or_id,
72
+ begin_message: nil,
73
+ general_prompt: nil,
74
+ general_tools: nil,
75
+ inbound_dynamic_variables_webhook_url: nil,
76
+ model: nil,
77
+ model_temperature: nil,
78
+ starting_state: nil,
79
+ states: nil,
80
+ extra_headers: nil,
81
+ extra_query: nil,
82
+ extra_body: nil,
83
+ timeout: nil
84
+ )
85
+ llm_id = llm_or_id.is_a?(Retell::SDK::Unofficial::RetellLLM) ? llm_or_id.llm_id : llm_or_id
86
+ validate_non_empty_string(llm_id, 'llm_id')
87
+
88
+ validate_model(model) if model
89
+ validate_model_temperature(model_temperature) if model_temperature
90
+ validate_general_tools(general_tools) if general_tools
91
+ validate_states(states) if states
92
+
93
+ payload = {
94
+ begin_message: begin_message,
95
+ general_prompt: general_prompt,
96
+ general_tools: general_tools,
97
+ inbound_dynamic_variables_webhook_url: inbound_dynamic_variables_webhook_url,
98
+ model: model,
99
+ model_temperature: model_temperature,
100
+ starting_state: starting_state,
101
+ states: states
102
+ }.compact
103
+
104
+ options = @client.make_request_options(
105
+ extra_headers: extra_headers,
106
+ extra_query: extra_query,
107
+ extra_body: extra_body,
108
+ timeout: timeout
109
+ )
110
+
111
+ @client.patch("/update-retell-llm/#{llm_id}", payload, **options)
112
+ end
113
+
114
+ def list(
115
+ extra_headers: nil,
116
+ extra_query: nil,
117
+ extra_body: nil,
118
+ timeout: nil
119
+ )
120
+ options = @client.make_request_options(
121
+ extra_headers: extra_headers,
122
+ extra_query: extra_query,
123
+ extra_body: extra_body,
124
+ timeout: timeout
125
+ )
126
+
127
+ @client.get('/list-retell-llms', **options)
128
+ end
129
+
130
+ def delete(
131
+ llm_or_id,
132
+ extra_headers: nil,
133
+ extra_query: nil,
134
+ extra_body: nil,
135
+ timeout: nil
136
+ )
137
+ llm_id = llm_or_id.is_a?(Retell::SDK::Unofficial::RetellLLM) ? llm_or_id.llm_id : llm_or_id
138
+ validate_non_empty_string(llm_id, 'llm_id')
139
+
140
+ options = @client.make_request_options(
141
+ extra_headers: extra_headers,
142
+ extra_query: extra_query,
143
+ extra_body: extra_body,
144
+ timeout: timeout
145
+ )
146
+
147
+ @client.delete("/delete-retell-llm/#{llm_id}", **options)
148
+ nil
149
+ end
150
+
151
+ private
152
+
153
+ def validate_model(model)
154
+ valid_models = ['gpt-4o', 'gpt-4o-mini', 'claude-3.5-sonnet', 'claude-3-haiku']
155
+ unless valid_models.include?(model)
156
+ raise ArgumentError, "Invalid model: #{model}. Valid models are: #{valid_models.join(', ')}"
157
+ end
158
+ end
159
+
160
+ def validate_model_temperature(model_temperature)
161
+ unless model_temperature >= 0 && model_temperature <= 1
162
+ raise ArgumentError, "Invalid model temperature: #{model_temperature}. Model temperature must be a between 0 and 1"
163
+ end
164
+ end
165
+
166
+ def validate_general_tools(tools)
167
+ tools.each { |tool| validate_general_tool(tool) }
168
+ end
169
+
170
+ def validate_states(states)
171
+ states.each do |state|
172
+ unless state.is_a?(Hash) && state.key?(:name)
173
+ raise ArgumentError, "Invalid state: each state must be a hash with a 'name' key"
174
+ end
175
+ validate_state(state)
176
+ end
177
+ end
178
+
179
+ def validate_general_tool(tool)
180
+ case tool[:type]
181
+ when 'end_call', 'press_digit'
182
+ validate_tool_keys(tool, required: [:name, :type], optional: [:description])
183
+ when 'transfer_call'
184
+ validate_tool_keys(tool, required: [:name, :number, :type], optional: [:description])
185
+ when 'check_availability_cal', 'book_appointment_cal'
186
+ validate_tool_keys(tool, required: [:cal_api_key, :event_type_id, :name, :type], optional: [:description, :timezone])
187
+ when 'custom'
188
+ validate_custom_tool(tool)
189
+ else
190
+ raise ArgumentError, "Unknown general tool type: #{tool[:type]}"
191
+ end
192
+ end
193
+
194
+ def validate_custom_tool(tool)
195
+ validate_tool_keys(tool,
196
+ required: [:name, :type, :description, :url, :speak_after_execution, :speak_during_execution],
197
+ optional: [:execution_message_description, :timeout_ms, :parameters]
198
+ )
199
+ validate_custom_tool_parameters(tool[:parameters]) if tool[:parameters]
200
+ validate_timeout_ms(tool[:timeout_ms]) if tool[:timeout_ms]
201
+ end
202
+
203
+ def validate_timeout_ms(timeout_ms)
204
+ unless timeout_ms.is_a?(Integer) && timeout_ms >= 1000 && timeout_ms <= 600000
205
+ raise ArgumentError, "timeout_ms must be an integer between 1000 and 600000"
206
+ end
207
+ end
208
+
209
+ def validate_custom_tool_parameters(parameters)
210
+ validate_tool_keys(parameters, required: [:type, :properties], optional: [:required])
211
+ end
212
+
213
+ def validate_state(state)
214
+ validate_tool_keys(state, required: [:name], optional: [:edges, :state_prompt, :tools])
215
+ validate_general_tools(state[:tools]) if state[:tools]
216
+ validate_state_edges(state[:edges]) if state[:edges]
217
+ end
218
+
219
+ def validate_state_edges(edges)
220
+ edges.each { |edge| validate_state_edge(edge) }
221
+ end
222
+
223
+ def validate_state_edge(edge)
224
+ validate_tool_keys(edge,
225
+ required: [:description, :destination_state_name],
226
+ optional: [:parameters]
227
+ )
228
+ validate_state_edge_parameters(edge[:parameters]) if edge[:parameters]
229
+ end
230
+
231
+ def validate_state_edge_parameters(parameters)
232
+ validate_tool_keys(parameters, required: [:type, :properties], optional: [:required])
233
+ end
234
+
235
+ def validate_tool_keys(hash, required: [], optional: [])
236
+ missing_keys = required - hash.keys
237
+ if missing_keys.any?
238
+ raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}"
239
+ end
240
+
241
+ unexpected_keys = hash.keys - (required + optional)
242
+ if unexpected_keys.any?
243
+ raise ArgumentError, "Unexpected keys found: #{unexpected_keys.join(', ')}"
244
+ end
245
+ end
246
+
247
+ def validate_non_empty_string(value, name)
248
+ raise ArgumentError, "#{name} cannot be empty" if value.empty?
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,50 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ module API
5
+ class Voice
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def retrieve(
11
+ voice_or_id,
12
+ extra_headers: nil,
13
+ extra_query: nil,
14
+ extra_body: nil,
15
+ timeout: nil
16
+ )
17
+ voice_id = voice_or_id.is_a?(Retell::SDK::Unofficial::Voice) ? voice_or_id.voice_id : voice_or_id
18
+
19
+ raise ArgumentError, "Expected a non-empty value for `voice_id` but received #{voice_id.inspect}" if voice_id.to_s.empty?
20
+
21
+ options = @client.make_request_options(
22
+ extra_headers: extra_headers,
23
+ extra_query: extra_query,
24
+ extra_body: extra_body,
25
+ timeout: timeout
26
+ )
27
+
28
+ @client.get("/get-voice/#{voice_id}", **options)
29
+ end
30
+
31
+ def list(
32
+ extra_headers: nil,
33
+ extra_query: nil,
34
+ extra_body: nil,
35
+ timeout: nil
36
+ )
37
+ options = @client.make_request_options(
38
+ extra_headers: extra_headers,
39
+ extra_query: extra_query,
40
+ extra_body: extra_body,
41
+ timeout: timeout
42
+ )
43
+
44
+ @client.get('/list-voices', **options)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,81 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class Base
5
+ class << self
6
+ attr_reader :attributes
7
+ end
8
+
9
+ attr_accessor :client
10
+
11
+ def self.inherited(subclass)
12
+ subclass.instance_variable_set(:@attributes, [])
13
+ end
14
+
15
+ def self.each_attribute
16
+ @attributes.each { |attr| yield attr }
17
+ end
18
+
19
+ attr_reader :changed_attributes
20
+
21
+ def self.writeable_attributes
22
+ @writeable_attributes ||= []
23
+ end
24
+
25
+ def initialize(client, raw_response)
26
+ @client = client
27
+ @changed_attributes = {}
28
+ self.class.attributes.each do |attr|
29
+ instance_variable_set("@#{attr}", raw_response[attr])
30
+ end
31
+ end
32
+
33
+ def [](key)
34
+ send(key) if self.class.attributes.include?(key.to_sym)
35
+ end
36
+
37
+ def []=(key, value)
38
+ if self.class.writeable_attributes.include?(key.to_sym)
39
+ instance_variable_set("@#{key}", value)
40
+ @changed_attributes[key.to_sym] = value
41
+ else
42
+ raise KeyError, "key not found or not writable: #{key}"
43
+ end
44
+ end
45
+
46
+ def to_h
47
+ self.class.attributes.each_with_object({}) do |attr, hash|
48
+ hash[attr] = send(attr)
49
+ end
50
+ end
51
+
52
+ def keys
53
+ self.class.attributes
54
+ end
55
+
56
+ def values
57
+ self.class.attributes.map { |attr| send(attr) }
58
+ end
59
+
60
+ def each
61
+ if block_given?
62
+ to_h.each do |key, value|
63
+ yield(key, value)
64
+ end
65
+ to_h
66
+ else
67
+ to_enum(:each)
68
+ end
69
+ end
70
+
71
+ def fetch(key, default = nil)
72
+ if self.class.attributes.include?(key.to_sym)
73
+ send(key)
74
+ else
75
+ block_given? ? yield(key) : (default || raise(KeyError, "key not found: #{key}"))
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,40 @@
1
+ require_relative 'base'
2
+
3
+ module Retell
4
+ module SDK
5
+ module Unofficial
6
+ class BaseCall < Base
7
+ @attributes = [
8
+ :call_id, :agent_id, :call_status, :call_type,
9
+ :start_timestamp, :end_timestamp, :transcript, :recording_url,
10
+ :public_log_url, :call_analysis, :e2e_latency, :llm_latency,
11
+ :llm_websocket_network_rtt_latency, :transcript_object,
12
+ :transcript_with_tool_calls, :disconnection_reason, :metadata,
13
+ :retell_llm_dynamic_variables, :opt_out_sensitive_data_storage
14
+ ]
15
+
16
+ attr_reader *@attributes
17
+
18
+ def id
19
+ call_id
20
+ end
21
+
22
+ def agent
23
+ agent_id
24
+ end
25
+
26
+ def status
27
+ call_status
28
+ end
29
+
30
+ def type
31
+ call_type
32
+ end
33
+
34
+ def analysis
35
+ call_analysis
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class BaseList
5
+ include Enumerable
6
+
7
+ attr_reader :items
8
+
9
+ def initialize(items_key, raw_response)
10
+ @items = raw_response[items_key]
11
+ end
12
+
13
+ def [](index)
14
+ @items[index]
15
+ end
16
+
17
+ def each
18
+ if block_given?
19
+ to_a.each do |value|
20
+ yield(value)
21
+ end
22
+ to_a
23
+ else
24
+ to_enum(:each)
25
+ end
26
+ end
27
+
28
+ def length
29
+ @items.length
30
+ end
31
+
32
+ alias_method :size, :length
33
+
34
+ def to_a
35
+ @items
36
+ end
37
+
38
+ def empty?
39
+ @items.empty?
40
+ end
41
+
42
+ def first
43
+ @items.first
44
+ end
45
+
46
+ def last
47
+ @items.last
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class Call
5
+ def self.new(client, raw_response)
6
+ case raw_response[:call_type]
7
+ when 'web_call'
8
+ Retell::SDK::Unofficial::WebCall.new(client, raw_response)
9
+ when 'phone_call'
10
+ Retell::SDK::Unofficial::PhoneCall.new(client, raw_response)
11
+ else
12
+ raise ArgumentError, "Unknown call type: #{raw_response[:call_type]}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module Retell
2
+ module SDK
3
+ module Unofficial
4
+ class CallList < BaseList
5
+ attr_reader :pagination_key
6
+
7
+ def initialize(raw_response)
8
+ super(:calls, raw_response)
9
+ @pagination_key = raw_response[:pagination_key]
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end