signalwire-agents 0.1.6__py3-none-any.whl → 1.0.7__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.
Files changed (140) hide show
  1. signalwire_agents/__init__.py +130 -4
  2. signalwire_agents/agent_server.py +438 -32
  3. signalwire_agents/agents/bedrock.py +296 -0
  4. signalwire_agents/cli/__init__.py +18 -0
  5. signalwire_agents/cli/build_search.py +1367 -0
  6. signalwire_agents/cli/config.py +80 -0
  7. signalwire_agents/cli/core/__init__.py +10 -0
  8. signalwire_agents/cli/core/agent_loader.py +470 -0
  9. signalwire_agents/cli/core/argparse_helpers.py +179 -0
  10. signalwire_agents/cli/core/dynamic_config.py +71 -0
  11. signalwire_agents/cli/core/service_loader.py +303 -0
  12. signalwire_agents/cli/execution/__init__.py +10 -0
  13. signalwire_agents/cli/execution/datamap_exec.py +446 -0
  14. signalwire_agents/cli/execution/webhook_exec.py +134 -0
  15. signalwire_agents/cli/init_project.py +1225 -0
  16. signalwire_agents/cli/output/__init__.py +10 -0
  17. signalwire_agents/cli/output/output_formatter.py +255 -0
  18. signalwire_agents/cli/output/swml_dump.py +186 -0
  19. signalwire_agents/cli/simulation/__init__.py +10 -0
  20. signalwire_agents/cli/simulation/data_generation.py +374 -0
  21. signalwire_agents/cli/simulation/data_overrides.py +200 -0
  22. signalwire_agents/cli/simulation/mock_env.py +282 -0
  23. signalwire_agents/cli/swaig_test_wrapper.py +52 -0
  24. signalwire_agents/cli/test_swaig.py +809 -0
  25. signalwire_agents/cli/types.py +81 -0
  26. signalwire_agents/core/__init__.py +2 -2
  27. signalwire_agents/core/agent/__init__.py +12 -0
  28. signalwire_agents/core/agent/config/__init__.py +12 -0
  29. signalwire_agents/core/agent/deployment/__init__.py +9 -0
  30. signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
  31. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  32. signalwire_agents/core/agent/prompt/manager.py +306 -0
  33. signalwire_agents/core/agent/routing/__init__.py +9 -0
  34. signalwire_agents/core/agent/security/__init__.py +9 -0
  35. signalwire_agents/core/agent/swml/__init__.py +9 -0
  36. signalwire_agents/core/agent/tools/__init__.py +15 -0
  37. signalwire_agents/core/agent/tools/decorator.py +97 -0
  38. signalwire_agents/core/agent/tools/registry.py +210 -0
  39. signalwire_agents/core/agent_base.py +959 -2166
  40. signalwire_agents/core/auth_handler.py +233 -0
  41. signalwire_agents/core/config_loader.py +259 -0
  42. signalwire_agents/core/contexts.py +707 -0
  43. signalwire_agents/core/data_map.py +487 -0
  44. signalwire_agents/core/function_result.py +1150 -1
  45. signalwire_agents/core/logging_config.py +376 -0
  46. signalwire_agents/core/mixins/__init__.py +28 -0
  47. signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
  48. signalwire_agents/core/mixins/auth_mixin.py +287 -0
  49. signalwire_agents/core/mixins/prompt_mixin.py +358 -0
  50. signalwire_agents/core/mixins/serverless_mixin.py +368 -0
  51. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  52. signalwire_agents/core/mixins/state_mixin.py +153 -0
  53. signalwire_agents/core/mixins/tool_mixin.py +230 -0
  54. signalwire_agents/core/mixins/web_mixin.py +1134 -0
  55. signalwire_agents/core/security/session_manager.py +174 -86
  56. signalwire_agents/core/security_config.py +333 -0
  57. signalwire_agents/core/skill_base.py +200 -0
  58. signalwire_agents/core/skill_manager.py +244 -0
  59. signalwire_agents/core/swaig_function.py +33 -9
  60. signalwire_agents/core/swml_builder.py +212 -12
  61. signalwire_agents/core/swml_handler.py +43 -13
  62. signalwire_agents/core/swml_renderer.py +123 -297
  63. signalwire_agents/core/swml_service.py +277 -260
  64. signalwire_agents/prefabs/concierge.py +6 -2
  65. signalwire_agents/prefabs/info_gatherer.py +149 -33
  66. signalwire_agents/prefabs/receptionist.py +14 -22
  67. signalwire_agents/prefabs/survey.py +6 -2
  68. signalwire_agents/schema.json +9218 -5489
  69. signalwire_agents/search/__init__.py +137 -0
  70. signalwire_agents/search/document_processor.py +1223 -0
  71. signalwire_agents/search/index_builder.py +804 -0
  72. signalwire_agents/search/migration.py +418 -0
  73. signalwire_agents/search/models.py +30 -0
  74. signalwire_agents/search/pgvector_backend.py +752 -0
  75. signalwire_agents/search/query_processor.py +502 -0
  76. signalwire_agents/search/search_engine.py +1264 -0
  77. signalwire_agents/search/search_service.py +574 -0
  78. signalwire_agents/skills/README.md +452 -0
  79. signalwire_agents/skills/__init__.py +23 -0
  80. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  81. signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
  82. signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
  83. signalwire_agents/skills/datasphere/README.md +210 -0
  84. signalwire_agents/skills/datasphere/__init__.py +12 -0
  85. signalwire_agents/skills/datasphere/skill.py +310 -0
  86. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  87. signalwire_agents/skills/datasphere_serverless/__init__.py +10 -0
  88. signalwire_agents/skills/datasphere_serverless/skill.py +237 -0
  89. signalwire_agents/skills/datetime/README.md +132 -0
  90. signalwire_agents/skills/datetime/__init__.py +10 -0
  91. signalwire_agents/skills/datetime/skill.py +126 -0
  92. signalwire_agents/skills/joke/README.md +149 -0
  93. signalwire_agents/skills/joke/__init__.py +10 -0
  94. signalwire_agents/skills/joke/skill.py +109 -0
  95. signalwire_agents/skills/math/README.md +161 -0
  96. signalwire_agents/skills/math/__init__.py +10 -0
  97. signalwire_agents/skills/math/skill.py +105 -0
  98. signalwire_agents/skills/mcp_gateway/README.md +230 -0
  99. signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
  100. signalwire_agents/skills/mcp_gateway/skill.py +421 -0
  101. signalwire_agents/skills/native_vector_search/README.md +210 -0
  102. signalwire_agents/skills/native_vector_search/__init__.py +10 -0
  103. signalwire_agents/skills/native_vector_search/skill.py +820 -0
  104. signalwire_agents/skills/play_background_file/README.md +218 -0
  105. signalwire_agents/skills/play_background_file/__init__.py +12 -0
  106. signalwire_agents/skills/play_background_file/skill.py +242 -0
  107. signalwire_agents/skills/registry.py +459 -0
  108. signalwire_agents/skills/spider/README.md +236 -0
  109. signalwire_agents/skills/spider/__init__.py +13 -0
  110. signalwire_agents/skills/spider/skill.py +598 -0
  111. signalwire_agents/skills/swml_transfer/README.md +395 -0
  112. signalwire_agents/skills/swml_transfer/__init__.py +10 -0
  113. signalwire_agents/skills/swml_transfer/skill.py +359 -0
  114. signalwire_agents/skills/weather_api/README.md +178 -0
  115. signalwire_agents/skills/weather_api/__init__.py +12 -0
  116. signalwire_agents/skills/weather_api/skill.py +191 -0
  117. signalwire_agents/skills/web_search/README.md +163 -0
  118. signalwire_agents/skills/web_search/__init__.py +10 -0
  119. signalwire_agents/skills/web_search/skill.py +739 -0
  120. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  121. signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
  122. signalwire_agents/skills/wikipedia_search/skill.py +210 -0
  123. signalwire_agents/utils/__init__.py +14 -0
  124. signalwire_agents/utils/schema_utils.py +111 -44
  125. signalwire_agents/web/__init__.py +17 -0
  126. signalwire_agents/web/web_service.py +559 -0
  127. signalwire_agents-1.0.7.data/data/share/man/man1/sw-agent-init.1 +307 -0
  128. signalwire_agents-1.0.7.data/data/share/man/man1/sw-search.1 +483 -0
  129. signalwire_agents-1.0.7.data/data/share/man/man1/swaig-test.1 +308 -0
  130. signalwire_agents-1.0.7.dist-info/METADATA +992 -0
  131. signalwire_agents-1.0.7.dist-info/RECORD +142 -0
  132. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/WHEEL +1 -1
  133. signalwire_agents-1.0.7.dist-info/entry_points.txt +4 -0
  134. signalwire_agents/core/state/file_state_manager.py +0 -219
  135. signalwire_agents/core/state/state_manager.py +0 -101
  136. signalwire_agents-0.1.6.data/data/schema.json +0 -5611
  137. signalwire_agents-0.1.6.dist-info/METADATA +0 -199
  138. signalwire_agents-0.1.6.dist-info/RECORD +0 -34
  139. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/licenses/LICENSE +0 -0
  140. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,487 @@
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
+ - Dict: Foreach configuration with keys:
261
+ - input_key: Key in API response containing the array
262
+ - output_key: Name for the built string variable
263
+ - max: Maximum number of items to process (optional)
264
+ - append: Template string to append for each item
265
+
266
+ Returns:
267
+ Self for method chaining
268
+
269
+ Example:
270
+ .foreach({
271
+ "input_key": "results",
272
+ "output_key": "formatted_results",
273
+ "max": 3,
274
+ "append": "Result: ${this.title} - ${this.summary}\n"
275
+ })
276
+ """
277
+ if not self._webhooks:
278
+ raise ValueError("Must add webhook before setting foreach")
279
+
280
+ if isinstance(foreach_config, dict):
281
+ # New format - validate required keys
282
+ required_keys = ["input_key", "output_key", "append"]
283
+ missing_keys = [key for key in required_keys if key not in foreach_config]
284
+ if missing_keys:
285
+ raise ValueError(f"foreach config missing required keys: {missing_keys}")
286
+
287
+ foreach_data = foreach_config
288
+ else:
289
+ raise ValueError("foreach_config must be a dictionary")
290
+
291
+ self._webhooks[-1]["foreach"] = foreach_data
292
+ return self
293
+
294
+ def output(self, result: SwaigFunctionResult) -> 'DataMap':
295
+ """
296
+ Set the output result for the most recent webhook
297
+
298
+ Args:
299
+ result: SwaigFunctionResult defining the response for this webhook
300
+
301
+ Returns:
302
+ Self for method chaining
303
+ """
304
+ if not self._webhooks:
305
+ raise ValueError("Must add webhook before setting output")
306
+
307
+ self._webhooks[-1]["output"] = result.to_dict()
308
+ return self
309
+
310
+ def fallback_output(self, result: SwaigFunctionResult) -> 'DataMap':
311
+ """
312
+ Set a fallback output result at the top level (used when all webhooks fail)
313
+
314
+ Args:
315
+ result: SwaigFunctionResult defining the fallback response
316
+
317
+ Returns:
318
+ Self for method chaining
319
+ """
320
+ self._output = result.to_dict()
321
+ return self
322
+
323
+ def error_keys(self, keys: List[str]) -> 'DataMap':
324
+ """
325
+ Set error keys for the most recent webhook (if webhooks exist) or top-level
326
+
327
+ Args:
328
+ keys: List of JSON keys whose presence indicates an error
329
+
330
+ Returns:
331
+ Self for method chaining
332
+ """
333
+ if self._webhooks:
334
+ # Add to most recent webhook
335
+ self._webhooks[-1]["error_keys"] = keys
336
+ else:
337
+ # Store as top-level error keys
338
+ self._error_keys = keys
339
+ return self
340
+
341
+ def global_error_keys(self, keys: List[str]) -> 'DataMap':
342
+ """
343
+ Set top-level error keys (applies to all webhooks)
344
+
345
+ Args:
346
+ keys: List of JSON keys whose presence indicates an error
347
+
348
+ Returns:
349
+ Self for method chaining
350
+ """
351
+ self._error_keys = keys
352
+ return self
353
+
354
+ def to_swaig_function(self) -> Dict[str, Any]:
355
+ """
356
+ Convert this DataMap to a SWAIG function definition
357
+
358
+ Returns:
359
+ Dictionary with function definition and data_map instead of url
360
+ """
361
+ # Build parameter schema
362
+ if self._parameters:
363
+ # Extract required params without mutating original dict
364
+ required_params = self._parameters.get("required", [])
365
+ param_properties = {k: v for k, v in self._parameters.items() if k != "required"}
366
+
367
+ param_schema = {
368
+ "type": "object",
369
+ "properties": param_properties
370
+ }
371
+ if required_params:
372
+ param_schema["required"] = required_params
373
+ else:
374
+ param_schema = {"type": "object", "properties": {}}
375
+
376
+ # Build data_map structure
377
+ data_map = {}
378
+
379
+ # Add expressions if present
380
+ if self._expressions:
381
+ data_map["expressions"] = self._expressions
382
+
383
+ # Add webhooks if present
384
+ if self._webhooks:
385
+ data_map["webhooks"] = self._webhooks
386
+
387
+ # Add output if present
388
+ if self._output:
389
+ data_map["output"] = self._output
390
+
391
+ # Add error_keys if present
392
+ if self._error_keys:
393
+ data_map["error_keys"] = self._error_keys
394
+
395
+ # Build final function definition with correct field names
396
+ function_def = {
397
+ "function": self.function_name,
398
+ "description": self._purpose or f"Execute {self.function_name}",
399
+ "parameters": param_schema,
400
+ "data_map": data_map
401
+ }
402
+
403
+ return function_def
404
+
405
+
406
+ def create_simple_api_tool(name: str, url: str, response_template: str,
407
+ parameters: Optional[Dict[str, Dict]] = None,
408
+ method: str = "GET", headers: Optional[Dict[str, str]] = None,
409
+ body: Optional[Dict[str, Any]] = None,
410
+ error_keys: Optional[List[str]] = None) -> DataMap:
411
+ """
412
+ Create a simple API tool with minimal configuration
413
+
414
+ Args:
415
+ name: Function name
416
+ url: API endpoint URL
417
+ response_template: Template for formatting the response
418
+ parameters: Optional parameter definitions
419
+ method: HTTP method (default: GET)
420
+ headers: Optional HTTP headers
421
+ body: Optional request body (for POST/PUT)
422
+ error_keys: Optional list of error indicator keys
423
+
424
+ Returns:
425
+ Configured DataMap object
426
+ """
427
+ data_map = DataMap(name)
428
+
429
+ # Add parameters if provided
430
+ if parameters:
431
+ for param_name, param_def in parameters.items():
432
+ required = param_def.get("required", False)
433
+ data_map.parameter(
434
+ param_name,
435
+ param_def.get("type", "string"),
436
+ param_def.get("description", f"{param_name} parameter"),
437
+ required=required
438
+ )
439
+
440
+ # Add webhook
441
+ data_map.webhook(method, url, headers)
442
+
443
+ # Add body if provided
444
+ if body:
445
+ data_map.body(body)
446
+
447
+ # Add error keys if provided
448
+ if error_keys:
449
+ data_map.error_keys(error_keys)
450
+
451
+ # Set output
452
+ data_map.output(SwaigFunctionResult(response_template))
453
+
454
+ return data_map
455
+
456
+
457
+ def create_expression_tool(name: str, patterns: Dict[str, Tuple[str, SwaigFunctionResult]],
458
+ parameters: Optional[Dict[str, Dict]] = None) -> DataMap:
459
+ """
460
+ Create an expression-based tool for pattern matching responses
461
+
462
+ Args:
463
+ name: Function name
464
+ patterns: Dictionary mapping test_values to (pattern, SwaigFunctionResult) tuples
465
+ parameters: Optional parameter definitions
466
+
467
+ Returns:
468
+ Configured DataMap object
469
+ """
470
+ data_map = DataMap(name)
471
+
472
+ # Add parameters if provided
473
+ if parameters:
474
+ for param_name, param_def in parameters.items():
475
+ required = param_def.get("required", False)
476
+ data_map.parameter(
477
+ param_name,
478
+ param_def.get("type", "string"),
479
+ param_def.get("description", f"{param_name} parameter"),
480
+ required=required
481
+ )
482
+
483
+ # Add expressions with corrected signature
484
+ for test_value, (pattern, result) in patterns.items():
485
+ data_map.expression(test_value, pattern, result)
486
+
487
+ return data_map