signalwire-agents 0.1.0__py3-none-any.whl → 0.1.2__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 +10 -1
- signalwire_agents/agent_server.py +73 -44
- signalwire_agents/core/__init__.py +9 -0
- signalwire_agents/core/agent_base.py +125 -33
- signalwire_agents/core/function_result.py +31 -12
- signalwire_agents/core/pom_builder.py +9 -0
- signalwire_agents/core/security/__init__.py +9 -0
- signalwire_agents/core/security/session_manager.py +9 -0
- signalwire_agents/core/state/__init__.py +9 -0
- signalwire_agents/core/state/file_state_manager.py +9 -0
- signalwire_agents/core/state/state_manager.py +9 -0
- signalwire_agents/core/swaig_function.py +9 -0
- signalwire_agents/core/swml_builder.py +9 -0
- signalwire_agents/core/swml_handler.py +9 -0
- signalwire_agents/core/swml_renderer.py +9 -0
- signalwire_agents/core/swml_service.py +88 -40
- signalwire_agents/prefabs/__init__.py +12 -1
- signalwire_agents/prefabs/concierge.py +9 -18
- signalwire_agents/prefabs/faq_bot.py +9 -18
- signalwire_agents/prefabs/info_gatherer.py +193 -183
- signalwire_agents/prefabs/receptionist.py +294 -0
- signalwire_agents/prefabs/survey.py +9 -18
- signalwire_agents/utils/__init__.py +9 -0
- signalwire_agents/utils/pom_utils.py +9 -0
- signalwire_agents/utils/schema_utils.py +9 -0
- signalwire_agents/utils/token_generators.py +9 -0
- signalwire_agents/utils/validators.py +9 -0
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.2.dist-info}/METADATA +75 -30
- signalwire_agents-0.1.2.dist-info/RECORD +34 -0
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.2.dist-info}/WHEEL +1 -1
- signalwire_agents-0.1.2.dist-info/licenses/LICENSE +21 -0
- signalwire_agents-0.1.0.dist-info/RECORD +0 -32
- {signalwire_agents-0.1.0.data → signalwire_agents-0.1.2.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,294 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""
|
11
|
+
ReceptionistAgent - Prefab agent for greeting callers and transferring them to appropriate departments
|
12
|
+
"""
|
13
|
+
|
14
|
+
from typing import List, Dict, Any, Optional, Union
|
15
|
+
import json
|
16
|
+
|
17
|
+
from signalwire_agents.core.agent_base import AgentBase
|
18
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
19
|
+
|
20
|
+
|
21
|
+
class ReceptionistAgent(AgentBase):
|
22
|
+
"""
|
23
|
+
A prefab agent designed to act as a receptionist that:
|
24
|
+
1. Greets callers
|
25
|
+
2. Collects basic information about their needs
|
26
|
+
3. Transfers them to the appropriate department
|
27
|
+
|
28
|
+
Example:
|
29
|
+
agent = ReceptionistAgent(
|
30
|
+
departments=[
|
31
|
+
{"name": "sales", "description": "For product inquiries, pricing, and purchasing", "number": "+15551235555"},
|
32
|
+
{"name": "support", "description": "For technical help and troubleshooting", "number": "+15551236666"}
|
33
|
+
]
|
34
|
+
)
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
departments: List[Dict[str, str]],
|
40
|
+
name: str = "receptionist",
|
41
|
+
route: str = "/receptionist",
|
42
|
+
greeting: str = "Thank you for calling. How can I help you today?",
|
43
|
+
voice: str = "elevenlabs.josh",
|
44
|
+
**kwargs
|
45
|
+
):
|
46
|
+
"""
|
47
|
+
Initialize a receptionist agent
|
48
|
+
|
49
|
+
Args:
|
50
|
+
departments: List of departments to transfer to, each with:
|
51
|
+
- name: Department identifier (e.g., "sales")
|
52
|
+
- description: Description of department's purpose
|
53
|
+
- number: Phone number for transfer
|
54
|
+
name: Agent name for the route
|
55
|
+
route: HTTP route for this agent
|
56
|
+
greeting: Initial greeting message
|
57
|
+
voice: Voice ID to use
|
58
|
+
**kwargs: Additional arguments for AgentBase
|
59
|
+
"""
|
60
|
+
# Initialize the base agent
|
61
|
+
super().__init__(
|
62
|
+
name=name,
|
63
|
+
route=route,
|
64
|
+
use_pom=True,
|
65
|
+
**kwargs
|
66
|
+
)
|
67
|
+
|
68
|
+
# Validate departments format
|
69
|
+
self._validate_departments(departments)
|
70
|
+
|
71
|
+
# Store greeting
|
72
|
+
self._greeting = greeting
|
73
|
+
|
74
|
+
# Set up global data with departments and initial state
|
75
|
+
self.set_global_data({
|
76
|
+
"departments": departments,
|
77
|
+
"caller_info": {}
|
78
|
+
})
|
79
|
+
|
80
|
+
# Build a prompt
|
81
|
+
self._build_prompt()
|
82
|
+
|
83
|
+
# Configure agent settings
|
84
|
+
self._configure_agent_settings(voice)
|
85
|
+
|
86
|
+
# Register tools
|
87
|
+
self._register_tools()
|
88
|
+
|
89
|
+
def _validate_departments(self, departments):
|
90
|
+
"""Validate that departments are in the correct format"""
|
91
|
+
if not departments:
|
92
|
+
raise ValueError("At least one department is required")
|
93
|
+
|
94
|
+
for i, dept in enumerate(departments):
|
95
|
+
if "name" not in dept:
|
96
|
+
raise ValueError(f"Department {i+1} is missing 'name' field")
|
97
|
+
if "description" not in dept:
|
98
|
+
raise ValueError(f"Department {i+1} is missing 'description' field")
|
99
|
+
if "number" not in dept:
|
100
|
+
raise ValueError(f"Department {i+1} is missing 'number' field")
|
101
|
+
|
102
|
+
def _build_prompt(self):
|
103
|
+
"""Build the agent's prompt with personality, goals, and instructions"""
|
104
|
+
|
105
|
+
# Set personality
|
106
|
+
self.prompt_add_section(
|
107
|
+
"Personality",
|
108
|
+
body="You are a friendly and professional receptionist. You speak clearly and efficiently while maintaining a warm, helpful tone."
|
109
|
+
)
|
110
|
+
|
111
|
+
# Set goal
|
112
|
+
self.prompt_add_section(
|
113
|
+
"Goal",
|
114
|
+
body="Your goal is to greet callers, collect their basic information, and transfer them to the appropriate department."
|
115
|
+
)
|
116
|
+
|
117
|
+
# Set instructions
|
118
|
+
self.prompt_add_section(
|
119
|
+
"Instructions",
|
120
|
+
bullets=[
|
121
|
+
f"Begin by greeting the caller with: '{self._greeting}'",
|
122
|
+
"Collect their name and a brief description of their needs.",
|
123
|
+
"Based on their needs, determine which department would be most appropriate.",
|
124
|
+
"Use the collect_caller_info function when you have their name and reason for calling.",
|
125
|
+
"Use the transfer_call function to transfer them to the appropriate department.",
|
126
|
+
"Before transferring, always confirm with the caller that they're being transferred to the right department.",
|
127
|
+
"If a caller's request doesn't clearly match a department, ask follow-up questions to clarify."
|
128
|
+
]
|
129
|
+
)
|
130
|
+
|
131
|
+
# Add context with department information
|
132
|
+
global_data = self._global_data
|
133
|
+
departments = global_data.get("departments", [])
|
134
|
+
|
135
|
+
department_bullets = []
|
136
|
+
for dept in departments:
|
137
|
+
department_bullets.append(f"{dept['name']}: {dept['description']}")
|
138
|
+
|
139
|
+
self.prompt_add_section(
|
140
|
+
"Available Departments",
|
141
|
+
bullets=department_bullets
|
142
|
+
)
|
143
|
+
|
144
|
+
# Add a post-prompt for summary generation
|
145
|
+
self.set_post_prompt("""
|
146
|
+
Return a JSON summary of the conversation:
|
147
|
+
{
|
148
|
+
"caller_name": "CALLER'S NAME",
|
149
|
+
"reason": "REASON FOR CALLING",
|
150
|
+
"department": "DEPARTMENT TRANSFERRED TO",
|
151
|
+
"satisfaction": "high/medium/low (estimated caller satisfaction)"
|
152
|
+
}
|
153
|
+
""")
|
154
|
+
|
155
|
+
def _configure_agent_settings(self, voice):
|
156
|
+
"""Configure additional agent settings"""
|
157
|
+
|
158
|
+
# Set AI behavior parameters
|
159
|
+
self.set_params({
|
160
|
+
"end_of_speech_timeout": 700,
|
161
|
+
"speech_event_timeout": 1000,
|
162
|
+
"transfer_summary": True # Enable call summary transfer between agents
|
163
|
+
})
|
164
|
+
|
165
|
+
# Set language with specified voice
|
166
|
+
self.add_language(
|
167
|
+
name="English",
|
168
|
+
code="en-US",
|
169
|
+
voice=voice,
|
170
|
+
speech_fillers=["Let me get that information for you...", "One moment please..."],
|
171
|
+
function_fillers=["I'm processing that...", "Let me check which department can help you best..."]
|
172
|
+
)
|
173
|
+
|
174
|
+
def _register_tools(self):
|
175
|
+
"""Register the tools this agent needs"""
|
176
|
+
|
177
|
+
# Define collect_caller_info tool
|
178
|
+
self.define_tool(
|
179
|
+
name="collect_caller_info",
|
180
|
+
description="Collect the caller's information for routing",
|
181
|
+
parameters={
|
182
|
+
"name": {
|
183
|
+
"type": "string",
|
184
|
+
"description": "The caller's name"
|
185
|
+
},
|
186
|
+
"reason": {
|
187
|
+
"type": "string",
|
188
|
+
"description": "The reason for the call"
|
189
|
+
}
|
190
|
+
},
|
191
|
+
handler=self._collect_caller_info_handler
|
192
|
+
)
|
193
|
+
|
194
|
+
# Define transfer_call tool
|
195
|
+
# First, get the department names from global data
|
196
|
+
global_data = self._global_data
|
197
|
+
departments = global_data.get("departments", [])
|
198
|
+
department_names = [dept["name"] for dept in departments]
|
199
|
+
|
200
|
+
self.define_tool(
|
201
|
+
name="transfer_call",
|
202
|
+
description="Transfer the caller to the appropriate department",
|
203
|
+
parameters={
|
204
|
+
"department": {
|
205
|
+
"type": "string",
|
206
|
+
"description": "The department to transfer to",
|
207
|
+
"enum": department_names
|
208
|
+
}
|
209
|
+
},
|
210
|
+
handler=self._transfer_call_handler
|
211
|
+
)
|
212
|
+
|
213
|
+
def _collect_caller_info_handler(self, args, raw_data):
|
214
|
+
"""Handler for collect_caller_info tool"""
|
215
|
+
|
216
|
+
# Get the caller info
|
217
|
+
name = args.get("name", "")
|
218
|
+
reason = args.get("reason", "")
|
219
|
+
|
220
|
+
# Create response with global data update
|
221
|
+
result = SwaigFunctionResult(
|
222
|
+
f"Thank you, {name}. I've noted that you're calling about {reason}."
|
223
|
+
)
|
224
|
+
|
225
|
+
# Update global data with caller info
|
226
|
+
result.add_actions([
|
227
|
+
{"set_global_data": {
|
228
|
+
"caller_info": {
|
229
|
+
"name": name,
|
230
|
+
"reason": reason
|
231
|
+
}
|
232
|
+
}}
|
233
|
+
])
|
234
|
+
|
235
|
+
return result
|
236
|
+
|
237
|
+
def _transfer_call_handler(self, args, raw_data):
|
238
|
+
"""Handler for transfer_call tool"""
|
239
|
+
|
240
|
+
# Get the department
|
241
|
+
department_name = args.get("department", "")
|
242
|
+
|
243
|
+
# Get global data
|
244
|
+
global_data = raw_data.get("global_data", {})
|
245
|
+
caller_info = global_data.get("caller_info", {})
|
246
|
+
name = caller_info.get("name", "the caller")
|
247
|
+
departments = global_data.get("departments", [])
|
248
|
+
|
249
|
+
# Find the department in the list
|
250
|
+
department = None
|
251
|
+
for dept in departments:
|
252
|
+
if dept["name"] == department_name:
|
253
|
+
department = dept
|
254
|
+
break
|
255
|
+
|
256
|
+
# If department not found, return error
|
257
|
+
if not department:
|
258
|
+
return SwaigFunctionResult(f"Sorry, I couldn't find the {department_name} department.")
|
259
|
+
|
260
|
+
# Get transfer number
|
261
|
+
transfer_number = department.get("number", "")
|
262
|
+
|
263
|
+
# Create message for caller
|
264
|
+
message = f"I'll transfer you to our {department_name} department now. Thank you for calling, {name}!"
|
265
|
+
|
266
|
+
# Create result with transfer SWML
|
267
|
+
result = SwaigFunctionResult(message)
|
268
|
+
|
269
|
+
# Add the SWML to execute the transfer
|
270
|
+
result.add_swml([
|
271
|
+
{
|
272
|
+
"play": {
|
273
|
+
"url": f"say:{message}"
|
274
|
+
}
|
275
|
+
},
|
276
|
+
{
|
277
|
+
"connect": {
|
278
|
+
"to": transfer_number
|
279
|
+
}
|
280
|
+
}
|
281
|
+
])
|
282
|
+
|
283
|
+
return result
|
284
|
+
|
285
|
+
def on_summary(self, summary, raw_data=None):
|
286
|
+
"""
|
287
|
+
Process the conversation summary
|
288
|
+
|
289
|
+
Args:
|
290
|
+
summary: Summary data from the conversation
|
291
|
+
raw_data: The complete raw POST data from the request
|
292
|
+
"""
|
293
|
+
# Subclasses can override this to handle the summary
|
294
|
+
pass
|
@@ -1,3 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
1
10
|
"""
|
2
11
|
SurveyAgent - Prefab agent for conducting automated surveys
|
3
12
|
"""
|
@@ -51,7 +60,6 @@ class SurveyAgent(AgentBase):
|
|
51
60
|
conclusion: Optional[str] = None,
|
52
61
|
brand_name: Optional[str] = None,
|
53
62
|
max_retries: int = 2,
|
54
|
-
schema_path: Optional[str] = None,
|
55
63
|
**kwargs
|
56
64
|
):
|
57
65
|
"""
|
@@ -70,30 +78,13 @@ class SurveyAgent(AgentBase):
|
|
70
78
|
conclusion: Optional custom conclusion message
|
71
79
|
brand_name: Optional brand or company name
|
72
80
|
max_retries: Maximum number of times to retry invalid answers
|
73
|
-
schema_path: Optional path to a custom schema
|
74
81
|
**kwargs: Additional arguments for AgentBase
|
75
82
|
"""
|
76
|
-
# Find schema.json if not provided
|
77
|
-
if not schema_path:
|
78
|
-
current_dir = os.path.dirname(os.path.abspath(__file__))
|
79
|
-
parent_dir = os.path.dirname(os.path.dirname(current_dir))
|
80
|
-
|
81
|
-
schema_locations = [
|
82
|
-
os.path.join(current_dir, "schema.json"),
|
83
|
-
os.path.join(parent_dir, "schema.json")
|
84
|
-
]
|
85
|
-
|
86
|
-
for loc in schema_locations:
|
87
|
-
if os.path.exists(loc):
|
88
|
-
schema_path = loc
|
89
|
-
break
|
90
|
-
|
91
83
|
# Initialize the base agent
|
92
84
|
super().__init__(
|
93
85
|
name="survey",
|
94
86
|
route="/survey",
|
95
87
|
use_pom=True,
|
96
|
-
schema_path=schema_path,
|
97
88
|
**kwargs
|
98
89
|
)
|
99
90
|
|
@@ -1,3 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
1
10
|
"""
|
2
11
|
Utilities for working with the SWML JSON schema.
|
3
12
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: signalwire_agents
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: SignalWire AI Agents SDK
|
5
5
|
Author-email: SignalWire Team <info@signalwire.com>
|
6
6
|
Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
16
16
|
Requires-Python: >=3.7
|
17
17
|
Description-Content-Type: text/markdown
|
18
|
+
License-File: LICENSE
|
18
19
|
Requires-Dist: fastapi==0.115.12
|
19
20
|
Requires-Dist: pydantic==2.11.4
|
20
21
|
Requires-Dist: PyYAML==6.0.2
|
@@ -23,6 +24,7 @@ Requires-Dist: setuptools==66.1.1
|
|
23
24
|
Requires-Dist: signalwire_pom==2.7.1
|
24
25
|
Requires-Dist: structlog==25.3.0
|
25
26
|
Requires-Dist: uvicorn==0.34.2
|
27
|
+
Dynamic: license-file
|
26
28
|
|
27
29
|
# SignalWire AI Agent SDK
|
28
30
|
|
@@ -33,8 +35,9 @@ A Python SDK for creating, hosting, and securing SignalWire AI agents as microse
|
|
33
35
|
- **Self-Contained Agents**: Each agent is both a web app and an AI persona
|
34
36
|
- **Prompt Object Model**: Structured prompt composition using POM
|
35
37
|
- **SWAIG Integration**: Easily define and handle AI tools/functions
|
36
|
-
- **
|
37
|
-
- **
|
38
|
+
- **Custom Routing**: Dynamic request handling for different paths and content
|
39
|
+
- **Security Built-In**: Session management, function-specific security tokens, and basic auth
|
40
|
+
- **State Management**: Persistent conversation state with automatic tracking
|
38
41
|
- **Prefab Archetypes**: Ready-to-use agent types for common scenarios
|
39
42
|
- **Multi-Agent Support**: Host multiple agents on a single server
|
40
43
|
|
@@ -53,12 +56,23 @@ from signalwire_agents.core.function_result import SwaigFunctionResult
|
|
53
56
|
class SimpleAgent(AgentBase):
|
54
57
|
def __init__(self):
|
55
58
|
super().__init__(name="simple", route="/simple")
|
56
|
-
|
57
|
-
|
58
|
-
self.
|
59
|
+
|
60
|
+
# Configure the agent's personality
|
61
|
+
self.prompt_add_section("Personality", body="You are a helpful assistant.")
|
62
|
+
self.prompt_add_section("Goal", body="Help users with basic questions.")
|
63
|
+
self.prompt_add_section("Instructions", bullets=["Be concise and clear."])
|
64
|
+
|
65
|
+
# Alternative using convenience methods:
|
66
|
+
# self.setPersonality("You are a helpful assistant.")
|
67
|
+
# self.setGoal("Help users with basic questions.")
|
68
|
+
# self.setInstructions(["Be concise and clear."])
|
59
69
|
|
60
|
-
@AgentBase.tool(
|
61
|
-
|
70
|
+
@AgentBase.tool(
|
71
|
+
name="get_time",
|
72
|
+
description="Get the current time",
|
73
|
+
parameters={}
|
74
|
+
)
|
75
|
+
def get_time(self, args, raw_data):
|
62
76
|
from datetime import datetime
|
63
77
|
now = datetime.now().strftime("%H:%M:%S")
|
64
78
|
return SwaigFunctionResult(f"The current time is {now}")
|
@@ -73,6 +87,7 @@ if __name__ == "__main__":
|
|
73
87
|
|
74
88
|
```python
|
75
89
|
from signalwire_agents import AgentBase
|
90
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
76
91
|
from signalwire_agents.core.state import FileStateManager
|
77
92
|
|
78
93
|
class StatefulAgent(AgentBase):
|
@@ -86,21 +101,44 @@ class StatefulAgent(AgentBase):
|
|
86
101
|
enable_state_tracking=True, # Enable state tracking
|
87
102
|
state_manager=state_manager # Use custom state manager
|
88
103
|
)
|
104
|
+
|
105
|
+
# When enable_state_tracking=True, startup_hook and hangup_hook
|
106
|
+
# are automatically registered to track session lifecycle
|
89
107
|
|
90
|
-
#
|
91
|
-
|
92
|
-
""
|
108
|
+
# Custom tool for accessing and updating state
|
109
|
+
@AgentBase.tool(
|
110
|
+
name="save_preference",
|
111
|
+
description="Save a user preference",
|
112
|
+
parameters={
|
113
|
+
"preference_name": {
|
114
|
+
"type": "string",
|
115
|
+
"description": "Name of the preference to save"
|
116
|
+
},
|
117
|
+
"preference_value": {
|
118
|
+
"type": "string",
|
119
|
+
"description": "Value of the preference"
|
120
|
+
}
|
121
|
+
}
|
122
|
+
)
|
123
|
+
def save_preference(self, args, raw_data):
|
124
|
+
# Get the call ID from the raw data
|
93
125
|
call_id = raw_data.get("call_id")
|
94
|
-
state = self.get_state(call_id) or {}
|
95
|
-
state["started_at"] = "2023-01-01T12:00:00Z"
|
96
|
-
self.update_state(call_id, state)
|
97
|
-
return "Call initialized"
|
98
126
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
127
|
+
if call_id:
|
128
|
+
# Get current state or empty dict if none exists
|
129
|
+
state = self.get_state(call_id) or {}
|
130
|
+
|
131
|
+
# Update the state
|
132
|
+
preferences = state.get("preferences", {})
|
133
|
+
preferences[args.get("preference_name")] = args.get("preference_value")
|
134
|
+
state["preferences"] = preferences
|
135
|
+
|
136
|
+
# Save the updated state
|
137
|
+
self.update_state(call_id, state)
|
138
|
+
|
139
|
+
return SwaigFunctionResult("Preference saved successfully")
|
140
|
+
else:
|
141
|
+
return SwaigFunctionResult("Could not save preference: No call ID")
|
104
142
|
```
|
105
143
|
|
106
144
|
## Using Prefab Agents
|
@@ -113,12 +151,20 @@ agent = InfoGathererAgent(
|
|
113
151
|
{"name": "full_name", "prompt": "What is your full name?"},
|
114
152
|
{"name": "reason", "prompt": "How can I help you today?"}
|
115
153
|
],
|
116
|
-
confirmation_template="Thanks {full_name}, I'll help you with {reason}."
|
154
|
+
confirmation_template="Thanks {full_name}, I'll help you with {reason}.",
|
155
|
+
name="info-gatherer",
|
156
|
+
route="/info-gatherer"
|
117
157
|
)
|
118
158
|
|
119
|
-
agent.serve(host="0.0.0.0", port=8000
|
159
|
+
agent.serve(host="0.0.0.0", port=8000)
|
120
160
|
```
|
121
161
|
|
162
|
+
Available prefabs include:
|
163
|
+
- `InfoGathererAgent`: Collects structured information from users
|
164
|
+
- `FAQBotAgent`: Answers questions based on a knowledge base
|
165
|
+
- `ConciergeAgent`: Routes users to specialized agents
|
166
|
+
- `SurveyAgent`: Conducts structured surveys with questions and rating scales
|
167
|
+
|
122
168
|
## Configuration
|
123
169
|
|
124
170
|
### Environment Variables
|
@@ -132,6 +178,7 @@ The SDK supports the following environment variables:
|
|
132
178
|
- `SWML_SSL_CERT_PATH`: Path to SSL certificate file
|
133
179
|
- `SWML_SSL_KEY_PATH`: Path to SSL private key file
|
134
180
|
- `SWML_DOMAIN`: Domain name for SSL certificate and external URLs
|
181
|
+
- `SWML_SCHEMA_PATH`: Optional path to override the location of the schema.json file
|
135
182
|
|
136
183
|
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.
|
137
184
|
|
@@ -139,15 +186,13 @@ To enable HTTPS directly (without a reverse proxy), set `SWML_SSL_ENABLED` to "t
|
|
139
186
|
|
140
187
|
## Documentation
|
141
188
|
|
142
|
-
|
189
|
+
The package includes comprehensive documentation in the `docs/` directory:
|
190
|
+
|
191
|
+
- [Agent Guide](docs/agent_guide.md) - Detailed guide to creating and customizing agents
|
192
|
+
- [Architecture](docs/architecture.md) - Overview of the SDK architecture and core concepts
|
193
|
+
- [SWML Service Guide](docs/swml_service_guide.md) - Guide to the underlying SWML service
|
143
194
|
|
144
|
-
-
|
145
|
-
- Using prefab agents
|
146
|
-
- SWAIG function definitions
|
147
|
-
- State management and persistence
|
148
|
-
- Security model
|
149
|
-
- Deployment options
|
150
|
-
- Multi-agent hosting
|
195
|
+
These documents provide in-depth explanations of the features, APIs, and usage patterns.
|
151
196
|
|
152
197
|
## License
|
153
198
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
signalwire_agents/__init__.py,sha256=gZDtAkfd7KIm-6XEXIhNXI553-1Kxks035kcHQFmXjY,800
|
2
|
+
signalwire_agents/agent_server.py,sha256=se_YzOQE5UUoRUKCbTnOg9qr4G3qN7iVuQLutwXEwFU,12850
|
3
|
+
signalwire_agents/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
|
4
|
+
signalwire_agents/core/__init__.py,sha256=mVDLbpq1pg_WwiqsQR28NNZwJ6-VUXFIfg-vN7pk0ew,806
|
5
|
+
signalwire_agents/core/agent_base.py,sha256=KIvueG5wIxveyALELISmPLWEkyhW5abU4hxq427ao_o,98466
|
6
|
+
signalwire_agents/core/function_result.py,sha256=vD8eBDJBQNNnss1jShadfogCZw_prODB6eBkTuVgZKA,3538
|
7
|
+
signalwire_agents/core/pom_builder.py,sha256=ywuiIfP8BeLBPo_G4X1teZlG6zTCMkW71CZnmyoDTAQ,6636
|
8
|
+
signalwire_agents/core/swaig_function.py,sha256=WoHGQuCmU9L9k39pttRunmfRtIa_PnNRn9W0Xq3MfIk,6316
|
9
|
+
signalwire_agents/core/swml_builder.py,sha256=Y_eHThVVso-_Hz4f73eEuu3HJstsM9PtBl-36GvAXhI,6594
|
10
|
+
signalwire_agents/core/swml_handler.py,sha256=KvphI_YY47VWGVXaix_N3SuQSyygHEUr9We6xESQK44,7002
|
11
|
+
signalwire_agents/core/swml_renderer.py,sha256=iobMWWoBR7dkHndI3Qlwf4C0fg2p7DmAU2Rb7ZmmLhA,13891
|
12
|
+
signalwire_agents/core/swml_service.py,sha256=oZKM9pKPxtbsMp3nYnx5FXtW3igj2Sc2wZbQujNVNrg,42097
|
13
|
+
signalwire_agents/core/security/__init__.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
14
|
+
signalwire_agents/core/security/session_manager.py,sha256=5y0Mr3cI63cO34ctFgq-KX26mdG0Tpr7rTEsgoTS_44,5229
|
15
|
+
signalwire_agents/core/state/__init__.py,sha256=qpYIfQBApet6VQsy6diS3yu0lMxCBC6REOUk5l1McUw,379
|
16
|
+
signalwire_agents/core/state/file_state_manager.py,sha256=52I_KVlmhHCbKIaHj74Q-ksP_-AF6ewYmOXCf4n5DTQ,7307
|
17
|
+
signalwire_agents/core/state/state_manager.py,sha256=76B4mDutMb826dK4c_IJhOXH09BW1bJu6EZa6Mh_LB4,2511
|
18
|
+
signalwire_agents/prefabs/__init__.py,sha256=MW11J63XH7KxF2MWguRsMFM9iqMWexaEO9ynDPL_PDM,715
|
19
|
+
signalwire_agents/prefabs/concierge.py,sha256=--esvAV1lozQGYXHAqvSg8_TtlIoVfQK75APJ5zqX9I,9779
|
20
|
+
signalwire_agents/prefabs/faq_bot.py,sha256=cUuHhnDB8S4aVg-DiQe4jBmCAPrYQrND_Mff9iaeEa0,10572
|
21
|
+
signalwire_agents/prefabs/info_gatherer.py,sha256=y9gxjq1vF0-TrE6m734dCcAHqxt0I_DyCqxoM45QY8U,9940
|
22
|
+
signalwire_agents/prefabs/receptionist.py,sha256=bYHbdvNepnMYgU6igcKY8AtCl2vBxUf_g6Z_2D1uD4g,10284
|
23
|
+
signalwire_agents/prefabs/survey.py,sha256=1pyUeZ5heDqFAYqkYs5fHN_jQ7TKqJInnOOUQEajSsY,14358
|
24
|
+
signalwire_agents/utils/__init__.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
25
|
+
signalwire_agents/utils/pom_utils.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
26
|
+
signalwire_agents/utils/schema_utils.py,sha256=LvFCFvJTQk_xYK0B-NXbkXKEF7Zmv-LqpV_vfpPnOb4,13473
|
27
|
+
signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
28
|
+
signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
29
|
+
signalwire_agents-0.1.2.data/data/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
|
30
|
+
signalwire_agents-0.1.2.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
|
31
|
+
signalwire_agents-0.1.2.dist-info/METADATA,sha256=WzQzw2cTR_5Rs_Gd5ypKcOxej87H81FAfYte-ODskVg,7414
|
32
|
+
signalwire_agents-0.1.2.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
33
|
+
signalwire_agents-0.1.2.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
|
34
|
+
signalwire_agents-0.1.2.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 SignalWire
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|