signalwire-agents 0.1.10__py3-none-any.whl → 0.1.12__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 +43 -4
- signalwire_agents/agent_server.py +268 -15
- signalwire_agents/cli/__init__.py +9 -0
- signalwire_agents/cli/build_search.py +457 -0
- signalwire_agents/cli/test_swaig.py +2609 -0
- signalwire_agents/core/agent_base.py +691 -82
- signalwire_agents/core/contexts.py +289 -0
- signalwire_agents/core/data_map.py +499 -0
- signalwire_agents/core/function_result.py +57 -10
- signalwire_agents/core/logging_config.py +232 -0
- signalwire_agents/core/skill_base.py +27 -37
- signalwire_agents/core/skill_manager.py +89 -23
- signalwire_agents/core/swaig_function.py +13 -1
- signalwire_agents/core/swml_handler.py +37 -13
- signalwire_agents/core/swml_service.py +37 -28
- signalwire_agents/search/__init__.py +131 -0
- signalwire_agents/search/document_processor.py +764 -0
- signalwire_agents/search/index_builder.py +534 -0
- signalwire_agents/search/query_processor.py +371 -0
- signalwire_agents/search/search_engine.py +383 -0
- signalwire_agents/search/search_service.py +251 -0
- signalwire_agents/skills/datasphere/__init__.py +12 -0
- signalwire_agents/skills/datasphere/skill.py +229 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +1 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +156 -0
- signalwire_agents/skills/datetime/skill.py +9 -5
- signalwire_agents/skills/joke/__init__.py +1 -0
- signalwire_agents/skills/joke/skill.py +88 -0
- signalwire_agents/skills/math/skill.py +9 -6
- signalwire_agents/skills/native_vector_search/__init__.py +1 -0
- signalwire_agents/skills/native_vector_search/skill.py +352 -0
- signalwire_agents/skills/registry.py +10 -4
- signalwire_agents/skills/web_search/skill.py +57 -21
- signalwire_agents/skills/wikipedia/__init__.py +9 -0
- signalwire_agents/skills/wikipedia/skill.py +180 -0
- signalwire_agents/utils/__init__.py +14 -0
- signalwire_agents/utils/schema_utils.py +111 -44
- signalwire_agents-0.1.12.dist-info/METADATA +863 -0
- signalwire_agents-0.1.12.dist-info/RECORD +67 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/WHEEL +1 -1
- signalwire_agents-0.1.12.dist-info/entry_points.txt +3 -0
- signalwire_agents-0.1.10.dist-info/METADATA +0 -319
- signalwire_agents-0.1.10.dist-info/RECORD +0 -44
- {signalwire_agents-0.1.10.data → signalwire_agents-0.1.12.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,499 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""
|
11
|
+
DataMap class for building SWAIG data_map configurations
|
12
|
+
"""
|
13
|
+
|
14
|
+
from typing import Dict, List, Any, Optional, Union, Pattern, Tuple
|
15
|
+
import re
|
16
|
+
from .function_result import SwaigFunctionResult
|
17
|
+
|
18
|
+
|
19
|
+
class DataMap:
|
20
|
+
"""
|
21
|
+
Builder class for creating SWAIG data_map configurations.
|
22
|
+
|
23
|
+
This provides a fluent interface for building data_map tools that execute
|
24
|
+
on the SignalWire server without requiring webhook endpoints. Works similar
|
25
|
+
to SwaigFunctionResult but for building data_map structures.
|
26
|
+
|
27
|
+
Example usage:
|
28
|
+
# Simple API call - output goes inside webhook
|
29
|
+
data_map = (DataMap('get_weather')
|
30
|
+
.purpose('Get current weather information')
|
31
|
+
.parameter('location', 'string', 'City name', required=True)
|
32
|
+
.webhook('GET', 'https://api.weather.com/v1/current?key=API_KEY&q=${location}')
|
33
|
+
.output(SwaigFunctionResult('Weather in ${location}: ${response.current.condition.text}, ${response.current.temp_f}°F'))
|
34
|
+
)
|
35
|
+
|
36
|
+
# Multiple webhooks with fallback
|
37
|
+
data_map = (DataMap('search_multi')
|
38
|
+
.purpose('Search with fallback APIs')
|
39
|
+
.parameter('query', 'string', 'Search query', required=True)
|
40
|
+
.webhook('GET', 'https://api.primary.com/search?q=${query}')
|
41
|
+
.output(SwaigFunctionResult('Primary result: ${response.title}'))
|
42
|
+
.webhook('GET', 'https://api.fallback.com/search?q=${query}')
|
43
|
+
.output(SwaigFunctionResult('Fallback result: ${response.title}'))
|
44
|
+
.fallback_output(SwaigFunctionResult('Sorry, all search APIs are unavailable'))
|
45
|
+
)
|
46
|
+
|
47
|
+
# Expression-based responses (no API calls)
|
48
|
+
data_map = (DataMap('file_control')
|
49
|
+
.purpose('Control file playback')
|
50
|
+
.parameter('command', 'string', 'Playback command')
|
51
|
+
.parameter('filename', 'string', 'File to control', required=False)
|
52
|
+
.expression('${args.command}', r'start.*', SwaigFunctionResult().add_action('start_playbook', {'file': '${args.filename}'}))
|
53
|
+
.expression('${args.command}', r'stop.*', SwaigFunctionResult().add_action('stop_playback', True))
|
54
|
+
)
|
55
|
+
|
56
|
+
# API with array processing
|
57
|
+
data_map = (DataMap('search_docs')
|
58
|
+
.purpose('Search documentation')
|
59
|
+
.parameter('query', 'string', 'Search query', required=True)
|
60
|
+
.webhook('POST', 'https://api.docs.com/search', headers={'Authorization': 'Bearer TOKEN'})
|
61
|
+
.body({'query': '${query}', 'limit': 3})
|
62
|
+
.output(SwaigFunctionResult('Found: ${response.results[0].title} - ${response.results[0].summary}'))
|
63
|
+
.foreach('${response.results}')
|
64
|
+
)
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self, function_name: str):
|
68
|
+
"""
|
69
|
+
Initialize a new DataMap builder
|
70
|
+
|
71
|
+
Args:
|
72
|
+
function_name: Name of the SWAIG function this data_map will create
|
73
|
+
"""
|
74
|
+
self.function_name = function_name
|
75
|
+
self._purpose = ""
|
76
|
+
self._parameters = {}
|
77
|
+
self._expressions = []
|
78
|
+
self._webhooks = []
|
79
|
+
self._output = None
|
80
|
+
self._error_keys = []
|
81
|
+
|
82
|
+
def purpose(self, description: str) -> 'DataMap':
|
83
|
+
"""
|
84
|
+
Set the function description/purpose
|
85
|
+
|
86
|
+
Args:
|
87
|
+
description: Human-readable description of what this function does
|
88
|
+
|
89
|
+
Returns:
|
90
|
+
Self for method chaining
|
91
|
+
"""
|
92
|
+
self._purpose = description
|
93
|
+
return self
|
94
|
+
|
95
|
+
def description(self, description: str) -> 'DataMap':
|
96
|
+
"""
|
97
|
+
Set the function description (alias for purpose)
|
98
|
+
|
99
|
+
Args:
|
100
|
+
description: Human-readable description of what this function does
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
Self for method chaining
|
104
|
+
"""
|
105
|
+
return self.purpose(description)
|
106
|
+
|
107
|
+
def parameter(self, name: str, param_type: str, description: str,
|
108
|
+
required: bool = False, enum: Optional[List[str]] = None) -> 'DataMap':
|
109
|
+
"""
|
110
|
+
Add a function parameter
|
111
|
+
|
112
|
+
Args:
|
113
|
+
name: Parameter name
|
114
|
+
param_type: JSON schema type (string, number, boolean, array, object)
|
115
|
+
description: Parameter description
|
116
|
+
required: Whether parameter is required
|
117
|
+
enum: Optional list of allowed values
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
Self for method chaining
|
121
|
+
"""
|
122
|
+
param_def = {
|
123
|
+
"type": param_type,
|
124
|
+
"description": description
|
125
|
+
}
|
126
|
+
|
127
|
+
if enum:
|
128
|
+
param_def["enum"] = enum
|
129
|
+
|
130
|
+
self._parameters[name] = param_def
|
131
|
+
|
132
|
+
if required:
|
133
|
+
if "required" not in self._parameters:
|
134
|
+
self._parameters["required"] = []
|
135
|
+
if name not in self._parameters["required"]:
|
136
|
+
self._parameters["required"].append(name)
|
137
|
+
|
138
|
+
return self
|
139
|
+
|
140
|
+
def expression(self, test_value: str, pattern: Union[str, Pattern], output: SwaigFunctionResult,
|
141
|
+
nomatch_output: Optional[SwaigFunctionResult] = None) -> 'DataMap':
|
142
|
+
"""
|
143
|
+
Add an expression pattern for pattern-based responses
|
144
|
+
|
145
|
+
Args:
|
146
|
+
test_value: Template string to test (e.g., "${args.command}")
|
147
|
+
pattern: Regex pattern string or compiled Pattern object to match against
|
148
|
+
output: SwaigFunctionResult to return when pattern matches
|
149
|
+
nomatch_output: Optional SwaigFunctionResult to return when pattern doesn't match
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
Self for method chaining
|
153
|
+
"""
|
154
|
+
if isinstance(pattern, Pattern):
|
155
|
+
pattern_str = pattern.pattern
|
156
|
+
else:
|
157
|
+
pattern_str = str(pattern)
|
158
|
+
|
159
|
+
expr_def = {
|
160
|
+
"string": test_value,
|
161
|
+
"pattern": pattern_str,
|
162
|
+
"output": output.to_dict()
|
163
|
+
}
|
164
|
+
|
165
|
+
if nomatch_output:
|
166
|
+
expr_def["nomatch-output"] = nomatch_output.to_dict()
|
167
|
+
|
168
|
+
self._expressions.append(expr_def)
|
169
|
+
return self
|
170
|
+
|
171
|
+
def webhook(self, method: str, url: str, headers: Optional[Dict[str, str]] = None,
|
172
|
+
form_param: Optional[str] = None,
|
173
|
+
input_args_as_params: bool = False,
|
174
|
+
require_args: Optional[List[str]] = None) -> 'DataMap':
|
175
|
+
"""
|
176
|
+
Add a webhook API call
|
177
|
+
|
178
|
+
Args:
|
179
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
180
|
+
url: API endpoint URL (can include ${variable} substitutions)
|
181
|
+
headers: Optional HTTP headers
|
182
|
+
form_param: Send JSON body as single form parameter with this name
|
183
|
+
input_args_as_params: Merge function arguments into params
|
184
|
+
require_args: Only execute if these arguments are present
|
185
|
+
|
186
|
+
Returns:
|
187
|
+
Self for method chaining
|
188
|
+
"""
|
189
|
+
webhook_def = {
|
190
|
+
"url": url,
|
191
|
+
"method": method.upper()
|
192
|
+
}
|
193
|
+
|
194
|
+
if headers:
|
195
|
+
webhook_def["headers"] = headers
|
196
|
+
if form_param:
|
197
|
+
webhook_def["form_param"] = form_param
|
198
|
+
if input_args_as_params:
|
199
|
+
webhook_def["input_args_as_params"] = True
|
200
|
+
if require_args:
|
201
|
+
webhook_def["require_args"] = require_args
|
202
|
+
|
203
|
+
self._webhooks.append(webhook_def)
|
204
|
+
return self
|
205
|
+
|
206
|
+
def webhook_expressions(self, expressions: List[Dict[str, Any]]) -> 'DataMap':
|
207
|
+
"""
|
208
|
+
Add expressions that run after the most recent webhook completes
|
209
|
+
|
210
|
+
Args:
|
211
|
+
expressions: List of expression definitions to check post-webhook
|
212
|
+
|
213
|
+
Returns:
|
214
|
+
Self for method chaining
|
215
|
+
"""
|
216
|
+
if not self._webhooks:
|
217
|
+
raise ValueError("Must add webhook before setting webhook expressions")
|
218
|
+
|
219
|
+
self._webhooks[-1]["expressions"] = expressions
|
220
|
+
return self
|
221
|
+
|
222
|
+
def body(self, data: Dict[str, Any]) -> 'DataMap':
|
223
|
+
"""
|
224
|
+
Set request body for the last added webhook (POST/PUT requests)
|
225
|
+
|
226
|
+
Args:
|
227
|
+
data: Request body data (can include ${variable} substitutions)
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
Self for method chaining
|
231
|
+
"""
|
232
|
+
if not self._webhooks:
|
233
|
+
raise ValueError("Must add webhook before setting body")
|
234
|
+
|
235
|
+
self._webhooks[-1]["body"] = data
|
236
|
+
return self
|
237
|
+
|
238
|
+
def params(self, data: Dict[str, Any]) -> 'DataMap':
|
239
|
+
"""
|
240
|
+
Set request params for the last added webhook (alias for body)
|
241
|
+
|
242
|
+
Args:
|
243
|
+
data: Request params data (can include ${variable} substitutions)
|
244
|
+
|
245
|
+
Returns:
|
246
|
+
Self for method chaining
|
247
|
+
"""
|
248
|
+
if not self._webhooks:
|
249
|
+
raise ValueError("Must add webhook before setting params")
|
250
|
+
|
251
|
+
self._webhooks[-1]["params"] = data
|
252
|
+
return self
|
253
|
+
|
254
|
+
def foreach(self, foreach_config: Union[str, Dict[str, Any]]) -> 'DataMap':
|
255
|
+
"""
|
256
|
+
Process an array from the webhook response using foreach mechanism
|
257
|
+
|
258
|
+
Args:
|
259
|
+
foreach_config: Either:
|
260
|
+
- String: JSON path to array in response (deprecated, kept for compatibility)
|
261
|
+
- Dict: Foreach configuration with keys:
|
262
|
+
- input_key: Key in API response containing the array
|
263
|
+
- output_key: Name for the built string variable
|
264
|
+
- max: Maximum number of items to process (optional)
|
265
|
+
- append: Template string to append for each item
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
Self for method chaining
|
269
|
+
|
270
|
+
Example:
|
271
|
+
.foreach({
|
272
|
+
"input_key": "results",
|
273
|
+
"output_key": "formatted_results",
|
274
|
+
"max": 3,
|
275
|
+
"append": "Result: ${this.title} - ${this.summary}\n"
|
276
|
+
})
|
277
|
+
"""
|
278
|
+
if not self._webhooks:
|
279
|
+
raise ValueError("Must add webhook before setting foreach")
|
280
|
+
|
281
|
+
if isinstance(foreach_config, str):
|
282
|
+
# Legacy support - convert string to basic foreach config
|
283
|
+
# Extract the key from "${response.key}" format if present
|
284
|
+
if foreach_config.startswith("${response.") and foreach_config.endswith("}"):
|
285
|
+
key = foreach_config[12:-1] # Remove "${response." and "}"
|
286
|
+
else:
|
287
|
+
key = foreach_config
|
288
|
+
|
289
|
+
foreach_data = {
|
290
|
+
"input_key": key,
|
291
|
+
"output_key": "foreach_output",
|
292
|
+
"append": "${this}"
|
293
|
+
}
|
294
|
+
else:
|
295
|
+
# New format - validate required keys
|
296
|
+
required_keys = ["input_key", "output_key", "append"]
|
297
|
+
missing_keys = [key for key in required_keys if key not in foreach_config]
|
298
|
+
if missing_keys:
|
299
|
+
raise ValueError(f"foreach config missing required keys: {missing_keys}")
|
300
|
+
|
301
|
+
foreach_data = foreach_config
|
302
|
+
|
303
|
+
self._webhooks[-1]["foreach"] = foreach_data
|
304
|
+
return self
|
305
|
+
|
306
|
+
def output(self, result: SwaigFunctionResult) -> 'DataMap':
|
307
|
+
"""
|
308
|
+
Set the output result for the most recent webhook
|
309
|
+
|
310
|
+
Args:
|
311
|
+
result: SwaigFunctionResult defining the response for this webhook
|
312
|
+
|
313
|
+
Returns:
|
314
|
+
Self for method chaining
|
315
|
+
"""
|
316
|
+
if not self._webhooks:
|
317
|
+
raise ValueError("Must add webhook before setting output")
|
318
|
+
|
319
|
+
self._webhooks[-1]["output"] = result.to_dict()
|
320
|
+
return self
|
321
|
+
|
322
|
+
def fallback_output(self, result: SwaigFunctionResult) -> 'DataMap':
|
323
|
+
"""
|
324
|
+
Set a fallback output result at the top level (used when all webhooks fail)
|
325
|
+
|
326
|
+
Args:
|
327
|
+
result: SwaigFunctionResult defining the fallback response
|
328
|
+
|
329
|
+
Returns:
|
330
|
+
Self for method chaining
|
331
|
+
"""
|
332
|
+
self._output = result.to_dict()
|
333
|
+
return self
|
334
|
+
|
335
|
+
def error_keys(self, keys: List[str]) -> 'DataMap':
|
336
|
+
"""
|
337
|
+
Set error keys for the most recent webhook (if webhooks exist) or top-level
|
338
|
+
|
339
|
+
Args:
|
340
|
+
keys: List of JSON keys whose presence indicates an error
|
341
|
+
|
342
|
+
Returns:
|
343
|
+
Self for method chaining
|
344
|
+
"""
|
345
|
+
if self._webhooks:
|
346
|
+
# Add to most recent webhook
|
347
|
+
self._webhooks[-1]["error_keys"] = keys
|
348
|
+
else:
|
349
|
+
# Store as top-level error keys
|
350
|
+
self._error_keys = keys
|
351
|
+
return self
|
352
|
+
|
353
|
+
def global_error_keys(self, keys: List[str]) -> 'DataMap':
|
354
|
+
"""
|
355
|
+
Set top-level error keys (applies to all webhooks)
|
356
|
+
|
357
|
+
Args:
|
358
|
+
keys: List of JSON keys whose presence indicates an error
|
359
|
+
|
360
|
+
Returns:
|
361
|
+
Self for method chaining
|
362
|
+
"""
|
363
|
+
self._error_keys = keys
|
364
|
+
return self
|
365
|
+
|
366
|
+
def to_swaig_function(self) -> Dict[str, Any]:
|
367
|
+
"""
|
368
|
+
Convert this DataMap to a SWAIG function definition
|
369
|
+
|
370
|
+
Returns:
|
371
|
+
Dictionary with function definition and data_map instead of url
|
372
|
+
"""
|
373
|
+
# Build parameter schema
|
374
|
+
if self._parameters:
|
375
|
+
# Extract required params without mutating original dict
|
376
|
+
required_params = self._parameters.get("required", [])
|
377
|
+
param_properties = {k: v for k, v in self._parameters.items() if k != "required"}
|
378
|
+
|
379
|
+
param_schema = {
|
380
|
+
"type": "object",
|
381
|
+
"properties": param_properties
|
382
|
+
}
|
383
|
+
if required_params:
|
384
|
+
param_schema["required"] = required_params
|
385
|
+
else:
|
386
|
+
param_schema = {"type": "object", "properties": {}}
|
387
|
+
|
388
|
+
# Build data_map structure
|
389
|
+
data_map = {}
|
390
|
+
|
391
|
+
# Add expressions if present
|
392
|
+
if self._expressions:
|
393
|
+
data_map["expressions"] = self._expressions
|
394
|
+
|
395
|
+
# Add webhooks if present
|
396
|
+
if self._webhooks:
|
397
|
+
data_map["webhooks"] = self._webhooks
|
398
|
+
|
399
|
+
# Add output if present
|
400
|
+
if self._output:
|
401
|
+
data_map["output"] = self._output
|
402
|
+
|
403
|
+
# Add error_keys if present
|
404
|
+
if self._error_keys:
|
405
|
+
data_map["error_keys"] = self._error_keys
|
406
|
+
|
407
|
+
# Build final function definition with correct field names
|
408
|
+
function_def = {
|
409
|
+
"function": self.function_name,
|
410
|
+
"description": self._purpose or f"Execute {self.function_name}",
|
411
|
+
"parameters": param_schema,
|
412
|
+
"data_map": data_map
|
413
|
+
}
|
414
|
+
|
415
|
+
return function_def
|
416
|
+
|
417
|
+
|
418
|
+
def create_simple_api_tool(name: str, url: str, response_template: str,
|
419
|
+
parameters: Optional[Dict[str, Dict]] = None,
|
420
|
+
method: str = "GET", headers: Optional[Dict[str, str]] = None,
|
421
|
+
body: Optional[Dict[str, Any]] = None,
|
422
|
+
error_keys: Optional[List[str]] = None) -> DataMap:
|
423
|
+
"""
|
424
|
+
Create a simple API tool with minimal configuration
|
425
|
+
|
426
|
+
Args:
|
427
|
+
name: Function name
|
428
|
+
url: API endpoint URL
|
429
|
+
response_template: Template for formatting the response
|
430
|
+
parameters: Optional parameter definitions
|
431
|
+
method: HTTP method (default: GET)
|
432
|
+
headers: Optional HTTP headers
|
433
|
+
body: Optional request body (for POST/PUT)
|
434
|
+
error_keys: Optional list of error indicator keys
|
435
|
+
|
436
|
+
Returns:
|
437
|
+
Configured DataMap object
|
438
|
+
"""
|
439
|
+
data_map = DataMap(name)
|
440
|
+
|
441
|
+
# Add parameters if provided
|
442
|
+
if parameters:
|
443
|
+
for param_name, param_def in parameters.items():
|
444
|
+
required = param_def.get("required", False)
|
445
|
+
data_map.parameter(
|
446
|
+
param_name,
|
447
|
+
param_def.get("type", "string"),
|
448
|
+
param_def.get("description", f"{param_name} parameter"),
|
449
|
+
required=required
|
450
|
+
)
|
451
|
+
|
452
|
+
# Add webhook
|
453
|
+
data_map.webhook(method, url, headers)
|
454
|
+
|
455
|
+
# Add body if provided
|
456
|
+
if body:
|
457
|
+
data_map.body(body)
|
458
|
+
|
459
|
+
# Add error keys if provided
|
460
|
+
if error_keys:
|
461
|
+
data_map.error_keys(error_keys)
|
462
|
+
|
463
|
+
# Set output
|
464
|
+
data_map.output(SwaigFunctionResult(response_template))
|
465
|
+
|
466
|
+
return data_map
|
467
|
+
|
468
|
+
|
469
|
+
def create_expression_tool(name: str, patterns: Dict[str, Tuple[str, SwaigFunctionResult]],
|
470
|
+
parameters: Optional[Dict[str, Dict]] = None) -> DataMap:
|
471
|
+
"""
|
472
|
+
Create an expression-based tool for pattern matching responses
|
473
|
+
|
474
|
+
Args:
|
475
|
+
name: Function name
|
476
|
+
patterns: Dictionary mapping test_values to (pattern, SwaigFunctionResult) tuples
|
477
|
+
parameters: Optional parameter definitions
|
478
|
+
|
479
|
+
Returns:
|
480
|
+
Configured DataMap object
|
481
|
+
"""
|
482
|
+
data_map = DataMap(name)
|
483
|
+
|
484
|
+
# Add parameters if provided
|
485
|
+
if parameters:
|
486
|
+
for param_name, param_def in parameters.items():
|
487
|
+
required = param_def.get("required", False)
|
488
|
+
data_map.parameter(
|
489
|
+
param_name,
|
490
|
+
param_def.get("type", "string"),
|
491
|
+
param_def.get("description", f"{param_name} parameter"),
|
492
|
+
required=required
|
493
|
+
)
|
494
|
+
|
495
|
+
# Add expressions with corrected signature
|
496
|
+
for test_value, (pattern, result) in patterns.items():
|
497
|
+
data_map.expression(test_value, pattern, result)
|
498
|
+
|
499
|
+
return data_map
|
@@ -187,6 +187,55 @@ class SwaigFunctionResult:
|
|
187
187
|
# Add to actions list
|
188
188
|
self.action.append(swml_action)
|
189
189
|
return self
|
190
|
+
|
191
|
+
def swml_transfer(self, dest: str, ai_response: str) -> 'SwaigFunctionResult':
|
192
|
+
"""
|
193
|
+
Add a SWML transfer action with AI response setup for when transfer completes.
|
194
|
+
|
195
|
+
This is a virtual helper that generates SWML to transfer the call to another
|
196
|
+
destination and sets up an AI response for when the transfer completes and
|
197
|
+
control returns to the agent.
|
198
|
+
|
199
|
+
For transfers, you typically want to enable post-processing so the AI speaks
|
200
|
+
the response first before executing the transfer.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
dest: Destination URL for the transfer (SWML endpoint, SIP address, etc.)
|
204
|
+
ai_response: Message the AI should say when transfer completes and control returns
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
Self for method chaining
|
208
|
+
|
209
|
+
Example:
|
210
|
+
# Transfer with post-processing (speak first, then transfer)
|
211
|
+
result = (
|
212
|
+
SwaigFunctionResult("I'm transferring you to support", post_process=True)
|
213
|
+
.swml_transfer(
|
214
|
+
"https://support.example.com/swml",
|
215
|
+
"The support call is complete. How else can I help?"
|
216
|
+
)
|
217
|
+
)
|
218
|
+
|
219
|
+
# Or enable post-processing with method chaining
|
220
|
+
result.swml_transfer(dest, ai_response).set_post_process(True)
|
221
|
+
"""
|
222
|
+
# Create the SWML action structure directly
|
223
|
+
swml_action = {
|
224
|
+
"SWML": {
|
225
|
+
"version": "1.0.0",
|
226
|
+
"sections": {
|
227
|
+
"main": [
|
228
|
+
{"set": {"ai_response": ai_response}},
|
229
|
+
{"transfer": {"dest": dest}}
|
230
|
+
]
|
231
|
+
}
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
# Add to actions list directly
|
236
|
+
self.action.append(swml_action)
|
237
|
+
|
238
|
+
return self
|
190
239
|
|
191
240
|
def update_global_data(self, data: Dict[str, Any]) -> 'SwaigFunctionResult':
|
192
241
|
"""
|
@@ -310,32 +359,30 @@ class SwaigFunctionResult:
|
|
310
359
|
action = {"say": text}
|
311
360
|
return self.add_action("say", action)
|
312
361
|
|
313
|
-
def
|
362
|
+
def play_background_file(self, filename: str, wait: bool = False) -> 'SwaigFunctionResult':
|
314
363
|
"""
|
315
|
-
Play audio file in background.
|
364
|
+
Play audio or video file in background.
|
316
365
|
|
317
366
|
Args:
|
318
|
-
filename: Audio filename/path
|
367
|
+
filename: Audio/video filename/path
|
319
368
|
wait: Whether to suppress attention-getting behavior during playback
|
320
369
|
|
321
370
|
Returns:
|
322
371
|
self for method chaining
|
323
372
|
"""
|
324
373
|
if wait:
|
325
|
-
|
374
|
+
return self.add_action("playback_bg", {"file": filename, "wait": True})
|
326
375
|
else:
|
327
|
-
|
328
|
-
return self.add_action("playback_bg", action)
|
376
|
+
return self.add_action("playback_bg", filename)
|
329
377
|
|
330
|
-
def
|
378
|
+
def stop_background_file(self) -> 'SwaigFunctionResult':
|
331
379
|
"""
|
332
|
-
Stop currently playing background
|
380
|
+
Stop currently playing background file.
|
333
381
|
|
334
382
|
Returns:
|
335
383
|
self for method chaining
|
336
384
|
"""
|
337
|
-
|
338
|
-
return self.add_action("stop_playback_bg", action)
|
385
|
+
return self.add_action("stop_playback_bg", True)
|
339
386
|
|
340
387
|
def set_end_of_speech_timeout(self, milliseconds: int) -> 'SwaigFunctionResult':
|
341
388
|
"""
|