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,109 @@
|
|
|
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 typing import List, Dict, Any
|
|
11
|
+
|
|
12
|
+
from signalwire_agents.core.skill_base import SkillBase
|
|
13
|
+
from signalwire_agents.core.data_map import DataMap
|
|
14
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class JokeSkill(SkillBase):
|
|
18
|
+
"""Joke telling capability using API Ninjas with DataMap"""
|
|
19
|
+
|
|
20
|
+
SKILL_NAME = "joke"
|
|
21
|
+
SKILL_DESCRIPTION = "Tell jokes using the API Ninjas joke API"
|
|
22
|
+
SKILL_VERSION = "1.0.0"
|
|
23
|
+
REQUIRED_PACKAGES = [] # DataMap doesn't require local packages
|
|
24
|
+
REQUIRED_ENV_VARS = [] # API key comes from parameters
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
|
|
28
|
+
"""Get parameter schema for joke skill"""
|
|
29
|
+
schema = super().get_parameter_schema()
|
|
30
|
+
schema.update({
|
|
31
|
+
"api_key": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "API Ninjas API key for joke service",
|
|
34
|
+
"required": True,
|
|
35
|
+
"hidden": True,
|
|
36
|
+
"env_var": "API_NINJAS_KEY"
|
|
37
|
+
},
|
|
38
|
+
"tool_name": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Custom name for the joke tool",
|
|
41
|
+
"default": "get_joke",
|
|
42
|
+
"required": False
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
return schema
|
|
46
|
+
|
|
47
|
+
def setup(self) -> bool:
|
|
48
|
+
"""Setup the joke skill"""
|
|
49
|
+
# Validate required parameters
|
|
50
|
+
required_params = ['api_key']
|
|
51
|
+
missing_params = [param for param in required_params if not self.params.get(param)]
|
|
52
|
+
if missing_params:
|
|
53
|
+
self.logger.error(f"Missing required parameters: {missing_params}")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
# Set parameters from config
|
|
57
|
+
self.api_key = self.params['api_key']
|
|
58
|
+
|
|
59
|
+
# Optional parameters with defaults
|
|
60
|
+
self.tool_name = self.params.get('tool_name', 'get_joke')
|
|
61
|
+
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
def register_tools(self) -> None:
|
|
65
|
+
"""Register joke tool using DataMap"""
|
|
66
|
+
|
|
67
|
+
# Create DataMap tool for jokes - uses required enum parameter
|
|
68
|
+
joke_tool = (DataMap(self.tool_name)
|
|
69
|
+
.description('Get a random joke from API Ninjas')
|
|
70
|
+
.parameter('type', 'string', 'Type of joke to get',
|
|
71
|
+
required=True, enum=['jokes', 'dadjokes'])
|
|
72
|
+
.webhook('GET', "https://api.api-ninjas.com/v1/${args.type}",
|
|
73
|
+
headers={'X-Api-Key': self.api_key})
|
|
74
|
+
.output(SwaigFunctionResult('Here\'s a joke: ${array[0].joke}'))
|
|
75
|
+
.error_keys(['error'])
|
|
76
|
+
.fallback_output(SwaigFunctionResult('Sorry, there is a problem with the joke service right now. Please try again later.'))
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Register the DataMap tool with the agent
|
|
80
|
+
self.agent.register_swaig_function(joke_tool.to_swaig_function())
|
|
81
|
+
|
|
82
|
+
def get_hints(self) -> List[str]:
|
|
83
|
+
"""Return speech recognition hints"""
|
|
84
|
+
# Currently no hints are provided, but you could add them like:
|
|
85
|
+
# return [
|
|
86
|
+
# "joke", "tell me a joke", "funny", "humor", "dad joke",
|
|
87
|
+
# "regular joke", "make me laugh", "comedy"
|
|
88
|
+
# ]
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
def get_global_data(self) -> Dict[str, Any]:
|
|
92
|
+
"""Return global data to be available in DataMap variables"""
|
|
93
|
+
return {
|
|
94
|
+
"joke_skill_enabled": True
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def get_prompt_sections(self) -> List[Dict[str, Any]]:
|
|
98
|
+
"""Return prompt sections to add to agent"""
|
|
99
|
+
return [
|
|
100
|
+
{
|
|
101
|
+
"title": "Joke Telling",
|
|
102
|
+
"body": "You can tell jokes to entertain users.",
|
|
103
|
+
"bullets": [
|
|
104
|
+
f"Use {self.tool_name} to tell jokes when users ask for humor",
|
|
105
|
+
"You can tell regular jokes or dad jokes",
|
|
106
|
+
"Be enthusiastic and fun when sharing jokes"
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Math Skill
|
|
2
|
+
|
|
3
|
+
The math skill provides safe mathematical calculation capabilities for agents. It allows users to perform basic arithmetic operations and complex mathematical expressions with security protections against code injection.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Safe mathematical expression evaluation
|
|
8
|
+
- Support for basic arithmetic operations
|
|
9
|
+
- Parentheses support for complex expressions
|
|
10
|
+
- Division by zero protection
|
|
11
|
+
- Security filtering to prevent code injection
|
|
12
|
+
- No external dependencies required
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- **Packages**: None (uses built-in Python functionality)
|
|
17
|
+
- **No external APIs required**
|
|
18
|
+
|
|
19
|
+
## Parameters
|
|
20
|
+
|
|
21
|
+
### Optional Parameters
|
|
22
|
+
|
|
23
|
+
- `swaig_fields` (dict): Additional SWAIG function configuration
|
|
24
|
+
- `secure` (boolean): Override security settings for the calculation function
|
|
25
|
+
- `fillers` (dict): Language-specific filler phrases while calculating
|
|
26
|
+
- Any other SWAIG function parameters
|
|
27
|
+
|
|
28
|
+
**Note**: This skill does not require any configuration parameters beyond the optional swaig_fields. It works out-of-the-box with no setup.
|
|
29
|
+
|
|
30
|
+
## Tools Created
|
|
31
|
+
|
|
32
|
+
- `calculate` - Perform a mathematical calculation with basic operations
|
|
33
|
+
|
|
34
|
+
## Usage Examples
|
|
35
|
+
|
|
36
|
+
### Basic Usage
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# No configuration needed - works immediately
|
|
40
|
+
agent.add_skill("math")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### With Custom Fillers
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
agent.add_skill("math", {
|
|
47
|
+
"swaig_fields": {
|
|
48
|
+
"fillers": {
|
|
49
|
+
"en-US": [
|
|
50
|
+
"Let me calculate that for you...",
|
|
51
|
+
"Crunching the numbers...",
|
|
52
|
+
"Computing the result...",
|
|
53
|
+
"Working out the math..."
|
|
54
|
+
],
|
|
55
|
+
"es-ES": [
|
|
56
|
+
"Déjame calcular eso...",
|
|
57
|
+
"Procesando los números...",
|
|
58
|
+
"Calculando el resultado..."
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Disabling Security (if needed)
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
agent.add_skill("math", {
|
|
69
|
+
"swaig_fields": {
|
|
70
|
+
"secure": False # Allow unauthenticated calculation requests
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## How It Works
|
|
76
|
+
|
|
77
|
+
### Calculation Function
|
|
78
|
+
- **Input**: Mathematical expression as a string
|
|
79
|
+
- **Processing**: Validates expression for safety, then evaluates it
|
|
80
|
+
- **Output**: Shows the original expression and the calculated result
|
|
81
|
+
- **Example**: "2 + 3 * 4 = 14"
|
|
82
|
+
|
|
83
|
+
### Supported Operations
|
|
84
|
+
|
|
85
|
+
- **Addition**: `+` (e.g., "5 + 3")
|
|
86
|
+
- **Subtraction**: `-` (e.g., "10 - 7")
|
|
87
|
+
- **Multiplication**: `*` (e.g., "6 * 8")
|
|
88
|
+
- **Division**: `/` (e.g., "15 / 3")
|
|
89
|
+
- **Modulo**: `%` (e.g., "17 % 5")
|
|
90
|
+
- **Exponentiation**: `**` (e.g., "2 ** 3")
|
|
91
|
+
- **Parentheses**: `()` for grouping (e.g., "(2 + 3) * 4")
|
|
92
|
+
|
|
93
|
+
### Expression Examples
|
|
94
|
+
|
|
95
|
+
- Simple: `"2 + 3"` → "2 + 3 = 5"
|
|
96
|
+
- Complex: `"(10 + 5) * 2 / 3"` → "(10 + 5) * 2 / 3 = 10.0"
|
|
97
|
+
- With decimals: `"3.14 * 2"` → "3.14 * 2 = 6.28"
|
|
98
|
+
- Powers: `"2 ** 8"` → "2 ** 8 = 256"
|
|
99
|
+
- Modulo: `"17 % 5"` → "17 % 5 = 2"
|
|
100
|
+
|
|
101
|
+
## Function Parameters
|
|
102
|
+
|
|
103
|
+
The calculate tool accepts one parameter:
|
|
104
|
+
|
|
105
|
+
- `expression` (string, required): Mathematical expression to evaluate
|
|
106
|
+
- Must contain only numbers, operators, and parentheses
|
|
107
|
+
- Operators allowed: `+`, `-`, `*`, `/`, `%`, `**`, `(`, `)`
|
|
108
|
+
- Decimal numbers are supported
|
|
109
|
+
- Spaces are allowed and ignored
|
|
110
|
+
|
|
111
|
+
## Security Features
|
|
112
|
+
|
|
113
|
+
### Input Validation
|
|
114
|
+
- Only allows safe mathematical characters: numbers, operators, parentheses, spaces, decimal points
|
|
115
|
+
- Blocks any potentially dangerous code or function calls
|
|
116
|
+
- Uses regex pattern matching for character validation
|
|
117
|
+
|
|
118
|
+
### Safe Evaluation
|
|
119
|
+
- Uses Python's `eval()` with restricted builtins (empty `__builtins__`)
|
|
120
|
+
- No access to system functions or imports
|
|
121
|
+
- Cannot execute arbitrary code
|
|
122
|
+
|
|
123
|
+
### Error Handling
|
|
124
|
+
- **Division by Zero**: Returns friendly error message
|
|
125
|
+
- **Invalid Syntax**: Returns error for malformed expressions
|
|
126
|
+
- **Illegal Characters**: Rejects expressions with non-math characters
|
|
127
|
+
- **Empty Input**: Prompts user to provide an expression
|
|
128
|
+
|
|
129
|
+
## Error Examples
|
|
130
|
+
|
|
131
|
+
- **Division by zero**: "Error: Division by zero is not allowed."
|
|
132
|
+
- **Invalid characters**: "Invalid expression. Only numbers and basic math operators are allowed."
|
|
133
|
+
- **Syntax error**: "Error calculating '2 + + 3': Invalid expression"
|
|
134
|
+
- **Empty input**: "Please provide a mathematical expression to calculate."
|
|
135
|
+
|
|
136
|
+
## Common Use Cases
|
|
137
|
+
|
|
138
|
+
1. **Basic Arithmetic**: "What's 15 + 27?"
|
|
139
|
+
2. **Percentage Calculations**: "What's 15% of 200?" → "200 * 0.15"
|
|
140
|
+
3. **Complex Expressions**: "Calculate (100 + 50) * 0.08"
|
|
141
|
+
4. **Powers and Roots**: "What's 2 to the power of 10?"
|
|
142
|
+
5. **Financial Calculations**: "If I have $500 and spend $125, how much is left?"
|
|
143
|
+
|
|
144
|
+
## Best Practices
|
|
145
|
+
|
|
146
|
+
1. **Default Behavior**: The skill works immediately without configuration
|
|
147
|
+
2. **User Education**: Help users understand they can use parentheses for complex calculations
|
|
148
|
+
3. **Expression Formatting**: The skill is forgiving with spaces and formatting
|
|
149
|
+
4. **Error Recovery**: Provide helpful guidance when users make syntax errors
|
|
150
|
+
5. **Security**: The skill is designed to be safe even with malicious input
|
|
151
|
+
|
|
152
|
+
## Agent Integration
|
|
153
|
+
|
|
154
|
+
When added to an agent, this skill automatically:
|
|
155
|
+
|
|
156
|
+
- Adds speech recognition hints for math-related words
|
|
157
|
+
- Provides prompt guidance about calculation capabilities
|
|
158
|
+
- Enables the agent to respond to mathematical questions
|
|
159
|
+
- Shows both the original expression and result for transparency
|
|
160
|
+
|
|
161
|
+
The skill is designed to be completely self-contained and secure, making it safe for any agent to use for mathematical calculations.
|
|
@@ -0,0 +1,105 @@
|
|
|
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 re
|
|
11
|
+
from typing import List, Dict, Any
|
|
12
|
+
|
|
13
|
+
from signalwire_agents.core.skill_base import SkillBase
|
|
14
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
|
15
|
+
|
|
16
|
+
class MathSkill(SkillBase):
|
|
17
|
+
"""Provides basic mathematical calculation capabilities"""
|
|
18
|
+
|
|
19
|
+
SKILL_NAME = "math"
|
|
20
|
+
SKILL_DESCRIPTION = "Perform basic mathematical calculations"
|
|
21
|
+
SKILL_VERSION = "1.0.0"
|
|
22
|
+
REQUIRED_PACKAGES = []
|
|
23
|
+
REQUIRED_ENV_VARS = []
|
|
24
|
+
|
|
25
|
+
def setup(self) -> bool:
|
|
26
|
+
"""Setup the math skill"""
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
def register_tools(self) -> None:
|
|
30
|
+
"""Register math tools with the agent"""
|
|
31
|
+
|
|
32
|
+
self.define_tool(
|
|
33
|
+
name="calculate",
|
|
34
|
+
description="Perform a mathematical calculation with basic operations (+, -, *, /, %, **)",
|
|
35
|
+
parameters={
|
|
36
|
+
"expression": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Mathematical expression to evaluate (e.g., '2 + 3 * 4', '(10 + 5) / 3')"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
handler=self._calculate_handler
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def _calculate_handler(self, args, raw_data):
|
|
45
|
+
"""Handler for calculate tool"""
|
|
46
|
+
expression = args.get("expression", "").strip()
|
|
47
|
+
|
|
48
|
+
if not expression:
|
|
49
|
+
return SwaigFunctionResult("Please provide a mathematical expression to calculate.")
|
|
50
|
+
|
|
51
|
+
# Security: only allow safe mathematical operations
|
|
52
|
+
safe_chars = re.compile(r'^[0-9+\-*/().\s%**]+$')
|
|
53
|
+
if not safe_chars.match(expression):
|
|
54
|
+
return SwaigFunctionResult(
|
|
55
|
+
"Invalid expression. Only numbers and basic math operators (+, -, *, /, %, **, parentheses) are allowed."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Evaluate the expression safely
|
|
60
|
+
result = eval(expression, {"__builtins__": {}}, {})
|
|
61
|
+
|
|
62
|
+
return SwaigFunctionResult(f"{expression} = {result}")
|
|
63
|
+
|
|
64
|
+
except ZeroDivisionError:
|
|
65
|
+
return SwaigFunctionResult("Error: Division by zero is not allowed.")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
return SwaigFunctionResult(f"Error calculating '{expression}': Invalid expression")
|
|
68
|
+
|
|
69
|
+
def get_hints(self) -> List[str]:
|
|
70
|
+
"""Return speech recognition hints"""
|
|
71
|
+
# Currently no hints provided, but you could add them like:
|
|
72
|
+
# return [
|
|
73
|
+
# "calculate", "math", "plus", "minus", "times", "multiply",
|
|
74
|
+
# "divide", "equals", "percent", "power", "squared"
|
|
75
|
+
# ]
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
def get_prompt_sections(self) -> List[Dict[str, Any]]:
|
|
79
|
+
"""Return prompt sections to add to agent"""
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
"title": "Mathematical Calculations",
|
|
83
|
+
"body": "You can perform mathematical calculations for users.",
|
|
84
|
+
"bullets": [
|
|
85
|
+
"Use the calculate tool for any math expressions",
|
|
86
|
+
"Supports basic operations: +, -, *, /, %, ** (power)",
|
|
87
|
+
"Can handle parentheses for complex expressions"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
|
|
94
|
+
"""
|
|
95
|
+
Get the parameter schema for the math skill
|
|
96
|
+
|
|
97
|
+
The math skill has no custom parameters - it inherits only
|
|
98
|
+
the base parameters from SkillBase.
|
|
99
|
+
"""
|
|
100
|
+
# Get base schema from parent
|
|
101
|
+
schema = super().get_parameter_schema()
|
|
102
|
+
|
|
103
|
+
# No additional parameters for math skill
|
|
104
|
+
|
|
105
|
+
return schema
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# MCP Gateway Skill
|
|
2
|
+
|
|
3
|
+
Bridge MCP (Model Context Protocol) servers with SignalWire SWAIG functions, allowing agents to seamlessly interact with MCP-based tools.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
The MCP Gateway skill connects SignalWire agents to MCP servers through a centralized gateway service. It dynamically discovers and registers MCP tools as SWAIG functions, maintaining session state throughout each call.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Dynamic tool discovery from MCP servers
|
|
12
|
+
- Session management tied to SignalWire call IDs
|
|
13
|
+
- Automatic cleanup on call hangup
|
|
14
|
+
- Support for multiple MCP services
|
|
15
|
+
- Selective tool loading
|
|
16
|
+
- HTTPS support with SSL verification
|
|
17
|
+
- Retry logic for resilient connections
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Running MCP Gateway service
|
|
22
|
+
- Network access to gateway
|
|
23
|
+
- Gateway credentials (username/password)
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Required Parameters
|
|
28
|
+
|
|
29
|
+
Either Basic Auth credentials OR Bearer token:
|
|
30
|
+
- `gateway_url`: URL of the MCP gateway service (default: "http://localhost:8100")
|
|
31
|
+
- `auth_user` + `auth_password`: Basic auth credentials
|
|
32
|
+
- OR `auth_token`: Bearer token for authentication
|
|
33
|
+
|
|
34
|
+
### Optional Parameters
|
|
35
|
+
|
|
36
|
+
- `services`: Array of services to load (default: all available)
|
|
37
|
+
- `name`: Service name
|
|
38
|
+
- `tools`: Array of tool names or "*" for all (default: all)
|
|
39
|
+
- `session_timeout`: Session timeout in seconds (default: 300)
|
|
40
|
+
- `tool_prefix`: Prefix for SWAIG function names (default: "mcp_")
|
|
41
|
+
- `retry_attempts`: Number of retry attempts (default: 3)
|
|
42
|
+
- `request_timeout`: HTTP request timeout in seconds (default: 30)
|
|
43
|
+
- `verify_ssl`: Verify SSL certificates (default: true)
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
### Basic Usage (All Services)
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from signalwire_agents import AgentBase
|
|
51
|
+
|
|
52
|
+
class MyAgent(AgentBase):
|
|
53
|
+
def __init__(self):
|
|
54
|
+
super().__init__(name="My Agent")
|
|
55
|
+
|
|
56
|
+
# Load all available MCP services
|
|
57
|
+
self.add_skill("mcp_gateway", {
|
|
58
|
+
"gateway_url": "http://localhost:8080",
|
|
59
|
+
"auth_user": "admin",
|
|
60
|
+
"auth_password": "changeme"
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
agent = MyAgent()
|
|
64
|
+
agent.run()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Selective Service Loading
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# Load specific services with specific tools
|
|
71
|
+
self.add_skill("mcp_gateway", {
|
|
72
|
+
"gateway_url": "https://gateway.example.com",
|
|
73
|
+
"auth_user": "admin",
|
|
74
|
+
"auth_password": "secret",
|
|
75
|
+
"services": [
|
|
76
|
+
{
|
|
77
|
+
"name": "todo",
|
|
78
|
+
"tools": ["add_todo", "list_todos"] # Only these tools
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "calculator",
|
|
82
|
+
"tools": "*" # All calculator tools
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"session_timeout": 600,
|
|
86
|
+
"tool_prefix": "ext_"
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### HTTPS with Self-Signed Certificate
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
self.add_skill("mcp_gateway", {
|
|
94
|
+
"gateway_url": "https://localhost:8443",
|
|
95
|
+
"auth_user": "admin",
|
|
96
|
+
"auth_password": "secret",
|
|
97
|
+
"verify_ssl": False # For self-signed certificates
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Bearer Token Authentication
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
self.add_skill("mcp_gateway", {
|
|
105
|
+
"gateway_url": "https://gateway.example.com",
|
|
106
|
+
"auth_token": "your-bearer-token-here",
|
|
107
|
+
"services": [{
|
|
108
|
+
"name": "todo"
|
|
109
|
+
}]
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Generated Functions
|
|
114
|
+
|
|
115
|
+
The skill dynamically generates SWAIG functions based on discovered MCP tools. Function names follow the pattern:
|
|
116
|
+
|
|
117
|
+
`{tool_prefix}{service_name}_{tool_name}`
|
|
118
|
+
|
|
119
|
+
For example, with default settings:
|
|
120
|
+
- `mcp_todo_add_todo` - Add a todo item
|
|
121
|
+
- `mcp_todo_list_todos` - List todo items
|
|
122
|
+
- `mcp_calculator_add` - Calculator addition
|
|
123
|
+
|
|
124
|
+
## Example Conversations
|
|
125
|
+
|
|
126
|
+
### Using Todo Service
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
User: "Add a task to buy milk"
|
|
130
|
+
Assistant: "I'll add that to your todo list."
|
|
131
|
+
[Calls mcp_todo_add_todo with text="buy milk"]
|
|
132
|
+
Assistant: "I've added 'buy milk' to your todo list."
|
|
133
|
+
|
|
134
|
+
User: "What's on my todo list?"
|
|
135
|
+
Assistant: "Let me check your todos."
|
|
136
|
+
[Calls mcp_todo_list_todos]
|
|
137
|
+
Assistant: "Here are your current todos:
|
|
138
|
+
○ #1 [medium] buy milk"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Multiple Services
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
User: "Add 'finish report' to my todos and calculate 15% of 200"
|
|
145
|
+
Assistant: "I'll add that todo and do the calculation for you."
|
|
146
|
+
[Calls mcp_todo_add_todo with text="finish report"]
|
|
147
|
+
[Calls mcp_calculator_percent with value=200, percent=15]
|
|
148
|
+
Assistant: "I've added 'finish report' to your todos. 15% of 200 is 30."
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Session Management
|
|
152
|
+
|
|
153
|
+
- Each SignalWire call gets its own MCP session
|
|
154
|
+
- Sessions persist across multiple tool calls
|
|
155
|
+
- Automatic cleanup on call hangup
|
|
156
|
+
- Configurable timeout for inactive sessions
|
|
157
|
+
|
|
158
|
+
### Custom Session ID
|
|
159
|
+
|
|
160
|
+
You can override the session ID by setting `mcp_call_id` in global_data:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# In your agent code
|
|
164
|
+
self.set_global_data({
|
|
165
|
+
"mcp_call_id": "custom-session-123"
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
# Or in a SWAIG function
|
|
169
|
+
result = SwaigFunctionResult("Session changed")
|
|
170
|
+
result.add_action("set_global_data", {"mcp_call_id": "new-session-456"})
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This is useful for:
|
|
174
|
+
- Managing multiple MCP sessions within a single call
|
|
175
|
+
- Sharing MCP sessions across different calls
|
|
176
|
+
- Custom session management strategies
|
|
177
|
+
|
|
178
|
+
## Troubleshooting
|
|
179
|
+
|
|
180
|
+
### Gateway Connection Failed
|
|
181
|
+
|
|
182
|
+
Check:
|
|
183
|
+
1. Gateway service is running
|
|
184
|
+
2. Correct URL and credentials
|
|
185
|
+
3. Network connectivity
|
|
186
|
+
4. Firewall rules
|
|
187
|
+
|
|
188
|
+
### SSL Certificate Errors
|
|
189
|
+
|
|
190
|
+
For self-signed certificates:
|
|
191
|
+
```python
|
|
192
|
+
"verify_ssl": False
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For custom CA certificates, ensure they're in the system trust store.
|
|
196
|
+
|
|
197
|
+
### Tool Not Found
|
|
198
|
+
|
|
199
|
+
Verify:
|
|
200
|
+
1. Service name is correct
|
|
201
|
+
2. Tool name matches exactly
|
|
202
|
+
3. Tool is included in service configuration
|
|
203
|
+
4. MCP server is returning tools correctly
|
|
204
|
+
|
|
205
|
+
### Session Timeouts
|
|
206
|
+
|
|
207
|
+
Increase timeout if needed:
|
|
208
|
+
```python
|
|
209
|
+
"session_timeout": 600 # 10 minutes
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Gateway Setup
|
|
213
|
+
|
|
214
|
+
To run the MCP Gateway service:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
cd mcp_gateway
|
|
218
|
+
python3 gateway_service.py
|
|
219
|
+
|
|
220
|
+
# Or with custom config
|
|
221
|
+
python3 gateway_service.py -c myconfig.json
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Security Considerations
|
|
225
|
+
|
|
226
|
+
1. Always use HTTPS in production
|
|
227
|
+
2. Use strong authentication credentials
|
|
228
|
+
3. Limit service access to required tools only
|
|
229
|
+
4. Monitor gateway logs for suspicious activity
|
|
230
|
+
5. Set appropriate session timeouts
|