signalwire-agents 0.1.1__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 +1 -1
- signalwire_agents/agent_server.py +1 -1
- signalwire_agents/core/__init__.py +29 -0
- signalwire_agents/core/agent_base.py +2541 -0
- signalwire_agents/core/function_result.py +123 -0
- signalwire_agents/core/pom_builder.py +204 -0
- signalwire_agents/core/security/__init__.py +9 -0
- signalwire_agents/core/security/session_manager.py +179 -0
- signalwire_agents/core/state/__init__.py +17 -0
- signalwire_agents/core/state/file_state_manager.py +219 -0
- signalwire_agents/core/state/state_manager.py +101 -0
- signalwire_agents/core/swaig_function.py +172 -0
- signalwire_agents/core/swml_builder.py +214 -0
- signalwire_agents/core/swml_handler.py +227 -0
- signalwire_agents/core/swml_renderer.py +368 -0
- signalwire_agents/core/swml_service.py +1057 -0
- signalwire_agents/prefabs/__init__.py +26 -0
- signalwire_agents/prefabs/concierge.py +267 -0
- signalwire_agents/prefabs/faq_bot.py +305 -0
- signalwire_agents/prefabs/info_gatherer.py +263 -0
- signalwire_agents/prefabs/receptionist.py +294 -0
- signalwire_agents/prefabs/survey.py +378 -0
- signalwire_agents/utils/__init__.py +9 -0
- signalwire_agents/utils/pom_utils.py +9 -0
- signalwire_agents/utils/schema_utils.py +357 -0
- signalwire_agents/utils/token_generators.py +9 -0
- signalwire_agents/utils/validators.py +9 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/METADATA +1 -1
- signalwire_agents-0.1.2.dist-info/RECORD +34 -0
- signalwire_agents-0.1.1.dist-info/RECORD +0 -9
- {signalwire_agents-0.1.1.data → signalwire_agents-0.1.2.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,263 @@
|
|
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
|
+
InfoGathererAgent - Prefab agent for collecting answers to a series of questions
|
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 InfoGathererAgent(AgentBase):
|
22
|
+
"""
|
23
|
+
A prefab agent designed to collect answers to a series of questions.
|
24
|
+
|
25
|
+
This agent will:
|
26
|
+
1. Ask if the user is ready to begin
|
27
|
+
2. Ask each question in sequence
|
28
|
+
3. Store the answers for later use
|
29
|
+
|
30
|
+
Example:
|
31
|
+
agent = InfoGathererAgent(
|
32
|
+
questions=[
|
33
|
+
{"key_name": "full_name", "question_text": "What is your full name?"},
|
34
|
+
{"key_name": "email", "question_text": "What is your email address?", "confirm": True},
|
35
|
+
{"key_name": "reason", "question_text": "How can I help you today?"}
|
36
|
+
]
|
37
|
+
)
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
questions: List[Dict[str, str]],
|
43
|
+
name: str = "info_gatherer",
|
44
|
+
route: str = "/info_gatherer",
|
45
|
+
**kwargs
|
46
|
+
):
|
47
|
+
"""
|
48
|
+
Initialize an information gathering agent
|
49
|
+
|
50
|
+
Args:
|
51
|
+
questions: List of questions to ask, each with:
|
52
|
+
- key_name: Identifier for storing the answer
|
53
|
+
- question_text: The actual question to ask the user
|
54
|
+
- confirm: (Optional) If set to True, the agent will confirm the answer before submitting
|
55
|
+
name: Agent name for the route
|
56
|
+
route: HTTP route for this agent
|
57
|
+
**kwargs: Additional arguments for AgentBase
|
58
|
+
"""
|
59
|
+
# Initialize the base agent
|
60
|
+
super().__init__(
|
61
|
+
name=name,
|
62
|
+
route=route,
|
63
|
+
use_pom=True,
|
64
|
+
**kwargs
|
65
|
+
)
|
66
|
+
|
67
|
+
# Validate questions format
|
68
|
+
self._validate_questions(questions)
|
69
|
+
|
70
|
+
# Set up global data with questions and initial state
|
71
|
+
self.set_global_data({
|
72
|
+
"questions": questions,
|
73
|
+
"question_index": 0,
|
74
|
+
"answers": []
|
75
|
+
})
|
76
|
+
|
77
|
+
# Build a minimal prompt
|
78
|
+
self._build_prompt()
|
79
|
+
|
80
|
+
# Configure additional agent settings
|
81
|
+
self._configure_agent_settings()
|
82
|
+
|
83
|
+
def _validate_questions(self, questions):
|
84
|
+
"""Validate that questions are in the correct format"""
|
85
|
+
if not questions:
|
86
|
+
raise ValueError("At least one question is required")
|
87
|
+
|
88
|
+
for i, question in enumerate(questions):
|
89
|
+
if "key_name" not in question:
|
90
|
+
raise ValueError(f"Question {i+1} is missing 'key_name' field")
|
91
|
+
if "question_text" not in question:
|
92
|
+
raise ValueError(f"Question {i+1} is missing 'question_text' field")
|
93
|
+
|
94
|
+
def _build_prompt(self):
|
95
|
+
"""Build a minimal prompt with just the objective"""
|
96
|
+
self.prompt_add_section(
|
97
|
+
"Objective",
|
98
|
+
body="Your role is to get answers to a series of questions. Begin by asking the user if they are ready to answer some questions. If they confirm they are ready, call the start_questions function to begin the process."
|
99
|
+
)
|
100
|
+
|
101
|
+
def _configure_agent_settings(self):
|
102
|
+
"""Configure additional agent settings"""
|
103
|
+
# Set AI behavior parameters
|
104
|
+
self.set_params({
|
105
|
+
"end_of_speech_timeout": 800,
|
106
|
+
"speech_event_timeout": 1000 # Slightly longer for thoughtful responses
|
107
|
+
})
|
108
|
+
|
109
|
+
def _generate_question_instruction(self, question_text: str, needs_confirmation: bool, is_first_question: bool = False) -> str:
|
110
|
+
"""
|
111
|
+
Generate the instruction text for asking a question
|
112
|
+
|
113
|
+
Args:
|
114
|
+
question_text: The question to ask
|
115
|
+
needs_confirmation: Whether confirmation is required
|
116
|
+
is_first_question: Whether this is the first question or a subsequent one
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
Formatted instruction text
|
120
|
+
"""
|
121
|
+
# Start with the appropriate prefix based on whether this is the first question
|
122
|
+
if is_first_question:
|
123
|
+
instruction = f"Ask the user to answer the following question: {question_text}\n\n"
|
124
|
+
else:
|
125
|
+
instruction = f"Previous Answer recorded. Now ask the user to answer the following question: {question_text}\n\n"
|
126
|
+
|
127
|
+
# Add the common part
|
128
|
+
instruction += "Make sure the answer fits the scope and context of the question before submitting it. "
|
129
|
+
|
130
|
+
# Add confirmation guidance if needed
|
131
|
+
if needs_confirmation:
|
132
|
+
instruction += "Insist that the user confirms the answer as many times as needed until they say it is correct."
|
133
|
+
else:
|
134
|
+
instruction += "You don't need the user to confirm the answer to this question."
|
135
|
+
|
136
|
+
return instruction
|
137
|
+
|
138
|
+
@AgentBase.tool(
|
139
|
+
name="start_questions",
|
140
|
+
description="Start the question sequence with the first question",
|
141
|
+
parameters={}
|
142
|
+
)
|
143
|
+
def start_questions(self, args, raw_data):
|
144
|
+
"""
|
145
|
+
Start the question sequence by retrieving the first question
|
146
|
+
|
147
|
+
This function gets the current question index from global_data
|
148
|
+
and returns the corresponding question.
|
149
|
+
"""
|
150
|
+
# Get global data
|
151
|
+
global_data = raw_data.get("global_data", {})
|
152
|
+
questions = global_data.get("questions", [])
|
153
|
+
question_index = global_data.get("question_index", 0)
|
154
|
+
|
155
|
+
# Check if we have questions
|
156
|
+
if not questions or question_index >= len(questions):
|
157
|
+
return SwaigFunctionResult("I don't have any questions to ask.")
|
158
|
+
|
159
|
+
# Get the current question
|
160
|
+
current_question = questions[question_index]
|
161
|
+
question_text = current_question.get("question_text", "")
|
162
|
+
needs_confirmation = current_question.get("confirm", False)
|
163
|
+
|
164
|
+
# Generate instruction using the helper method
|
165
|
+
instruction = self._generate_question_instruction(
|
166
|
+
question_text=question_text,
|
167
|
+
needs_confirmation=needs_confirmation,
|
168
|
+
is_first_question=True
|
169
|
+
)
|
170
|
+
|
171
|
+
# Return a prompt to ask the question
|
172
|
+
return SwaigFunctionResult(instruction)
|
173
|
+
|
174
|
+
@AgentBase.tool(
|
175
|
+
name="submit_answer",
|
176
|
+
description="Submit an answer to the current question and move to the next one",
|
177
|
+
parameters={
|
178
|
+
"answer": {
|
179
|
+
"type": "string",
|
180
|
+
"description": "The user's answer to the current question"
|
181
|
+
}
|
182
|
+
}
|
183
|
+
)
|
184
|
+
def submit_answer(self, args, raw_data):
|
185
|
+
"""
|
186
|
+
Submit an answer to the current question and move to the next one
|
187
|
+
|
188
|
+
This function:
|
189
|
+
1. Stores the answer in global_data
|
190
|
+
2. Increments the question index
|
191
|
+
3. Returns the next question or completion message
|
192
|
+
"""
|
193
|
+
# Get the answer
|
194
|
+
answer = args.get("answer", "")
|
195
|
+
|
196
|
+
# Get global data
|
197
|
+
global_data = raw_data.get("global_data", {})
|
198
|
+
questions = global_data.get("questions", [])
|
199
|
+
question_index = global_data.get("question_index", 0)
|
200
|
+
answers = global_data.get("answers", [])
|
201
|
+
|
202
|
+
# Check if we're within bounds
|
203
|
+
if question_index >= len(questions):
|
204
|
+
return SwaigFunctionResult("All questions have already been answered.")
|
205
|
+
|
206
|
+
# Get the current question
|
207
|
+
current_question = questions[question_index]
|
208
|
+
key_name = current_question.get("key_name", "")
|
209
|
+
|
210
|
+
# Store the answer
|
211
|
+
new_answer = {"key_name": key_name, "answer": answer}
|
212
|
+
new_answers = answers + [new_answer]
|
213
|
+
|
214
|
+
# Increment question index
|
215
|
+
new_question_index = question_index + 1
|
216
|
+
|
217
|
+
print(f"new_question_index: {new_question_index} len(questions): {len(questions)}")
|
218
|
+
|
219
|
+
# Check if we have more questions
|
220
|
+
if new_question_index < len(questions):
|
221
|
+
|
222
|
+
print(f"Asking next question: {new_question_index} question: {questions[new_question_index]}")
|
223
|
+
|
224
|
+
# Get the next question
|
225
|
+
next_question = questions[new_question_index]
|
226
|
+
next_question_text = next_question.get("question_text", "")
|
227
|
+
needs_confirmation = next_question.get("confirm", False)
|
228
|
+
|
229
|
+
# Generate instruction using the helper method
|
230
|
+
instruction = self._generate_question_instruction(
|
231
|
+
question_text=next_question_text,
|
232
|
+
needs_confirmation=needs_confirmation,
|
233
|
+
is_first_question=False
|
234
|
+
)
|
235
|
+
|
236
|
+
# Create response with the global data update and next question
|
237
|
+
result = SwaigFunctionResult(instruction)
|
238
|
+
|
239
|
+
# Add actions to update global data
|
240
|
+
result.add_actions([
|
241
|
+
{"set_global_data": {
|
242
|
+
"answers": new_answers,
|
243
|
+
"question_index": new_question_index
|
244
|
+
}}
|
245
|
+
])
|
246
|
+
|
247
|
+
return result
|
248
|
+
else:
|
249
|
+
# No more questions - create response with global data update and completion message
|
250
|
+
result = SwaigFunctionResult(
|
251
|
+
"Thank you! All questions have been answered. You can now summarize the information collected or ask if there's anything else the user would like to discuss."
|
252
|
+
)
|
253
|
+
|
254
|
+
# Add actions to update global data
|
255
|
+
result.add_actions([
|
256
|
+
{"set_global_data": {
|
257
|
+
"answers": new_answers,
|
258
|
+
"question_index": new_question_index
|
259
|
+
}}
|
260
|
+
])
|
261
|
+
|
262
|
+
return result
|
263
|
+
|
@@ -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
|