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,992 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: signalwire_agents
|
|
3
|
+
Version: 1.0.7
|
|
4
|
+
Summary: SignalWire AI Agents SDK
|
|
5
|
+
Author-email: SignalWire Team <info@signalwire.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/signalwire/signalwire-agents
|
|
8
|
+
Keywords: signalwire,ai,agents,voice,telephony,swaig,swml
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: fastapi>=0.115.12
|
|
22
|
+
Requires-Dist: pydantic>=2.11.4
|
|
23
|
+
Requires-Dist: PyYAML>=6.0.2
|
|
24
|
+
Requires-Dist: Requests>=2.32.3
|
|
25
|
+
Requires-Dist: setuptools<81,>=66.1.1
|
|
26
|
+
Requires-Dist: signalwire_pom>=2.7.1
|
|
27
|
+
Requires-Dist: structlog>=25.3.0
|
|
28
|
+
Requires-Dist: uvicorn>=0.34.2
|
|
29
|
+
Requires-Dist: beautifulsoup4>=4.12.3
|
|
30
|
+
Requires-Dist: pytz>=2023.3
|
|
31
|
+
Requires-Dist: lxml>=4.9.0
|
|
32
|
+
Provides-Extra: search-queryonly
|
|
33
|
+
Requires-Dist: numpy>=1.24.0; extra == "search-queryonly"
|
|
34
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "search-queryonly"
|
|
35
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "search-queryonly"
|
|
36
|
+
Requires-Dist: nltk>=3.8; extra == "search-queryonly"
|
|
37
|
+
Provides-Extra: search
|
|
38
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "search"
|
|
39
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "search"
|
|
40
|
+
Requires-Dist: nltk>=3.8; extra == "search"
|
|
41
|
+
Requires-Dist: numpy>=1.24.0; extra == "search"
|
|
42
|
+
Provides-Extra: search-full
|
|
43
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "search-full"
|
|
44
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "search-full"
|
|
45
|
+
Requires-Dist: nltk>=3.8; extra == "search-full"
|
|
46
|
+
Requires-Dist: numpy>=1.24.0; extra == "search-full"
|
|
47
|
+
Requires-Dist: pdfplumber>=0.9.0; extra == "search-full"
|
|
48
|
+
Requires-Dist: python-docx>=0.8.11; extra == "search-full"
|
|
49
|
+
Requires-Dist: markdown>=3.4.0; extra == "search-full"
|
|
50
|
+
Requires-Dist: striprtf>=0.0.26; extra == "search-full"
|
|
51
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "search-full"
|
|
52
|
+
Requires-Dist: python-pptx>=0.6.21; extra == "search-full"
|
|
53
|
+
Requires-Dist: python-magic>=0.4.27; extra == "search-full"
|
|
54
|
+
Provides-Extra: search-nlp
|
|
55
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "search-nlp"
|
|
56
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "search-nlp"
|
|
57
|
+
Requires-Dist: nltk>=3.8; extra == "search-nlp"
|
|
58
|
+
Requires-Dist: numpy>=1.24.0; extra == "search-nlp"
|
|
59
|
+
Requires-Dist: spacy>=3.6.0; extra == "search-nlp"
|
|
60
|
+
Provides-Extra: search-all
|
|
61
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "search-all"
|
|
62
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "search-all"
|
|
63
|
+
Requires-Dist: nltk>=3.8; extra == "search-all"
|
|
64
|
+
Requires-Dist: numpy>=1.24.0; extra == "search-all"
|
|
65
|
+
Requires-Dist: spacy>=3.6.0; extra == "search-all"
|
|
66
|
+
Requires-Dist: pdfplumber>=0.9.0; extra == "search-all"
|
|
67
|
+
Requires-Dist: python-docx>=0.8.11; extra == "search-all"
|
|
68
|
+
Requires-Dist: markdown>=3.4.0; extra == "search-all"
|
|
69
|
+
Requires-Dist: striprtf>=0.0.26; extra == "search-all"
|
|
70
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "search-all"
|
|
71
|
+
Requires-Dist: python-pptx>=0.6.21; extra == "search-all"
|
|
72
|
+
Requires-Dist: python-magic>=0.4.27; extra == "search-all"
|
|
73
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "search-all"
|
|
74
|
+
Requires-Dist: pgvector>=0.2.0; extra == "search-all"
|
|
75
|
+
Provides-Extra: pgvector
|
|
76
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "pgvector"
|
|
77
|
+
Requires-Dist: pgvector>=0.2.0; extra == "pgvector"
|
|
78
|
+
Provides-Extra: all
|
|
79
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
|
|
80
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "all"
|
|
81
|
+
Requires-Dist: nltk>=3.8; extra == "all"
|
|
82
|
+
Requires-Dist: numpy>=1.24.0; extra == "all"
|
|
83
|
+
Requires-Dist: spacy>=3.6.0; extra == "all"
|
|
84
|
+
Requires-Dist: pdfplumber>=0.9.0; extra == "all"
|
|
85
|
+
Requires-Dist: python-docx>=0.8.11; extra == "all"
|
|
86
|
+
Requires-Dist: markdown>=3.4.0; extra == "all"
|
|
87
|
+
Requires-Dist: striprtf>=0.0.26; extra == "all"
|
|
88
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "all"
|
|
89
|
+
Requires-Dist: python-pptx>=0.6.21; extra == "all"
|
|
90
|
+
Requires-Dist: python-magic>=0.4.27; extra == "all"
|
|
91
|
+
Dynamic: license-file
|
|
92
|
+
|
|
93
|
+
<!-- Header -->
|
|
94
|
+
<div align="center">
|
|
95
|
+
<a href="https://signalwire.com" target="_blank">
|
|
96
|
+
<img src="https://github.com/user-attachments/assets/0c8ed3b9-8c50-4dc6-9cc4-cc6cd137fd50" width="500" />
|
|
97
|
+
</a>
|
|
98
|
+
|
|
99
|
+
# Agents SDK
|
|
100
|
+
|
|
101
|
+
#### _A Python SDK for creating, hosting, and securing SignalWire AI agents as microservices with minimal boilerplate._
|
|
102
|
+
|
|
103
|
+
<br/>
|
|
104
|
+
|
|
105
|
+
<p align="center">
|
|
106
|
+
<a href="https://developer.signalwire.com/sdks/agents-sdk" target="_blank">📖 Documentation</a> <code>#</code>
|
|
107
|
+
<a href="https://github.com/signalwire/signalwire-docs/issues/new/choose" target="_blank">🐛 Report an issue</a> <code>#</code>
|
|
108
|
+
<a href="https://pypi.org/project/signalwire-agents/" target="_blank">🐍 PyPI</a>
|
|
109
|
+
</p>
|
|
110
|
+
|
|
111
|
+
<br/>
|
|
112
|
+
|
|
113
|
+
<!-- Badges -->
|
|
114
|
+
<div align="center">
|
|
115
|
+
<a href="https://discord.com/invite/F2WNYTNjuF" target="_blank"><img src="https://img.shields.io/badge/Discord%20Community-5865F2" alt="Discord" /></a>
|
|
116
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/MIT-License-blue" alt="MIT License" /></a>
|
|
117
|
+
<a href="https://github.com/signalwire" target="_blank"><img src="https://img.shields.io/badge/GitHub-%23121011.svg?logo=github&logoColor=white&" alt="GitHub" /></a>
|
|
118
|
+
<a href="https://github.com/signalwire/docs" target="_blank"><img src="https://img.shields.io/github/stars/signalwire/signalwire-agents" alt="GitHub Stars" /></a>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<br/>
|
|
122
|
+
|
|
123
|
+
<a href="https://signalwire.com/signup" target="_blank">
|
|
124
|
+
<img src="https://github.com/user-attachments/assets/c2510c86-ae03-42a9-be06-ab9bcea948e1" alt="Sign Up" height="65"/>
|
|
125
|
+
</a>
|
|
126
|
+
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
## Features
|
|
130
|
+
|
|
131
|
+
| | |
|
|
132
|
+
|-------------------------------|:-----------------------------------------------------------------------------:|
|
|
133
|
+
| 🤖 **Self-Contained Agents** | Each agent is both a web app and an AI persona |
|
|
134
|
+
| 📝 **Prompt Object Model** | Structured prompt composition using POM |
|
|
135
|
+
| ⚙️ **SWAIG Integration** | Easily define and handle AI tools/functions |
|
|
136
|
+
| 🔧 **Dynamic Configuration** | Configure agents per-request for multi-tenant apps and personalization |
|
|
137
|
+
| 🗺️ **Custom Routing** | Dynamic request handling for different paths and content |
|
|
138
|
+
| 📞 **SIP Integration** | Route SIP calls to agents based on SIP usernames |
|
|
139
|
+
| 🔒 **Security Built-In** | Session management, function-specific security tokens, and basic auth |
|
|
140
|
+
| 💾 **State Management** | Persistent conversation state with automatic tracking |
|
|
141
|
+
| 🏗️ **Prefab Archetypes** | Ready-to-use agent types for common scenarios |
|
|
142
|
+
| 🏢 **Multi-Agent Support** | Host multiple agents on a single server |
|
|
143
|
+
| � **Modular Skills System** | Add capabilities to agents with simple one-liner calls |
|
|
144
|
+
| 🔍 **Local Search System** | Offline document search with vector similarity and keyword search |
|
|
145
|
+
|
|
146
|
+
## Installation
|
|
147
|
+
|
|
148
|
+
### Basic Installation
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pip install signalwire-agents
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Optional Search Functionality
|
|
155
|
+
|
|
156
|
+
The SDK includes optional local search capabilities that can be installed separately to avoid adding large dependencies to the base installation:
|
|
157
|
+
|
|
158
|
+
#### Search Installation Options
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Query existing .swsearch files only (smallest footprint)
|
|
162
|
+
pip install signalwire-agents[search-queryonly]
|
|
163
|
+
|
|
164
|
+
# Basic search (vector search + keyword search + building indexes)
|
|
165
|
+
pip install signalwire-agents[search]
|
|
166
|
+
|
|
167
|
+
# Full search with document processing (PDF, DOCX, etc.)
|
|
168
|
+
pip install signalwire-agents[search-full]
|
|
169
|
+
|
|
170
|
+
# Advanced NLP features (includes spaCy)
|
|
171
|
+
pip install signalwire-agents[search-nlp]
|
|
172
|
+
|
|
173
|
+
# All search features
|
|
174
|
+
pip install signalwire-agents[search-all]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### What Each Option Includes
|
|
178
|
+
|
|
179
|
+
| Option | Size | Features |
|
|
180
|
+
|--------|------|----------|
|
|
181
|
+
| `search-queryonly` | ~400MB | Query existing .swsearch files only (no building/processing) |
|
|
182
|
+
| `search` | ~500MB | Vector embeddings, keyword search, basic text processing |
|
|
183
|
+
| `search-full` | ~600MB | + PDF, DOCX, Excel, PowerPoint, HTML, Markdown processing |
|
|
184
|
+
| `search-nlp` | ~600MB | + Advanced spaCy NLP features |
|
|
185
|
+
| `search-all` | ~700MB | All search features combined |
|
|
186
|
+
|
|
187
|
+
**When to use `search-queryonly`:**
|
|
188
|
+
- Production containers with pre-built `.swsearch` files
|
|
189
|
+
- Lambda/serverless deployments
|
|
190
|
+
- Agents that only need to query knowledge bases (not build them)
|
|
191
|
+
- Smaller deployment footprint requirements
|
|
192
|
+
|
|
193
|
+
#### Search Features
|
|
194
|
+
|
|
195
|
+
- **Local/Offline Search**: No external API dependencies
|
|
196
|
+
- **Hybrid Search**: Vector similarity + keyword search
|
|
197
|
+
- **Smart Document Processing**: Markdown, Python, PDF, DOCX, etc.
|
|
198
|
+
- **Multiple Languages**: English, Spanish, with extensible framework
|
|
199
|
+
- **CLI Tools**: Build search indexes from document directories
|
|
200
|
+
- **HTTP API**: Standalone or embedded search service
|
|
201
|
+
|
|
202
|
+
#### Usage Example
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
# Only available with search extras installed
|
|
206
|
+
from signalwire_agents.search import IndexBuilder, SearchEngine
|
|
207
|
+
|
|
208
|
+
# Build search index
|
|
209
|
+
builder = IndexBuilder()
|
|
210
|
+
builder.build_index(
|
|
211
|
+
source_dir="./docs",
|
|
212
|
+
output_file="knowledge.swsearch",
|
|
213
|
+
file_types=['md', 'txt', 'pdf']
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Search documents
|
|
217
|
+
engine = SearchEngine("knowledge.swsearch")
|
|
218
|
+
results = engine.search(
|
|
219
|
+
query_vector=embeddings,
|
|
220
|
+
enhanced_text="search query",
|
|
221
|
+
count=5
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
<details>
|
|
226
|
+
<summary><h2>Documentation</h2></summary>
|
|
227
|
+
|
|
228
|
+
### Skills System
|
|
229
|
+
|
|
230
|
+
The SignalWire Agents SDK includes a powerful modular skills system that allows you to add complex capabilities to your agents with simple one-liner calls:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from signalwire_agents import AgentBase
|
|
234
|
+
|
|
235
|
+
# Create an agent
|
|
236
|
+
agent = AgentBase("My Assistant", route="/assistant")
|
|
237
|
+
|
|
238
|
+
# Add skills with one-liners
|
|
239
|
+
agent.add_skill("web_search", {
|
|
240
|
+
"api_key": "your-google-api-key",
|
|
241
|
+
"search_engine_id": "your-search-engine-id"
|
|
242
|
+
}) # Web search capability
|
|
243
|
+
agent.add_skill("datetime") # Current date/time info
|
|
244
|
+
agent.add_skill("math") # Mathematical calculations
|
|
245
|
+
|
|
246
|
+
# Configure skills with parameters
|
|
247
|
+
agent.add_skill("web_search", {
|
|
248
|
+
"api_key": "your-google-api-key",
|
|
249
|
+
"search_engine_id": "your-search-engine-id",
|
|
250
|
+
"num_results": 1, # Get 1 search results
|
|
251
|
+
"no_results_message": "Sorry, I couldn't find anything about '{query}'. Try rephrasing your question."
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
# Advanced: Customize SWAIG function properties
|
|
255
|
+
agent.add_skill("math", {
|
|
256
|
+
"swaig_fields": {
|
|
257
|
+
"secure": False, # Override security settings
|
|
258
|
+
"fillers": {"en-US": ["Calculating..."]} # Custom filler phrases
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
# Multiple web search instances with different tool names
|
|
263
|
+
agent.add_skill("web_search", {
|
|
264
|
+
"api_key": "your-google-api-key",
|
|
265
|
+
"search_engine_id": "general-search-engine-id",
|
|
266
|
+
"tool_name": "search_general", # Creates search_general tool
|
|
267
|
+
"num_results": 1
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
agent.add_skill("web_search", {
|
|
271
|
+
"api_key": "your-google-api-key",
|
|
272
|
+
"search_engine_id": "news-search-engine-id",
|
|
273
|
+
"tool_name": "search_news", # Creates search_news tool
|
|
274
|
+
"num_results": 3,
|
|
275
|
+
"delay": 0.5
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
# Multiple DataSphere instances with different tool names
|
|
279
|
+
agent.add_skill("datasphere", {
|
|
280
|
+
"space_name": "my-space",
|
|
281
|
+
"project_id": "my-project",
|
|
282
|
+
"token": "my-token",
|
|
283
|
+
"document_id": "drinks-doc",
|
|
284
|
+
"tool_name": "search_drinks", # Creates search_drinks tool
|
|
285
|
+
"count": 2
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
agent.add_skill("datasphere", {
|
|
289
|
+
"space_name": "my-space",
|
|
290
|
+
"project_id": "my-project",
|
|
291
|
+
"token": "my-token",
|
|
292
|
+
"document_id": "food-doc",
|
|
293
|
+
"tool_name": "search_recipes", # Creates search_recipes tool
|
|
294
|
+
"tags": ["Food", "Recipes"]
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
agent.serve()
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Available Built-in Skills
|
|
301
|
+
|
|
302
|
+
- **web_search**: Google Custom Search API integration with web scraping (supports multiple instances)
|
|
303
|
+
- **datetime**: Current date and time with timezone support
|
|
304
|
+
- **math**: Safe mathematical expression evaluation
|
|
305
|
+
- **datasphere**: SignalWire DataSphere knowledge search (supports multiple instances)
|
|
306
|
+
- **native_vector_search**: Offline document search with vector similarity and keyword search
|
|
307
|
+
|
|
308
|
+
#### Benefits
|
|
309
|
+
|
|
310
|
+
- **One-liner integration**: `agent.add_skill("skill_name")`
|
|
311
|
+
- **Configurable parameters**: `agent.add_skill("skill_name", {"param": "value"})`
|
|
312
|
+
- **Automatic discovery**: Skills are automatically found from the skills directory
|
|
313
|
+
- **Dependency validation**: Clear error messages for missing requirements
|
|
314
|
+
- **Modular architecture**: Skills are self-contained and reusable
|
|
315
|
+
|
|
316
|
+
For detailed documentation, see [Skills System README](docs/skills_system.md).
|
|
317
|
+
|
|
318
|
+
### DataMap Tools
|
|
319
|
+
|
|
320
|
+
The SDK provides a DataMap system for creating SWAIG tools that integrate directly with REST APIs without requiring custom webhook endpoints. DataMap tools execute on the SignalWire server, making them simpler to deploy than traditional webhook-based tools.
|
|
321
|
+
|
|
322
|
+
#### Basic DataMap Usage
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from signalwire_agents import AgentBase
|
|
326
|
+
from signalwire_agents.core.data_map import DataMap
|
|
327
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
|
328
|
+
|
|
329
|
+
class APIAgent(AgentBase):
|
|
330
|
+
def __init__(self):
|
|
331
|
+
super().__init__(name="api-agent", route="/api")
|
|
332
|
+
|
|
333
|
+
# Create a simple weather API tool
|
|
334
|
+
weather_tool = (DataMap('get_weather')
|
|
335
|
+
.description('Get current weather information')
|
|
336
|
+
.parameter('location', 'string', 'City name', required=True)
|
|
337
|
+
.webhook('GET', 'https://api.weather.com/v1/current?key=YOUR_API_KEY&q=${location}')
|
|
338
|
+
.output(SwaigFunctionResult('Weather in ${location}: ${response.current.condition.text}, ${response.current.temp_f}°F'))
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Register the tool with the agent
|
|
342
|
+
self.register_swaig_function(weather_tool.to_swaig_function())
|
|
343
|
+
|
|
344
|
+
agent = APIAgent()
|
|
345
|
+
agent.serve()
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### Advanced DataMap Examples
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
# POST API with authentication
|
|
352
|
+
search_tool = (DataMap('search_knowledge')
|
|
353
|
+
.description('Search company knowledge base')
|
|
354
|
+
.parameter('query', 'string', 'Search query', required=True)
|
|
355
|
+
.webhook('POST', 'https://api.company.com/search',
|
|
356
|
+
headers={'Authorization': 'Bearer YOUR_TOKEN'})
|
|
357
|
+
.body({'query': '${query}', 'limit': 3})
|
|
358
|
+
.output(SwaigFunctionResult('Found: ${response.title} - ${response.summary}'))
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Expression-based tools (no API calls)
|
|
362
|
+
control_tool = (DataMap('file_control')
|
|
363
|
+
.description('Control file playback')
|
|
364
|
+
.parameter('command', 'string', 'Playback command')
|
|
365
|
+
.parameter('filename', 'string', 'File to control', required=False)
|
|
366
|
+
.expression(r'start.*', SwaigFunctionResult().add_action('start_playback', {'file': '${args.filename}'}))
|
|
367
|
+
.expression(r'stop.*', SwaigFunctionResult().add_action('stop_playback', True))
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Process API response arrays
|
|
371
|
+
docs_tool = (DataMap('get_latest_docs')
|
|
372
|
+
.description('Get latest documentation')
|
|
373
|
+
.webhook('GET', 'https://api.docs.com/latest')
|
|
374
|
+
.foreach('${response.documents}')
|
|
375
|
+
.output(SwaigFunctionResult('Document: ${foreach.title} (${foreach.updated_date})'))
|
|
376
|
+
)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### Helper Functions
|
|
380
|
+
|
|
381
|
+
For simpler use cases, use the convenience functions:
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
from signalwire_agents.core.data_map import create_simple_api_tool, create_expression_tool
|
|
385
|
+
|
|
386
|
+
# Simple API tool
|
|
387
|
+
weather = create_simple_api_tool(
|
|
388
|
+
name='get_weather',
|
|
389
|
+
url='https://api.weather.com/v1/current?key=API_KEY&q=${location}',
|
|
390
|
+
response_template='Weather in ${location}: ${response.current.condition.text}',
|
|
391
|
+
parameters={'location': {'type': 'string', 'description': 'City name', 'required': True}}
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# Expression-based tool
|
|
395
|
+
file_control = create_expression_tool(
|
|
396
|
+
name='file_control',
|
|
397
|
+
patterns={
|
|
398
|
+
r'start.*': SwaigFunctionResult().add_action('start_playback', {'file': '${args.filename}'}),
|
|
399
|
+
r'stop.*': SwaigFunctionResult().add_action('stop_playback', True)
|
|
400
|
+
},
|
|
401
|
+
parameters={'command': {'type': 'string', 'description': 'Playback command'}}
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Register with agent
|
|
405
|
+
self.register_swaig_function(weather.to_swaig_function())
|
|
406
|
+
self.register_swaig_function(file_control.to_swaig_function())
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
#### Variable Expansion
|
|
410
|
+
|
|
411
|
+
DataMap tools support powerful variable expansion using `${variable}` syntax:
|
|
412
|
+
|
|
413
|
+
- **Function arguments**: `${args.parameter_name}`
|
|
414
|
+
- **API responses**: `${response.field.nested_field}`
|
|
415
|
+
- **Array processing**: `${foreach.item_field}` (when using foreach)
|
|
416
|
+
- **Global data**: `${global_data.key}`
|
|
417
|
+
- **Metadata**: `${meta_data.call_id}`
|
|
418
|
+
|
|
419
|
+
#### Benefits of DataMap Tools
|
|
420
|
+
|
|
421
|
+
- **No webhook infrastructure**: Tools run on SignalWire servers
|
|
422
|
+
- **Simplified deployment**: No need to expose endpoints
|
|
423
|
+
- **Built-in authentication**: Support for API keys, Bearer tokens, Basic auth
|
|
424
|
+
- **Response processing**: Built-in JSON path traversal and array iteration
|
|
425
|
+
- **Error handling**: Automatic error detection with `error_keys`
|
|
426
|
+
- **Pattern matching**: Expression-based responses without API calls
|
|
427
|
+
|
|
428
|
+
For detailed documentation, see [DataMap Guide](docs/datamap_guide.md).
|
|
429
|
+
|
|
430
|
+
### Contexts and Steps
|
|
431
|
+
|
|
432
|
+
The SignalWire Agents SDK provides a powerful enhancement to traditional prompts through the **Contexts and Steps** system. This feature allows you to add structured, workflow-driven AI interactions on top of your base prompt, with explicit navigation control and step-by-step guidance.
|
|
433
|
+
|
|
434
|
+
#### Why Use Contexts and Steps?
|
|
435
|
+
|
|
436
|
+
- **Structured Workflows**: Define clear, step-by-step processes for complex interactions
|
|
437
|
+
- **Navigation Control**: Explicitly control which steps or contexts users can access
|
|
438
|
+
- **Completion Criteria**: Set specific criteria for step completion and progression
|
|
439
|
+
- **Function Restrictions**: Limit which AI tools are available in each step
|
|
440
|
+
- **Workflow Isolation**: Create separate contexts for different conversation flows
|
|
441
|
+
- **Enhanced Base Prompts**: Adds structured workflows on top of your existing prompt foundation
|
|
442
|
+
|
|
443
|
+
#### Basic Usage
|
|
444
|
+
|
|
445
|
+
```python
|
|
446
|
+
from signalwire_agents import AgentBase
|
|
447
|
+
|
|
448
|
+
class WorkflowAgent(AgentBase):
|
|
449
|
+
def __init__(self):
|
|
450
|
+
super().__init__(name="Workflow Assistant", route="/workflow")
|
|
451
|
+
|
|
452
|
+
# Set base prompt (required even when using contexts)
|
|
453
|
+
self.prompt_add_section("Role", "You are a helpful workflow assistant.")
|
|
454
|
+
self.prompt_add_section("Instructions", "Guide users through structured processes step by step.")
|
|
455
|
+
|
|
456
|
+
# Define contexts and steps (adds structured workflow to base prompt)
|
|
457
|
+
contexts = self.define_contexts()
|
|
458
|
+
|
|
459
|
+
# Create a single context named "default" (required for single context)
|
|
460
|
+
context = contexts.add_context("default")
|
|
461
|
+
|
|
462
|
+
# Add step-by-step workflow
|
|
463
|
+
context.add_step("greeting") \
|
|
464
|
+
.set_text("Welcome! I'm here to help you complete your application. Let's start with your personal information.") \
|
|
465
|
+
.set_step_criteria("User has provided their name and confirmed they want to continue") \
|
|
466
|
+
.set_valid_steps(["personal_info"]) # Can only go to personal_info step
|
|
467
|
+
|
|
468
|
+
context.add_step("personal_info") \
|
|
469
|
+
.add_section("Instructions", "Collect the user's personal information") \
|
|
470
|
+
.add_bullets(["Ask for full name", "Ask for email address", "Ask for phone number"]) \
|
|
471
|
+
.set_step_criteria("All personal information has been collected and confirmed") \
|
|
472
|
+
.set_valid_steps(["review", "personal_info"]) # Can stay or move to review
|
|
473
|
+
|
|
474
|
+
context.add_step("review") \
|
|
475
|
+
.set_text("Let me review the information you've provided. Please confirm if everything is correct.") \
|
|
476
|
+
.set_step_criteria("User has confirmed or requested changes") \
|
|
477
|
+
.set_valid_steps(["personal_info", "complete"]) # Can go back or complete
|
|
478
|
+
|
|
479
|
+
context.add_step("complete") \
|
|
480
|
+
.set_text("Thank you! Your application has been submitted successfully.") \
|
|
481
|
+
.set_step_criteria("Application processing is complete")
|
|
482
|
+
# No valid_steps = end of workflow
|
|
483
|
+
|
|
484
|
+
agent = WorkflowAgent()
|
|
485
|
+
agent.serve()
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
#### Advanced Features
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
class MultiContextAgent(AgentBase):
|
|
492
|
+
def __init__(self):
|
|
493
|
+
super().__init__(name="Multi-Context Agent", route="/multi-context")
|
|
494
|
+
|
|
495
|
+
# Set base prompt (required)
|
|
496
|
+
self.prompt_add_section("Role", "You are a versatile AI assistant.")
|
|
497
|
+
self.prompt_add_section("Capabilities", "You can help with calculations and provide time information.")
|
|
498
|
+
|
|
499
|
+
# Add skills
|
|
500
|
+
self.add_skill("datetime")
|
|
501
|
+
self.add_skill("math")
|
|
502
|
+
|
|
503
|
+
# Define contexts for different service modes
|
|
504
|
+
contexts = self.define_contexts()
|
|
505
|
+
|
|
506
|
+
# Main conversation context
|
|
507
|
+
main_context = contexts.add_context("main")
|
|
508
|
+
main_context.add_step("welcome") \
|
|
509
|
+
.set_text("Welcome! I can help with calculations or provide date/time info. What would you like to do?") \
|
|
510
|
+
.set_step_criteria("User has chosen a service type") \
|
|
511
|
+
.set_valid_contexts(["calculator", "datetime_info"]) # Can switch contexts
|
|
512
|
+
|
|
513
|
+
# Calculator context with function restrictions
|
|
514
|
+
calc_context = contexts.add_context("calculator")
|
|
515
|
+
calc_context.add_step("math_mode") \
|
|
516
|
+
.add_section("Role", "You are a mathematical assistant") \
|
|
517
|
+
.add_section("Instructions", "Help users with calculations") \
|
|
518
|
+
.set_functions(["math"]) # Only math function available \
|
|
519
|
+
.set_step_criteria("Calculation is complete") \
|
|
520
|
+
.set_valid_contexts(["main"]) # Can return to main
|
|
521
|
+
|
|
522
|
+
# DateTime context
|
|
523
|
+
datetime_context = contexts.add_context("datetime_info")
|
|
524
|
+
datetime_context.add_step("time_mode") \
|
|
525
|
+
.set_text("I can provide current date and time information. What would you like to know?") \
|
|
526
|
+
.set_functions(["datetime"]) # Only datetime function available \
|
|
527
|
+
.set_step_criteria("Date/time information has been provided") \
|
|
528
|
+
.set_valid_contexts(["main"]) # Can return to main
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### Context and Step Methods
|
|
532
|
+
|
|
533
|
+
##### Context Methods
|
|
534
|
+
- `add_step(name)`: Create a new step in this context
|
|
535
|
+
- `set_valid_contexts(contexts)`: Control which contexts can be accessed from this context
|
|
536
|
+
|
|
537
|
+
##### Step Methods
|
|
538
|
+
- `set_text(text)`: Set direct text prompt for the step
|
|
539
|
+
- `add_section(title, body)`: Add POM-style section (alternative to set_text)
|
|
540
|
+
- `add_bullets(bullets)`: Add bullet points to the current or last section
|
|
541
|
+
- `set_step_criteria(criteria)`: Define completion criteria for this step
|
|
542
|
+
- `set_functions(functions)`: Restrict available functions ("none" or array of function names)
|
|
543
|
+
- `set_valid_steps(steps)`: Control navigation to other steps in same context
|
|
544
|
+
- `set_valid_contexts(contexts)`: Control navigation to other contexts
|
|
545
|
+
|
|
546
|
+
#### Navigation Rules
|
|
547
|
+
|
|
548
|
+
- **Valid Steps**: If omitted, only "next" step is implied. If specified, only those steps are allowed.
|
|
549
|
+
- **Valid Contexts**: If omitted, user is trapped in current context. If specified, can navigate to those contexts.
|
|
550
|
+
- **Single Context**: Must be named "default" for single-context workflows.
|
|
551
|
+
- **Function Restrictions**: Use `set_functions(["function_name"])` or `set_functions("none")` to control AI tool access.
|
|
552
|
+
|
|
553
|
+
#### Complete Example: Customer Support Workflow
|
|
554
|
+
|
|
555
|
+
```python
|
|
556
|
+
class SupportAgent(AgentBase):
|
|
557
|
+
def __init__(self):
|
|
558
|
+
super().__init__(name="Customer Support", route="/support")
|
|
559
|
+
|
|
560
|
+
# Set base prompt (required)
|
|
561
|
+
self.prompt_add_section("Role", "You are a professional customer support representative.")
|
|
562
|
+
self.prompt_add_section("Goal", "Provide excellent customer service using structured workflows.")
|
|
563
|
+
|
|
564
|
+
# Add skills for enhanced capabilities
|
|
565
|
+
self.add_skill("datetime")
|
|
566
|
+
self.add_skill("web_search", {"api_key": "your-key", "search_engine_id": "your-id"})
|
|
567
|
+
|
|
568
|
+
# Define support workflow contexts
|
|
569
|
+
contexts = self.define_contexts()
|
|
570
|
+
|
|
571
|
+
# Triage context
|
|
572
|
+
triage = contexts.add_context("triage")
|
|
573
|
+
triage.add_step("initial_greeting") \
|
|
574
|
+
.add_section("Current Task", "Understand the customer's issue and route them appropriately") \
|
|
575
|
+
.add_bullets("Questions to Ask", ["What problem are you experiencing?", "How urgent is this issue?", "Have you tried any troubleshooting steps?"]) \
|
|
576
|
+
.set_step_criteria("Issue type has been identified") \
|
|
577
|
+
.set_valid_contexts(["technical_support", "billing_support", "general_inquiry"])
|
|
578
|
+
|
|
579
|
+
# Technical support context
|
|
580
|
+
tech = contexts.add_context("technical_support")
|
|
581
|
+
tech.add_step("technical_diagnosis") \
|
|
582
|
+
.add_section("Current Task", "Help diagnose and resolve technical issues") \
|
|
583
|
+
.add_section("Available Tools", "Use web search to find solutions and datetime to check service windows") \
|
|
584
|
+
.set_functions(["web_search", "datetime"]) # Can search for solutions and check times \
|
|
585
|
+
.set_step_criteria("Technical issue is resolved or escalated") \
|
|
586
|
+
.set_valid_contexts(["triage"]) # Can return to triage
|
|
587
|
+
|
|
588
|
+
# Billing support context
|
|
589
|
+
billing = contexts.add_context("billing_support")
|
|
590
|
+
billing.add_step("billing_assistance") \
|
|
591
|
+
.set_text("I'll help you with your billing inquiry. Please provide your account details.") \
|
|
592
|
+
.set_functions("none") # No external tools for sensitive billing info \
|
|
593
|
+
.set_step_criteria("Billing issue is addressed") \
|
|
594
|
+
.set_valid_contexts(["triage"])
|
|
595
|
+
|
|
596
|
+
# General inquiry context
|
|
597
|
+
general = contexts.add_context("general_inquiry")
|
|
598
|
+
general.add_step("general_help") \
|
|
599
|
+
.set_text("I'm here to help with general questions. What can I assist you with?") \
|
|
600
|
+
.set_functions(["web_search", "datetime"]) # Full access to search and time \
|
|
601
|
+
.set_step_criteria("Inquiry has been answered") \
|
|
602
|
+
.set_valid_contexts(["triage"])
|
|
603
|
+
|
|
604
|
+
agent = SupportAgent()
|
|
605
|
+
agent.serve()
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
#### Benefits
|
|
609
|
+
|
|
610
|
+
- **Clear Structure**: Explicit workflow definition makes agent behavior predictable
|
|
611
|
+
- **Enhanced Control**: Fine-grained control over function access and navigation
|
|
612
|
+
- **Improved UX**: Users understand where they are in the process and what's expected
|
|
613
|
+
- **Debugging**: Easy to trace and debug workflow issues
|
|
614
|
+
- **Scalability**: Complex multi-step processes are easier to maintain
|
|
615
|
+
|
|
616
|
+
For detailed documentation and advanced examples, see [Contexts and Steps Guide](docs/contexts_guide.md).
|
|
617
|
+
|
|
618
|
+
### Quick Start
|
|
619
|
+
|
|
620
|
+
```python
|
|
621
|
+
from signalwire_agents import AgentBase
|
|
622
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
|
623
|
+
|
|
624
|
+
class SimpleAgent(AgentBase):
|
|
625
|
+
def __init__(self):
|
|
626
|
+
super().__init__(name="simple", route="/simple")
|
|
627
|
+
|
|
628
|
+
# Configure the agent's personality
|
|
629
|
+
self.prompt_add_section("Personality", body="You are a helpful assistant.")
|
|
630
|
+
self.prompt_add_section("Goal", body="Help users with basic questions.")
|
|
631
|
+
self.prompt_add_section("Instructions", bullets=["Be concise and clear."])
|
|
632
|
+
|
|
633
|
+
# Note: Use prompt_add_section() for all prompt configuration
|
|
634
|
+
|
|
635
|
+
@AgentBase.tool(
|
|
636
|
+
name="get_time",
|
|
637
|
+
description="Get the current time",
|
|
638
|
+
parameters={}
|
|
639
|
+
)
|
|
640
|
+
def get_time(self, args, raw_data):
|
|
641
|
+
from datetime import datetime
|
|
642
|
+
now = datetime.now().strftime("%H:%M:%S")
|
|
643
|
+
return SwaigFunctionResult(f"The current time is {now}")
|
|
644
|
+
|
|
645
|
+
# Run the agent
|
|
646
|
+
if __name__ == "__main__":
|
|
647
|
+
agent = SimpleAgent()
|
|
648
|
+
agent.serve(host="0.0.0.0", port=8000)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### Customizing LLM Parameters
|
|
652
|
+
|
|
653
|
+
The SDK allows you to customize LLM parameters for both the main prompt and post-prompt, giving you fine control over the AI's behavior:
|
|
654
|
+
|
|
655
|
+
```python
|
|
656
|
+
from signalwire_agents import AgentBase
|
|
657
|
+
|
|
658
|
+
class PreciseAgent(AgentBase):
|
|
659
|
+
def __init__(self):
|
|
660
|
+
super().__init__(name="precise", route="/precise")
|
|
661
|
+
|
|
662
|
+
# Configure the agent's personality
|
|
663
|
+
self.prompt_add_section("Role", "You are a precise technical assistant.")
|
|
664
|
+
self.prompt_add_section("Instructions", "Provide accurate, detailed information.")
|
|
665
|
+
|
|
666
|
+
# Set custom LLM parameters for the main prompt
|
|
667
|
+
# These parameters are passed to the server which validates them based on the model
|
|
668
|
+
self.set_prompt_llm_params(
|
|
669
|
+
temperature=0.3, # Low temperature for more consistent responses
|
|
670
|
+
top_p=0.9, # Slightly reduced for focused responses
|
|
671
|
+
barge_confidence=0.7, # Moderate interruption threshold
|
|
672
|
+
presence_penalty=0.1, # Slight penalty for repetition
|
|
673
|
+
frequency_penalty=0.2 # Encourage varied vocabulary
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
# Set post-prompt for summaries
|
|
677
|
+
self.set_post_prompt("Provide a concise summary of the key points discussed.")
|
|
678
|
+
|
|
679
|
+
# Different parameters for post-prompt (summaries should be even more focused)
|
|
680
|
+
self.set_post_prompt_llm_params(
|
|
681
|
+
temperature=0.2, # Very low for consistent summaries
|
|
682
|
+
top_p=0.85 # More focused token selection
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
agent = PreciseAgent()
|
|
686
|
+
agent.serve()
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
#### Common LLM Parameters
|
|
690
|
+
|
|
691
|
+
The SDK accepts any parameters which are passed to the server for validation based on the model. Common parameters include:
|
|
692
|
+
|
|
693
|
+
- **temperature**: Controls randomness. Lower = more focused, higher = more creative
|
|
694
|
+
- **top_p**: Nucleus sampling. Lower = more focused on likely tokens
|
|
695
|
+
- **barge_confidence**: ASR confidence to interrupt. Higher = harder to interrupt (main prompt only)
|
|
696
|
+
- **presence_penalty**: Topic diversity. Positive = new topics
|
|
697
|
+
- **frequency_penalty**: Repetition control. Positive = varied vocabulary
|
|
698
|
+
|
|
699
|
+
Note: No defaults are sent unless explicitly set. The server handles validation and applies appropriate defaults based on the model.
|
|
700
|
+
|
|
701
|
+
For more details on LLM parameter tuning, see [LLM Parameters Guide](docs/llm_parameters.md).
|
|
702
|
+
|
|
703
|
+
### Using Prefab Agents
|
|
704
|
+
|
|
705
|
+
```python
|
|
706
|
+
from signalwire_agents.prefabs import InfoGathererAgent
|
|
707
|
+
|
|
708
|
+
agent = InfoGathererAgent(
|
|
709
|
+
fields=[
|
|
710
|
+
{"name": "full_name", "prompt": "What is your full name?"},
|
|
711
|
+
{"name": "reason", "prompt": "How can I help you today?"}
|
|
712
|
+
],
|
|
713
|
+
confirmation_template="Thanks {full_name}, I'll help you with {reason}.",
|
|
714
|
+
name="info-gatherer",
|
|
715
|
+
route="/info-gatherer"
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
agent.serve(host="0.0.0.0", port=8000)
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
Available prefabs include:
|
|
722
|
+
- `InfoGathererAgent`: Collects structured information from users
|
|
723
|
+
- `FAQBotAgent`: Answers questions based on a knowledge base
|
|
724
|
+
- `ConciergeAgent`: Routes users to specialized agents
|
|
725
|
+
- `SurveyAgent`: Conducts structured surveys with questions and rating scales
|
|
726
|
+
- `ReceptionistAgent`: Greets callers and transfers them to appropriate departments
|
|
727
|
+
|
|
728
|
+
### Dynamic Agent Configuration
|
|
729
|
+
|
|
730
|
+
Configure agents dynamically based on request parameters for multi-tenant applications, A/B testing, and personalization.
|
|
731
|
+
|
|
732
|
+
#### Static vs Dynamic Configuration
|
|
733
|
+
|
|
734
|
+
- **Static**: Agent configured once at startup (traditional approach)
|
|
735
|
+
- **Dynamic**: Agent configured fresh for each request based on parameters
|
|
736
|
+
|
|
737
|
+
#### Basic Example
|
|
738
|
+
|
|
739
|
+
```python
|
|
740
|
+
from signalwire_agents import AgentBase
|
|
741
|
+
|
|
742
|
+
class DynamicAgent(AgentBase):
|
|
743
|
+
def __init__(self):
|
|
744
|
+
super().__init__(name="dynamic-agent", route="/dynamic")
|
|
745
|
+
|
|
746
|
+
# Set up dynamic configuration callback
|
|
747
|
+
self.set_dynamic_config_callback(self.configure_per_request)
|
|
748
|
+
|
|
749
|
+
def configure_per_request(self, query_params, body_params, headers, agent):
|
|
750
|
+
"""Configure agent based on request parameters"""
|
|
751
|
+
|
|
752
|
+
# Extract parameters from request
|
|
753
|
+
tier = query_params.get('tier', 'standard')
|
|
754
|
+
language = query_params.get('language', 'en')
|
|
755
|
+
customer_id = query_params.get('customer_id')
|
|
756
|
+
|
|
757
|
+
# Configure voice and language
|
|
758
|
+
if language == 'es':
|
|
759
|
+
agent.add_language("Spanish", "es-ES", "rime.spore:mistv2")
|
|
760
|
+
else:
|
|
761
|
+
agent.add_language("English", "en-US", "rime.spore:mistv2")
|
|
762
|
+
|
|
763
|
+
# Configure based on service tier
|
|
764
|
+
if tier == 'premium':
|
|
765
|
+
agent.set_params({"end_of_speech_timeout": 300}) # Faster response
|
|
766
|
+
agent.prompt_add_section("Service Level", "You provide premium support.")
|
|
767
|
+
else:
|
|
768
|
+
agent.set_params({"end_of_speech_timeout": 500}) # Standard response
|
|
769
|
+
agent.prompt_add_section("Service Level", "You provide standard support.")
|
|
770
|
+
|
|
771
|
+
# Personalize with customer data
|
|
772
|
+
global_data = {"tier": tier, "language": language}
|
|
773
|
+
if customer_id:
|
|
774
|
+
global_data["customer_id"] = customer_id
|
|
775
|
+
agent.set_global_data(global_data)
|
|
776
|
+
|
|
777
|
+
# Usage examples:
|
|
778
|
+
# curl "http://localhost:3000/dynamic?tier=premium&language=es&customer_id=123"
|
|
779
|
+
# curl "http://localhost:3000/dynamic?tier=standard&language=en"
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
#### Use Cases
|
|
783
|
+
|
|
784
|
+
- **Multi-tenant SaaS**: Different configurations per customer/organization
|
|
785
|
+
- **A/B Testing**: Test different agent behaviors with different user groups
|
|
786
|
+
- **Personalization**: Customize voice, prompts, and behavior per user
|
|
787
|
+
- **Localization**: Language and cultural adaptation based on user location
|
|
788
|
+
- **Dynamic Pricing**: Adjust features and capabilities based on subscription tiers
|
|
789
|
+
|
|
790
|
+
#### Preserving Dynamic State in SWAIG Callbacks
|
|
791
|
+
|
|
792
|
+
When using dynamic configuration to add skills or tools based on request parameters, there's a challenge: SWAIG webhook callbacks are separate HTTP requests that won't have the original query parameters. The SDK provides `add_swaig_query_params()` to solve this:
|
|
793
|
+
|
|
794
|
+
```python
|
|
795
|
+
class DynamicAgent(AgentBase):
|
|
796
|
+
def __init__(self):
|
|
797
|
+
super().__init__(name="dynamic-agent", route="/agent")
|
|
798
|
+
self.set_dynamic_config_callback(self.configure_per_request)
|
|
799
|
+
|
|
800
|
+
def configure_per_request(self, query_params, body_params, headers, agent):
|
|
801
|
+
tier = query_params.get('tier', 'basic')
|
|
802
|
+
region = query_params.get('region', 'us-east')
|
|
803
|
+
|
|
804
|
+
if tier == 'premium':
|
|
805
|
+
# Add premium skills dynamically
|
|
806
|
+
agent.add_skill('advanced_search', {
|
|
807
|
+
'api_key': 'your-api-key',
|
|
808
|
+
'num_results': 5
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
# IMPORTANT: Preserve parameters for SWAIG callbacks
|
|
812
|
+
agent.add_swaig_query_params({
|
|
813
|
+
'tier': tier,
|
|
814
|
+
'region': region
|
|
815
|
+
})
|
|
816
|
+
|
|
817
|
+
# Now when SignalWire calls the SWAIG webhook, these params
|
|
818
|
+
# will be included, triggering the same dynamic configuration
|
|
819
|
+
|
|
820
|
+
# Initial request: GET /agent?tier=premium®ion=eu-west
|
|
821
|
+
# SWAIG callback: POST /swaig/?tier=premium®ion=eu-west
|
|
822
|
+
# Result: Premium skills are available in both requests!
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
**Key Points:**
|
|
826
|
+
|
|
827
|
+
- **Problem**: Dynamically added skills/tools won't exist during SWAIG callbacks without the original request parameters
|
|
828
|
+
- **Solution**: Use `add_swaig_query_params()` to include critical parameters in all SWAIG webhook URLs
|
|
829
|
+
- **Clear State**: Use `clear_swaig_query_params()` if needed to reset parameters between requests
|
|
830
|
+
- **Token Safety**: The SDK automatically renames security tokens from `token` to `__token` to avoid parameter collisions
|
|
831
|
+
|
|
832
|
+
This ensures that any dynamic configuration based on request parameters is consistently applied across the initial SWML request and all subsequent SWAIG function callbacks.
|
|
833
|
+
|
|
834
|
+
For detailed documentation and advanced examples, see the [Agent Guide](docs/agent_guide.md#dynamic-agent-configuration).
|
|
835
|
+
|
|
836
|
+
### Configuration
|
|
837
|
+
|
|
838
|
+
#### Environment Variables
|
|
839
|
+
|
|
840
|
+
The SDK supports the following environment variables:
|
|
841
|
+
|
|
842
|
+
- `SWML_BASIC_AUTH_USER`: Username for basic auth (default: auto-generated)
|
|
843
|
+
- `SWML_BASIC_AUTH_PASSWORD`: Password for basic auth (default: auto-generated)
|
|
844
|
+
- `SWML_PROXY_URL_BASE`: Base URL to use when behind a reverse proxy, used for constructing webhook URLs
|
|
845
|
+
- `SWML_SSL_ENABLED`: Enable HTTPS/SSL support (values: "true", "1", "yes")
|
|
846
|
+
- `SWML_SSL_CERT_PATH`: Path to SSL certificate file
|
|
847
|
+
- `SWML_SSL_KEY_PATH`: Path to SSL private key file
|
|
848
|
+
- `SWML_DOMAIN`: Domain name for SSL certificate and external URLs
|
|
849
|
+
- `SWML_SCHEMA_PATH`: Optional path to override the location of the schema.json file
|
|
850
|
+
|
|
851
|
+
When the auth environment variables are set, they will be used for all agents instead of generating random credentials. The proxy URL base is useful when your service is behind a reverse proxy or when you need external services to access your webhooks.
|
|
852
|
+
|
|
853
|
+
To enable HTTPS directly (without a reverse proxy), set `SWML_SSL_ENABLED` to "true", provide valid paths to your certificate and key files, and specify your domain name.
|
|
854
|
+
|
|
855
|
+
### Testing
|
|
856
|
+
|
|
857
|
+
The SDK includes powerful CLI tools for development and testing:
|
|
858
|
+
|
|
859
|
+
- **`swaig-test`**: Comprehensive local testing and serverless environment simulation
|
|
860
|
+
- **`sw-search`**: Build local search indexes from document directories and search within them
|
|
861
|
+
|
|
862
|
+
#### Local Testing with swaig-test
|
|
863
|
+
|
|
864
|
+
Test your agents locally without deployment:
|
|
865
|
+
|
|
866
|
+
```bash
|
|
867
|
+
# Install the SDK
|
|
868
|
+
pip install -e .
|
|
869
|
+
|
|
870
|
+
# Discover agents in a file
|
|
871
|
+
swaig-test examples/my_agent.py
|
|
872
|
+
|
|
873
|
+
# List available functions
|
|
874
|
+
swaig-test examples/my_agent.py --list-tools
|
|
875
|
+
|
|
876
|
+
# Test SWAIG functions with CLI syntax
|
|
877
|
+
swaig-test examples/my_agent.py --exec get_weather --location "New York"
|
|
878
|
+
|
|
879
|
+
# Multi-agent support
|
|
880
|
+
swaig-test examples/multi_agent.py --route /agent-path --list-tools
|
|
881
|
+
swaig-test examples/multi_agent.py --agent-class AgentName --exec function_name
|
|
882
|
+
|
|
883
|
+
# Generate and inspect SWML documents
|
|
884
|
+
swaig-test examples/my_agent.py --dump-swml
|
|
885
|
+
swaig-test examples/my_agent.py --dump-swml --raw | jq '.'
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
#### Serverless Environment Simulation
|
|
889
|
+
|
|
890
|
+
Test your agents in simulated serverless environments without deployment:
|
|
891
|
+
|
|
892
|
+
```bash
|
|
893
|
+
# Test in AWS Lambda environment
|
|
894
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda --dump-swml
|
|
895
|
+
|
|
896
|
+
# Test Lambda function execution with proper response format
|
|
897
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda \
|
|
898
|
+
--exec get_weather --location "Miami" --full-request
|
|
899
|
+
|
|
900
|
+
# Test with custom Lambda configuration
|
|
901
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda \
|
|
902
|
+
--aws-function-name my-production-function \
|
|
903
|
+
--aws-region us-west-2 \
|
|
904
|
+
--exec my_function --param value
|
|
905
|
+
|
|
906
|
+
# Test CGI environment
|
|
907
|
+
swaig-test examples/my_agent.py --simulate-serverless cgi \
|
|
908
|
+
--cgi-host my-server.com --cgi-https --dump-swml
|
|
909
|
+
|
|
910
|
+
# Test Google Cloud Functions
|
|
911
|
+
swaig-test examples/my_agent.py --simulate-serverless cloud_function \
|
|
912
|
+
--gcp-function-url https://my-function.cloudfunctions.net \
|
|
913
|
+
--exec my_function
|
|
914
|
+
|
|
915
|
+
# Test Azure Functions
|
|
916
|
+
swaig-test examples/my_agent.py --simulate-serverless azure_function \
|
|
917
|
+
--azure-function-url https://my-function.azurewebsites.net \
|
|
918
|
+
--exec my_function
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
#### Environment Management
|
|
922
|
+
|
|
923
|
+
Use environment files for consistent testing across platforms:
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
# Create environment file
|
|
927
|
+
cat > production.env << EOF
|
|
928
|
+
AWS_LAMBDA_FUNCTION_NAME=prod-my-agent
|
|
929
|
+
AWS_REGION=us-east-1
|
|
930
|
+
API_KEY=prod_api_key_123
|
|
931
|
+
DEBUG=false
|
|
932
|
+
EOF
|
|
933
|
+
|
|
934
|
+
# Test with environment file
|
|
935
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda \
|
|
936
|
+
--env-file production.env --exec my_function
|
|
937
|
+
|
|
938
|
+
# Override specific variables
|
|
939
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda \
|
|
940
|
+
--env-file production.env --env DEBUG=true --dump-swml
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
#### Cross-Platform Testing
|
|
944
|
+
|
|
945
|
+
Test the same agent across multiple serverless platforms:
|
|
946
|
+
|
|
947
|
+
```bash
|
|
948
|
+
# Test across all platforms
|
|
949
|
+
for platform in lambda cgi cloud_function azure_function; do
|
|
950
|
+
echo "Testing $platform..."
|
|
951
|
+
swaig-test examples/my_agent.py --simulate-serverless $platform \
|
|
952
|
+
--exec my_function --param value
|
|
953
|
+
done
|
|
954
|
+
|
|
955
|
+
# Compare webhook URLs across platforms
|
|
956
|
+
swaig-test examples/my_agent.py --simulate-serverless lambda --dump-swml | grep web_hook_url
|
|
957
|
+
swaig-test examples/my_agent.py --simulate-serverless cgi --cgi-host example.com --dump-swml | grep web_hook_url
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
#### Key Benefits
|
|
961
|
+
|
|
962
|
+
- **No Deployment Required**: Test serverless behavior locally
|
|
963
|
+
- **Environment Simulation**: Complete platform-specific environment variable setup
|
|
964
|
+
- **URL Generation**: Verify webhook URLs are generated correctly for each platform
|
|
965
|
+
- **Function Execution**: Test with platform-specific request/response formats
|
|
966
|
+
- **Environment Files**: Reusable configurations for different stages
|
|
967
|
+
- **Multi-Platform**: Test Lambda, CGI, Cloud Functions, and Azure Functions
|
|
968
|
+
|
|
969
|
+
For detailed testing documentation, see the [CLI Testing Guide](docs/cli_testing_guide.md).
|
|
970
|
+
|
|
971
|
+
### Documentation
|
|
972
|
+
|
|
973
|
+
The package includes comprehensive documentation in the `docs/` directory:
|
|
974
|
+
|
|
975
|
+
- [Agent Guide](docs/agent_guide.md) - Detailed guide to creating and customizing agents, including dynamic configuration
|
|
976
|
+
- [Architecture](docs/architecture.md) - Overview of the SDK architecture and core concepts
|
|
977
|
+
- [SWML Service Guide](docs/swml_service_guide.md) - Guide to the underlying SWML service
|
|
978
|
+
- [Local Search System](docs/search-system.md) - Complete guide to the local search system with vector similarity and keyword search
|
|
979
|
+
- [Skills System](docs/skills_system.md) - Detailed documentation on the modular skills system
|
|
980
|
+
- [CLI Tools](docs/cli.md) - Command-line interface tools for development and testing
|
|
981
|
+
|
|
982
|
+
These documents provide in-depth explanations of the features, APIs, and usage patterns.
|
|
983
|
+
|
|
984
|
+
</details
|
|
985
|
+
|
|
986
|
+
### ***[Read the official docs.](https://developer.signalwire.com/sdks/agents-sdk)***
|
|
987
|
+
|
|
988
|
+
---
|
|
989
|
+
|
|
990
|
+
## License
|
|
991
|
+
|
|
992
|
+
MIT
|