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.
- signalwire_agents/__init__.py +130 -4
- signalwire_agents/agent_server.py +438 -32
- signalwire_agents/agents/bedrock.py +296 -0
- signalwire_agents/cli/__init__.py +18 -0
- signalwire_agents/cli/build_search.py +1367 -0
- signalwire_agents/cli/config.py +80 -0
- signalwire_agents/cli/core/__init__.py +10 -0
- signalwire_agents/cli/core/agent_loader.py +470 -0
- signalwire_agents/cli/core/argparse_helpers.py +179 -0
- signalwire_agents/cli/core/dynamic_config.py +71 -0
- signalwire_agents/cli/core/service_loader.py +303 -0
- signalwire_agents/cli/execution/__init__.py +10 -0
- signalwire_agents/cli/execution/datamap_exec.py +446 -0
- signalwire_agents/cli/execution/webhook_exec.py +134 -0
- signalwire_agents/cli/init_project.py +1225 -0
- signalwire_agents/cli/output/__init__.py +10 -0
- signalwire_agents/cli/output/output_formatter.py +255 -0
- signalwire_agents/cli/output/swml_dump.py +186 -0
- signalwire_agents/cli/simulation/__init__.py +10 -0
- signalwire_agents/cli/simulation/data_generation.py +374 -0
- signalwire_agents/cli/simulation/data_overrides.py +200 -0
- signalwire_agents/cli/simulation/mock_env.py +282 -0
- signalwire_agents/cli/swaig_test_wrapper.py +52 -0
- signalwire_agents/cli/test_swaig.py +809 -0
- signalwire_agents/cli/types.py +81 -0
- signalwire_agents/core/__init__.py +2 -2
- signalwire_agents/core/agent/__init__.py +12 -0
- signalwire_agents/core/agent/config/__init__.py +12 -0
- signalwire_agents/core/agent/deployment/__init__.py +9 -0
- signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
- signalwire_agents/core/agent/prompt/__init__.py +14 -0
- signalwire_agents/core/agent/prompt/manager.py +306 -0
- signalwire_agents/core/agent/routing/__init__.py +9 -0
- signalwire_agents/core/agent/security/__init__.py +9 -0
- signalwire_agents/core/agent/swml/__init__.py +9 -0
- signalwire_agents/core/agent/tools/__init__.py +15 -0
- signalwire_agents/core/agent/tools/decorator.py +97 -0
- signalwire_agents/core/agent/tools/registry.py +210 -0
- signalwire_agents/core/agent_base.py +959 -2166
- signalwire_agents/core/auth_handler.py +233 -0
- signalwire_agents/core/config_loader.py +259 -0
- signalwire_agents/core/contexts.py +707 -0
- signalwire_agents/core/data_map.py +487 -0
- signalwire_agents/core/function_result.py +1150 -1
- signalwire_agents/core/logging_config.py +376 -0
- signalwire_agents/core/mixins/__init__.py +28 -0
- signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
- signalwire_agents/core/mixins/auth_mixin.py +287 -0
- signalwire_agents/core/mixins/prompt_mixin.py +358 -0
- signalwire_agents/core/mixins/serverless_mixin.py +368 -0
- signalwire_agents/core/mixins/skill_mixin.py +55 -0
- signalwire_agents/core/mixins/state_mixin.py +153 -0
- signalwire_agents/core/mixins/tool_mixin.py +230 -0
- signalwire_agents/core/mixins/web_mixin.py +1134 -0
- signalwire_agents/core/security/session_manager.py +174 -86
- signalwire_agents/core/security_config.py +333 -0
- signalwire_agents/core/skill_base.py +200 -0
- signalwire_agents/core/skill_manager.py +244 -0
- signalwire_agents/core/swaig_function.py +33 -9
- signalwire_agents/core/swml_builder.py +212 -12
- signalwire_agents/core/swml_handler.py +43 -13
- signalwire_agents/core/swml_renderer.py +123 -297
- signalwire_agents/core/swml_service.py +277 -260
- signalwire_agents/prefabs/concierge.py +6 -2
- signalwire_agents/prefabs/info_gatherer.py +149 -33
- signalwire_agents/prefabs/receptionist.py +14 -22
- signalwire_agents/prefabs/survey.py +6 -2
- signalwire_agents/schema.json +9218 -5489
- signalwire_agents/search/__init__.py +137 -0
- signalwire_agents/search/document_processor.py +1223 -0
- signalwire_agents/search/index_builder.py +804 -0
- signalwire_agents/search/migration.py +418 -0
- signalwire_agents/search/models.py +30 -0
- signalwire_agents/search/pgvector_backend.py +752 -0
- signalwire_agents/search/query_processor.py +502 -0
- signalwire_agents/search/search_engine.py +1264 -0
- signalwire_agents/search/search_service.py +574 -0
- signalwire_agents/skills/README.md +452 -0
- signalwire_agents/skills/__init__.py +23 -0
- signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
- signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
- signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
- signalwire_agents/skills/datasphere/README.md +210 -0
- signalwire_agents/skills/datasphere/__init__.py +12 -0
- signalwire_agents/skills/datasphere/skill.py +310 -0
- signalwire_agents/skills/datasphere_serverless/README.md +258 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +10 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +237 -0
- signalwire_agents/skills/datetime/README.md +132 -0
- signalwire_agents/skills/datetime/__init__.py +10 -0
- signalwire_agents/skills/datetime/skill.py +126 -0
- signalwire_agents/skills/joke/README.md +149 -0
- signalwire_agents/skills/joke/__init__.py +10 -0
- signalwire_agents/skills/joke/skill.py +109 -0
- signalwire_agents/skills/math/README.md +161 -0
- signalwire_agents/skills/math/__init__.py +10 -0
- signalwire_agents/skills/math/skill.py +105 -0
- signalwire_agents/skills/mcp_gateway/README.md +230 -0
- signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
- signalwire_agents/skills/mcp_gateway/skill.py +421 -0
- signalwire_agents/skills/native_vector_search/README.md +210 -0
- signalwire_agents/skills/native_vector_search/__init__.py +10 -0
- signalwire_agents/skills/native_vector_search/skill.py +820 -0
- signalwire_agents/skills/play_background_file/README.md +218 -0
- signalwire_agents/skills/play_background_file/__init__.py +12 -0
- signalwire_agents/skills/play_background_file/skill.py +242 -0
- signalwire_agents/skills/registry.py +459 -0
- signalwire_agents/skills/spider/README.md +236 -0
- signalwire_agents/skills/spider/__init__.py +13 -0
- signalwire_agents/skills/spider/skill.py +598 -0
- signalwire_agents/skills/swml_transfer/README.md +395 -0
- signalwire_agents/skills/swml_transfer/__init__.py +10 -0
- signalwire_agents/skills/swml_transfer/skill.py +359 -0
- signalwire_agents/skills/weather_api/README.md +178 -0
- signalwire_agents/skills/weather_api/__init__.py +12 -0
- signalwire_agents/skills/weather_api/skill.py +191 -0
- signalwire_agents/skills/web_search/README.md +163 -0
- signalwire_agents/skills/web_search/__init__.py +10 -0
- signalwire_agents/skills/web_search/skill.py +739 -0
- signalwire_agents/skills/wikipedia_search/README.md +228 -0
- signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
- signalwire_agents/skills/wikipedia_search/skill.py +210 -0
- signalwire_agents/utils/__init__.py +14 -0
- signalwire_agents/utils/schema_utils.py +111 -44
- signalwire_agents/web/__init__.py +17 -0
- signalwire_agents/web/web_service.py +559 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/sw-agent-init.1 +307 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/sw-search.1 +483 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/swaig-test.1 +308 -0
- signalwire_agents-1.0.7.dist-info/METADATA +992 -0
- signalwire_agents-1.0.7.dist-info/RECORD +142 -0
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/WHEEL +1 -1
- signalwire_agents-1.0.7.dist-info/entry_points.txt +4 -0
- signalwire_agents/core/state/file_state_manager.py +0 -219
- signalwire_agents/core/state/state_manager.py +0 -101
- signalwire_agents-0.1.6.data/data/schema.json +0 -5611
- signalwire_agents-0.1.6.dist-info/METADATA +0 -199
- signalwire_agents-0.1.6.dist-info/RECORD +0 -34
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/licenses/LICENSE +0 -0
- {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,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
|
+
```
|