signalwire-agents 0.1.20__py3-none-any.whl → 0.1.23__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 +1 -1
- signalwire_agents/agent_server.py +50 -11
- signalwire_agents/core/__init__.py +2 -2
- signalwire_agents/core/agent/__init__.py +14 -0
- signalwire_agents/core/agent/config/__init__.py +14 -0
- signalwire_agents/core/agent/config/ephemeral.py +176 -0
- signalwire_agents/core/agent/deployment/__init__.py +0 -0
- signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
- signalwire_agents/core/agent/prompt/__init__.py +14 -0
- signalwire_agents/core/agent/prompt/manager.py +288 -0
- signalwire_agents/core/agent/routing/__init__.py +0 -0
- signalwire_agents/core/agent/security/__init__.py +0 -0
- signalwire_agents/core/agent/swml/__init__.py +0 -0
- signalwire_agents/core/agent/tools/__init__.py +15 -0
- signalwire_agents/core/agent/tools/decorator.py +95 -0
- signalwire_agents/core/agent/tools/registry.py +192 -0
- signalwire_agents/core/agent_base.py +131 -413
- signalwire_agents/core/data_map.py +3 -15
- signalwire_agents/core/skill_manager.py +0 -17
- signalwire_agents/core/swaig_function.py +0 -2
- signalwire_agents/core/swml_builder.py +207 -11
- signalwire_agents/core/swml_renderer.py +123 -312
- signalwire_agents/core/swml_service.py +25 -94
- signalwire_agents/search/index_builder.py +1 -1
- signalwire_agents/skills/api_ninjas_trivia/__init__.py +3 -0
- signalwire_agents/skills/api_ninjas_trivia/skill.py +192 -0
- signalwire_agents/skills/play_background_file/__init__.py +3 -0
- signalwire_agents/skills/play_background_file/skill.py +197 -0
- signalwire_agents/skills/weather_api/__init__.py +3 -0
- signalwire_agents/skills/weather_api/skill.py +154 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/METADATA +5 -8
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/RECORD +37 -18
- {signalwire_agents-0.1.20.data → signalwire_agents-0.1.23.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
"""
|
2
|
+
API Ninjas Trivia Skill
|
3
|
+
|
4
|
+
A configurable skill for getting trivia questions from API Ninjas with customizable
|
5
|
+
categories and multiple tool instances.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Any, List
|
9
|
+
from signalwire_agents.core import SwaigFunctionResult
|
10
|
+
from signalwire_agents.core.skill_base import SkillBase
|
11
|
+
|
12
|
+
|
13
|
+
class ApiNinjasTriviaSkill(SkillBase):
|
14
|
+
"""
|
15
|
+
Skill for getting trivia questions from API Ninjas with configurable categories.
|
16
|
+
|
17
|
+
Supports multiple instances with different tool names and category combinations.
|
18
|
+
Uses DataMap for serverless execution with dynamic enum generation.
|
19
|
+
|
20
|
+
Configuration:
|
21
|
+
- tool_name: Custom name for the generated SWAIG function
|
22
|
+
- api_key: API Ninjas API key
|
23
|
+
- categories: Array of category strings to enable
|
24
|
+
|
25
|
+
Available categories:
|
26
|
+
- artliterature: Art and Literature
|
27
|
+
- language: Language
|
28
|
+
- sciencenature: Science and Nature
|
29
|
+
- general: General Knowledge
|
30
|
+
- fooddrink: Food and Drink
|
31
|
+
- peopleplaces: People and Places
|
32
|
+
- geography: Geography
|
33
|
+
- historyholidays: History and Holidays
|
34
|
+
- entertainment: Entertainment
|
35
|
+
- toysgames: Toys and Games
|
36
|
+
- music: Music
|
37
|
+
- mathematics: Mathematics
|
38
|
+
- religionmythology: Religion and Mythology
|
39
|
+
- sportsleisure: Sports and Leisure
|
40
|
+
|
41
|
+
Example:
|
42
|
+
agent.add_skill("api_ninjas_trivia", {
|
43
|
+
"tool_name": "get_science_trivia",
|
44
|
+
"api_key": "your_api_key",
|
45
|
+
"categories": ["sciencenature", "mathematics", "general"]
|
46
|
+
})
|
47
|
+
"""
|
48
|
+
|
49
|
+
SKILL_NAME = "api_ninjas_trivia"
|
50
|
+
SKILL_DESCRIPTION = "Get trivia questions from API Ninjas"
|
51
|
+
SUPPORTS_MULTIPLE_INSTANCES = True
|
52
|
+
REQUIRED_ENV_VARS = [] # API key can be passed via params
|
53
|
+
|
54
|
+
# Valid API Ninjas trivia categories with human-readable descriptions
|
55
|
+
VALID_CATEGORIES = {
|
56
|
+
"artliterature": "Art and Literature",
|
57
|
+
"language": "Language",
|
58
|
+
"sciencenature": "Science and Nature",
|
59
|
+
"general": "General Knowledge",
|
60
|
+
"fooddrink": "Food and Drink",
|
61
|
+
"peopleplaces": "People and Places",
|
62
|
+
"geography": "Geography",
|
63
|
+
"historyholidays": "History and Holidays",
|
64
|
+
"entertainment": "Entertainment",
|
65
|
+
"toysgames": "Toys and Games",
|
66
|
+
"music": "Music",
|
67
|
+
"mathematics": "Mathematics",
|
68
|
+
"religionmythology": "Religion and Mythology",
|
69
|
+
"sportsleisure": "Sports and Leisure"
|
70
|
+
}
|
71
|
+
|
72
|
+
def __init__(self, agent, params: Dict[str, Any] = None):
|
73
|
+
"""
|
74
|
+
Initialize the skill with configuration parameters.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
agent: The agent instance this skill belongs to
|
78
|
+
params: Configuration dictionary containing:
|
79
|
+
- tool_name: Custom tool name (default: "get_trivia")
|
80
|
+
- api_key: API Ninjas API key (required)
|
81
|
+
- categories: Array of category strings (default: all categories)
|
82
|
+
"""
|
83
|
+
super().__init__(agent, params)
|
84
|
+
|
85
|
+
# Extract configuration
|
86
|
+
self.tool_name = self.params.get('tool_name', 'get_trivia')
|
87
|
+
self.api_key = self.params.get('api_key')
|
88
|
+
self.categories = self.params.get('categories', list(self.VALID_CATEGORIES.keys()))
|
89
|
+
|
90
|
+
# Validate configuration
|
91
|
+
self._validate_config()
|
92
|
+
|
93
|
+
def _validate_config(self):
|
94
|
+
"""Validate the skill configuration."""
|
95
|
+
# Validate API key
|
96
|
+
if not self.api_key or not isinstance(self.api_key, str):
|
97
|
+
raise ValueError("api_key parameter is required and must be a non-empty string")
|
98
|
+
|
99
|
+
# Validate categories
|
100
|
+
if not isinstance(self.categories, list) or len(self.categories) == 0:
|
101
|
+
raise ValueError("categories parameter must be a non-empty list")
|
102
|
+
|
103
|
+
# Validate each category
|
104
|
+
for i, category in enumerate(self.categories):
|
105
|
+
if not isinstance(category, str):
|
106
|
+
raise ValueError(f"Category {i} must be a string")
|
107
|
+
if category not in self.VALID_CATEGORIES:
|
108
|
+
valid_cats = ', '.join(self.VALID_CATEGORIES.keys())
|
109
|
+
raise ValueError(f"Category '{category}' is not valid. Valid categories: {valid_cats}")
|
110
|
+
|
111
|
+
def setup(self) -> bool:
|
112
|
+
"""
|
113
|
+
Setup the skill - validates API key is available.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
True if setup successful
|
117
|
+
"""
|
118
|
+
# API key validation already done in _validate_config
|
119
|
+
return True
|
120
|
+
|
121
|
+
def register_tools(self) -> None:
|
122
|
+
"""Register SWAIG tools with the agent"""
|
123
|
+
tools = self.get_tools()
|
124
|
+
for tool in tools:
|
125
|
+
# Merge any swaig_fields from params into the tool
|
126
|
+
if self.swaig_fields:
|
127
|
+
tool.update(self.swaig_fields)
|
128
|
+
self.agent.register_swaig_function(tool)
|
129
|
+
|
130
|
+
def get_instance_key(self) -> str:
|
131
|
+
"""
|
132
|
+
Generate a unique instance key for this skill configuration.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Unique key combining skill name and tool name
|
136
|
+
"""
|
137
|
+
return f"{self.SKILL_NAME}_{self.tool_name}"
|
138
|
+
|
139
|
+
def get_tools(self) -> List[Dict[str, Any]]:
|
140
|
+
"""
|
141
|
+
Generate the SWAIG tool with DataMap webhook.
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
List containing the generated tool configuration
|
145
|
+
"""
|
146
|
+
# Build enum values and descriptions
|
147
|
+
enum_values = []
|
148
|
+
descriptions = []
|
149
|
+
|
150
|
+
for category in self.categories:
|
151
|
+
enum_values.append(category)
|
152
|
+
descriptions.append(f"{category}: {self.VALID_CATEGORIES[category]}")
|
153
|
+
|
154
|
+
# Build parameter description
|
155
|
+
description = "Category for trivia question. Options: " + "; ".join(descriptions)
|
156
|
+
|
157
|
+
# Create the tool configuration with DataMap webhook
|
158
|
+
tool = {
|
159
|
+
"function": self.tool_name,
|
160
|
+
"description": f"Get trivia questions for {self.tool_name.replace('_', ' ')}",
|
161
|
+
"parameters": {
|
162
|
+
"type": "object",
|
163
|
+
"properties": {
|
164
|
+
"category": {
|
165
|
+
"type": "string",
|
166
|
+
"description": description,
|
167
|
+
"enum": enum_values
|
168
|
+
}
|
169
|
+
},
|
170
|
+
"required": ["category"]
|
171
|
+
},
|
172
|
+
"data_map": {
|
173
|
+
"webhooks": [
|
174
|
+
{
|
175
|
+
"url": "https://api.api-ninjas.com/v1/trivia?category=%{args.category}",
|
176
|
+
"method": "GET",
|
177
|
+
"headers": {
|
178
|
+
"X-Api-Key": self.api_key
|
179
|
+
},
|
180
|
+
"output": SwaigFunctionResult(
|
181
|
+
"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."
|
182
|
+
).to_dict()
|
183
|
+
}
|
184
|
+
],
|
185
|
+
"error_keys": ["error"],
|
186
|
+
"output": SwaigFunctionResult(
|
187
|
+
"Sorry, I cannot get trivia questions right now. Please try again later."
|
188
|
+
).to_dict()
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
return [tool]
|
@@ -0,0 +1,197 @@
|
|
1
|
+
"""
|
2
|
+
Play Background File Skill
|
3
|
+
|
4
|
+
A configurable skill for managing background file playback with custom tool names
|
5
|
+
and multiple file collections. Supports both audio and video files.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Any, List
|
9
|
+
from signalwire_agents.core import SwaigFunctionResult
|
10
|
+
from signalwire_agents.core.skill_base import SkillBase
|
11
|
+
|
12
|
+
|
13
|
+
class PlayBackgroundFileSkill(SkillBase):
|
14
|
+
"""
|
15
|
+
Skill for playing background files (audio/video) with configurable tool names.
|
16
|
+
|
17
|
+
Supports multiple instances with different tool names and file collections.
|
18
|
+
Uses DataMap for serverless execution with dynamic enum generation.
|
19
|
+
|
20
|
+
Configuration:
|
21
|
+
- tool_name: Custom name for the generated SWAIG function
|
22
|
+
- files: Array of file objects with key, description, url, and optional wait
|
23
|
+
|
24
|
+
Example:
|
25
|
+
agent.add_skill("play_background_file", {
|
26
|
+
"tool_name": "play_testimonial",
|
27
|
+
"files": [
|
28
|
+
{
|
29
|
+
"key": "massey",
|
30
|
+
"description": "Customer success story from Massey Energy",
|
31
|
+
"url": "https://example.com/massey.mp4",
|
32
|
+
"wait": True
|
33
|
+
}
|
34
|
+
]
|
35
|
+
})
|
36
|
+
"""
|
37
|
+
|
38
|
+
SKILL_NAME = "play_background_file"
|
39
|
+
SKILL_DESCRIPTION = "Control background file playback"
|
40
|
+
SUPPORTS_MULTIPLE_INSTANCES = True
|
41
|
+
|
42
|
+
def __init__(self, agent, params: Dict[str, Any] = None):
|
43
|
+
"""
|
44
|
+
Initialize the skill with configuration parameters.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
agent: The agent instance this skill belongs to
|
48
|
+
params: Configuration dictionary containing:
|
49
|
+
- tool_name: Custom tool name (default: "play_background_file")
|
50
|
+
- files: Array of file objects with key, description, url, wait
|
51
|
+
"""
|
52
|
+
super().__init__(agent, params)
|
53
|
+
|
54
|
+
# Extract configuration
|
55
|
+
self.tool_name = self.params.get('tool_name', 'play_background_file')
|
56
|
+
self.files = self.params.get('files', [])
|
57
|
+
|
58
|
+
# Validate configuration
|
59
|
+
self._validate_config()
|
60
|
+
|
61
|
+
def _validate_config(self):
|
62
|
+
"""Validate the skill configuration."""
|
63
|
+
if not isinstance(self.files, list) or len(self.files) == 0:
|
64
|
+
raise ValueError("files parameter must be a non-empty list")
|
65
|
+
|
66
|
+
for i, file_obj in enumerate(self.files):
|
67
|
+
if not isinstance(file_obj, dict):
|
68
|
+
raise ValueError(f"File {i} must be a dictionary")
|
69
|
+
|
70
|
+
required_fields = ['key', 'description', 'url']
|
71
|
+
for field in required_fields:
|
72
|
+
if field not in file_obj:
|
73
|
+
raise ValueError(f"File {i} missing required field: {field}")
|
74
|
+
if not isinstance(file_obj[field], str) or not file_obj[field].strip():
|
75
|
+
raise ValueError(f"File {i} field '{field}' must be a non-empty string")
|
76
|
+
|
77
|
+
# Validate optional wait field
|
78
|
+
if 'wait' in file_obj and not isinstance(file_obj['wait'], bool):
|
79
|
+
raise ValueError(f"File {i} field 'wait' must be a boolean")
|
80
|
+
|
81
|
+
# Validate key format (alphanumeric, underscores, hyphens)
|
82
|
+
key = file_obj['key']
|
83
|
+
if not key.replace('_', '').replace('-', '').isalnum():
|
84
|
+
raise ValueError(f"File {i} key '{key}' must contain only alphanumeric characters, underscores, and hyphens")
|
85
|
+
|
86
|
+
def get_instance_key(self) -> str:
|
87
|
+
"""
|
88
|
+
Generate a unique instance key for this skill configuration.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Unique key combining skill name and tool name
|
92
|
+
"""
|
93
|
+
return f"{self.SKILL_NAME}_{self.tool_name}"
|
94
|
+
|
95
|
+
def setup(self) -> bool:
|
96
|
+
"""
|
97
|
+
Setup the skill - no external dependencies needed.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
True if setup successful
|
101
|
+
"""
|
102
|
+
return True
|
103
|
+
|
104
|
+
def register_tools(self) -> None:
|
105
|
+
"""Register SWAIG tools with the agent"""
|
106
|
+
tools = self.get_tools()
|
107
|
+
for tool in tools:
|
108
|
+
# Merge any swaig_fields from params into the tool
|
109
|
+
if self.swaig_fields:
|
110
|
+
tool.update(self.swaig_fields)
|
111
|
+
self.agent.register_swaig_function(tool)
|
112
|
+
|
113
|
+
def get_tools(self) -> List[Dict[str, Any]]:
|
114
|
+
"""
|
115
|
+
Generate the SWAIG tool with DataMap expressions.
|
116
|
+
|
117
|
+
Returns:
|
118
|
+
List containing the generated tool configuration
|
119
|
+
"""
|
120
|
+
# Build enum values and descriptions
|
121
|
+
enum_values = []
|
122
|
+
descriptions = []
|
123
|
+
|
124
|
+
for file_obj in self.files:
|
125
|
+
enum_values.append(f"start_{file_obj['key']}")
|
126
|
+
descriptions.append(f"start_{file_obj['key']}: {file_obj['description']}")
|
127
|
+
|
128
|
+
enum_values.append("stop")
|
129
|
+
descriptions.append("stop: Stop any currently playing background file")
|
130
|
+
|
131
|
+
# Build parameter description
|
132
|
+
description = "Action to perform. Options: " + "; ".join(descriptions)
|
133
|
+
|
134
|
+
# Create the tool configuration
|
135
|
+
tool = {
|
136
|
+
"function": self.tool_name,
|
137
|
+
"description": f"Control background file playback for {self.tool_name.replace('_', ' ')}",
|
138
|
+
"parameters": {
|
139
|
+
"type": "object",
|
140
|
+
"properties": {
|
141
|
+
"action": {
|
142
|
+
"type": "string",
|
143
|
+
"description": description,
|
144
|
+
"enum": enum_values
|
145
|
+
}
|
146
|
+
},
|
147
|
+
"required": ["action"]
|
148
|
+
},
|
149
|
+
"wait_for_fillers": True,
|
150
|
+
"skip_fillers": True,
|
151
|
+
"data_map": {
|
152
|
+
"expressions": self._build_expressions()
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
return [tool]
|
157
|
+
|
158
|
+
def _build_expressions(self) -> List[Dict[str, Any]]:
|
159
|
+
"""
|
160
|
+
Build DataMap expressions for file playback and stop actions.
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
List of expression configurations
|
164
|
+
"""
|
165
|
+
expressions = []
|
166
|
+
|
167
|
+
# Add expressions for each file
|
168
|
+
for file_obj in self.files:
|
169
|
+
key = file_obj['key']
|
170
|
+
url = file_obj['url']
|
171
|
+
wait = file_obj.get('wait', False)
|
172
|
+
description = file_obj['description']
|
173
|
+
|
174
|
+
# Create the result with appropriate response
|
175
|
+
result = SwaigFunctionResult(
|
176
|
+
f"Tell the user you are now going to play {description} for them.",
|
177
|
+
post_process=True
|
178
|
+
).play_background_file(url, wait=wait)
|
179
|
+
|
180
|
+
expressions.append({
|
181
|
+
"string": "${args.action}",
|
182
|
+
"pattern": f"/start_{key}/i",
|
183
|
+
"output": result.to_dict()
|
184
|
+
})
|
185
|
+
|
186
|
+
# Add stop expression
|
187
|
+
stop_result = SwaigFunctionResult(
|
188
|
+
"Tell the user you have stopped the background file playback."
|
189
|
+
).stop_background_file()
|
190
|
+
|
191
|
+
expressions.append({
|
192
|
+
"string": "${args.action}",
|
193
|
+
"pattern": "/stop/i",
|
194
|
+
"output": stop_result.to_dict()
|
195
|
+
})
|
196
|
+
|
197
|
+
return expressions
|
@@ -0,0 +1,154 @@
|
|
1
|
+
"""
|
2
|
+
Weather API Skill
|
3
|
+
|
4
|
+
A configurable skill for getting weather information from WeatherAPI.com with customizable
|
5
|
+
temperature units and TTS-friendly responses.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Any, List
|
9
|
+
from signalwire_agents.core import SwaigFunctionResult
|
10
|
+
from signalwire_agents.core.skill_base import SkillBase
|
11
|
+
|
12
|
+
|
13
|
+
class WeatherApiSkill(SkillBase):
|
14
|
+
"""
|
15
|
+
Skill for getting weather information from WeatherAPI.com.
|
16
|
+
|
17
|
+
Provides current weather data with configurable temperature units and
|
18
|
+
TTS-optimized natural language responses.
|
19
|
+
|
20
|
+
Configuration:
|
21
|
+
- tool_name: Custom name for the generated SWAIG function
|
22
|
+
- api_key: WeatherAPI.com API key
|
23
|
+
- temperature_unit: "fahrenheit" or "celsius" for temperature display
|
24
|
+
|
25
|
+
Example:
|
26
|
+
agent.add_skill("weather_api", {
|
27
|
+
"tool_name": "get_weather",
|
28
|
+
"api_key": "your_weatherapi_key",
|
29
|
+
"temperature_unit": "fahrenheit"
|
30
|
+
})
|
31
|
+
"""
|
32
|
+
|
33
|
+
SKILL_NAME = "weather_api"
|
34
|
+
SKILL_DESCRIPTION = "Get current weather information from WeatherAPI.com"
|
35
|
+
SUPPORTS_MULTIPLE_INSTANCES = False
|
36
|
+
REQUIRED_ENV_VARS = [] # API key can be passed via params
|
37
|
+
|
38
|
+
def __init__(self, agent, params: Dict[str, Any] = None):
|
39
|
+
"""
|
40
|
+
Initialize the skill with configuration parameters.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
agent: The agent instance this skill belongs to
|
44
|
+
params: Configuration dictionary containing:
|
45
|
+
- tool_name: Custom tool name (default: "get_weather")
|
46
|
+
- api_key: WeatherAPI.com API key (required)
|
47
|
+
- temperature_unit: "fahrenheit" or "celsius" (default: "fahrenheit")
|
48
|
+
"""
|
49
|
+
super().__init__(agent, params)
|
50
|
+
|
51
|
+
# Extract configuration
|
52
|
+
self.tool_name = self.params.get('tool_name', 'get_weather')
|
53
|
+
self.api_key = self.params.get('api_key')
|
54
|
+
self.temperature_unit = self.params.get('temperature_unit', 'fahrenheit')
|
55
|
+
|
56
|
+
# Validate configuration
|
57
|
+
self._validate_config()
|
58
|
+
|
59
|
+
def _validate_config(self):
|
60
|
+
"""Validate the skill configuration."""
|
61
|
+
# Validate API key
|
62
|
+
if not self.api_key or not isinstance(self.api_key, str):
|
63
|
+
raise ValueError("api_key parameter is required and must be a non-empty string")
|
64
|
+
|
65
|
+
# Validate temperature unit
|
66
|
+
if self.temperature_unit not in ['fahrenheit', 'celsius']:
|
67
|
+
raise ValueError("temperature_unit must be either 'fahrenheit' or 'celsius'")
|
68
|
+
|
69
|
+
def setup(self) -> bool:
|
70
|
+
"""
|
71
|
+
Setup the skill - validates API key is available.
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
True if setup successful
|
75
|
+
"""
|
76
|
+
# API key validation already done in _validate_config
|
77
|
+
return True
|
78
|
+
|
79
|
+
def register_tools(self) -> None:
|
80
|
+
"""Register SWAIG tools with the agent"""
|
81
|
+
tools = self.get_tools()
|
82
|
+
for tool in tools:
|
83
|
+
# Merge any swaig_fields from params into the tool
|
84
|
+
if self.swaig_fields:
|
85
|
+
tool.update(self.swaig_fields)
|
86
|
+
self.agent.register_swaig_function(tool)
|
87
|
+
|
88
|
+
def get_tools(self) -> List[Dict[str, Any]]:
|
89
|
+
"""
|
90
|
+
Generate the SWAIG tool with DataMap webhook.
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
List containing the generated tool configuration
|
94
|
+
"""
|
95
|
+
# Determine temperature fields based on unit
|
96
|
+
if self.temperature_unit == 'fahrenheit':
|
97
|
+
temp_field = 'temp_f'
|
98
|
+
feels_like_field = 'feelslike_f'
|
99
|
+
unit_name = 'Fahrenheit'
|
100
|
+
else:
|
101
|
+
temp_field = 'temp_c'
|
102
|
+
feels_like_field = 'feelslike_c'
|
103
|
+
unit_name = 'Celsius'
|
104
|
+
|
105
|
+
# Create TTS-friendly response instruction
|
106
|
+
response_instruction = (
|
107
|
+
f"Tell the user the current weather conditions. "
|
108
|
+
f"Express all temperatures in {unit_name} using natural language numbers "
|
109
|
+
f"without abbreviations or symbols for clear text-to-speech pronunciation. "
|
110
|
+
f"For example, say 'seventy two degrees {unit_name}' instead of '72F' or '72°F'. "
|
111
|
+
f"Include the condition, current temperature, wind direction and speed, "
|
112
|
+
f"cloud coverage percentage, and what the temperature feels like."
|
113
|
+
)
|
114
|
+
|
115
|
+
# Build the weather data template
|
116
|
+
weather_template = (
|
117
|
+
f"{response_instruction} "
|
118
|
+
f"Current conditions: ${{current.condition.text}}. "
|
119
|
+
f"Temperature: ${{current.{temp_field}}} degrees {unit_name}. "
|
120
|
+
f"Wind: ${{current.wind_dir}} at ${{current.wind_mph}} miles per hour. "
|
121
|
+
f"Cloud coverage: ${{current.cloud}} percent. "
|
122
|
+
f"Feels like: ${{current.{feels_like_field}}} degrees {unit_name}."
|
123
|
+
)
|
124
|
+
|
125
|
+
# Create the tool configuration with DataMap webhook
|
126
|
+
tool = {
|
127
|
+
"function": self.tool_name,
|
128
|
+
"description": f"Get current weather information for any location",
|
129
|
+
"parameters": {
|
130
|
+
"type": "object",
|
131
|
+
"properties": {
|
132
|
+
"location": {
|
133
|
+
"type": "string",
|
134
|
+
"description": "The city, state, country, or location to get weather for"
|
135
|
+
}
|
136
|
+
},
|
137
|
+
"required": ["location"]
|
138
|
+
},
|
139
|
+
"data_map": {
|
140
|
+
"webhooks": [
|
141
|
+
{
|
142
|
+
"url": f"https://api.weatherapi.com/v1/current.json?key={self.api_key}&q=%{{args.location}}&aqi=no",
|
143
|
+
"method": "GET",
|
144
|
+
"output": SwaigFunctionResult(weather_template).to_dict()
|
145
|
+
}
|
146
|
+
],
|
147
|
+
"error_keys": ["error"],
|
148
|
+
"output": SwaigFunctionResult(
|
149
|
+
"Sorry, I cannot get weather information right now. Please try again later or check if the location name is correct."
|
150
|
+
).to_dict()
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
return [tool]
|
@@ -1,10 +1,10 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: signalwire_agents
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.23
|
4
4
|
Summary: SignalWire AI Agents SDK
|
5
5
|
Author-email: SignalWire Team <info@signalwire.com>
|
6
|
-
Project-URL: Homepage, https://github.com/signalwire/signalwire-
|
7
|
-
Classifier: Development Status ::
|
6
|
+
Project-URL: Homepage, https://github.com/signalwire/signalwire-agents
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
8
8
|
Classifier: Intended Audience :: Developers
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
@@ -176,7 +176,7 @@ agent.serve()
|
|
176
176
|
- **Dependency validation**: Clear error messages for missing requirements
|
177
177
|
- **Modular architecture**: Skills are self-contained and reusable
|
178
178
|
|
179
|
-
For detailed documentation, see [Skills System README](docs/
|
179
|
+
For detailed documentation, see [Skills System README](docs/skills_system.md).
|
180
180
|
|
181
181
|
## DataMap Tools
|
182
182
|
|
@@ -572,10 +572,7 @@ class SimpleAgent(AgentBase):
|
|
572
572
|
self.prompt_add_section("Goal", body="Help users with basic questions.")
|
573
573
|
self.prompt_add_section("Instructions", bullets=["Be concise and clear."])
|
574
574
|
|
575
|
-
#
|
576
|
-
# self.setPersonality("You are a helpful assistant.")
|
577
|
-
# self.setGoal("Help users with basic questions.")
|
578
|
-
# self.setInstructions(["Be concise and clear."])
|
575
|
+
# Note: Use prompt_add_section() for all prompt configuration
|
579
576
|
|
580
577
|
@AgentBase.tool(
|
581
578
|
name="get_time",
|