signalwire-agents 0.1.6__py3-none-any.whl → 0.1.8__py3-none-any.whl
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.
- signalwire_agents/__init__.py +1 -1
- signalwire_agents/core/agent_base.py +1507 -1270
- signalwire_agents/core/function_result.py +1031 -1
- signalwire_agents/core/security/session_manager.py +174 -86
- signalwire_agents/core/swml_builder.py +5 -1
- signalwire_agents/core/swml_service.py +90 -49
- signalwire_agents/prefabs/concierge.py +9 -2
- signalwire_agents/prefabs/faq_bot.py +3 -0
- signalwire_agents/prefabs/info_gatherer.py +152 -33
- signalwire_agents/prefabs/receptionist.py +17 -22
- signalwire_agents/prefabs/survey.py +9 -2
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-0.1.8.dist-info}/METADATA +71 -2
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-0.1.8.dist-info}/RECORD +17 -17
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-0.1.8.dist-info}/WHEEL +1 -1
- {signalwire_agents-0.1.6.data → signalwire_agents-0.1.8.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-0.1.8.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-0.1.8.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,19 @@ class SwaigFunctionResult:
|
|
19
19
|
Wrapper around SWAIG function responses that handles proper formatting
|
20
20
|
of response text and actions.
|
21
21
|
|
22
|
+
The result object has three main components:
|
23
|
+
1. response: Text the AI should say back to the user
|
24
|
+
2. action: List of structured actions to execute
|
25
|
+
3. post_process: Whether to let AI take another turn before executing actions
|
26
|
+
|
27
|
+
Post-processing behavior:
|
28
|
+
- post_process=False (default): Execute actions immediately after AI response
|
29
|
+
- post_process=True: Let AI respond to user one more time, then execute actions
|
30
|
+
|
31
|
+
This is useful for confirmation workflows like:
|
32
|
+
"I'll transfer you to sales. Do you have any other questions first?"
|
33
|
+
(AI can handle follow-up, then execute the transfer)
|
34
|
+
|
22
35
|
Example:
|
23
36
|
return SwaigFunctionResult("Found your order")
|
24
37
|
|
@@ -42,16 +55,31 @@ class SwaigFunctionResult:
|
|
42
55
|
{"play": {"url": "music.mp3"}}
|
43
56
|
])
|
44
57
|
)
|
58
|
+
|
59
|
+
# With post-processing enabled
|
60
|
+
return (
|
61
|
+
SwaigFunctionResult("Let me transfer you to billing", post_process=True)
|
62
|
+
.connect("+15551234567", final=True)
|
63
|
+
)
|
64
|
+
|
65
|
+
# Using the connect helper
|
66
|
+
return (
|
67
|
+
SwaigFunctionResult("I'll transfer you to our sales team now")
|
68
|
+
.connect("sales@company.com", final=False, from_addr="+15559876543")
|
69
|
+
)
|
45
70
|
"""
|
46
|
-
def __init__(self, response: Optional[str] = None):
|
71
|
+
def __init__(self, response: Optional[str] = None, post_process: bool = False):
|
47
72
|
"""
|
48
73
|
Initialize a new SWAIG function result
|
49
74
|
|
50
75
|
Args:
|
51
76
|
response: Optional natural language response to include
|
77
|
+
post_process: Whether to let AI take another turn before executing actions.
|
78
|
+
Defaults to False (execute actions immediately after response).
|
52
79
|
"""
|
53
80
|
self.response = response or ""
|
54
81
|
self.action: List[Dict[str, Any]] = []
|
82
|
+
self.post_process = post_process
|
55
83
|
|
56
84
|
def set_response(self, response: str) -> 'SwaigFunctionResult':
|
57
85
|
"""
|
@@ -66,6 +94,23 @@ class SwaigFunctionResult:
|
|
66
94
|
self.response = response
|
67
95
|
return self
|
68
96
|
|
97
|
+
def set_post_process(self, post_process: bool) -> 'SwaigFunctionResult':
|
98
|
+
"""
|
99
|
+
Set whether to enable post-processing for this result.
|
100
|
+
|
101
|
+
Post-processing allows the AI to take one more turn with the user
|
102
|
+
before executing any actions. This is useful for confirmation workflows.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
post_process: True to let AI respond once more before executing actions,
|
106
|
+
False to execute actions immediately after the response.
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
Self for method chaining
|
110
|
+
"""
|
111
|
+
self.post_process = post_process
|
112
|
+
return self
|
113
|
+
|
69
114
|
def add_action(self, name: str, data: Any) -> 'SwaigFunctionResult':
|
70
115
|
"""
|
71
116
|
Add a structured action to the response
|
@@ -93,6 +138,983 @@ class SwaigFunctionResult:
|
|
93
138
|
self.action.extend(actions)
|
94
139
|
return self
|
95
140
|
|
141
|
+
def connect(self, destination: str, final: bool = True, from_addr: Optional[str] = None) -> 'SwaigFunctionResult':
|
142
|
+
"""
|
143
|
+
Add a connect action to transfer/connect the call to another destination.
|
144
|
+
|
145
|
+
This is a convenience method that abstracts the SWML connect verb, so users
|
146
|
+
don't need to manually construct SWML documents.
|
147
|
+
|
148
|
+
Transfer behavior:
|
149
|
+
- final=True: Permanent transfer - call exits the agent completely,
|
150
|
+
SWML replaces the agent and call continues there
|
151
|
+
- final=False: Temporary transfer - if far end hangs up, call returns
|
152
|
+
to the agent to continue the conversation
|
153
|
+
|
154
|
+
Args:
|
155
|
+
destination: Where to connect the call (phone number, SIP address, etc.)
|
156
|
+
final: Whether this is a permanent transfer (True) or temporary (False).
|
157
|
+
Defaults to True for permanent transfers.
|
158
|
+
from_addr: Optional caller ID override (phone number or SIP address).
|
159
|
+
If None, uses the current call's from address.
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
Self for method chaining
|
163
|
+
|
164
|
+
Example:
|
165
|
+
# Permanent transfer to a phone number
|
166
|
+
result.connect("+15551234567", final=True)
|
167
|
+
|
168
|
+
# Temporary transfer to SIP address with custom caller ID
|
169
|
+
result.connect("support@company.com", final=False, from_addr="+15559876543")
|
170
|
+
"""
|
171
|
+
# Build the connect verb parameters
|
172
|
+
connect_params = {"to": destination}
|
173
|
+
if from_addr is not None:
|
174
|
+
connect_params["from"] = from_addr
|
175
|
+
|
176
|
+
# Create the SWML action
|
177
|
+
swml_action = {
|
178
|
+
"SWML": {
|
179
|
+
"sections": {
|
180
|
+
"main": [{"connect": connect_params}]
|
181
|
+
},
|
182
|
+
"version": "1.0.0"
|
183
|
+
},
|
184
|
+
"transfer": str(final).lower() # Convert boolean to "true"/"false" string
|
185
|
+
}
|
186
|
+
|
187
|
+
# Add to actions list
|
188
|
+
self.action.append(swml_action)
|
189
|
+
return self
|
190
|
+
|
191
|
+
def update_global_data(self, data: Dict[str, Any]) -> 'SwaigFunctionResult':
|
192
|
+
"""
|
193
|
+
Update global agent data variables.
|
194
|
+
|
195
|
+
This is a convenience method that abstracts the set_global_data action.
|
196
|
+
Global data persists across the entire agent session and is available
|
197
|
+
in prompt variables and can be accessed by all functions.
|
198
|
+
|
199
|
+
Args:
|
200
|
+
data: Dictionary of key-value pairs to set/update in global data
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
self for method chaining
|
204
|
+
"""
|
205
|
+
action = {"set_global_data": data}
|
206
|
+
return self.add_action("set_global_data", action)
|
207
|
+
|
208
|
+
def execute_swml(self, swml_content, transfer: bool = False) -> 'SwaigFunctionResult':
|
209
|
+
"""
|
210
|
+
Execute SWML content with optional transfer behavior.
|
211
|
+
|
212
|
+
Args:
|
213
|
+
swml_content: Can be:
|
214
|
+
- String: Raw SWML JSON text
|
215
|
+
- Dict: SWML data structure
|
216
|
+
- SWML object: SignalWire SWML SDK object with .to_dict() method
|
217
|
+
transfer: Boolean - whether call should exit agent after execution
|
218
|
+
|
219
|
+
Returns:
|
220
|
+
self for method chaining
|
221
|
+
"""
|
222
|
+
# Detect input type and normalize to appropriate format
|
223
|
+
if isinstance(swml_content, str):
|
224
|
+
# Raw SWML string - use as-is
|
225
|
+
swml_data = swml_content
|
226
|
+
elif hasattr(swml_content, 'to_dict'):
|
227
|
+
# SWML SDK object - convert to dict
|
228
|
+
swml_data = swml_content.to_dict()
|
229
|
+
elif isinstance(swml_content, dict):
|
230
|
+
# Dict - use directly
|
231
|
+
swml_data = swml_content
|
232
|
+
else:
|
233
|
+
raise TypeError("swml_content must be string, dict, or SWML object")
|
234
|
+
|
235
|
+
action = {"SWML": swml_data}
|
236
|
+
if transfer:
|
237
|
+
action["transfer"] = "true"
|
238
|
+
|
239
|
+
return self.add_action("SWML", action)
|
240
|
+
|
241
|
+
def hangup(self) -> 'SwaigFunctionResult':
|
242
|
+
"""
|
243
|
+
Terminate the call.
|
244
|
+
|
245
|
+
Returns:
|
246
|
+
self for method chaining
|
247
|
+
"""
|
248
|
+
action = {"hangup": True}
|
249
|
+
return self.add_action("hangup", action)
|
250
|
+
|
251
|
+
def hold(self, timeout: int = 300) -> 'SwaigFunctionResult':
|
252
|
+
"""
|
253
|
+
Put the call on hold with optional timeout.
|
254
|
+
|
255
|
+
Args:
|
256
|
+
timeout: Timeout in seconds (max 900, default 300)
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
self for method chaining
|
260
|
+
"""
|
261
|
+
# Clamp timeout to valid range
|
262
|
+
timeout = max(0, min(timeout, 900))
|
263
|
+
action = {"hold": timeout}
|
264
|
+
return self.add_action("hold", action)
|
265
|
+
|
266
|
+
def wait_for_user(self, enabled: Optional[bool] = None, timeout: Optional[int] = None, answer_first: bool = False) -> 'SwaigFunctionResult':
|
267
|
+
"""
|
268
|
+
Control how agent waits for user input.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
enabled: Boolean to enable/disable waiting
|
272
|
+
timeout: Number of seconds to wait
|
273
|
+
answer_first: Special "answer_first" mode
|
274
|
+
|
275
|
+
Returns:
|
276
|
+
self for method chaining
|
277
|
+
"""
|
278
|
+
if answer_first:
|
279
|
+
wait_value = "answer_first"
|
280
|
+
elif timeout is not None:
|
281
|
+
wait_value = timeout
|
282
|
+
elif enabled is not None:
|
283
|
+
wait_value = enabled
|
284
|
+
else:
|
285
|
+
wait_value = True
|
286
|
+
|
287
|
+
action = {"wait_for_user": wait_value}
|
288
|
+
return self.add_action("wait_for_user", action)
|
289
|
+
|
290
|
+
def stop(self) -> 'SwaigFunctionResult':
|
291
|
+
"""
|
292
|
+
Stop the agent execution.
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
self for method chaining
|
296
|
+
"""
|
297
|
+
action = {"stop": True}
|
298
|
+
return self.add_action("stop", action)
|
299
|
+
|
300
|
+
def say(self, text: str) -> 'SwaigFunctionResult':
|
301
|
+
"""
|
302
|
+
Make the agent speak specific text.
|
303
|
+
|
304
|
+
Args:
|
305
|
+
text: Text for agent to speak
|
306
|
+
|
307
|
+
Returns:
|
308
|
+
self for method chaining
|
309
|
+
"""
|
310
|
+
action = {"say": text}
|
311
|
+
return self.add_action("say", action)
|
312
|
+
|
313
|
+
def play_background_audio(self, filename: str, wait: bool = False) -> 'SwaigFunctionResult':
|
314
|
+
"""
|
315
|
+
Play audio file in background.
|
316
|
+
|
317
|
+
Args:
|
318
|
+
filename: Audio filename/path
|
319
|
+
wait: Whether to suppress attention-getting behavior during playback
|
320
|
+
|
321
|
+
Returns:
|
322
|
+
self for method chaining
|
323
|
+
"""
|
324
|
+
if wait:
|
325
|
+
action = {"playback_bg": {"file": filename, "wait": True}}
|
326
|
+
else:
|
327
|
+
action = {"playback_bg": filename}
|
328
|
+
return self.add_action("playback_bg", action)
|
329
|
+
|
330
|
+
def stop_background_audio(self) -> 'SwaigFunctionResult':
|
331
|
+
"""
|
332
|
+
Stop currently playing background audio.
|
333
|
+
|
334
|
+
Returns:
|
335
|
+
self for method chaining
|
336
|
+
"""
|
337
|
+
action = {"stop_playback_bg": True}
|
338
|
+
return self.add_action("stop_playback_bg", action)
|
339
|
+
|
340
|
+
def set_end_of_speech_timeout(self, milliseconds: int) -> 'SwaigFunctionResult':
|
341
|
+
"""
|
342
|
+
Adjust end of speech timeout - milliseconds of silence after speaking
|
343
|
+
has been detected to finalize speech recognition.
|
344
|
+
|
345
|
+
Args:
|
346
|
+
milliseconds: Timeout in milliseconds
|
347
|
+
|
348
|
+
Returns:
|
349
|
+
self for method chaining
|
350
|
+
"""
|
351
|
+
action = {"end_of_speech_timeout": milliseconds}
|
352
|
+
return self.add_action("end_of_speech_timeout", action)
|
353
|
+
|
354
|
+
def set_speech_event_timeout(self, milliseconds: int) -> 'SwaigFunctionResult':
|
355
|
+
"""
|
356
|
+
Adjust speech event timeout - milliseconds since last speech detection
|
357
|
+
event to finalize recognition. Works better in noisy environments.
|
358
|
+
|
359
|
+
Args:
|
360
|
+
milliseconds: Timeout in milliseconds
|
361
|
+
|
362
|
+
Returns:
|
363
|
+
self for method chaining
|
364
|
+
"""
|
365
|
+
action = {"speech_event_timeout": milliseconds}
|
366
|
+
return self.add_action("speech_event_timeout", action)
|
367
|
+
|
368
|
+
def remove_global_data(self, keys: Union[str, List[str]]) -> 'SwaigFunctionResult':
|
369
|
+
"""
|
370
|
+
Remove global agent data variables.
|
371
|
+
|
372
|
+
Args:
|
373
|
+
keys: Single key string or list of keys to remove
|
374
|
+
|
375
|
+
Returns:
|
376
|
+
self for method chaining
|
377
|
+
"""
|
378
|
+
action = {"unset_global_data": keys}
|
379
|
+
return self.add_action("unset_global_data", action)
|
380
|
+
|
381
|
+
def set_metadata(self, data: Dict[str, Any]) -> 'SwaigFunctionResult':
|
382
|
+
"""
|
383
|
+
Set metadata scoped to current function's meta_data_token.
|
384
|
+
|
385
|
+
Args:
|
386
|
+
data: Dictionary of key-value pairs for metadata
|
387
|
+
|
388
|
+
Returns:
|
389
|
+
self for method chaining
|
390
|
+
"""
|
391
|
+
action = {"set_meta_data": data}
|
392
|
+
return self.add_action("set_meta_data", action)
|
393
|
+
|
394
|
+
def remove_metadata(self, keys: Union[str, List[str]]) -> 'SwaigFunctionResult':
|
395
|
+
"""
|
396
|
+
Remove metadata from current function's meta_data_token scope.
|
397
|
+
|
398
|
+
Args:
|
399
|
+
keys: Single key string or list of keys to remove
|
400
|
+
|
401
|
+
Returns:
|
402
|
+
self for method chaining
|
403
|
+
"""
|
404
|
+
action = {"unset_meta_data": keys}
|
405
|
+
return self.add_action("unset_meta_data", action)
|
406
|
+
|
407
|
+
def toggle_functions(self, function_toggles: List[Dict[str, Any]]) -> 'SwaigFunctionResult':
|
408
|
+
"""
|
409
|
+
Enable/disable specific SWAIG functions.
|
410
|
+
|
411
|
+
Args:
|
412
|
+
function_toggles: List of dicts with 'function' and 'active' keys
|
413
|
+
|
414
|
+
Returns:
|
415
|
+
self for method chaining
|
416
|
+
"""
|
417
|
+
action = {"toggle_functions": function_toggles}
|
418
|
+
return self.add_action("toggle_functions", action)
|
419
|
+
|
420
|
+
def enable_functions_on_timeout(self, enabled: bool = True) -> 'SwaigFunctionResult':
|
421
|
+
"""
|
422
|
+
Enable function calls on speaker timeout.
|
423
|
+
|
424
|
+
Args:
|
425
|
+
enabled: Whether to enable functions on timeout
|
426
|
+
|
427
|
+
Returns:
|
428
|
+
self for method chaining
|
429
|
+
"""
|
430
|
+
action = {"functions_on_speaker_timeout": enabled}
|
431
|
+
return self.add_action("functions_on_speaker_timeout", action)
|
432
|
+
|
433
|
+
def enable_extensive_data(self, enabled: bool = True) -> 'SwaigFunctionResult':
|
434
|
+
"""
|
435
|
+
Send full data to LLM for this turn only, then use smaller replacement
|
436
|
+
in subsequent turns.
|
437
|
+
|
438
|
+
Args:
|
439
|
+
enabled: Whether to send extensive data this turn only
|
440
|
+
|
441
|
+
Returns:
|
442
|
+
self for method chaining
|
443
|
+
"""
|
444
|
+
action = {"extensive_data": enabled}
|
445
|
+
return self.add_action("extensive_data", action)
|
446
|
+
|
447
|
+
def update_settings(self, settings: Dict[str, Any]) -> 'SwaigFunctionResult':
|
448
|
+
"""
|
449
|
+
Update agent runtime settings.
|
450
|
+
|
451
|
+
Supported settings:
|
452
|
+
- frequency-penalty: Float (-2.0 to 2.0)
|
453
|
+
- presence-penalty: Float (-2.0 to 2.0)
|
454
|
+
- max-tokens: Integer (0 to 4096)
|
455
|
+
- top-p: Float (0.0 to 1.0)
|
456
|
+
- confidence: Float (0.0 to 1.0)
|
457
|
+
- barge-confidence: Float (0.0 to 1.0)
|
458
|
+
- temperature: Float (0.0 to 2.0, clamped to 1.5)
|
459
|
+
|
460
|
+
Args:
|
461
|
+
settings: Dictionary of settings to update
|
462
|
+
|
463
|
+
Returns:
|
464
|
+
self for method chaining
|
465
|
+
"""
|
466
|
+
action = {"settings": settings}
|
467
|
+
return self.add_action("settings", action)
|
468
|
+
|
469
|
+
def switch_context(self, system_prompt: Optional[str] = None, user_prompt: Optional[str] = None,
|
470
|
+
consolidate: bool = False, full_reset: bool = False) -> 'SwaigFunctionResult':
|
471
|
+
"""
|
472
|
+
Change agent context/prompt during conversation.
|
473
|
+
|
474
|
+
Args:
|
475
|
+
system_prompt: New system prompt
|
476
|
+
user_prompt: User message to add
|
477
|
+
consolidate: Whether to summarize existing conversation
|
478
|
+
full_reset: Whether to do complete context reset
|
479
|
+
|
480
|
+
Returns:
|
481
|
+
self for method chaining
|
482
|
+
"""
|
483
|
+
if system_prompt and not user_prompt and not consolidate and not full_reset:
|
484
|
+
# Simple string context switch
|
485
|
+
action = {"context_switch": system_prompt}
|
486
|
+
else:
|
487
|
+
# Advanced object context switch
|
488
|
+
context_data = {}
|
489
|
+
if system_prompt:
|
490
|
+
context_data["system_prompt"] = system_prompt
|
491
|
+
if user_prompt:
|
492
|
+
context_data["user_prompt"] = user_prompt
|
493
|
+
if consolidate:
|
494
|
+
context_data["consolidate"] = True
|
495
|
+
if full_reset:
|
496
|
+
context_data["full_reset"] = True
|
497
|
+
action = {"context_switch": context_data}
|
498
|
+
|
499
|
+
return self.add_action("context_switch", action)
|
500
|
+
|
501
|
+
def simulate_user_input(self, text: str) -> 'SwaigFunctionResult':
|
502
|
+
"""
|
503
|
+
Queue simulated user input.
|
504
|
+
|
505
|
+
Args:
|
506
|
+
text: Text to simulate as user input
|
507
|
+
|
508
|
+
Returns:
|
509
|
+
self for method chaining
|
510
|
+
"""
|
511
|
+
action = {"user_input": text}
|
512
|
+
return self.add_action("user_input", action)
|
513
|
+
|
514
|
+
def send_sms(self, to_number: str, from_number: str, body: Optional[str] = None,
|
515
|
+
media: Optional[List[str]] = None, tags: Optional[List[str]] = None,
|
516
|
+
region: Optional[str] = None) -> 'SwaigFunctionResult':
|
517
|
+
"""
|
518
|
+
Send a text message to a PSTN phone number using SWML.
|
519
|
+
|
520
|
+
This is a virtual helper that generates SWML to send SMS messages.
|
521
|
+
Either body or media (or both) must be provided.
|
522
|
+
|
523
|
+
Args:
|
524
|
+
to_number: Phone number in E.164 format to send to
|
525
|
+
from_number: Phone number in E.164 format to send from
|
526
|
+
body: Body text of the message (optional if media provided)
|
527
|
+
media: Array of URLs to send in the message (optional if body provided)
|
528
|
+
tags: Array of tags to associate with the message for UI searching
|
529
|
+
region: Region to originate the message from
|
530
|
+
|
531
|
+
Returns:
|
532
|
+
self for method chaining
|
533
|
+
|
534
|
+
Raises:
|
535
|
+
ValueError: If neither body nor media is provided
|
536
|
+
"""
|
537
|
+
# Validate that at least body or media is provided
|
538
|
+
if not body and not media:
|
539
|
+
raise ValueError("Either body or media must be provided")
|
540
|
+
|
541
|
+
# Build the send_sms parameters
|
542
|
+
sms_params = {
|
543
|
+
"to_number": to_number,
|
544
|
+
"from_number": from_number
|
545
|
+
}
|
546
|
+
|
547
|
+
# Add optional parameters
|
548
|
+
if body:
|
549
|
+
sms_params["body"] = body
|
550
|
+
if media:
|
551
|
+
sms_params["media"] = media
|
552
|
+
if tags:
|
553
|
+
sms_params["tags"] = tags
|
554
|
+
if region:
|
555
|
+
sms_params["region"] = region
|
556
|
+
|
557
|
+
# Generate SWML document
|
558
|
+
swml_doc = {
|
559
|
+
"version": "1.0.0",
|
560
|
+
"sections": {
|
561
|
+
"main": [
|
562
|
+
{"send_sms": sms_params}
|
563
|
+
]
|
564
|
+
}
|
565
|
+
}
|
566
|
+
|
567
|
+
# Use execute_swml to add the action
|
568
|
+
return self.execute_swml(swml_doc)
|
569
|
+
|
570
|
+
def pay(self, payment_connector_url: str, input_method: str = "dtmf",
|
571
|
+
status_url: Optional[str] = None, payment_method: str = "credit-card",
|
572
|
+
timeout: int = 5, max_attempts: int = 1, security_code: bool = True,
|
573
|
+
postal_code: Union[bool, str] = True, min_postal_code_length: int = 0,
|
574
|
+
token_type: str = "reusable", charge_amount: Optional[str] = None,
|
575
|
+
currency: str = "usd", language: str = "en-US", voice: str = "woman",
|
576
|
+
description: Optional[str] = None, valid_card_types: str = "visa mastercard amex",
|
577
|
+
parameters: Optional[List[Dict[str, str]]] = None,
|
578
|
+
prompts: Optional[List[Dict[str, Any]]] = None) -> 'SwaigFunctionResult':
|
579
|
+
"""
|
580
|
+
Process payment using SWML pay action.
|
581
|
+
|
582
|
+
This is a virtual helper that generates SWML for payment processing.
|
583
|
+
|
584
|
+
Args:
|
585
|
+
payment_connector_url: URL to make payment requests to (required)
|
586
|
+
input_method: Method to collect payment details ("dtmf" or "voice")
|
587
|
+
status_url: URL for status change notifications
|
588
|
+
payment_method: Payment method ("credit-card" currently supported)
|
589
|
+
timeout: Seconds to wait for next digit (default: 5)
|
590
|
+
max_attempts: Number of retry attempts (default: 1)
|
591
|
+
security_code: Whether to prompt for security code (default: True)
|
592
|
+
postal_code: Whether to prompt for postal code, or actual postcode
|
593
|
+
min_postal_code_length: Minimum postal code digits (default: 0)
|
594
|
+
token_type: Payment type ("one-time" or "reusable", default: "reusable")
|
595
|
+
charge_amount: Amount to charge as decimal string
|
596
|
+
currency: Currency code (default: "usd")
|
597
|
+
language: Language for prompts (default: "en-US")
|
598
|
+
voice: TTS voice to use (default: "woman")
|
599
|
+
description: Custom payment description
|
600
|
+
valid_card_types: Space-separated card types (default: "visa mastercard amex")
|
601
|
+
parameters: Array of name/value pairs for payment connector
|
602
|
+
prompts: Array of custom prompt configurations
|
603
|
+
|
604
|
+
Returns:
|
605
|
+
self for method chaining
|
606
|
+
"""
|
607
|
+
# Build the pay parameters
|
608
|
+
pay_params = {
|
609
|
+
"payment_connector_url": payment_connector_url,
|
610
|
+
"input": input_method,
|
611
|
+
"payment_method": payment_method,
|
612
|
+
"timeout": str(timeout),
|
613
|
+
"max_attempts": str(max_attempts),
|
614
|
+
"security_code": str(security_code).lower(),
|
615
|
+
"min_postal_code_length": str(min_postal_code_length),
|
616
|
+
"token_type": token_type,
|
617
|
+
"currency": currency,
|
618
|
+
"language": language,
|
619
|
+
"voice": voice,
|
620
|
+
"valid_card_types": valid_card_types
|
621
|
+
}
|
622
|
+
|
623
|
+
# Handle postal_code (can be boolean or string)
|
624
|
+
if isinstance(postal_code, bool):
|
625
|
+
pay_params["postal_code"] = str(postal_code).lower()
|
626
|
+
else:
|
627
|
+
pay_params["postal_code"] = postal_code
|
628
|
+
|
629
|
+
# Add optional parameters
|
630
|
+
if status_url:
|
631
|
+
pay_params["status_url"] = status_url
|
632
|
+
if charge_amount:
|
633
|
+
pay_params["charge_amount"] = charge_amount
|
634
|
+
if description:
|
635
|
+
pay_params["description"] = description
|
636
|
+
if parameters:
|
637
|
+
pay_params["parameters"] = parameters
|
638
|
+
if prompts:
|
639
|
+
pay_params["prompts"] = prompts
|
640
|
+
|
641
|
+
# Generate SWML document
|
642
|
+
swml_doc = {
|
643
|
+
"version": "1.0.0",
|
644
|
+
"sections": {
|
645
|
+
"main": [
|
646
|
+
{"pay": pay_params}
|
647
|
+
]
|
648
|
+
}
|
649
|
+
}
|
650
|
+
|
651
|
+
# Use execute_swml to add the action
|
652
|
+
return self.execute_swml(swml_doc)
|
653
|
+
|
654
|
+
def record_call(self, control_id: Optional[str] = None, stereo: bool = False,
|
655
|
+
format: str = "wav", direction: str = "both",
|
656
|
+
terminators: Optional[str] = None, beep: bool = False,
|
657
|
+
input_sensitivity: float = 44.0, initial_timeout: float = 0.0,
|
658
|
+
end_silence_timeout: float = 0.0, max_length: Optional[float] = None,
|
659
|
+
status_url: Optional[str] = None) -> 'SwaigFunctionResult':
|
660
|
+
"""
|
661
|
+
Start background call recording using SWML.
|
662
|
+
|
663
|
+
This is a virtual helper that generates SWML to start recording the call
|
664
|
+
in the background. Unlike foreground recording, the script continues
|
665
|
+
executing while recording happens in the background.
|
666
|
+
|
667
|
+
Args:
|
668
|
+
control_id: Identifier for this recording (for use with stop_record_call)
|
669
|
+
stereo: Record in stereo (default: False)
|
670
|
+
format: Recording format - "wav" or "mp3" (default: "wav")
|
671
|
+
direction: Audio direction - "speak", "listen", or "both" (default: "both")
|
672
|
+
terminators: Digits that stop recording when pressed
|
673
|
+
beep: Play beep before recording (default: False)
|
674
|
+
input_sensitivity: Input sensitivity for recording (default: 44.0)
|
675
|
+
initial_timeout: Time in seconds to wait for speech start (default: 0.0)
|
676
|
+
end_silence_timeout: Time in seconds to wait in silence before ending (default: 0.0)
|
677
|
+
max_length: Maximum recording length in seconds
|
678
|
+
status_url: URL to send recording status events to
|
679
|
+
|
680
|
+
Returns:
|
681
|
+
self for method chaining
|
682
|
+
"""
|
683
|
+
# Validate format parameter
|
684
|
+
if format not in ["wav", "mp3"]:
|
685
|
+
raise ValueError("format must be 'wav' or 'mp3'")
|
686
|
+
|
687
|
+
# Validate direction parameter
|
688
|
+
if direction not in ["speak", "listen", "both"]:
|
689
|
+
raise ValueError("direction must be 'speak', 'listen', or 'both'")
|
690
|
+
|
691
|
+
# Build the record_call parameters
|
692
|
+
record_params = {
|
693
|
+
"stereo": stereo,
|
694
|
+
"format": format,
|
695
|
+
"direction": direction,
|
696
|
+
"beep": beep,
|
697
|
+
"input_sensitivity": input_sensitivity,
|
698
|
+
"initial_timeout": initial_timeout,
|
699
|
+
"end_silence_timeout": end_silence_timeout
|
700
|
+
}
|
701
|
+
|
702
|
+
# Add optional parameters
|
703
|
+
if control_id:
|
704
|
+
record_params["control_id"] = control_id
|
705
|
+
if terminators:
|
706
|
+
record_params["terminators"] = terminators
|
707
|
+
if max_length:
|
708
|
+
record_params["max_length"] = max_length
|
709
|
+
if status_url:
|
710
|
+
record_params["status_url"] = status_url
|
711
|
+
|
712
|
+
# Generate SWML document
|
713
|
+
swml_doc = {
|
714
|
+
"version": "1.0.0",
|
715
|
+
"sections": {
|
716
|
+
"main": [
|
717
|
+
{"record_call": record_params}
|
718
|
+
]
|
719
|
+
}
|
720
|
+
}
|
721
|
+
|
722
|
+
# Use execute_swml to add the action
|
723
|
+
return self.execute_swml(swml_doc)
|
724
|
+
|
725
|
+
def stop_record_call(self, control_id: Optional[str] = None) -> 'SwaigFunctionResult':
|
726
|
+
"""
|
727
|
+
Stop an active background call recording using SWML.
|
728
|
+
|
729
|
+
This is a virtual helper that generates SWML to stop a recording that
|
730
|
+
was started with record_call().
|
731
|
+
|
732
|
+
Args:
|
733
|
+
control_id: Identifier for the recording to stop. If not provided,
|
734
|
+
the most recent recording will be stopped.
|
735
|
+
|
736
|
+
Returns:
|
737
|
+
self for method chaining
|
738
|
+
"""
|
739
|
+
# Build the stop_record_call parameters
|
740
|
+
stop_params = {}
|
741
|
+
if control_id:
|
742
|
+
stop_params["control_id"] = control_id
|
743
|
+
|
744
|
+
# Generate SWML document
|
745
|
+
swml_doc = {
|
746
|
+
"version": "1.0.0",
|
747
|
+
"sections": {
|
748
|
+
"main": [
|
749
|
+
{"stop_record_call": stop_params}
|
750
|
+
]
|
751
|
+
}
|
752
|
+
}
|
753
|
+
|
754
|
+
# Use execute_swml to add the action
|
755
|
+
return self.execute_swml(swml_doc)
|
756
|
+
|
757
|
+
def join_room(self, name: str) -> 'SwaigFunctionResult':
|
758
|
+
"""
|
759
|
+
Join a RELAY room using SWML.
|
760
|
+
|
761
|
+
This is a virtual helper that generates SWML to join a RELAY room,
|
762
|
+
which enables multi-party communication and collaboration.
|
763
|
+
|
764
|
+
Args:
|
765
|
+
name: The name of the room to join (required)
|
766
|
+
|
767
|
+
Returns:
|
768
|
+
self for method chaining
|
769
|
+
"""
|
770
|
+
# Build the join_room parameters
|
771
|
+
join_params = {"name": name}
|
772
|
+
|
773
|
+
# Generate SWML document
|
774
|
+
swml_doc = {
|
775
|
+
"version": "1.0.0",
|
776
|
+
"sections": {
|
777
|
+
"main": [
|
778
|
+
{"join_room": join_params}
|
779
|
+
]
|
780
|
+
}
|
781
|
+
}
|
782
|
+
|
783
|
+
# Use execute_swml to add the action
|
784
|
+
return self.execute_swml(swml_doc)
|
785
|
+
|
786
|
+
def sip_refer(self, to_uri: str) -> 'SwaigFunctionResult':
|
787
|
+
"""
|
788
|
+
Send SIP REFER to a SIP call using SWML.
|
789
|
+
|
790
|
+
This is a virtual helper that generates SWML to send a SIP REFER
|
791
|
+
message, which is used for call transfer in SIP environments.
|
792
|
+
|
793
|
+
Args:
|
794
|
+
to_uri: The SIP URI to send the REFER to (required)
|
795
|
+
|
796
|
+
Returns:
|
797
|
+
self for method chaining
|
798
|
+
"""
|
799
|
+
# Build the sip_refer parameters
|
800
|
+
refer_params = {"to_uri": to_uri}
|
801
|
+
|
802
|
+
# Generate SWML document
|
803
|
+
swml_doc = {
|
804
|
+
"version": "1.0.0",
|
805
|
+
"sections": {
|
806
|
+
"main": [
|
807
|
+
{"sip_refer": refer_params}
|
808
|
+
]
|
809
|
+
}
|
810
|
+
}
|
811
|
+
|
812
|
+
# Use execute_swml to add the action
|
813
|
+
return self.execute_swml(swml_doc)
|
814
|
+
|
815
|
+
def join_conference(self, name: str, muted: bool = False, beep: str = "true",
|
816
|
+
start_on_enter: bool = True, end_on_exit: bool = False,
|
817
|
+
wait_url: Optional[str] = None, max_participants: int = 250,
|
818
|
+
record: str = "do-not-record", region: Optional[str] = None,
|
819
|
+
trim: str = "trim-silence", coach: Optional[str] = None,
|
820
|
+
status_callback_event: Optional[str] = None,
|
821
|
+
status_callback: Optional[str] = None,
|
822
|
+
status_callback_method: str = "POST",
|
823
|
+
recording_status_callback: Optional[str] = None,
|
824
|
+
recording_status_callback_method: str = "POST",
|
825
|
+
recording_status_callback_event: str = "completed",
|
826
|
+
result: Optional[Any] = None) -> 'SwaigFunctionResult':
|
827
|
+
"""
|
828
|
+
Join an ad-hoc audio conference with RELAY and CXML calls using SWML.
|
829
|
+
|
830
|
+
This is a virtual helper that generates SWML to join audio conferences
|
831
|
+
with extensive configuration options for call management and recording.
|
832
|
+
|
833
|
+
Args:
|
834
|
+
name: Name of conference (required)
|
835
|
+
muted: Whether to join muted (default: False)
|
836
|
+
beep: Beep configuration - "true", "false", "onEnter", "onExit" (default: "true")
|
837
|
+
start_on_enter: Whether conference starts when this participant enters (default: True)
|
838
|
+
end_on_exit: Whether conference ends when this participant exits (default: False)
|
839
|
+
wait_url: SWML URL for hold music (default: None for default hold music)
|
840
|
+
max_participants: Maximum participants <= 250 (default: 250)
|
841
|
+
record: Recording mode - "do-not-record", "record-from-start" (default: "do-not-record")
|
842
|
+
region: Conference region (default: None)
|
843
|
+
trim: Trim silence - "trim-silence", "do-not-trim" (default: "trim-silence")
|
844
|
+
coach: SWML Call ID or CXML CallSid for coaching (default: None)
|
845
|
+
status_callback_event: Events to report - "start end join leave mute hold modify speaker announcement" (default: None)
|
846
|
+
status_callback: URL for status callbacks (default: None)
|
847
|
+
status_callback_method: HTTP method - "GET", "POST" (default: "POST")
|
848
|
+
recording_status_callback: URL for recording status callbacks (default: None)
|
849
|
+
recording_status_callback_method: HTTP method - "GET", "POST" (default: "POST")
|
850
|
+
recording_status_callback_event: Recording events - "in-progress completed absent" (default: "completed")
|
851
|
+
result: Switch on return_value when object {} or cond when array [] (default: None)
|
852
|
+
|
853
|
+
Returns:
|
854
|
+
self for method chaining
|
855
|
+
|
856
|
+
Raises:
|
857
|
+
ValueError: If beep value is invalid or max_participants exceeds 250
|
858
|
+
"""
|
859
|
+
# Validate beep parameter
|
860
|
+
valid_beep_values = ["true", "false", "onEnter", "onExit"]
|
861
|
+
if beep not in valid_beep_values:
|
862
|
+
raise ValueError(f"beep must be one of {valid_beep_values}")
|
863
|
+
|
864
|
+
# Validate max_participants
|
865
|
+
if max_participants <= 0 or max_participants > 250:
|
866
|
+
raise ValueError("max_participants must be a positive integer <= 250")
|
867
|
+
|
868
|
+
# Validate record parameter
|
869
|
+
valid_record_values = ["do-not-record", "record-from-start"]
|
870
|
+
if record not in valid_record_values:
|
871
|
+
raise ValueError(f"record must be one of {valid_record_values}")
|
872
|
+
|
873
|
+
# Validate trim parameter
|
874
|
+
valid_trim_values = ["trim-silence", "do-not-trim"]
|
875
|
+
if trim not in valid_trim_values:
|
876
|
+
raise ValueError(f"trim must be one of {valid_trim_values}")
|
877
|
+
|
878
|
+
# Validate status_callback_method
|
879
|
+
valid_methods = ["GET", "POST"]
|
880
|
+
if status_callback_method not in valid_methods:
|
881
|
+
raise ValueError(f"status_callback_method must be one of {valid_methods}")
|
882
|
+
if recording_status_callback_method not in valid_methods:
|
883
|
+
raise ValueError(f"recording_status_callback_method must be one of {valid_methods}")
|
884
|
+
|
885
|
+
# Build the join_conference parameters - start with required parameter
|
886
|
+
if isinstance(name, str) and not name.strip():
|
887
|
+
raise ValueError("name cannot be empty")
|
888
|
+
|
889
|
+
# For simple case, can just be the conference name
|
890
|
+
if (not muted and beep == "true" and start_on_enter and not end_on_exit and
|
891
|
+
wait_url is None and max_participants == 250 and record == "do-not-record" and
|
892
|
+
region is None and trim == "trim-silence" and coach is None and
|
893
|
+
status_callback_event is None and status_callback is None and
|
894
|
+
status_callback_method == "POST" and recording_status_callback is None and
|
895
|
+
recording_status_callback_method == "POST" and recording_status_callback_event == "completed" and
|
896
|
+
result is None):
|
897
|
+
# Simple form - just the conference name
|
898
|
+
join_params = name
|
899
|
+
else:
|
900
|
+
# Full object form with parameters
|
901
|
+
join_params = {"name": name}
|
902
|
+
|
903
|
+
# Add non-default parameters
|
904
|
+
if muted:
|
905
|
+
join_params["muted"] = muted
|
906
|
+
if beep != "true":
|
907
|
+
join_params["beep"] = beep
|
908
|
+
if not start_on_enter:
|
909
|
+
join_params["start_on_enter"] = start_on_enter
|
910
|
+
if end_on_exit:
|
911
|
+
join_params["end_on_exit"] = end_on_exit
|
912
|
+
if wait_url:
|
913
|
+
join_params["wait_url"] = wait_url
|
914
|
+
if max_participants != 250:
|
915
|
+
join_params["max_participants"] = max_participants
|
916
|
+
if record != "do-not-record":
|
917
|
+
join_params["record"] = record
|
918
|
+
if region:
|
919
|
+
join_params["region"] = region
|
920
|
+
if trim != "trim-silence":
|
921
|
+
join_params["trim"] = trim
|
922
|
+
if coach:
|
923
|
+
join_params["coach"] = coach
|
924
|
+
if status_callback_event:
|
925
|
+
join_params["status_callback_event"] = status_callback_event
|
926
|
+
if status_callback:
|
927
|
+
join_params["status_callback"] = status_callback
|
928
|
+
if status_callback_method != "POST":
|
929
|
+
join_params["status_callback_method"] = status_callback_method
|
930
|
+
if recording_status_callback:
|
931
|
+
join_params["recording_status_callback"] = recording_status_callback
|
932
|
+
if recording_status_callback_method != "POST":
|
933
|
+
join_params["recording_status_callback_method"] = recording_status_callback_method
|
934
|
+
if recording_status_callback_event != "completed":
|
935
|
+
join_params["recording_status_callback_event"] = recording_status_callback_event
|
936
|
+
if result is not None:
|
937
|
+
join_params["result"] = result
|
938
|
+
|
939
|
+
# Generate SWML document
|
940
|
+
swml_doc = {
|
941
|
+
"version": "1.0.0",
|
942
|
+
"sections": {
|
943
|
+
"main": [
|
944
|
+
{"join_conference": join_params}
|
945
|
+
]
|
946
|
+
}
|
947
|
+
}
|
948
|
+
|
949
|
+
# Use execute_swml to add the action
|
950
|
+
return self.execute_swml(swml_doc)
|
951
|
+
|
952
|
+
def tap(self, uri: str, control_id: Optional[str] = None, direction: str = "both",
|
953
|
+
codec: str = "PCMU", rtp_ptime: int = 20,
|
954
|
+
status_url: Optional[str] = None) -> 'SwaigFunctionResult':
|
955
|
+
"""
|
956
|
+
Start background call tap using SWML.
|
957
|
+
|
958
|
+
This is a virtual helper that generates SWML to start background call tapping.
|
959
|
+
Media is streamed over Websocket or RTP to customer controlled URI.
|
960
|
+
|
961
|
+
Args:
|
962
|
+
uri: Destination of tap media stream (required)
|
963
|
+
Formats: rtp://IP:port, ws://example.com, or wss://example.com
|
964
|
+
control_id: Identifier for this tap to use with stop_tap (optional)
|
965
|
+
Default is generated and stored in tap_control_id variable
|
966
|
+
direction: Direction of audio to tap (default: "both")
|
967
|
+
"speak" = what party says
|
968
|
+
"hear" = what party hears
|
969
|
+
"both" = what party hears and says
|
970
|
+
codec: Codec for tap media stream - "PCMU" or "PCMA" (default: "PCMU")
|
971
|
+
rtp_ptime: Packetization time in milliseconds for RTP (default: 20)
|
972
|
+
status_url: URL for status change requests (optional)
|
973
|
+
|
974
|
+
Returns:
|
975
|
+
self for method chaining
|
976
|
+
|
977
|
+
Raises:
|
978
|
+
ValueError: If direction or codec values are invalid
|
979
|
+
"""
|
980
|
+
# Validate direction parameter
|
981
|
+
valid_directions = ["speak", "hear", "both"]
|
982
|
+
if direction not in valid_directions:
|
983
|
+
raise ValueError(f"direction must be one of {valid_directions}")
|
984
|
+
|
985
|
+
# Validate codec parameter
|
986
|
+
valid_codecs = ["PCMU", "PCMA"]
|
987
|
+
if codec not in valid_codecs:
|
988
|
+
raise ValueError(f"codec must be one of {valid_codecs}")
|
989
|
+
|
990
|
+
# Validate rtp_ptime
|
991
|
+
if rtp_ptime <= 0:
|
992
|
+
raise ValueError("rtp_ptime must be a positive integer")
|
993
|
+
|
994
|
+
# Build the tap parameters
|
995
|
+
tap_params = {"uri": uri}
|
996
|
+
|
997
|
+
# Add optional parameters if they differ from defaults
|
998
|
+
if control_id:
|
999
|
+
tap_params["control_id"] = control_id
|
1000
|
+
if direction != "both":
|
1001
|
+
tap_params["direction"] = direction
|
1002
|
+
if codec != "PCMU":
|
1003
|
+
tap_params["codec"] = codec
|
1004
|
+
if rtp_ptime != 20:
|
1005
|
+
tap_params["rtp_ptime"] = rtp_ptime
|
1006
|
+
if status_url:
|
1007
|
+
tap_params["status_url"] = status_url
|
1008
|
+
|
1009
|
+
# Generate SWML document
|
1010
|
+
swml_doc = {
|
1011
|
+
"version": "1.0.0",
|
1012
|
+
"sections": {
|
1013
|
+
"main": [
|
1014
|
+
{"tap": tap_params}
|
1015
|
+
]
|
1016
|
+
}
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
# Use execute_swml to add the action
|
1020
|
+
return self.execute_swml(swml_doc)
|
1021
|
+
|
1022
|
+
def stop_tap(self, control_id: Optional[str] = None) -> 'SwaigFunctionResult':
|
1023
|
+
"""
|
1024
|
+
Stop an active tap stream using SWML.
|
1025
|
+
|
1026
|
+
This is a virtual helper that generates SWML to stop a tap stream
|
1027
|
+
that was started with tap().
|
1028
|
+
|
1029
|
+
Args:
|
1030
|
+
control_id: ID of the tap to stop (optional)
|
1031
|
+
If not set, the last tap started will be stopped
|
1032
|
+
|
1033
|
+
Returns:
|
1034
|
+
self for method chaining
|
1035
|
+
"""
|
1036
|
+
# Build the stop_tap parameters
|
1037
|
+
if control_id:
|
1038
|
+
stop_params = {"control_id": control_id}
|
1039
|
+
else:
|
1040
|
+
# For simple case with no control_id, use empty object
|
1041
|
+
stop_params = {}
|
1042
|
+
|
1043
|
+
# Generate SWML document
|
1044
|
+
swml_doc = {
|
1045
|
+
"version": "1.0.0",
|
1046
|
+
"sections": {
|
1047
|
+
"main": [
|
1048
|
+
{"stop_tap": stop_params}
|
1049
|
+
]
|
1050
|
+
}
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
# Use execute_swml to add the action
|
1054
|
+
return self.execute_swml(swml_doc)
|
1055
|
+
|
1056
|
+
@staticmethod
|
1057
|
+
def create_payment_prompt(for_situation: str, actions: List[Dict[str, str]],
|
1058
|
+
card_type: Optional[str] = None,
|
1059
|
+
error_type: Optional[str] = None) -> Dict[str, Any]:
|
1060
|
+
"""
|
1061
|
+
Create a payment prompt structure for use with pay() method.
|
1062
|
+
|
1063
|
+
Args:
|
1064
|
+
for_situation: Situation to use prompt for (e.g., "payment-card-number")
|
1065
|
+
actions: List of actions with 'type' and 'phrase' keys
|
1066
|
+
card_type: Space-separated card types for this prompt
|
1067
|
+
error_type: Space-separated error types for this prompt
|
1068
|
+
|
1069
|
+
Returns:
|
1070
|
+
Dictionary representing the prompt structure
|
1071
|
+
"""
|
1072
|
+
prompt = {
|
1073
|
+
"for": for_situation,
|
1074
|
+
"actions": actions
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
if card_type:
|
1078
|
+
prompt["card_type"] = card_type
|
1079
|
+
if error_type:
|
1080
|
+
prompt["error_type"] = error_type
|
1081
|
+
|
1082
|
+
return prompt
|
1083
|
+
|
1084
|
+
@staticmethod
|
1085
|
+
def create_payment_action(action_type: str, phrase: str) -> Dict[str, str]:
|
1086
|
+
"""
|
1087
|
+
Create a payment action for use in payment prompts.
|
1088
|
+
|
1089
|
+
Args:
|
1090
|
+
action_type: "Say" for text-to-speech or "Play" for audio file
|
1091
|
+
phrase: Sentence to say or URL to play
|
1092
|
+
|
1093
|
+
Returns:
|
1094
|
+
Dictionary representing the action
|
1095
|
+
"""
|
1096
|
+
return {
|
1097
|
+
"type": action_type,
|
1098
|
+
"phrase": phrase
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
@staticmethod
|
1102
|
+
def create_payment_parameter(name: str, value: str) -> Dict[str, str]:
|
1103
|
+
"""
|
1104
|
+
Create a payment parameter for use with pay() method.
|
1105
|
+
|
1106
|
+
Args:
|
1107
|
+
name: Parameter name
|
1108
|
+
value: Parameter value
|
1109
|
+
|
1110
|
+
Returns:
|
1111
|
+
Dictionary representing the parameter
|
1112
|
+
"""
|
1113
|
+
return {
|
1114
|
+
"name": name,
|
1115
|
+
"value": value
|
1116
|
+
}
|
1117
|
+
|
96
1118
|
def to_dict(self) -> Dict[str, Any]:
|
97
1119
|
"""
|
98
1120
|
Convert to the JSON structure expected by SWAIG
|
@@ -101,6 +1123,9 @@ class SwaigFunctionResult:
|
|
101
1123
|
- 'response': Text to be spoken by the AI
|
102
1124
|
- 'action': Array of action objects
|
103
1125
|
|
1126
|
+
Optional:
|
1127
|
+
- 'post_process': Boolean controlling when actions execute
|
1128
|
+
|
104
1129
|
Returns:
|
105
1130
|
Dictionary in SWAIG function response format
|
106
1131
|
"""
|
@@ -115,6 +1140,11 @@ class SwaigFunctionResult:
|
|
115
1140
|
if self.action:
|
116
1141
|
result["action"] = self.action
|
117
1142
|
|
1143
|
+
# Add post_process if enabled and we have actions
|
1144
|
+
# (post_process only matters when there are actions to execute)
|
1145
|
+
if self.post_process and self.action:
|
1146
|
+
result["post_process"] = True
|
1147
|
+
|
118
1148
|
# Ensure we have at least one of response or action
|
119
1149
|
if not result:
|
120
1150
|
# Default response if neither is present
|