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,452 @@
1
+ # SignalWire Agents Skills System
2
+
3
+ This directory contains the modular skills system for SignalWire AI Agents. Skills provide reusable, configurable capabilities that can be easily added to any agent.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Creating a New Skill](#creating-a-new-skill)
9
+ - [Skill Structure](#skill-structure)
10
+ - [Required Files](#required-files)
11
+ - [Skill Class Implementation](#skill-class-implementation)
12
+ - [DataMap Integration](#datamap-integration)
13
+ - [Package Configuration](#package-configuration)
14
+ - [Testing Your Skill](#testing-your-skill)
15
+ - [Documentation](#documentation)
16
+ - [Examples](#examples)
17
+ - [Best Practices](#best-practices)
18
+
19
+ ## Overview
20
+
21
+ The skills system provides:
22
+ - **Modular capabilities**: Reusable functionality across agents
23
+ - **Automatic discovery**: Skills are automatically found and registered
24
+ - **Configuration validation**: Parameter checking and error handling
25
+ - **DataMap integration**: Serverless API execution without custom webhooks
26
+ - **Documentation**: Built-in help and usage information
27
+
28
+ ## Creating a New Skill
29
+
30
+ ### Step 1: Create Skill Directory
31
+
32
+ Create a new directory under `signalwire_agents/skills/` with your skill name:
33
+
34
+ ```bash
35
+ mkdir signalwire_agents/skills/your_skill_name
36
+ ```
37
+
38
+ ### Step 2: Required Files
39
+
40
+ Every skill needs these files:
41
+
42
+ ```
43
+ signalwire_agents/skills/your_skill_name/
44
+ ├── __init__.py # Package initialization
45
+ ├── skill.py # Main skill implementation
46
+ └── README.md # Skill documentation
47
+ ```
48
+
49
+ ### Step 3: Update Package Configuration
50
+
51
+ Add your skill to `pyproject.toml`:
52
+
53
+ ```toml
54
+ [tool.setuptools]
55
+ packages = [
56
+ # ... existing packages ...
57
+ "signalwire_agents.skills.your_skill_name"
58
+ ]
59
+ ```
60
+
61
+ ## Skill Structure
62
+
63
+ ### `__init__.py`
64
+
65
+ Simple package initialization:
66
+
67
+ ```python
68
+ """Your Skill Name for SignalWire Agents"""
69
+ ```
70
+
71
+ ### `skill.py`
72
+
73
+ Main skill implementation inheriting from `SkillBase`:
74
+
75
+ ```python
76
+ """
77
+ Copyright (c) 2025 SignalWire
78
+
79
+ This file is part of the SignalWire AI Agents SDK.
80
+
81
+ Licensed under the MIT License.
82
+ See LICENSE file in the project root for full license information.
83
+ """
84
+
85
+ from typing import List, Dict, Any
86
+
87
+ from signalwire_agents.core.skill_base import SkillBase
88
+ from signalwire_agents.core.data_map import DataMap
89
+ from signalwire_agents.core.function_result import SwaigFunctionResult
90
+
91
+
92
+ class YourSkillClass(SkillBase):
93
+ """Your skill description"""
94
+
95
+ SKILL_NAME = "your_skill_name"
96
+ SKILL_DESCRIPTION = "Brief description of what your skill does"
97
+ SKILL_VERSION = "1.0.0"
98
+ REQUIRED_PACKAGES = [] # List of required Python packages
99
+ REQUIRED_ENV_VARS = [] # List of required environment variables
100
+
101
+ def setup(self) -> bool:
102
+ """Setup and validate skill configuration"""
103
+ # Validate required parameters
104
+ required_params = ['param1', 'param2']
105
+ missing_params = [param for param in required_params if not self.params.get(param)]
106
+ if missing_params:
107
+ self.logger.error(f"Missing required parameters: {missing_params}")
108
+ return False
109
+
110
+ # Store configuration
111
+ self.param1 = self.params['param1']
112
+ self.param2 = self.params.get('param2', 'default_value')
113
+
114
+ return True
115
+
116
+ def register_tools(self) -> None:
117
+ """Register skill tools/functions"""
118
+ # Implement your tool registration here
119
+ pass
120
+
121
+ def get_hints(self) -> List[str]:
122
+ """Return speech recognition hints"""
123
+ # Currently no hints provided, but you could add them like:
124
+ # return ["hint1", "hint2", "phrase users might say"]
125
+ return []
126
+
127
+ def get_global_data(self) -> Dict[str, Any]:
128
+ """Return global data for DataMap variables"""
129
+ return {
130
+ "skill_param": self.param1
131
+ }
132
+
133
+ def get_prompt_sections(self) -> List[Dict[str, Any]]:
134
+ """Return prompt sections to add to agent"""
135
+ return [
136
+ {
137
+ "title": "Your Skill Capability",
138
+ "body": "Description of what the agent can do with this skill.",
139
+ "bullets": [
140
+ "Specific capability 1",
141
+ "Specific capability 2"
142
+ ]
143
+ }
144
+ ]
145
+ ```
146
+
147
+ ## Skill Class Implementation
148
+
149
+ ### Required Class Attributes
150
+
151
+ - `SKILL_NAME`: Unique identifier for the skill
152
+ - `SKILL_DESCRIPTION`: Brief description shown in skill listings
153
+ - `SKILL_VERSION`: Version string for the skill
154
+ - `REQUIRED_PACKAGES`: List of Python packages needed
155
+ - `REQUIRED_ENV_VARS`: List of environment variables needed
156
+
157
+ ### Required Methods
158
+
159
+ #### `setup(self) -> bool`
160
+ - Validate and store configuration parameters
161
+ - Return `True` if setup successful, `False` otherwise
162
+ - Access parameters via `self.params`
163
+
164
+ #### `register_tools(self) -> None`
165
+ - Register SWAIG functions/tools with the agent
166
+ - Use `self.agent.register_swaig_function()` for custom functions
167
+ - Use DataMap for external API integration
168
+
169
+ #### `get_hints(self) -> List[str]`
170
+ - Return phrases for speech recognition hints (optional)
171
+ - Help the ASR system recognize skill-related requests
172
+ - Can return empty list if no hints are needed
173
+
174
+ #### `get_global_data(self) -> Dict[str, Any]`
175
+ - Return data available to DataMap expressions
176
+ - Access via `${global.key}` in DataMap configurations
177
+
178
+ #### `get_prompt_sections(self) -> List[Dict[str, Any]]`
179
+ - Return prompt sections to add to the agent
180
+ - Structure: `{"title": str, "body": str, "bullets": List[str]}`
181
+
182
+ ## DataMap Integration
183
+
184
+ For skills that need to call external APIs, use DataMap for serverless execution:
185
+
186
+ ```python
187
+ def register_tools(self) -> None:
188
+ """Register DataMap-based tool"""
189
+
190
+ tool = (DataMap("tool_name")
191
+ .description("What this tool does")
192
+ .parameter("param1", "string", "Parameter description", required=True)
193
+ .parameter("param2", "string", "Optional parameter", required=False)
194
+ .webhook("GET", "https://api.example.com/endpoint/${args.param1}",
195
+ headers={"Authorization": f"Bearer {self.api_key}"})
196
+ .output(SwaigFunctionResult("Result: ${response.data}"))
197
+ .error_keys(["error", "message"])
198
+ )
199
+
200
+ self.agent.register_swaig_function(tool.to_swaig_function())
201
+ ```
202
+
203
+ ### DataMap Features
204
+
205
+ - **Dynamic URLs**: Use `${args.param}` for user inputs
206
+ - **Headers**: Add authentication and other headers
207
+ - **Response Processing**: Extract data with `${response.field}`
208
+ - **Error Handling**: Specify error keys to watch for
209
+ - **Defaults**: Use `${args.param || "default"}` for fallbacks
210
+
211
+ ## Package Configuration
212
+
213
+ After creating your skill, update `pyproject.toml` to include it in the package:
214
+
215
+ ```toml
216
+ [tool.setuptools]
217
+ packages = [
218
+ "signalwire_agents",
219
+ "signalwire_agents.prefabs",
220
+ "signalwire_agents.utils",
221
+ "signalwire_agents.core",
222
+ "signalwire_agents.core.state",
223
+ "signalwire_agents.core.security",
224
+ "signalwire_agents.skills",
225
+ "signalwire_agents.skills.web_search",
226
+ "signalwire_agents.skills.datetime",
227
+ "signalwire_agents.skills.math",
228
+ "signalwire_agents.skills.joke",
229
+ "signalwire_agents.skills.datasphere",
230
+ "signalwire_agents.skills.wikipedia_search",
231
+ "signalwire_agents.skills.your_skill_name" # Add your skill here
232
+ ]
233
+ ```
234
+
235
+ ## Testing Your Skill
236
+
237
+ ### 1. Install the Package
238
+
239
+ ```bash
240
+ pip install . --force-reinstall
241
+ ```
242
+
243
+ ### 2. Test Skill Discovery
244
+
245
+ ```python
246
+ from signalwire_agents.skills.registry import skill_registry
247
+
248
+ skill_registry.discover_skills()
249
+ skills = skill_registry.list_skills()
250
+ print(f"Found {len(skills)} skills:")
251
+ for skill in skills:
252
+ print(f" • {skill['name']}: {skill['description']}")
253
+ ```
254
+
255
+ ### 3. Test Skill Usage
256
+
257
+ ```python
258
+ from signalwire_agents import AgentBase
259
+
260
+ class TestAgent(AgentBase):
261
+ def __init__(self):
262
+ super().__init__(name="Test Agent", route="/test")
263
+
264
+ # Add your skill
265
+ self.add_skill("your_skill_name", {
266
+ "param1": "value1",
267
+ "param2": "value2"
268
+ })
269
+
270
+ agent = TestAgent()
271
+ # Test that skill loads without errors
272
+ ```
273
+
274
+ ## Documentation
275
+
276
+ ### `README.md` Template
277
+
278
+ Create a comprehensive README for your skill:
279
+
280
+ ```markdown
281
+ # Your Skill Name
282
+
283
+ Brief description of what the skill does.
284
+
285
+ ## Description
286
+
287
+ Detailed explanation of the skill's purpose and capabilities.
288
+
289
+ ## Features
290
+
291
+ - Feature 1
292
+ - Feature 2
293
+ - Feature 3
294
+
295
+ ## Requirements
296
+
297
+ - Required service accounts or API keys
298
+ - Any external dependencies
299
+
300
+ ## Configuration
301
+
302
+ ### Required Parameters
303
+
304
+ - `param1`: Description of required parameter
305
+ - `param2`: Description of another required parameter
306
+
307
+ ### Optional Parameters
308
+
309
+ - `optional_param`: Description with default value
310
+
311
+ ## Usage
312
+
313
+ ### Basic Usage
314
+
315
+ ```python
316
+ from signalwire_agents import AgentBase
317
+
318
+ class MyAgent(AgentBase):
319
+ def __init__(self):
320
+ super().__init__(name="My Agent", route="/agent")
321
+
322
+ self.add_skill("your_skill_name", {
323
+ "param1": "value1",
324
+ "param2": "value2"
325
+ })
326
+
327
+ agent = MyAgent()
328
+ agent.serve()
329
+ ```
330
+
331
+ ### Advanced Configuration
332
+
333
+ Examples of advanced usage patterns.
334
+
335
+ ## Generated Functions
336
+
337
+ List the SWAIG functions your skill creates and their parameters.
338
+
339
+ ## Example Conversations
340
+
341
+ Show example interactions between users and agents using your skill.
342
+
343
+ ## Troubleshooting
344
+
345
+ Common issues and solutions.
346
+ ```
347
+
348
+ ## Examples
349
+
350
+ Create example scripts in the `examples/` directory:
351
+
352
+ ```python
353
+ #!/usr/bin/env python3
354
+ """
355
+ Your Skill Demo - Description
356
+
357
+ This demo shows how to use your skill with the SignalWire Agents SDK.
358
+
359
+ Run with: python examples/your_skill_demo.py
360
+ """
361
+
362
+ from signalwire_agents import AgentBase
363
+
364
+ class YourSkillAgent(AgentBase):
365
+ def __init__(self):
366
+ super().__init__(
367
+ name="Your Skill Demo Agent",
368
+ route="/your-skill-demo"
369
+ )
370
+
371
+ # Configure the agent
372
+ self.prompt_add_section("Goal", body="What this demo agent does")
373
+
374
+ # Add your skill
375
+ self.add_skill("your_skill_name", {
376
+ "param1": "demo-value"
377
+ })
378
+
379
+ def main():
380
+ print("=" * 60)
381
+ print("YOUR SKILL DEMO")
382
+ print("=" * 60)
383
+
384
+ agent = YourSkillAgent()
385
+ agent.serve(host="0.0.0.0", port=3000)
386
+
387
+ if __name__ == "__main__":
388
+ main()
389
+ ```
390
+
391
+ ## Best Practices
392
+
393
+ ### Configuration
394
+ - Always validate required parameters in `setup()`
395
+ - Provide sensible defaults for optional parameters
396
+ - Use descriptive parameter names
397
+ - Log errors clearly when configuration fails
398
+
399
+ ### Error Handling
400
+ - Return `False` from `setup()` if configuration is invalid
401
+ - Handle API errors gracefully in DataMap configurations
402
+ - Use `self.logger` for consistent logging
403
+ - Provide helpful error messages
404
+
405
+ ### Documentation
406
+ - Write clear, comprehensive README files
407
+ - Include working code examples
408
+ - Document all parameters and their purposes
409
+ - Show example conversations
410
+
411
+ ### Security
412
+ - Never hard-code API keys or secrets
413
+ - Validate all user inputs
414
+ - Use HTTPS for all external API calls
415
+ - Be careful with user data in logs
416
+
417
+ ### Performance
418
+ - Cache expensive operations when possible
419
+ - Use appropriate timeouts for external calls
420
+ - Consider rate limiting for API calls
421
+ - Keep skill setup fast
422
+
423
+ ### Compatibility
424
+ - Avoid breaking changes in minor versions
425
+ - Test with different Python versions
426
+ - Document any external dependencies
427
+ - Use standard library when possible
428
+
429
+ ## Troubleshooting
430
+
431
+ ### Skill Not Found
432
+ - Check that the skill directory is in `signalwire_agents/skills/`
433
+ - Verify `pyproject.toml` includes your skill package
434
+ - Reinstall the package after changes: `pip install . --force-reinstall`
435
+
436
+ ### Import Errors
437
+ - Ensure all required packages are installed
438
+ - Check import statements in your skill.py
439
+ - Verify Python path and virtual environment
440
+
441
+ ### Configuration Errors
442
+ - Check parameter validation logic in `setup()`
443
+ - Ensure required parameters are being passed
444
+ - Look for typos in parameter names
445
+
446
+ ### DataMap Issues
447
+ - Validate API endpoints and authentication
448
+ - Check response format matches your expectations
449
+ - Test API calls independently first
450
+ - Use `.error_keys()` to handle API errors
451
+
452
+ Remember to always test your skill thoroughly and provide clear documentation for other developers!
@@ -0,0 +1,215 @@
1
+ # API Ninjas Trivia Skill
2
+
3
+ A configurable skill for getting trivia questions from API Ninjas with customizable categories and multiple tool instances. Supports serverless DataMap webhook execution.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Instances**: Create different tools with unique names and category sets
8
+ - **Configurable Categories**: Choose from 14 trivia categories
9
+ - **Dynamic Enum Generation**: Function parameters built from selected categories
10
+ - **DataMap Efficiency**: Serverless webhook execution, no agent processing load
11
+ - **API Integration**: Direct webhook to API Ninjas trivia endpoint
12
+ - **Error Handling**: Graceful fallback for API failures
13
+
14
+ ## Configuration
15
+
16
+ ### Basic Structure
17
+
18
+ ```python
19
+ agent.add_skill("api_ninjas_trivia", {
20
+ "tool_name": "your_custom_tool_name",
21
+ "api_key": "your_api_ninjas_key",
22
+ "categories": ["category1", "category2", ...] # Optional, defaults to all
23
+ })
24
+ ```
25
+
26
+ ### Parameters
27
+
28
+ - **tool_name** (string): Custom name for the generated SWAIG function (default: "get_trivia")
29
+ - **api_key** (string, required): Your API Ninjas API key
30
+ - **categories** (array, optional): List of category strings to enable (default: all categories)
31
+
32
+ ### Available Categories
33
+
34
+ | Category | Description |
35
+ |----------|-------------|
36
+ | `artliterature` | Art and Literature |
37
+ | `language` | Language |
38
+ | `sciencenature` | Science and Nature |
39
+ | `general` | General Knowledge |
40
+ | `fooddrink` | Food and Drink |
41
+ | `peopleplaces` | People and Places |
42
+ | `geography` | Geography |
43
+ | `historyholidays` | History and Holidays |
44
+ | `entertainment` | Entertainment |
45
+ | `toysgames` | Toys and Games |
46
+ | `music` | Music |
47
+ | `mathematics` | Mathematics |
48
+ | `religionmythology` | Religion and Mythology |
49
+ | `sportsleisure` | Sports and Leisure |
50
+
51
+ ## Usage Examples
52
+
53
+ ### All Categories (Default)
54
+
55
+ ```python
56
+ agent.add_skill("api_ninjas_trivia", {
57
+ "tool_name": "get_trivia",
58
+ "api_key": "your_api_key"
59
+ })
60
+ ```
61
+
62
+ **Generated Tool**: `get_trivia(category)`
63
+ **Actions**: All 14 categories available
64
+
65
+ ### Science & Math Only
66
+
67
+ ```python
68
+ agent.add_skill("api_ninjas_trivia", {
69
+ "tool_name": "get_science_trivia",
70
+ "api_key": "your_api_key",
71
+ "categories": ["sciencenature", "mathematics"]
72
+ })
73
+ ```
74
+
75
+ **Generated Tool**: `get_science_trivia(category)`
76
+ **Actions**: `["sciencenature", "mathematics"]`
77
+
78
+ ### Entertainment Trivia
79
+
80
+ ```python
81
+ agent.add_skill("api_ninjas_trivia", {
82
+ "tool_name": "get_entertainment_trivia",
83
+ "api_key": "your_api_key",
84
+ "categories": ["entertainment", "music", "toysgames"]
85
+ })
86
+ ```
87
+
88
+ **Generated Tool**: `get_entertainment_trivia(category)`
89
+ **Actions**: `["entertainment", "music", "toysgames"]`
90
+
91
+ ### Geography & History
92
+
93
+ ```python
94
+ agent.add_skill("api_ninjas_trivia", {
95
+ "tool_name": "get_geography_trivia",
96
+ "api_key": "your_api_key",
97
+ "categories": ["geography", "historyholidays", "peopleplaces"]
98
+ })
99
+ ```
100
+
101
+ **Generated Tool**: `get_geography_trivia(category)`
102
+ **Actions**: `["geography", "historyholidays", "peopleplaces"]`
103
+
104
+ ## Generated SWAIG Function
105
+
106
+ For the science & math example above, the skill generates:
107
+
108
+ ```json
109
+ {
110
+ "function": "get_science_trivia",
111
+ "description": "Get trivia questions for get science trivia",
112
+ "parameters": {
113
+ "type": "object",
114
+ "properties": {
115
+ "category": {
116
+ "type": "string",
117
+ "description": "Category for trivia question. Options: sciencenature: Science and Nature; mathematics: Mathematics",
118
+ "enum": ["sciencenature", "mathematics"]
119
+ }
120
+ },
121
+ "required": ["category"]
122
+ },
123
+ "data_map": {
124
+ "webhook": {
125
+ "url": "https://api.api-ninjas.com/v1/trivia?category=%{args.category}",
126
+ "method": "GET",
127
+ "headers": {
128
+ "X-Api-Key": "your_api_key"
129
+ }
130
+ },
131
+ "output": {
132
+ "response": "Category %{array[0].category} question: %{array[0].question} Answer: %{array[0].answer}, be sure to give the user time to answer before saying the answer."
133
+ },
134
+ "error_keys": ["error"],
135
+ "fallback_output": {
136
+ "response": "Sorry, I cannot get trivia questions right now. Please try again later."
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Execution Flow
143
+
144
+ 1. **AI calls function**: `get_science_trivia(category: "mathematics")`
145
+ 2. **DataMap webhook**: `GET https://api.api-ninjas.com/v1/trivia?category=mathematics`
146
+ 3. **API response**: `[{"category": "Mathematics", "question": "What is 2+2?", "answer": "4"}]`
147
+ 4. **AI responds**: "Category Mathematics question: What is 2+2? Answer: 4, be sure to give the user time to answer before saying the answer."
148
+
149
+ ## Multiple Instances
150
+
151
+ You can add multiple instances for different use cases:
152
+
153
+ ```python
154
+ # General trivia with all categories
155
+ agent.add_skill("api_ninjas_trivia", {
156
+ "tool_name": "get_trivia",
157
+ "api_key": "your_api_key"
158
+ })
159
+
160
+ # Science-focused trivia
161
+ agent.add_skill("api_ninjas_trivia", {
162
+ "tool_name": "get_science_trivia",
163
+ "api_key": "your_api_key",
164
+ "categories": ["sciencenature", "mathematics"]
165
+ })
166
+
167
+ # Entertainment trivia
168
+ agent.add_skill("api_ninjas_trivia", {
169
+ "tool_name": "get_fun_trivia",
170
+ "api_key": "your_api_key",
171
+ "categories": ["entertainment", "music", "toysgames", "sportsleisure"]
172
+ })
173
+
174
+ # Educational trivia
175
+ agent.add_skill("api_ninjas_trivia", {
176
+ "tool_name": "get_educational_trivia",
177
+ "api_key": "your_api_key",
178
+ "categories": ["geography", "historyholidays", "artliterature", "language"]
179
+ })
180
+ ```
181
+
182
+ This creates four separate tools: `get_trivia`, `get_science_trivia`, `get_fun_trivia`, and `get_educational_trivia`.
183
+
184
+ ## API Integration
185
+
186
+ The skill integrates directly with API Ninjas trivia endpoint:
187
+
188
+ - **Endpoint**: `https://api.api-ninjas.com/v1/trivia`
189
+ - **Method**: GET
190
+ - **Parameters**: `category` (from enum)
191
+ - **Headers**: `X-Api-Key` with your API key
192
+ - **Response**: Array with question, answer, and category
193
+
194
+ ## Benefits
195
+
196
+ - **Reusable**: Same skill supports different category combinations
197
+ - **Configurable**: Easy to add/remove categories without code changes
198
+ - **Efficient**: DataMap webhook execution, no agent load
199
+ - **Type Safe**: Enum parameters prevent invalid categories
200
+ - **Error Handling**: Graceful fallback for API failures
201
+ - **Maintainable**: Centralized trivia logic
202
+
203
+ ## Error Handling
204
+
205
+ - **Invalid Categories**: Validation at skill initialization
206
+ - **API Failures**: Fallback response for network/API issues
207
+ - **Missing API Key**: Clear error message during setup
208
+
209
+ ## Implementation Notes
210
+
211
+ - Categories are validated against the official API Ninjas list
212
+ - Enum values use exact API category names
213
+ - Human-readable descriptions are auto-generated
214
+ - Instance keys combine skill name and tool name for uniqueness
215
+ - API key is stored securely in DataMap headers