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,237 @@
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
+ import base64
11
+ from typing import Optional, List, Dict, Any
12
+
13
+ from signalwire_agents.core.skill_base import SkillBase
14
+ from signalwire_agents.core.data_map import DataMap
15
+ from signalwire_agents.core.function_result import SwaigFunctionResult
16
+
17
+ class DataSphereServerlessSkill(SkillBase):
18
+ """SignalWire DataSphere knowledge search using DataMap (serverless execution)"""
19
+
20
+ SKILL_NAME = "datasphere_serverless"
21
+ SKILL_DESCRIPTION = "Search knowledge using SignalWire DataSphere with serverless DataMap execution"
22
+ SKILL_VERSION = "1.0.0"
23
+ REQUIRED_PACKAGES = [] # DataMap handles API calls serverlessly
24
+ REQUIRED_ENV_VARS = [] # No required env vars since all config comes from params
25
+
26
+ # Enable multiple instances support
27
+ SUPPORTS_MULTIPLE_INSTANCES = True
28
+
29
+ @classmethod
30
+ def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
31
+ """Get parameter schema for DataSphere Serverless skill"""
32
+ schema = super().get_parameter_schema()
33
+ schema.update({
34
+ "space_name": {
35
+ "type": "string",
36
+ "description": "SignalWire space name (e.g., 'mycompany' from mycompany.signalwire.com)",
37
+ "required": True
38
+ },
39
+ "project_id": {
40
+ "type": "string",
41
+ "description": "SignalWire project ID",
42
+ "required": True,
43
+ "env_var": "SIGNALWIRE_PROJECT_ID"
44
+ },
45
+ "token": {
46
+ "type": "string",
47
+ "description": "SignalWire API token",
48
+ "required": True,
49
+ "hidden": True,
50
+ "env_var": "SIGNALWIRE_TOKEN"
51
+ },
52
+ "document_id": {
53
+ "type": "string",
54
+ "description": "DataSphere document ID to search within",
55
+ "required": True
56
+ },
57
+ "count": {
58
+ "type": "integer",
59
+ "description": "Number of search results to return",
60
+ "default": 1,
61
+ "required": False,
62
+ "minimum": 1,
63
+ "maximum": 10
64
+ },
65
+ "distance": {
66
+ "type": "number",
67
+ "description": "Maximum distance threshold for results (lower is more relevant)",
68
+ "default": 3.0,
69
+ "required": False,
70
+ "minimum": 0.0,
71
+ "maximum": 10.0
72
+ },
73
+ "tags": {
74
+ "type": "array",
75
+ "description": "Tags to filter search results",
76
+ "required": False,
77
+ "items": {
78
+ "type": "string"
79
+ }
80
+ },
81
+ "language": {
82
+ "type": "string",
83
+ "description": "Language code for query expansion (e.g., 'en', 'es')",
84
+ "required": False
85
+ },
86
+ "pos_to_expand": {
87
+ "type": "array",
88
+ "description": "Parts of speech to expand with synonyms",
89
+ "required": False,
90
+ "items": {
91
+ "type": "string",
92
+ "enum": ["NOUN", "VERB", "ADJ", "ADV"]
93
+ }
94
+ },
95
+ "max_synonyms": {
96
+ "type": "integer",
97
+ "description": "Maximum number of synonyms to use for query expansion",
98
+ "required": False,
99
+ "minimum": 1,
100
+ "maximum": 10
101
+ },
102
+ "no_results_message": {
103
+ "type": "string",
104
+ "description": "Message to return when no results are found",
105
+ "default": "I couldn't find any relevant information for '{query}' in the knowledge base. Try rephrasing your question or asking about a different topic.",
106
+ "required": False
107
+ }
108
+ })
109
+ return schema
110
+
111
+ def get_instance_key(self) -> str:
112
+ """
113
+ Get the key used to track this skill instance
114
+
115
+ For DataSphere Serverless, we use 'search_knowledge' as the default tool name
116
+ """
117
+ tool_name = self.params.get('tool_name', 'search_knowledge')
118
+ return f"{self.SKILL_NAME}_{tool_name}"
119
+
120
+ def setup(self) -> bool:
121
+ """Setup the datasphere serverless skill"""
122
+ # Validate required parameters
123
+ required_params = ['space_name', 'project_id', 'token', 'document_id']
124
+ missing_params = [param for param in required_params if not self.params.get(param)]
125
+ if missing_params:
126
+ self.logger.error(f"Missing required parameters: {missing_params}")
127
+ return False
128
+
129
+ # Set required parameters
130
+ self.space_name = self.params['space_name']
131
+ self.project_id = self.params['project_id']
132
+ self.token = self.params['token']
133
+ self.document_id = self.params['document_id']
134
+
135
+ # Set optional parameters with defaults
136
+ self.count = self.params.get('count', 1)
137
+ self.distance = self.params.get('distance', 3.0)
138
+ self.tags = self.params.get('tags', None)
139
+ self.language = self.params.get('language', None)
140
+ self.pos_to_expand = self.params.get('pos_to_expand', None)
141
+ self.max_synonyms = self.params.get('max_synonyms', None)
142
+
143
+ # Tool name (for multiple instances)
144
+ self.tool_name = self.params.get('tool_name', 'search_knowledge')
145
+
146
+ # No results message
147
+ self.no_results_message = self.params.get('no_results_message',
148
+ "I couldn't find any relevant information for '{query}' in the knowledge base. "
149
+ "Try rephrasing your question or asking about a different topic."
150
+ )
151
+
152
+ # Build API URL
153
+ self.api_url = f"https://{self.space_name}.signalwire.com/api/datasphere/documents/search"
154
+
155
+ # Build auth header for DataMap
156
+ auth_string = f"{self.project_id}:{self.token}"
157
+ self.auth_header = base64.b64encode(auth_string.encode()).decode()
158
+
159
+ return True
160
+
161
+ def register_tools(self) -> None:
162
+ """Register knowledge search tool using DataMap"""
163
+
164
+ # Build webhook params with configuration values
165
+ webhook_params = {
166
+ "document_id": self.document_id,
167
+ "query_string": "${args.query}", # Only this is dynamic from user input
168
+ "count": self.count
169
+ }
170
+
171
+ # Add optional parameters only if they were provided
172
+ if self.tags is not None:
173
+ webhook_params["tags"] = self.tags
174
+ if self.language is not None:
175
+ webhook_params["language"] = self.language
176
+ if self.pos_to_expand is not None:
177
+ webhook_params["pos_to_expand"] = self.pos_to_expand
178
+ if self.max_synonyms is not None:
179
+ webhook_params["max_synonyms"] = self.max_synonyms
180
+
181
+ # Create DataMap tool for DataSphere search
182
+ datasphere_tool = (DataMap(self.tool_name)
183
+ .description("Search the knowledge base for information on any topic and return relevant results")
184
+ .parameter('query', 'string', 'The search query - what information you\'re looking for in the knowledge base', required=True)
185
+ .webhook('POST', self.api_url,
186
+ headers={
187
+ 'Content-Type': 'application/json',
188
+ 'Authorization': f'Basic {self.auth_header}'
189
+ })
190
+ .params(webhook_params)
191
+ .foreach({
192
+ "input_key": "chunks",
193
+ "output_key": "formatted_results",
194
+ "max": self.count,
195
+ "append": "=== RESULT ===\n${this.text}\n" + "="*50 + "\n\n"
196
+ })
197
+ .output(SwaigFunctionResult('I found results for "${args.query}":\n\n${formatted_results}'))
198
+ .error_keys(['error'])
199
+ .fallback_output(SwaigFunctionResult(self.no_results_message.replace('{query}', '${args.query}')))
200
+ )
201
+
202
+ # Convert DataMap to SWAIG function and apply swaig_fields
203
+ swaig_function = datasphere_tool.to_swaig_function()
204
+
205
+ # Merge swaig_fields from skill params into the function definition
206
+ swaig_function.update(self.swaig_fields)
207
+
208
+ # Register the enhanced DataMap tool with the agent
209
+ self.agent.register_swaig_function(swaig_function)
210
+
211
+ def get_hints(self) -> List[str]:
212
+ """Return speech recognition hints"""
213
+ return []
214
+
215
+ def get_global_data(self) -> Dict[str, Any]:
216
+ """Return global data for agent context"""
217
+ return {
218
+ "datasphere_serverless_enabled": True,
219
+ "document_id": self.document_id,
220
+ "knowledge_provider": "SignalWire DataSphere (Serverless)"
221
+ }
222
+
223
+ def get_prompt_sections(self) -> List[Dict[str, Any]]:
224
+ """Return prompt sections to add to agent"""
225
+ return [
226
+ {
227
+ "title": "Knowledge Search Capability (Serverless)",
228
+ "body": f"You can search a knowledge base for information using the {self.tool_name} tool.",
229
+ "bullets": [
230
+ f"Use the {self.tool_name} tool when users ask for information that might be in the knowledge base",
231
+ "Search for relevant information using clear, specific queries",
232
+ "Summarize search results in a clear, helpful way",
233
+ "If no results are found, suggest the user try rephrasing their question",
234
+ "This tool executes on SignalWire servers for optimal performance"
235
+ ]
236
+ }
237
+ ]
@@ -0,0 +1,132 @@
1
+ # DateTime Skill
2
+
3
+ The datetime skill provides current date and time information with timezone support. It allows agents to tell users the current time and date in any timezone around the world.
4
+
5
+ ## Features
6
+
7
+ - Current time retrieval with timezone support
8
+ - Current date retrieval with timezone support
9
+ - Automatic timezone conversion using pytz
10
+ - Human-readable time and date formatting
11
+ - UTC default with custom timezone options
12
+
13
+ ## Requirements
14
+
15
+ - **Packages**: `pytz`
16
+ - **No external APIs required**
17
+
18
+ ## Parameters
19
+
20
+ ### Optional Parameters
21
+
22
+ - `swaig_fields` (dict): Additional SWAIG function configuration
23
+ - `secure` (boolean): Override security settings for the time/date functions
24
+ - `fillers` (dict): Language-specific filler phrases while retrieving time/date
25
+ - Any other SWAIG function parameters
26
+
27
+ **Note**: This skill does not require any configuration parameters beyond the optional swaig_fields. It works out-of-the-box with no setup.
28
+
29
+ ## Tools Created
30
+
31
+ - `get_current_time` - Get the current time, optionally in a specific timezone
32
+ - `get_current_date` - Get the current date, optionally in a specific timezone
33
+
34
+ ## Usage Examples
35
+
36
+ ### Basic Usage
37
+
38
+ ```python
39
+ # No configuration needed - works immediately
40
+ agent.add_skill("datetime")
41
+ ```
42
+
43
+ ### With Custom Fillers
44
+
45
+ ```python
46
+ agent.add_skill("datetime", {
47
+ "swaig_fields": {
48
+ "fillers": {
49
+ "en-US": [
50
+ "Let me check the time...",
51
+ "Looking up the current date...",
52
+ "Getting the time for you..."
53
+ ],
54
+ "es-ES": [
55
+ "Déjame verificar la hora...",
56
+ "Consultando la fecha actual..."
57
+ ]
58
+ }
59
+ }
60
+ })
61
+ ```
62
+
63
+ ### Disabling Security (if needed)
64
+
65
+ ```python
66
+ agent.add_skill("datetime", {
67
+ "swaig_fields": {
68
+ "secure": False # Allow unauthenticated time/date requests
69
+ }
70
+ })
71
+ ```
72
+
73
+ ## How It Works
74
+
75
+ ### Time Function
76
+ - **Input**: Optional timezone parameter (e.g., "America/New_York", "Europe/London")
77
+ - **Default**: UTC timezone if no timezone specified
78
+ - **Output**: Time in 12-hour format with AM/PM and timezone abbreviation
79
+ - **Example**: "The current time is 02:30:45 PM EST"
80
+
81
+ ### Date Function
82
+ - **Input**: Optional timezone parameter for date calculation
83
+ - **Default**: UTC timezone if no timezone specified
84
+ - **Output**: Full date in readable format
85
+ - **Example**: "Today's date is Friday, December 15, 2023"
86
+
87
+ ### Timezone Support
88
+ - Uses the `pytz` library for accurate timezone handling
89
+ - Supports all standard timezone names (e.g., "America/New_York", "Asia/Tokyo")
90
+ - Handles daylight saving time automatically
91
+ - Falls back to UTC for invalid timezone names
92
+
93
+ ## Function Parameters
94
+
95
+ Both tools accept the same optional parameter:
96
+
97
+ - `timezone` (string, optional): Timezone name for the time/date
98
+ - Examples: "America/New_York", "Europe/London", "Asia/Tokyo", "UTC"
99
+ - Default: "UTC" if not specified
100
+ - Invalid timezones will cause an error message to be returned
101
+
102
+ ## Error Handling
103
+
104
+ - **Invalid Timezone**: Returns error message with the invalid timezone name
105
+ - **System Issues**: Returns friendly error message for any datetime calculation problems
106
+ - **Graceful Fallback**: Continues to work even if timezone data is corrupted
107
+
108
+ ## Common Timezone Examples
109
+
110
+ - **US Timezones**: "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles"
111
+ - **European Timezones**: "Europe/London", "Europe/Paris", "Europe/Berlin", "Europe/Rome"
112
+ - **Asian Timezones**: "Asia/Tokyo", "Asia/Shanghai", "Asia/Kolkata", "Asia/Dubai"
113
+ - **Other Regions**: "Australia/Sydney", "Africa/Cairo", "America/Sao_Paulo"
114
+
115
+ ## Best Practices
116
+
117
+ 1. **Default Behavior**: The skill works immediately without configuration
118
+ 2. **User Queries**: Handle questions like "What time is it?", "What's today's date?", "What time is it in Tokyo?"
119
+ 3. **Timezone Validation**: The skill gracefully handles invalid timezone names
120
+ 4. **Localization**: Use fillers in different languages for multilingual agents
121
+ 5. **Performance**: Very fast since no external API calls are required
122
+
123
+ ## Agent Integration
124
+
125
+ When added to an agent, this skill automatically:
126
+
127
+ - Adds speech recognition hints for time/date related words
128
+ - Provides prompt guidance about time/date capabilities
129
+ - Enables the agent to respond to time and date questions
130
+ - Works with any timezone the user requests
131
+
132
+ The skill is designed to be maintenance-free and always available, making it ideal for customer service and general-purpose agents that need to provide time and date information.
@@ -0,0 +1,10 @@
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
+ """DateTime Skill for SignalWire Agents"""
@@ -0,0 +1,126 @@
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 datetime import datetime, timezone
11
+ import pytz
12
+ from typing import List, Dict, Any
13
+
14
+ from signalwire_agents.core.skill_base import SkillBase
15
+ from signalwire_agents.core.function_result import SwaigFunctionResult
16
+
17
+ class DateTimeSkill(SkillBase):
18
+ """Provides current date, time, and timezone information"""
19
+
20
+ SKILL_NAME = "datetime"
21
+ SKILL_DESCRIPTION = "Get current date, time, and timezone information"
22
+ SKILL_VERSION = "1.0.0"
23
+ REQUIRED_PACKAGES = ["pytz"]
24
+ REQUIRED_ENV_VARS = []
25
+
26
+ def setup(self) -> bool:
27
+ """Setup the datetime skill"""
28
+ return self.validate_packages()
29
+
30
+ def register_tools(self) -> None:
31
+ """Register datetime tools with the agent"""
32
+
33
+ self.define_tool(
34
+ name="get_current_time",
35
+ description="Get the current time, optionally in a specific timezone",
36
+ parameters={
37
+ "timezone": {
38
+ "type": "string",
39
+ "description": "Timezone name (e.g., 'America/New_York', 'Europe/London'). Defaults to UTC."
40
+ }
41
+ },
42
+ handler=self._get_time_handler
43
+ )
44
+
45
+ self.define_tool(
46
+ name="get_current_date",
47
+ description="Get the current date",
48
+ parameters={
49
+ "timezone": {
50
+ "type": "string",
51
+ "description": "Timezone name for the date. Defaults to UTC."
52
+ }
53
+ },
54
+ handler=self._get_date_handler
55
+ )
56
+
57
+ def _get_time_handler(self, args, raw_data):
58
+ """Handler for get_current_time tool"""
59
+ timezone_name = args.get("timezone", "UTC")
60
+
61
+ try:
62
+ if timezone_name.upper() == "UTC":
63
+ tz = timezone.utc
64
+ else:
65
+ tz = pytz.timezone(timezone_name)
66
+
67
+ now = datetime.now(tz)
68
+ time_str = now.strftime("%I:%M:%S %p %Z")
69
+
70
+ return SwaigFunctionResult(f"The current time is {time_str}")
71
+
72
+ except Exception as e:
73
+ return SwaigFunctionResult(f"Error getting time: {str(e)}")
74
+
75
+ def _get_date_handler(self, args, raw_data):
76
+ """Handler for get_current_date tool"""
77
+ timezone_name = args.get("timezone", "UTC")
78
+
79
+ try:
80
+ if timezone_name.upper() == "UTC":
81
+ tz = timezone.utc
82
+ else:
83
+ tz = pytz.timezone(timezone_name)
84
+
85
+ now = datetime.now(tz)
86
+ date_str = now.strftime("%A, %B %d, %Y")
87
+
88
+ return SwaigFunctionResult(f"Today's date is {date_str}")
89
+
90
+ except Exception as e:
91
+ return SwaigFunctionResult(f"Error getting date: {str(e)}")
92
+
93
+ def get_hints(self) -> List[str]:
94
+ """Return speech recognition hints"""
95
+ # Currently no hints provided, but you could add them like:
96
+ # return ["time", "date", "today", "now", "current", "timezone"]
97
+ return []
98
+
99
+ def get_prompt_sections(self) -> List[Dict[str, Any]]:
100
+ """Return prompt sections to add to agent"""
101
+ return [
102
+ {
103
+ "title": "Date and Time Information",
104
+ "body": "You can provide current date and time information.",
105
+ "bullets": [
106
+ "Use get_current_time to tell users what time it is",
107
+ "Use get_current_date to tell users today's date",
108
+ "Both tools support different timezones"
109
+ ]
110
+ }
111
+ ]
112
+
113
+ @classmethod
114
+ def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
115
+ """
116
+ Get the parameter schema for the datetime skill
117
+
118
+ The datetime skill has no custom parameters - it inherits only
119
+ the base parameters from SkillBase.
120
+ """
121
+ # Get base schema from parent
122
+ schema = super().get_parameter_schema()
123
+
124
+ # No additional parameters for datetime skill
125
+
126
+ return schema
@@ -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,10 @@
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
+ """Joke Skill for SignalWire Agents using DataMap"""