signalwire-agents 0.1.6__py3-none-any.whl → 1.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. signalwire_agents/__init__.py +130 -4
  2. signalwire_agents/agent_server.py +438 -32
  3. signalwire_agents/agents/bedrock.py +296 -0
  4. signalwire_agents/cli/__init__.py +18 -0
  5. signalwire_agents/cli/build_search.py +1367 -0
  6. signalwire_agents/cli/config.py +80 -0
  7. signalwire_agents/cli/core/__init__.py +10 -0
  8. signalwire_agents/cli/core/agent_loader.py +470 -0
  9. signalwire_agents/cli/core/argparse_helpers.py +179 -0
  10. signalwire_agents/cli/core/dynamic_config.py +71 -0
  11. signalwire_agents/cli/core/service_loader.py +303 -0
  12. signalwire_agents/cli/execution/__init__.py +10 -0
  13. signalwire_agents/cli/execution/datamap_exec.py +446 -0
  14. signalwire_agents/cli/execution/webhook_exec.py +134 -0
  15. signalwire_agents/cli/init_project.py +1225 -0
  16. signalwire_agents/cli/output/__init__.py +10 -0
  17. signalwire_agents/cli/output/output_formatter.py +255 -0
  18. signalwire_agents/cli/output/swml_dump.py +186 -0
  19. signalwire_agents/cli/simulation/__init__.py +10 -0
  20. signalwire_agents/cli/simulation/data_generation.py +374 -0
  21. signalwire_agents/cli/simulation/data_overrides.py +200 -0
  22. signalwire_agents/cli/simulation/mock_env.py +282 -0
  23. signalwire_agents/cli/swaig_test_wrapper.py +52 -0
  24. signalwire_agents/cli/test_swaig.py +809 -0
  25. signalwire_agents/cli/types.py +81 -0
  26. signalwire_agents/core/__init__.py +2 -2
  27. signalwire_agents/core/agent/__init__.py +12 -0
  28. signalwire_agents/core/agent/config/__init__.py +12 -0
  29. signalwire_agents/core/agent/deployment/__init__.py +9 -0
  30. signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
  31. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  32. signalwire_agents/core/agent/prompt/manager.py +306 -0
  33. signalwire_agents/core/agent/routing/__init__.py +9 -0
  34. signalwire_agents/core/agent/security/__init__.py +9 -0
  35. signalwire_agents/core/agent/swml/__init__.py +9 -0
  36. signalwire_agents/core/agent/tools/__init__.py +15 -0
  37. signalwire_agents/core/agent/tools/decorator.py +97 -0
  38. signalwire_agents/core/agent/tools/registry.py +210 -0
  39. signalwire_agents/core/agent_base.py +959 -2166
  40. signalwire_agents/core/auth_handler.py +233 -0
  41. signalwire_agents/core/config_loader.py +259 -0
  42. signalwire_agents/core/contexts.py +707 -0
  43. signalwire_agents/core/data_map.py +487 -0
  44. signalwire_agents/core/function_result.py +1150 -1
  45. signalwire_agents/core/logging_config.py +376 -0
  46. signalwire_agents/core/mixins/__init__.py +28 -0
  47. signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
  48. signalwire_agents/core/mixins/auth_mixin.py +287 -0
  49. signalwire_agents/core/mixins/prompt_mixin.py +358 -0
  50. signalwire_agents/core/mixins/serverless_mixin.py +368 -0
  51. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  52. signalwire_agents/core/mixins/state_mixin.py +153 -0
  53. signalwire_agents/core/mixins/tool_mixin.py +230 -0
  54. signalwire_agents/core/mixins/web_mixin.py +1134 -0
  55. signalwire_agents/core/security/session_manager.py +174 -86
  56. signalwire_agents/core/security_config.py +333 -0
  57. signalwire_agents/core/skill_base.py +200 -0
  58. signalwire_agents/core/skill_manager.py +244 -0
  59. signalwire_agents/core/swaig_function.py +33 -9
  60. signalwire_agents/core/swml_builder.py +212 -12
  61. signalwire_agents/core/swml_handler.py +43 -13
  62. signalwire_agents/core/swml_renderer.py +123 -297
  63. signalwire_agents/core/swml_service.py +277 -260
  64. signalwire_agents/prefabs/concierge.py +6 -2
  65. signalwire_agents/prefabs/info_gatherer.py +149 -33
  66. signalwire_agents/prefabs/receptionist.py +14 -22
  67. signalwire_agents/prefabs/survey.py +6 -2
  68. signalwire_agents/schema.json +9218 -5489
  69. signalwire_agents/search/__init__.py +137 -0
  70. signalwire_agents/search/document_processor.py +1223 -0
  71. signalwire_agents/search/index_builder.py +804 -0
  72. signalwire_agents/search/migration.py +418 -0
  73. signalwire_agents/search/models.py +30 -0
  74. signalwire_agents/search/pgvector_backend.py +752 -0
  75. signalwire_agents/search/query_processor.py +502 -0
  76. signalwire_agents/search/search_engine.py +1264 -0
  77. signalwire_agents/search/search_service.py +574 -0
  78. signalwire_agents/skills/README.md +452 -0
  79. signalwire_agents/skills/__init__.py +23 -0
  80. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  81. signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
  82. signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
  83. signalwire_agents/skills/datasphere/README.md +210 -0
  84. signalwire_agents/skills/datasphere/__init__.py +12 -0
  85. signalwire_agents/skills/datasphere/skill.py +310 -0
  86. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  87. signalwire_agents/skills/datasphere_serverless/__init__.py +10 -0
  88. signalwire_agents/skills/datasphere_serverless/skill.py +237 -0
  89. signalwire_agents/skills/datetime/README.md +132 -0
  90. signalwire_agents/skills/datetime/__init__.py +10 -0
  91. signalwire_agents/skills/datetime/skill.py +126 -0
  92. signalwire_agents/skills/joke/README.md +149 -0
  93. signalwire_agents/skills/joke/__init__.py +10 -0
  94. signalwire_agents/skills/joke/skill.py +109 -0
  95. signalwire_agents/skills/math/README.md +161 -0
  96. signalwire_agents/skills/math/__init__.py +10 -0
  97. signalwire_agents/skills/math/skill.py +105 -0
  98. signalwire_agents/skills/mcp_gateway/README.md +230 -0
  99. signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
  100. signalwire_agents/skills/mcp_gateway/skill.py +421 -0
  101. signalwire_agents/skills/native_vector_search/README.md +210 -0
  102. signalwire_agents/skills/native_vector_search/__init__.py +10 -0
  103. signalwire_agents/skills/native_vector_search/skill.py +820 -0
  104. signalwire_agents/skills/play_background_file/README.md +218 -0
  105. signalwire_agents/skills/play_background_file/__init__.py +12 -0
  106. signalwire_agents/skills/play_background_file/skill.py +242 -0
  107. signalwire_agents/skills/registry.py +459 -0
  108. signalwire_agents/skills/spider/README.md +236 -0
  109. signalwire_agents/skills/spider/__init__.py +13 -0
  110. signalwire_agents/skills/spider/skill.py +598 -0
  111. signalwire_agents/skills/swml_transfer/README.md +395 -0
  112. signalwire_agents/skills/swml_transfer/__init__.py +10 -0
  113. signalwire_agents/skills/swml_transfer/skill.py +359 -0
  114. signalwire_agents/skills/weather_api/README.md +178 -0
  115. signalwire_agents/skills/weather_api/__init__.py +12 -0
  116. signalwire_agents/skills/weather_api/skill.py +191 -0
  117. signalwire_agents/skills/web_search/README.md +163 -0
  118. signalwire_agents/skills/web_search/__init__.py +10 -0
  119. signalwire_agents/skills/web_search/skill.py +739 -0
  120. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  121. signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
  122. signalwire_agents/skills/wikipedia_search/skill.py +210 -0
  123. signalwire_agents/utils/__init__.py +14 -0
  124. signalwire_agents/utils/schema_utils.py +111 -44
  125. signalwire_agents/web/__init__.py +17 -0
  126. signalwire_agents/web/web_service.py +559 -0
  127. signalwire_agents-1.0.7.data/data/share/man/man1/sw-agent-init.1 +307 -0
  128. signalwire_agents-1.0.7.data/data/share/man/man1/sw-search.1 +483 -0
  129. signalwire_agents-1.0.7.data/data/share/man/man1/swaig-test.1 +308 -0
  130. signalwire_agents-1.0.7.dist-info/METADATA +992 -0
  131. signalwire_agents-1.0.7.dist-info/RECORD +142 -0
  132. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/WHEEL +1 -1
  133. signalwire_agents-1.0.7.dist-info/entry_points.txt +4 -0
  134. signalwire_agents/core/state/file_state_manager.py +0 -219
  135. signalwire_agents/core/state/state_manager.py +0 -101
  136. signalwire_agents-0.1.6.data/data/schema.json +0 -5611
  137. signalwire_agents-0.1.6.dist-info/METADATA +0 -199
  138. signalwire_agents-0.1.6.dist-info/RECORD +0 -34
  139. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/licenses/LICENSE +0 -0
  140. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,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> &nbsp; &nbsp; <code>#</code> &nbsp; &nbsp;
107
+ <a href="https://github.com/signalwire/signalwire-docs/issues/new/choose" target="_blank">🐛 Report an issue</a> &nbsp; &nbsp; <code>#</code> &nbsp; &nbsp;
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&region=eu-west
821
+ # SWAIG callback: POST /swaig/?tier=premium&region=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