-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfunctions.py
273 lines (248 loc) · 10.8 KB
/
functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
import json
from datetime import datetime, timedelta
import asyncio
from business_logic import (
get_customer,
get_customer_appointments,
get_customer_orders,
schedule_appointment,
get_available_appointment_slots,
prepare_agent_filler_message,
prepare_farewell_message
)
async def find_customer(params):
"""Look up a customer by phone, email, or ID."""
phone = params.get("phone")
email = params.get("email")
customer_id = params.get("customer_id")
result = await get_customer(phone=phone, email=email, customer_id=customer_id)
return result
async def get_appointments(params):
"""Get appointments for a customer."""
customer_id = params.get("customer_id")
if not customer_id:
return {"error": "customer_id is required"}
result = await get_customer_appointments(customer_id)
return result
async def get_orders(params):
"""Get orders for a customer."""
customer_id = params.get("customer_id")
if not customer_id:
return {"error": "customer_id is required"}
result = await get_customer_orders(customer_id)
return result
async def create_appointment(params):
"""Schedule a new appointment."""
customer_id = params.get("customer_id")
date = params.get("date")
service = params.get("service")
if not all([customer_id, date, service]):
return {"error": "customer_id, date, and service are required"}
result = await schedule_appointment(customer_id, date, service)
return result
async def check_availability(params):
"""Check available appointment slots."""
start_date = params.get("start_date")
end_date = params.get("end_date", (datetime.fromisoformat(start_date) + timedelta(days=7)).isoformat())
if not start_date:
return {"error": "start_date is required"}
result = await get_available_appointment_slots(start_date, end_date)
return result
async def agent_filler(websocket, params):
"""
Handle agent filler messages while maintaining proper function call protocol.
"""
result = await prepare_agent_filler_message(websocket, **params)
return result
async def end_call(websocket, params):
"""
End the conversation and close the connection.
"""
farewell_type = params.get("farewell_type", "general")
result = await prepare_farewell_message(websocket, farewell_type)
return result
# Function definitions that will be sent to the Voice Agent API
FUNCTION_DEFINITIONS = [
{
"name": "agent_filler",
"description": """Use this function to provide natural conversational filler before looking up information.
ALWAYS call this function first with message_type='lookup' when you're about to look up customer information.
After calling this function, you MUST immediately follow up with the appropriate lookup function (e.g., find_customer).""",
"parameters": {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of filler message to use. Use 'lookup' when about to search for information.",
"enum": ["lookup", "general"]
}
},
"required": ["message_type"]
}
},
{
"name": "find_customer",
"description": """Look up a customer's account information. Use context clues to determine what type of identifier the user is providing:
Customer ID formats:
- Numbers only (e.g., '169', '42') → Format as 'CUST0169', 'CUST0042'
- With prefix (e.g., 'CUST169', 'customer 42') → Format as 'CUST0169', 'CUST0042'
Phone number recognition:
- Standard format: '555-123-4567' → Format as '+15551234567'
- With area code: '(555) 123-4567' → Format as '+15551234567'
- Spoken naturally: 'five five five, one two three, four five six seven' → Format as '+15551234567'
- International: '+1 555-123-4567' → Use as is
- Always add +1 country code if not provided
Email address recognition:
- Spoken naturally: 'my email is john dot smith at example dot com' → Format as '[email protected]'
- With domain: '[email protected]' → Use as is
- Spelled out: 'j o h n at example dot com' → Format as '[email protected]'""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID. Format as CUSTXXXX where XXXX is the number padded to 4 digits with leading zeros. Example: if user says '42', pass 'CUST0042'"
},
"phone": {
"type": "string",
"description": """Phone number with country code. Format as +1XXXXXXXXXX:
- Add +1 if not provided
- Remove any spaces, dashes, or parentheses
- Convert spoken numbers to digits
Example: 'five five five one two three four five six seven' → '+15551234567'"""
},
"email": {
"type": "string",
"description": """Email address in standard format:
- Convert 'dot' to '.'
- Convert 'at' to '@'
- Remove spaces between spelled out letters
Example: 'j dot smith at example dot com' → '[email protected]'"""
}
}
}
},
{
"name": "get_appointments",
"description": """Retrieve all appointments for a customer. Use this function when:
- A customer asks about their upcoming appointments
- A customer wants to know their appointment schedule
- A customer asks 'When is my next appointment?'
Always verify you have the customer's account first using find_customer before checking appointments.""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
}
},
"required": ["customer_id"]
}
},
{
"name": "get_orders",
"description": """Retrieve order history for a customer. Use this function when:
- A customer asks about their orders
- A customer wants to check order status
- A customer asks questions like 'Where is my order?' or 'What did I order?'
Always verify you have the customer's account first using find_customer before checking orders.""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
}
},
"required": ["customer_id"]
}
},
{
"name": "create_appointment",
"description": """Schedule a new appointment for a customer. Use this function when:
- A customer wants to book a new appointment
- A customer asks to schedule a service
Before scheduling:
1. Verify customer account exists using find_customer
2. Check availability using check_availability
3. Confirm date/time and service type with customer before booking""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
},
"date": {
"type": "string",
"description": "Appointment date and time in ISO format (YYYY-MM-DDTHH:MM:SS). Must be a time slot confirmed as available."
},
"service": {
"type": "string",
"description": "Type of service requested. Must be one of the following: Consultation, Follow-up, Review, or Planning",
"enum": ["Consultation", "Follow-up", "Review", "Planning"]
}
},
"required": ["customer_id", "date", "service"]
}
},
{
"name": "check_availability",
"description": """Check available appointment slots within a date range. Use this function when:
- A customer wants to know available appointment times
- Before scheduling a new appointment
- A customer asks 'When can I come in?' or 'What times are available?'
After checking availability, present options to the customer in a natural way, like:
'I have openings on [date] at [time] or [date] at [time]. Which works better for you?'""",
"parameters": {
"type": "object",
"properties": {
"start_date": {
"type": "string",
"description": "Start date in ISO format (YYYY-MM-DDTHH:MM:SS). Usually today's date for immediate availability checks."
},
"end_date": {
"type": "string",
"description": "End date in ISO format. Optional - defaults to 7 days after start_date. Use for specific date range requests."
}
},
"required": ["start_date"]
}
},
{
"name": "end_call",
"description": """End the conversation and close the connection. Call this function when:
- User says goodbye, thank you, etc.
- User indicates they're done ("that's all I need", "I'm all set", etc.)
- User wants to end the conversation
Examples of triggers:
- "Thank you, bye!"
- "That's all I needed, thanks"
- "Have a good day"
- "Goodbye"
- "I'm done"
Do not call this function if the user is just saying thanks but continuing the conversation.""",
"parameters": {
"type": "object",
"properties": {
"farewell_type": {
"type": "string",
"description": "Type of farewell to use in response",
"enum": ["thanks", "general", "help"]
}
},
"required": ["farewell_type"]
}
}
]
# Map function names to their implementations
FUNCTION_MAP = {
"find_customer": find_customer,
"get_appointments": get_appointments,
"get_orders": get_orders,
"create_appointment": create_appointment,
"check_availability": check_availability,
"agent_filler": agent_filler,
"end_call": end_call
}