signalwire-agents 0.1.23__py3-none-any.whl → 0.1.24__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 (64) hide show
  1. signalwire_agents/__init__.py +1 -1
  2. signalwire_agents/agent_server.py +2 -1
  3. signalwire_agents/cli/config.py +61 -0
  4. signalwire_agents/cli/core/__init__.py +1 -0
  5. signalwire_agents/cli/core/agent_loader.py +254 -0
  6. signalwire_agents/cli/core/argparse_helpers.py +164 -0
  7. signalwire_agents/cli/core/dynamic_config.py +62 -0
  8. signalwire_agents/cli/execution/__init__.py +1 -0
  9. signalwire_agents/cli/execution/datamap_exec.py +437 -0
  10. signalwire_agents/cli/execution/webhook_exec.py +125 -0
  11. signalwire_agents/cli/output/__init__.py +1 -0
  12. signalwire_agents/cli/output/output_formatter.py +132 -0
  13. signalwire_agents/cli/output/swml_dump.py +177 -0
  14. signalwire_agents/cli/simulation/__init__.py +1 -0
  15. signalwire_agents/cli/simulation/data_generation.py +365 -0
  16. signalwire_agents/cli/simulation/data_overrides.py +187 -0
  17. signalwire_agents/cli/simulation/mock_env.py +271 -0
  18. signalwire_agents/cli/test_swaig.py +522 -2539
  19. signalwire_agents/cli/types.py +72 -0
  20. signalwire_agents/core/agent/__init__.py +1 -3
  21. signalwire_agents/core/agent/config/__init__.py +1 -3
  22. signalwire_agents/core/agent/prompt/manager.py +25 -7
  23. signalwire_agents/core/agent/tools/decorator.py +2 -0
  24. signalwire_agents/core/agent/tools/registry.py +8 -0
  25. signalwire_agents/core/agent_base.py +492 -3053
  26. signalwire_agents/core/function_result.py +31 -42
  27. signalwire_agents/core/mixins/__init__.py +28 -0
  28. signalwire_agents/core/mixins/ai_config_mixin.py +373 -0
  29. signalwire_agents/core/mixins/auth_mixin.py +287 -0
  30. signalwire_agents/core/mixins/prompt_mixin.py +345 -0
  31. signalwire_agents/core/mixins/serverless_mixin.py +368 -0
  32. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  33. signalwire_agents/core/mixins/state_mixin.py +219 -0
  34. signalwire_agents/core/mixins/tool_mixin.py +295 -0
  35. signalwire_agents/core/mixins/web_mixin.py +1130 -0
  36. signalwire_agents/core/skill_manager.py +3 -1
  37. signalwire_agents/core/swaig_function.py +10 -1
  38. signalwire_agents/core/swml_service.py +140 -58
  39. signalwire_agents/skills/README.md +452 -0
  40. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  41. signalwire_agents/skills/datasphere/README.md +210 -0
  42. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  43. signalwire_agents/skills/datetime/README.md +132 -0
  44. signalwire_agents/skills/joke/README.md +149 -0
  45. signalwire_agents/skills/math/README.md +161 -0
  46. signalwire_agents/skills/native_vector_search/skill.py +33 -13
  47. signalwire_agents/skills/play_background_file/README.md +218 -0
  48. signalwire_agents/skills/spider/README.md +236 -0
  49. signalwire_agents/skills/spider/__init__.py +4 -0
  50. signalwire_agents/skills/spider/skill.py +479 -0
  51. signalwire_agents/skills/swml_transfer/README.md +395 -0
  52. signalwire_agents/skills/swml_transfer/__init__.py +1 -0
  53. signalwire_agents/skills/swml_transfer/skill.py +257 -0
  54. signalwire_agents/skills/weather_api/README.md +178 -0
  55. signalwire_agents/skills/web_search/README.md +163 -0
  56. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  57. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/METADATA +47 -2
  58. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/RECORD +62 -22
  59. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/entry_points.txt +1 -1
  60. signalwire_agents/core/agent/config/ephemeral.py +0 -176
  61. signalwire_agents-0.1.23.data/data/schema.json +0 -5611
  62. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/WHEEL +0 -0
  63. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/licenses/LICENSE +0 -0
  64. {signalwire_agents-0.1.23.dist-info → signalwire_agents-0.1.24.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,149 @@
1
+ # Joke Skill
2
+
3
+ Tell jokes using the API Ninjas joke API with DataMap integration.
4
+
5
+ ## Description
6
+
7
+ The Joke skill provides joke-telling capabilities to your SignalWire AI agents using the API Ninjas joke API. This skill demonstrates how to use DataMap for external API integration without requiring custom webhook endpoints.
8
+
9
+ ## Features
10
+
11
+ - **Random Jokes**: Get random jokes from API Ninjas
12
+ - **Dad Jokes**: Specifically request dad jokes
13
+ - **DataMap Integration**: Uses DataMap for serverless API execution
14
+ - **Configurable Tool Name**: Support for custom tool names
15
+ - **Required Parameter Validation**: Ensures joke type is specified
16
+
17
+ ## Requirements
18
+
19
+ - API Ninjas API key
20
+ - No additional Python packages required (DataMap handles API calls)
21
+
22
+ ## Configuration
23
+
24
+ ### Required Parameters
25
+
26
+ - `api_key`: Your API Ninjas API key
27
+
28
+ ### Optional Parameters
29
+
30
+ - `tool_name`: Custom name for the joke function (default: "get_joke")
31
+
32
+ ## Usage
33
+
34
+ ### Basic Usage
35
+
36
+ ```python
37
+ from signalwire_agents import AgentBase
38
+
39
+ class MyAgent(AgentBase):
40
+ def __init__(self):
41
+ super().__init__(name="Joke Agent", route="/jokes")
42
+
43
+ # Add joke skill
44
+ self.add_skill("joke", {
45
+ "api_key": "your-api-ninjas-api-key"
46
+ })
47
+
48
+ agent = MyAgent()
49
+ agent.serve()
50
+ ```
51
+
52
+ ### Advanced Usage
53
+
54
+ ```python
55
+ # Custom tool name
56
+ self.add_skill("joke", {
57
+ "api_key": "your-api-ninjas-api-key",
58
+ "tool_name": "tell_joke"
59
+ })
60
+ ```
61
+
62
+ ## Function Details
63
+
64
+ ### `get_joke(type: str)`
65
+
66
+ **Parameters:**
67
+ - `type` (required): Type of joke to get
68
+ - `"jokes"` - Regular jokes
69
+ - `"dadjokes"` - Dad jokes
70
+
71
+ **Returns:** A joke from the API Ninjas joke database
72
+
73
+ **Example Usage:**
74
+ - "Tell me a joke" (AI will choose the type)
75
+ - "Tell me a dad joke" (AI will use type="dadjokes")
76
+ - "Get me a regular joke" (AI will use type="jokes")
77
+
78
+ ## API Integration
79
+
80
+ This skill integrates with the API Ninjas Jokes API:
81
+ - **Endpoint**: `https://api.api-ninjas.com/v1/{type}`
82
+ - **Method**: GET
83
+ - **Authentication**: X-Api-Key header
84
+ - **Response**: JSON array with joke objects
85
+
86
+ ## Getting an API Key
87
+
88
+ 1. Visit [API Ninjas](https://api.api-ninjas.com/)
89
+ 2. Sign up for a free account
90
+ 3. Get your API key from the dashboard
91
+ 4. Use the key in your skill configuration
92
+
93
+ ## Error Handling
94
+
95
+ - Missing API key: Skill setup will fail with clear error message
96
+ - Invalid joke type: Parameter validation ensures only valid types are accepted
97
+ - API errors: Handled gracefully with user-friendly error messages
98
+
99
+ ## DataMap Implementation
100
+
101
+ This skill demonstrates DataMap usage with:
102
+
103
+ - **External API**: Calls API Ninjas joke endpoints
104
+ - **Dynamic URLs**: Uses `${args.type}` for different joke types
105
+ - **Header Authentication**: Includes API key in request headers
106
+ - **Response Processing**: Extracts joke from API response array
107
+ - **Error Handling**: Handles API errors gracefully
108
+
109
+ ## Troubleshooting
110
+
111
+ ### Common Issues
112
+
113
+ 1. **"Missing required parameters: ['api_key']"**
114
+ - Ensure you provide a valid API Ninjas API key
115
+
116
+ 2. **"No jokes returned"**
117
+ - Check your API key is valid
118
+ - Verify API Ninjas service is accessible
119
+ - Check API quota/rate limits
120
+
121
+ 3. **"Tool not found"**
122
+ - Ensure skill loaded successfully
123
+ - Check for any setup errors in logs
124
+
125
+ ### Debugging
126
+
127
+ Enable debug logging to see API requests:
128
+
129
+ ```python
130
+ import logging
131
+ logging.basicConfig(level=logging.DEBUG)
132
+ ```
133
+
134
+ ## API Reference
135
+
136
+ ### API Ninjas Endpoints
137
+
138
+ - `GET /v1/jokes` - Random jokes
139
+ - `GET /v1/dadjokes` - Dad jokes
140
+
141
+ ### Response Format
142
+
143
+ ```json
144
+ [
145
+ {
146
+ "joke": "Why don't scientists trust atoms? Because they make up everything!"
147
+ }
148
+ ]
149
+ ```
@@ -0,0 +1,161 @@
1
+ # Math Skill
2
+
3
+ The math skill provides safe mathematical calculation capabilities for agents. It allows users to perform basic arithmetic operations and complex mathematical expressions with security protections against code injection.
4
+
5
+ ## Features
6
+
7
+ - Safe mathematical expression evaluation
8
+ - Support for basic arithmetic operations
9
+ - Parentheses support for complex expressions
10
+ - Division by zero protection
11
+ - Security filtering to prevent code injection
12
+ - No external dependencies required
13
+
14
+ ## Requirements
15
+
16
+ - **Packages**: None (uses built-in Python functionality)
17
+ - **No external APIs required**
18
+
19
+ ## Parameters
20
+
21
+ ### Optional Parameters
22
+
23
+ - `swaig_fields` (dict): Additional SWAIG function configuration
24
+ - `secure` (boolean): Override security settings for the calculation function
25
+ - `fillers` (dict): Language-specific filler phrases while calculating
26
+ - Any other SWAIG function parameters
27
+
28
+ **Note**: This skill does not require any configuration parameters beyond the optional swaig_fields. It works out-of-the-box with no setup.
29
+
30
+ ## Tools Created
31
+
32
+ - `calculate` - Perform a mathematical calculation with basic operations
33
+
34
+ ## Usage Examples
35
+
36
+ ### Basic Usage
37
+
38
+ ```python
39
+ # No configuration needed - works immediately
40
+ agent.add_skill("math")
41
+ ```
42
+
43
+ ### With Custom Fillers
44
+
45
+ ```python
46
+ agent.add_skill("math", {
47
+ "swaig_fields": {
48
+ "fillers": {
49
+ "en-US": [
50
+ "Let me calculate that for you...",
51
+ "Crunching the numbers...",
52
+ "Computing the result...",
53
+ "Working out the math..."
54
+ ],
55
+ "es-ES": [
56
+ "Déjame calcular eso...",
57
+ "Procesando los números...",
58
+ "Calculando el resultado..."
59
+ ]
60
+ }
61
+ }
62
+ })
63
+ ```
64
+
65
+ ### Disabling Security (if needed)
66
+
67
+ ```python
68
+ agent.add_skill("math", {
69
+ "swaig_fields": {
70
+ "secure": False # Allow unauthenticated calculation requests
71
+ }
72
+ })
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ ### Calculation Function
78
+ - **Input**: Mathematical expression as a string
79
+ - **Processing**: Validates expression for safety, then evaluates it
80
+ - **Output**: Shows the original expression and the calculated result
81
+ - **Example**: "2 + 3 * 4 = 14"
82
+
83
+ ### Supported Operations
84
+
85
+ - **Addition**: `+` (e.g., "5 + 3")
86
+ - **Subtraction**: `-` (e.g., "10 - 7")
87
+ - **Multiplication**: `*` (e.g., "6 * 8")
88
+ - **Division**: `/` (e.g., "15 / 3")
89
+ - **Modulo**: `%` (e.g., "17 % 5")
90
+ - **Exponentiation**: `**` (e.g., "2 ** 3")
91
+ - **Parentheses**: `()` for grouping (e.g., "(2 + 3) * 4")
92
+
93
+ ### Expression Examples
94
+
95
+ - Simple: `"2 + 3"` → "2 + 3 = 5"
96
+ - Complex: `"(10 + 5) * 2 / 3"` → "(10 + 5) * 2 / 3 = 10.0"
97
+ - With decimals: `"3.14 * 2"` → "3.14 * 2 = 6.28"
98
+ - Powers: `"2 ** 8"` → "2 ** 8 = 256"
99
+ - Modulo: `"17 % 5"` → "17 % 5 = 2"
100
+
101
+ ## Function Parameters
102
+
103
+ The calculate tool accepts one parameter:
104
+
105
+ - `expression` (string, required): Mathematical expression to evaluate
106
+ - Must contain only numbers, operators, and parentheses
107
+ - Operators allowed: `+`, `-`, `*`, `/`, `%`, `**`, `(`, `)`
108
+ - Decimal numbers are supported
109
+ - Spaces are allowed and ignored
110
+
111
+ ## Security Features
112
+
113
+ ### Input Validation
114
+ - Only allows safe mathematical characters: numbers, operators, parentheses, spaces, decimal points
115
+ - Blocks any potentially dangerous code or function calls
116
+ - Uses regex pattern matching for character validation
117
+
118
+ ### Safe Evaluation
119
+ - Uses Python's `eval()` with restricted builtins (empty `__builtins__`)
120
+ - No access to system functions or imports
121
+ - Cannot execute arbitrary code
122
+
123
+ ### Error Handling
124
+ - **Division by Zero**: Returns friendly error message
125
+ - **Invalid Syntax**: Returns error for malformed expressions
126
+ - **Illegal Characters**: Rejects expressions with non-math characters
127
+ - **Empty Input**: Prompts user to provide an expression
128
+
129
+ ## Error Examples
130
+
131
+ - **Division by zero**: "Error: Division by zero is not allowed."
132
+ - **Invalid characters**: "Invalid expression. Only numbers and basic math operators are allowed."
133
+ - **Syntax error**: "Error calculating '2 + + 3': Invalid expression"
134
+ - **Empty input**: "Please provide a mathematical expression to calculate."
135
+
136
+ ## Common Use Cases
137
+
138
+ 1. **Basic Arithmetic**: "What's 15 + 27?"
139
+ 2. **Percentage Calculations**: "What's 15% of 200?" → "200 * 0.15"
140
+ 3. **Complex Expressions**: "Calculate (100 + 50) * 0.08"
141
+ 4. **Powers and Roots**: "What's 2 to the power of 10?"
142
+ 5. **Financial Calculations**: "If I have $500 and spend $125, how much is left?"
143
+
144
+ ## Best Practices
145
+
146
+ 1. **Default Behavior**: The skill works immediately without configuration
147
+ 2. **User Education**: Help users understand they can use parentheses for complex calculations
148
+ 3. **Expression Formatting**: The skill is forgiving with spaces and formatting
149
+ 4. **Error Recovery**: Provide helpful guidance when users make syntax errors
150
+ 5. **Security**: The skill is designed to be safe even with malicious input
151
+
152
+ ## Agent Integration
153
+
154
+ When added to an agent, this skill automatically:
155
+
156
+ - Adds speech recognition hints for math-related words
157
+ - Provides prompt guidance about calculation capabilities
158
+ - Enables the agent to respond to mathematical questions
159
+ - Shows both the original expression and result for transparency
160
+
161
+ The skill is designed to be completely self-contained and secure, making it safe for any agent to use for mathematical calculations.
@@ -190,6 +190,37 @@ class NativeVectorSearchSkill(SkillBase):
190
190
  **self.swaig_fields
191
191
  )
192
192
 
193
+ # Add our tool to the Knowledge Search section
194
+ search_mode = "remote search server" if self.use_remote else "local document indexes"
195
+ section_title = "Knowledge Search"
196
+
197
+ # Try to check if section exists, but handle if method doesn't exist
198
+ section_exists = False
199
+ try:
200
+ if hasattr(self.agent, 'prompt_has_section'):
201
+ section_exists = self.agent.prompt_has_section(section_title)
202
+ except Exception:
203
+ # Method might not work, assume section doesn't exist
204
+ pass
205
+
206
+ if section_exists:
207
+ # Add bullet to existing section
208
+ self.agent.prompt_add_to_section(
209
+ title=section_title,
210
+ bullet=f"Use {self.tool_name} to search {search_mode}: {description}"
211
+ )
212
+ else:
213
+ # Create the section with this tool
214
+ self.agent.prompt_add_section(
215
+ title=section_title,
216
+ body="You can search various knowledge sources using the following tools:",
217
+ bullets=[
218
+ f"Use {self.tool_name} to search {search_mode}: {description}",
219
+ "Search for relevant information using clear, specific queries",
220
+ "If no results are found, suggest the user try rephrasing their question or try another knowledge source"
221
+ ]
222
+ )
223
+
193
224
  def _search_handler(self, args, raw_data):
194
225
  """Handle search requests"""
195
226
 
@@ -365,19 +396,8 @@ class NativeVectorSearchSkill(SkillBase):
365
396
 
366
397
  def get_prompt_sections(self) -> List[Dict[str, Any]]:
367
398
  """Return prompt sections to add to agent"""
368
- search_mode = "remote search server" if self.use_remote else "local document indexes"
369
- return [
370
- {
371
- "title": "Document Search",
372
- "body": f"You can search {search_mode} using the {self.tool_name} tool.",
373
- "bullets": [
374
- f"Use the {self.tool_name} tool when users ask questions about topics that might be in the indexed documents",
375
- "Search for relevant information using clear, specific queries",
376
- "Provide helpful summaries of the search results",
377
- "If no results are found, suggest the user try rephrasing their question or ask about different topics"
378
- ]
379
- }
380
- ]
399
+ # We'll handle this in register_tools after the agent is set
400
+ return []
381
401
 
382
402
  def _add_prompt_section(self, agent):
383
403
  """Add prompt section to agent (called during skill loading)"""
@@ -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