signalwire-agents 0.1.13__py3-none-any.whl → 1.0.17.dev4__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 (143) hide show
  1. signalwire_agents/__init__.py +99 -15
  2. signalwire_agents/agent_server.py +248 -60
  3. signalwire_agents/agents/bedrock.py +296 -0
  4. signalwire_agents/cli/__init__.py +9 -0
  5. signalwire_agents/cli/build_search.py +951 -41
  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/dokku.py +2320 -0
  13. signalwire_agents/cli/execution/__init__.py +10 -0
  14. signalwire_agents/cli/execution/datamap_exec.py +446 -0
  15. signalwire_agents/cli/execution/webhook_exec.py +134 -0
  16. signalwire_agents/cli/init_project.py +2636 -0
  17. signalwire_agents/cli/output/__init__.py +10 -0
  18. signalwire_agents/cli/output/output_formatter.py +255 -0
  19. signalwire_agents/cli/output/swml_dump.py +186 -0
  20. signalwire_agents/cli/simulation/__init__.py +10 -0
  21. signalwire_agents/cli/simulation/data_generation.py +374 -0
  22. signalwire_agents/cli/simulation/data_overrides.py +200 -0
  23. signalwire_agents/cli/simulation/mock_env.py +282 -0
  24. signalwire_agents/cli/swaig_test_wrapper.py +52 -0
  25. signalwire_agents/cli/test_swaig.py +566 -2366
  26. signalwire_agents/cli/types.py +81 -0
  27. signalwire_agents/core/__init__.py +2 -2
  28. signalwire_agents/core/agent/__init__.py +12 -0
  29. signalwire_agents/core/agent/config/__init__.py +12 -0
  30. signalwire_agents/core/agent/deployment/__init__.py +9 -0
  31. signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
  32. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  33. signalwire_agents/core/agent/prompt/manager.py +306 -0
  34. signalwire_agents/core/agent/routing/__init__.py +9 -0
  35. signalwire_agents/core/agent/security/__init__.py +9 -0
  36. signalwire_agents/core/agent/swml/__init__.py +9 -0
  37. signalwire_agents/core/agent/tools/__init__.py +15 -0
  38. signalwire_agents/core/agent/tools/decorator.py +97 -0
  39. signalwire_agents/core/agent/tools/registry.py +210 -0
  40. signalwire_agents/core/agent_base.py +845 -2916
  41. signalwire_agents/core/auth_handler.py +233 -0
  42. signalwire_agents/core/config_loader.py +259 -0
  43. signalwire_agents/core/contexts.py +418 -0
  44. signalwire_agents/core/data_map.py +3 -15
  45. signalwire_agents/core/function_result.py +116 -44
  46. signalwire_agents/core/logging_config.py +162 -18
  47. signalwire_agents/core/mixins/__init__.py +28 -0
  48. signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
  49. signalwire_agents/core/mixins/auth_mixin.py +280 -0
  50. signalwire_agents/core/mixins/prompt_mixin.py +358 -0
  51. signalwire_agents/core/mixins/serverless_mixin.py +460 -0
  52. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  53. signalwire_agents/core/mixins/state_mixin.py +153 -0
  54. signalwire_agents/core/mixins/tool_mixin.py +230 -0
  55. signalwire_agents/core/mixins/web_mixin.py +1142 -0
  56. signalwire_agents/core/security_config.py +333 -0
  57. signalwire_agents/core/skill_base.py +84 -1
  58. signalwire_agents/core/skill_manager.py +62 -20
  59. signalwire_agents/core/swaig_function.py +18 -5
  60. signalwire_agents/core/swml_builder.py +207 -11
  61. signalwire_agents/core/swml_handler.py +27 -21
  62. signalwire_agents/core/swml_renderer.py +123 -312
  63. signalwire_agents/core/swml_service.py +171 -203
  64. signalwire_agents/mcp_gateway/__init__.py +29 -0
  65. signalwire_agents/mcp_gateway/gateway_service.py +564 -0
  66. signalwire_agents/mcp_gateway/mcp_manager.py +513 -0
  67. signalwire_agents/mcp_gateway/session_manager.py +218 -0
  68. signalwire_agents/prefabs/concierge.py +0 -3
  69. signalwire_agents/prefabs/faq_bot.py +0 -3
  70. signalwire_agents/prefabs/info_gatherer.py +0 -3
  71. signalwire_agents/prefabs/receptionist.py +0 -3
  72. signalwire_agents/prefabs/survey.py +0 -3
  73. signalwire_agents/schema.json +9218 -5489
  74. signalwire_agents/search/__init__.py +7 -1
  75. signalwire_agents/search/document_processor.py +490 -31
  76. signalwire_agents/search/index_builder.py +307 -37
  77. signalwire_agents/search/migration.py +418 -0
  78. signalwire_agents/search/models.py +30 -0
  79. signalwire_agents/search/pgvector_backend.py +748 -0
  80. signalwire_agents/search/query_processor.py +162 -31
  81. signalwire_agents/search/search_engine.py +916 -35
  82. signalwire_agents/search/search_service.py +376 -53
  83. signalwire_agents/skills/README.md +452 -0
  84. signalwire_agents/skills/__init__.py +14 -2
  85. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  86. signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
  87. signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
  88. signalwire_agents/skills/datasphere/README.md +210 -0
  89. signalwire_agents/skills/datasphere/skill.py +84 -3
  90. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  91. signalwire_agents/skills/datasphere_serverless/__init__.py +9 -0
  92. signalwire_agents/skills/datasphere_serverless/skill.py +82 -1
  93. signalwire_agents/skills/datetime/README.md +132 -0
  94. signalwire_agents/skills/datetime/__init__.py +9 -0
  95. signalwire_agents/skills/datetime/skill.py +20 -7
  96. signalwire_agents/skills/joke/README.md +149 -0
  97. signalwire_agents/skills/joke/__init__.py +9 -0
  98. signalwire_agents/skills/joke/skill.py +21 -0
  99. signalwire_agents/skills/math/README.md +161 -0
  100. signalwire_agents/skills/math/__init__.py +9 -0
  101. signalwire_agents/skills/math/skill.py +18 -4
  102. signalwire_agents/skills/mcp_gateway/README.md +230 -0
  103. signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
  104. signalwire_agents/skills/mcp_gateway/skill.py +421 -0
  105. signalwire_agents/skills/native_vector_search/README.md +210 -0
  106. signalwire_agents/skills/native_vector_search/__init__.py +9 -0
  107. signalwire_agents/skills/native_vector_search/skill.py +569 -101
  108. signalwire_agents/skills/play_background_file/README.md +218 -0
  109. signalwire_agents/skills/play_background_file/__init__.py +12 -0
  110. signalwire_agents/skills/play_background_file/skill.py +242 -0
  111. signalwire_agents/skills/registry.py +395 -40
  112. signalwire_agents/skills/spider/README.md +236 -0
  113. signalwire_agents/skills/spider/__init__.py +13 -0
  114. signalwire_agents/skills/spider/skill.py +598 -0
  115. signalwire_agents/skills/swml_transfer/README.md +395 -0
  116. signalwire_agents/skills/swml_transfer/__init__.py +10 -0
  117. signalwire_agents/skills/swml_transfer/skill.py +359 -0
  118. signalwire_agents/skills/weather_api/README.md +178 -0
  119. signalwire_agents/skills/weather_api/__init__.py +12 -0
  120. signalwire_agents/skills/weather_api/skill.py +191 -0
  121. signalwire_agents/skills/web_search/README.md +163 -0
  122. signalwire_agents/skills/web_search/__init__.py +9 -0
  123. signalwire_agents/skills/web_search/skill.py +586 -112
  124. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  125. signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
  126. signalwire_agents/skills/{wikipedia → wikipedia_search}/skill.py +33 -3
  127. signalwire_agents/web/__init__.py +17 -0
  128. signalwire_agents/web/web_service.py +559 -0
  129. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-agent-init.1 +400 -0
  130. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-search.1 +483 -0
  131. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/swaig-test.1 +308 -0
  132. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/METADATA +347 -215
  133. signalwire_agents-1.0.17.dev4.dist-info/RECORD +147 -0
  134. signalwire_agents-1.0.17.dev4.dist-info/entry_points.txt +6 -0
  135. signalwire_agents/core/state/file_state_manager.py +0 -219
  136. signalwire_agents/core/state/state_manager.py +0 -101
  137. signalwire_agents/skills/wikipedia/__init__.py +0 -9
  138. signalwire_agents-0.1.13.data/data/schema.json +0 -5611
  139. signalwire_agents-0.1.13.dist-info/RECORD +0 -67
  140. signalwire_agents-0.1.13.dist-info/entry_points.txt +0 -3
  141. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/WHEEL +0 -0
  142. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/licenses/LICENSE +0 -0
  143. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,218 @@
1
+ # Play Background File Skill
2
+
3
+ A configurable skill for managing background file playback with custom tool names and multiple file collections. Supports both audio and video files with serverless DataMap execution.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Instances**: Create different tools with unique names and file sets
8
+ - **Dynamic Enum Generation**: Function parameters built from your file configuration
9
+ - **Audio & Video Support**: Works with any background file type
10
+ - **DataMap Efficiency**: Serverless execution, no agent processing load
11
+ - **Post-Processing**: AI speaks first, then executes the action
12
+ - **Configurable Wait**: Control attention-getting behavior per file
13
+
14
+ ## Configuration
15
+
16
+ ### Basic Structure
17
+
18
+ ```python
19
+ agent.add_skill("play_background_file", {
20
+ "tool_name": "your_custom_tool_name",
21
+ "files": [
22
+ {
23
+ "key": "unique_identifier",
24
+ "description": "Human readable description",
25
+ "url": "https://example.com/file.mp4",
26
+ "wait": True # Optional, defaults to False
27
+ }
28
+ ]
29
+ })
30
+ ```
31
+
32
+ ### Parameters
33
+
34
+ - **tool_name** (string): Custom name for the generated SWAIG function (default: "play_background_file")
35
+ - **files** (array): List of file objects to manage
36
+
37
+ ### File Object Properties
38
+
39
+ - **key** (string, required): Unique identifier for the file (alphanumeric, underscores, hyphens only)
40
+ - **description** (string, required): Human-readable description used in AI responses
41
+ - **url** (string, required): URL to the audio/video file
42
+ - **wait** (boolean, optional): Whether to suppress attention-getting behavior during playback (default: false)
43
+
44
+ ## Usage Examples
45
+
46
+ ### Single File Testimonial
47
+
48
+ ```python
49
+ agent.add_skill("play_background_file", {
50
+ "tool_name": "play_testimonial",
51
+ "files": [
52
+ {
53
+ "key": "massey_success",
54
+ "description": "Customer success story from Massey Energy",
55
+ "url": "https://tatooine.cantina.cloud/vids/massey.mp4",
56
+ "wait": True
57
+ }
58
+ ]
59
+ })
60
+ ```
61
+
62
+ **Generated Tool**: `play_testimonial(action)`
63
+ **Actions**: `["start_massey_success", "stop"]`
64
+
65
+ ### Multiple Demo Videos
66
+
67
+ ```python
68
+ agent.add_skill("play_background_file", {
69
+ "tool_name": "play_demo",
70
+ "files": [
71
+ {
72
+ "key": "product_overview",
73
+ "description": "Product overview demonstration",
74
+ "url": "https://example.com/overview.mp4",
75
+ "wait": False
76
+ },
77
+ {
78
+ "key": "advanced_features",
79
+ "description": "Advanced features walkthrough",
80
+ "url": "https://example.com/advanced.mp4",
81
+ "wait": True
82
+ }
83
+ ]
84
+ })
85
+ ```
86
+
87
+ **Generated Tool**: `play_demo(action)`
88
+ **Actions**: `["start_product_overview", "start_advanced_features", "stop"]`
89
+
90
+ ### Music Playlist
91
+
92
+ ```python
93
+ agent.add_skill("play_background_file", {
94
+ "tool_name": "play_music",
95
+ "files": [
96
+ {
97
+ "key": "jazz",
98
+ "description": "smooth jazz background music",
99
+ "url": "https://example.com/jazz.mp3"
100
+ },
101
+ {
102
+ "key": "classical",
103
+ "description": "classical piano background music",
104
+ "url": "https://example.com/classical.mp3"
105
+ }
106
+ ]
107
+ })
108
+ ```
109
+
110
+ ## Generated SWAIG Function
111
+
112
+ For the testimonial example above, the skill generates:
113
+
114
+ ```json
115
+ {
116
+ "function": "play_testimonial",
117
+ "description": "Control background file playback for play testimonial",
118
+ "parameters": {
119
+ "type": "object",
120
+ "properties": {
121
+ "action": {
122
+ "type": "string",
123
+ "description": "Action to perform. Options: start_massey_success: Customer success story from Massey Energy; stop: Stop any currently playing background file",
124
+ "enum": ["start_massey_success", "stop"]
125
+ }
126
+ },
127
+ "required": ["action"]
128
+ },
129
+ "data_map": {
130
+ "expressions": [
131
+ {
132
+ "string": "${args.action}",
133
+ "pattern": "/start_massey_success/i",
134
+ "output": {
135
+ "response": "Tell the user you are now going to play Customer success story from Massey Energy for them.",
136
+ "action": [
137
+ {
138
+ "playback_bg": {
139
+ "file": "https://tatooine.cantina.cloud/vids/massey.mp4",
140
+ "wait": true
141
+ }
142
+ }
143
+ ],
144
+ "post_process": true
145
+ }
146
+ },
147
+ {
148
+ "string": "${args.action}",
149
+ "pattern": "/stop/i",
150
+ "output": {
151
+ "response": "Tell the user you have stopped the background file playback.",
152
+ "action": [
153
+ {
154
+ "stop_playback_bg": true
155
+ }
156
+ ],
157
+ "post_process": true
158
+ }
159
+ }
160
+ ]
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Execution Flow
166
+
167
+ 1. **AI calls function**: `play_testimonial(action: "start_massey_success")`
168
+ 2. **DataMap matches pattern**: `/start_massey_success/i`
169
+ 3. **AI receives instruction**: "Tell the user you are now going to play Customer success story from Massey Energy for them."
170
+ 4. **AI responds naturally**: The AI interprets this and responds in its own voice
171
+ 5. **Action executes**: Background video starts playing
172
+ 6. **User experience**: Natural AI announcement before action
173
+
174
+ ## Multiple Instances
175
+
176
+ You can add multiple instances of this skill with different tool names:
177
+
178
+ ```python
179
+ # Testimonials
180
+ agent.add_skill("play_background_file", {
181
+ "tool_name": "play_testimonial",
182
+ "files": [{"key": "massey", "description": "Massey Energy success story", "url": "..."}]
183
+ })
184
+
185
+ # Demos
186
+ agent.add_skill("play_background_file", {
187
+ "tool_name": "play_demo",
188
+ "files": [
189
+ {"key": "overview", "description": "Product overview", "url": "..."},
190
+ {"key": "features", "description": "Advanced features", "url": "..."}
191
+ ]
192
+ })
193
+
194
+ # Music
195
+ agent.add_skill("play_background_file", {
196
+ "tool_name": "play_music",
197
+ "files": [{"key": "jazz", "description": "Jazz music", "url": "..."}]
198
+ })
199
+ ```
200
+
201
+ This creates three separate tools: `play_testimonial`, `play_demo`, and `play_music`.
202
+
203
+ ## Benefits
204
+
205
+ - **Reusable**: Same skill manages different file collections
206
+ - **Configurable**: Easy to add/remove files without code changes
207
+ - **Efficient**: DataMap serverless execution
208
+ - **Type Safe**: Enum parameters prevent invalid file references
209
+ - **Natural**: AI speaks before actions with post-processing
210
+ - **Maintainable**: Centralized file management logic
211
+
212
+ ## Implementation Notes
213
+
214
+ - File keys are converted to `start_{key}` enum values
215
+ - All tools include a "stop" action to halt playback
216
+ - Post-processing is enabled by default for natural conversation flow
217
+ - The skill validates all configuration parameters at initialization
218
+ - Instance keys combine skill name and tool name for uniqueness
@@ -0,0 +1,12 @@
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
+ from .skill import PlayBackgroundFileSkill
11
+
12
+ __all__ = ['PlayBackgroundFileSkill']
@@ -0,0 +1,242 @@
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
+ Play Background File Skill
12
+
13
+ A configurable skill for managing background file playback with custom tool names
14
+ and multiple file collections. Supports both audio and video files.
15
+ """
16
+
17
+ from typing import Dict, Any, List
18
+ from signalwire_agents.core import SwaigFunctionResult
19
+ from signalwire_agents.core.skill_base import SkillBase
20
+
21
+
22
+ class PlayBackgroundFileSkill(SkillBase):
23
+ """
24
+ Skill for playing background files (audio/video) with configurable tool names.
25
+
26
+ Supports multiple instances with different tool names and file collections.
27
+ Uses DataMap for serverless execution with dynamic enum generation.
28
+
29
+ Configuration:
30
+ - tool_name: Custom name for the generated SWAIG function
31
+ - files: Array of file objects with key, description, url, and optional wait
32
+
33
+ Example:
34
+ agent.add_skill("play_background_file", {
35
+ "tool_name": "play_testimonial",
36
+ "files": [
37
+ {
38
+ "key": "massey",
39
+ "description": "Customer success story from Massey Energy",
40
+ "url": "https://example.com/massey.mp4",
41
+ "wait": True
42
+ }
43
+ ]
44
+ })
45
+ """
46
+
47
+ SKILL_NAME = "play_background_file"
48
+ SKILL_DESCRIPTION = "Control background file playback"
49
+ SUPPORTS_MULTIPLE_INSTANCES = True
50
+
51
+ @classmethod
52
+ def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
53
+ """Get parameter schema for Play Background File skill"""
54
+ schema = super().get_parameter_schema()
55
+ schema.update({
56
+ "files": {
57
+ "type": "array",
58
+ "description": "Array of file configurations to make available for playback",
59
+ "required": True,
60
+ "items": {
61
+ "type": "object",
62
+ "properties": {
63
+ "key": {
64
+ "type": "string",
65
+ "description": "Unique identifier for the file"
66
+ },
67
+ "description": {
68
+ "type": "string",
69
+ "description": "Human-readable description of the file"
70
+ },
71
+ "url": {
72
+ "type": "string",
73
+ "description": "URL of the audio/video file to play"
74
+ },
75
+ "wait": {
76
+ "type": "boolean",
77
+ "description": "Whether to wait for file to finish playing",
78
+ "default": False
79
+ }
80
+ },
81
+ "required": ["key", "description", "url"]
82
+ }
83
+ }
84
+ })
85
+ return schema
86
+
87
+ def __init__(self, agent, params: Dict[str, Any] = None):
88
+ """
89
+ Initialize the skill with configuration parameters.
90
+
91
+ Args:
92
+ agent: The agent instance this skill belongs to
93
+ params: Configuration dictionary containing:
94
+ - tool_name: Custom tool name (default: "play_background_file")
95
+ - files: Array of file objects with key, description, url, wait
96
+ """
97
+ super().__init__(agent, params)
98
+
99
+ # Extract configuration
100
+ self.tool_name = self.params.get('tool_name', 'play_background_file')
101
+ self.files = self.params.get('files', [])
102
+
103
+ # Validate configuration
104
+ self._validate_config()
105
+
106
+ def _validate_config(self):
107
+ """Validate the skill configuration."""
108
+ if not isinstance(self.files, list) or len(self.files) == 0:
109
+ raise ValueError("files parameter must be a non-empty list")
110
+
111
+ for i, file_obj in enumerate(self.files):
112
+ if not isinstance(file_obj, dict):
113
+ raise ValueError(f"File {i} must be a dictionary")
114
+
115
+ required_fields = ['key', 'description', 'url']
116
+ for field in required_fields:
117
+ if field not in file_obj:
118
+ raise ValueError(f"File {i} missing required field: {field}")
119
+ if not isinstance(file_obj[field], str) or not file_obj[field].strip():
120
+ raise ValueError(f"File {i} field '{field}' must be a non-empty string")
121
+
122
+ # Validate optional wait field
123
+ if 'wait' in file_obj and not isinstance(file_obj['wait'], bool):
124
+ raise ValueError(f"File {i} field 'wait' must be a boolean")
125
+
126
+ # Validate key format (alphanumeric, underscores, hyphens)
127
+ key = file_obj['key']
128
+ if not key.replace('_', '').replace('-', '').isalnum():
129
+ raise ValueError(f"File {i} key '{key}' must contain only alphanumeric characters, underscores, and hyphens")
130
+
131
+ def get_instance_key(self) -> str:
132
+ """
133
+ Generate a unique instance key for this skill configuration.
134
+
135
+ Returns:
136
+ Unique key combining skill name and tool name
137
+ """
138
+ return f"{self.SKILL_NAME}_{self.tool_name}"
139
+
140
+ def setup(self) -> bool:
141
+ """
142
+ Setup the skill - no external dependencies needed.
143
+
144
+ Returns:
145
+ True if setup successful
146
+ """
147
+ return True
148
+
149
+ def register_tools(self) -> None:
150
+ """Register SWAIG tools with the agent"""
151
+ tools = self.get_tools()
152
+ for tool in tools:
153
+ # Merge any swaig_fields from params into the tool
154
+ if self.swaig_fields:
155
+ tool.update(self.swaig_fields)
156
+ self.agent.register_swaig_function(tool)
157
+
158
+ def get_tools(self) -> List[Dict[str, Any]]:
159
+ """
160
+ Generate the SWAIG tool with DataMap expressions.
161
+
162
+ Returns:
163
+ List containing the generated tool configuration
164
+ """
165
+ # Build enum values and descriptions
166
+ enum_values = []
167
+ descriptions = []
168
+
169
+ for file_obj in self.files:
170
+ enum_values.append(f"start_{file_obj['key']}")
171
+ descriptions.append(f"start_{file_obj['key']}: {file_obj['description']}")
172
+
173
+ enum_values.append("stop")
174
+ descriptions.append("stop: Stop any currently playing background file")
175
+
176
+ # Build parameter description
177
+ description = "Action to perform. Options: " + "; ".join(descriptions)
178
+
179
+ # Create the tool configuration
180
+ tool = {
181
+ "function": self.tool_name,
182
+ "description": f"Control background file playback for {self.tool_name.replace('_', ' ')}",
183
+ "parameters": {
184
+ "type": "object",
185
+ "properties": {
186
+ "action": {
187
+ "type": "string",
188
+ "description": description,
189
+ "enum": enum_values
190
+ }
191
+ },
192
+ "required": ["action"]
193
+ },
194
+ "wait_for_fillers": True,
195
+ "skip_fillers": True,
196
+ "data_map": {
197
+ "expressions": self._build_expressions()
198
+ }
199
+ }
200
+
201
+ return [tool]
202
+
203
+ def _build_expressions(self) -> List[Dict[str, Any]]:
204
+ """
205
+ Build DataMap expressions for file playback and stop actions.
206
+
207
+ Returns:
208
+ List of expression configurations
209
+ """
210
+ expressions = []
211
+
212
+ # Add expressions for each file
213
+ for file_obj in self.files:
214
+ key = file_obj['key']
215
+ url = file_obj['url']
216
+ wait = file_obj.get('wait', False)
217
+ description = file_obj['description']
218
+
219
+ # Create the result with appropriate response
220
+ result = SwaigFunctionResult(
221
+ f"Tell the user you are now going to play {description} for them.",
222
+ post_process=True
223
+ ).play_background_file(url, wait=wait)
224
+
225
+ expressions.append({
226
+ "string": "${args.action}",
227
+ "pattern": f"/start_{key}/i",
228
+ "output": result.to_dict()
229
+ })
230
+
231
+ # Add stop expression
232
+ stop_result = SwaigFunctionResult(
233
+ "Tell the user you have stopped the background file playback."
234
+ ).stop_background_file()
235
+
236
+ expressions.append({
237
+ "string": "${args.action}",
238
+ "pattern": "/stop/i",
239
+ "output": stop_result.to_dict()
240
+ })
241
+
242
+ return expressions