We have learned how to create an appointment booking chatbot using OpenAI’s function calling feature and the Google Calendar API. We have seen how can we use ChatGPT to create closed domain chatbots using the function calling feature. You can create more such closed domain chatbots using ChatGPT and also can integrate any external APIs.
In today’s fast-paced world, automated systems are becoming increasingly popular for simplifying tasks and improving productivity. Fortunately, advancements in AI and automation have paved the way for innovative solutions that streamline this process.
OpenAI’s function call feature revolutionizes the way we interact with AI models. It allows developers to make direct API calls, generating dynamic responses based on user inputs. By utilizing this powerful functionality of ChatGPT, we will create an appointment booking chatbot that can engage in natural and meaningful conversations, providing users with a personalized and interactive experience.
In this blog post, we will explore how to create a chatbot using OpenAI’s function calling feature which we will integrate with the Google Calendar API, allowing users to effortlessly book appointments without the need for manual intervention. Let’s dive in and discover the journey of building an intelligent appointment booking chatbot, harnessing the power of ChatGPT and OpenAI’s function calling feature integrated with the Google Calendar API.
If you’re unfamiliar with OpenAI’s function calling feature, we recommend referring to our blog post titled OpenAI Function Calling With External API Examples. This comprehensive guide provides a step-by-step overview of how to use this powerful functionality. It covers the usage of OpenAI’s function calling feature and its integration with Google Calendar API, offering detailed explanations and practical examples. The script will allow us to effortlessly book, reschedule, delete, and check the availability of appointments.
Step 1:
To utilize the Google Calendar API for appointment scheduling, it is essential to set up your environment correctly. You can perform this setup by following the instructions provided in the following link: https://developers.google.com/calendar/api/quickstart/python#set_up_your_environment
You need to follow each step, including enabling the API, configuring the OAuth consent screen, authorizing credentials for a desktop application, and installing the necessary Google client library for setting up the API correctly.
Step 2:
After completing the setup process, it is important to specify the appropriate edit scope for your application. On the desktop, you can navigate to the “Edit App” section which is located next to the name of your app. From there, Go to the scopes section and include the “https://www.googleapis.com/auth/calendar” scope within your “Your sensitive scopes” settings. Once added, remember to save the changes to ensure that your application has the necessary permissions to interact with the Google Calendar API effectively.
Step 3:
Next, we will configure the sample example. To do this, you will need to create a file named “quickstart.py” in your working directory. Copy and paste the provided code snippet into this file. Make sure you have “credentials.json” file in your working directory.
from __future__ import print_function
import datetime
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/calendar']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
return
# Prints the start and name of the next 10 events
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
print(start, event['summary'])
except HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
When you run “quickstart.py” for the first time, it will prompt you to grant access to the application. If you have multiple Google accounts, you can choose the desired account and click on the “Accept” button.
This step will create “token.json” file in your working directory. This file will serve as the authorization token used to authenticate and authorize access when declaring the specified scope.
Step 4:
We can now proceed to develop a Python script to create an appointment booking chatbot that leverages OpenAI’s function calling feature to schedule, reschedule, delete, and check appointments with the integration of the Google Calendar API.
First, we need to import the required libraries.
import json
import requests
from datetime import date, datetime, timedelta
from time import time
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import pytz
Step 5:
Next, we need to specify the scope for accessing and managing calendar-related data through the Google Calendar API. Then, we create an instance of the Credentials object using the authorized user information stored in the “token.json” file. Finally, we pass the credentials object to build a service object, which allows us to interact with the Google Calendar API. To do this, you need to add below lines of code in your script.
Make sure that you have a “token.json” file in your working directory.
SCOPES = ['https://www.googleapis.com/auth/calendar']
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
service = build('calendar', 'v3', credentials=creds)
Step 6:
GPT_MODEL = "gpt-3.5-turbo-0613"
openai_api_key = ""
def chat_completion_request(messages, functions=None, function_call=None, model=GPT_MODEL):
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer " + openai_api_key,
}
json_data = {"model": model, "messages": messages}
if functions is not None:
json_data.update({"functions": functions})
if function_call is not None:
json_data.update({"function_call": function_call})
try:
response = requests.post(
"https://api.openai.com/v1/chat/completions",
headers=headers,
json=json_data,
)
return response
except Exception as e:
print("Unable to generate ChatCompletion response")
print(f"Exception: {e}")
return e
Step 7:
Let’s create a function for appointment booking that extracts the necessary parameters from the argument object returned as the response from the ChatGPT function call.
The user will be asked to provide the start date, start time, and email address. The start date and time will be merged to form a single parameter, which will be passed to the Google Calendar API call as the designated start time for the meeting. By default, the ending time of the meeting will be set to two hours after the start time. The provided email address will be assigned as the attendee of the meeting. The event JSON, containing these details, will be passed as a parameter to the insert event in the Google Calendar API to book an appointment.
limit1 = datetime.strptime("10:00:00", "%H:%M:%S").time()
limit2 = datetime.strptime("17:00:00", "%H:%M:%S").time()
limit3 = datetime.strptime("12:00:00", "%H:%M:%S").time()
def appointment_booking(arguments):
try:
provided_date = str(datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d").date())
provided_time = str(datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time())
start_date_time = provided_date + " " + provided_time
timezone = pytz.timezone('Asia/Kolkata')
start_date_time = timezone.localize(datetime.strptime(start_date_time, "%Y-%m-%d %H:%M:%S"))
email_address = json.loads(arguments)['email_address']
end_date_time = start_date_time + timedelta(hours=2)
if provided_date and provided_time and email_address:
slot_checking = appointment_checking(arguments)
if slot_checking == "Slot is available for appointment. Would you like to proceed?":
if start_date_time < datetime.now(timezone):
return "Please enter valid date and time."
else:
if day_list[start_date_time.date().weekday()] == "Saturday":
if start_date_time.time() >= limit1 and start_date_time.time() <= limit3:
event = {
'summary': "Appointment booking Chatbot using OpenAI's function calling feature",
'location': "Ahmedabad",
'description': "This appointment has been scheduled as the demo of the appointment booking chatbot using OpenAI function calling feature by Pragnakalp Techlabs.",
'start': {
'dateTime': start_date_time.strftime("%Y-%m-%dT%H:%M:%S"),
'timeZone': 'Asia/Kolkata',
},
'end': {
'dateTime': end_date_time.strftime("%Y-%m-%dT%H:%M:%S"),
'timeZone': 'Asia/Kolkata',
},
'attendees': [
{'email': email_address},
],
'reminders': {
'useDefault': False,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
}
service.events().insert(calendarId='primary', body=event).execute()
return "Appointment added successfully."
else:
return "Please try to book an appointment into working hours, which is 10 AM to 2 PM at saturday."
else:
if start_date_time.time() >= limit1 and start_date_time.time() <= limit2:
event = {
'summary': "Appointment booking Chatbot using OpenAI's function calling feature",
'location': "Ahmedabad",
'description': "This appointment has been scheduled as the demo of the appointment booking chatbot using OpenAI function calling feature by Pragnakalp Techlabs.",
'start': {
'dateTime': start_date_time.strftime("%Y-%m-%dT%H:%M:%S"),
'timeZone': 'Asia/Kolkata',
},
'end': {
'dateTime': end_date_time.strftime("%Y-%m-%dT%H:%M:%S"),
'timeZone': 'Asia/Kolkata',
},
'attendees': [
{'email': email_address},
],
'reminders': {
'useDefault': False,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
}
service.events().insert(calendarId='primary', body=event).execute()
return "Appointment added successfully."
else:
return "Please try to book an appointment into working hours, which is 10 AM to 7 PM."
else:
return slot_checking
else:
return "Please provide all necessary details: Start date, End date and Email address."
except:
return "We are facing an error while processing your request. Please try again."
Step 8:
Now, we will define a function that helps in rescheduling appointments. During the rescheduling process, the user will be asked to provide a new date, new time, and email ID for identification purposes. Subsequently, the start time of the event will be generated by merging the provided start time and date.
The function begins by fetching the event object of the appointment that needs to be rescheduled. It then proceeds to update the start and end times of the event. Finally, the updated event object is passed as a parameter to the Google Calendar API’s update event.
def appointment_reschedule(arguments):
try:
provided_date = str(datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d").date())
provided_time = str(datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time())
start_date_time = provided_date + " " + provided_time
timezone = pytz.timezone('Asia/Kolkata')
start_date_time = timezone.localize(datetime.strptime(start_date_time, "%Y-%m-%d %H:%M:%S"))
email_address = json.loads(arguments)['email_address']
if provided_date and provided_time and email_address:
if start_date_time < datetime.now(timezone):
return "Please enter valid date and time."
else:
if day_list[start_date_time.date().weekday()] == "Saturday":
if start_date_time.time() >= limit1 and start_date_time.time() <= limit3:
end_date_time = start_date_time + timedelta(hours=2)
events = service.events().list(calendarId="primary").execute()
id = ""
final_event = None
for event in events['items']:
if event['attendees'][0]['email'] == email_address:
id = event['id']
final_event = event
if final_event:
if appointment_checking(arguments) == "Slot is available for appointment. Would you like to proceed?":
final_event['start']['dateTime'] = start_date_time.strftime("%Y-%m-%dT%H:%M:%S")
final_event['end']['dateTime'] = end_date_time.strftime("%Y-%m-%dT%H:%M:%S")
service.events().update(calendarId='primary', eventId=id, body=final_event).execute()
return "Appointment rescheduled."
else:
return "Sorry, slot is not available at this time, please try a different time."
else:
return "No registered event found on your id."
else:
return "Please try to book an appointment into working hours, which is 10 AM to 2 PM at saturday."
else:
if start_date_time.time() >= limit1 and start_date_time.time() <= limit2:
end_date_time = start_date_time + timedelta(hours=2)
events = service.events().list(calendarId="primary").execute()
id = ""
final_event = None
for event in events['items']:
if event['attendees'][0]['email'] == email_address:
id = event['id']
final_event = event
if final_event:
if appointment_checking(arguments) == "Slot is available for appointment. Would you like to proceed?":
final_event['start']['dateTime'] = start_date_time.strftime("%Y-%m-%dT%H:%M:%S")
final_event['end']['dateTime'] = end_date_time.strftime("%Y-%m-%dT%H:%M:%S")
service.events().update(calendarId='primary', eventId=id, body=final_event).execute()
return "Appointment rescheduled."
else:
return "Sorry, slot is not available at this time, please try a different time."
else:
return "No registered event found on your id."
else:
return "Please try to book an appointment into working hours, which is 10 AM to 7 PM."
else:
return "Please provide all necessary details: Start date, End date and Email address."
except:
return "We are unable to process, please try again."
Step 9:
Moving forward, we will add the functionality of appointment deletion. During this process, the user will be asked to provide a date, time, and email address. Then, we will check if any registered event is scheduled under the provided email address. If an event is found, it will be deleted using the delete event provided by the Google Calendar API.
To implement the appointment deletion functionality, please include the following lines of code:
def appointment_delete(arguments):
try:
provided_date = str(datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d").date())
provided_time = str(datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time())
email_address = json.loads(arguments)['email_address']
if provided_date and provided_time and email_address:
start_date_time = provided_date + " " + provided_time
timezone = pytz.timezone('Asia/Kolkata')
start_date_time = timezone.localize(datetime.strptime(start_date_time, "%Y-%m-%d %H:%M:%S"))
if start_date_time < datetime.now(timezone):
return "Please enter valid date and time."
else:
events = service.events().list(calendarId="primary").execute()
id = ""
for event in events['items']:
if event['attendees'][0]['email'] == email_address:
if datetime.fromisoformat(str(event['start']['dateTime'])) == datetime.fromisoformat(str(start_date_time)):
id = event['id']
if id:
service.events().delete(calendarId='primary', eventId=id).execute()
return "Appointment deleted successfully."
else:
return "No registered event found on your id."
else:
return "Please provide all necessary details: Start date, End date and Email address."
except:
return "We are unable to process, please try again."
Step 10:
For our last functionality, we will implement the appointment checking functionality. Users will be prompted to enter their desired date and time for the slot check. To verify the availability, we will utilize the “list” event from the Google Calendar API.
Please add the below lines of code to implement appointment checking functionality.
def appointment_checking(arguments):
try:
provided_date = str(datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d").date())
provided_time = str(datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time())
start_date_time = provided_date + " " + provided_time
timezone = pytz.timezone('Asia/Kolkata')
start_date_time = timezone.localize(datetime.strptime(start_date_time, "%Y-%m-%d %H:%M:%S"))
if start_date_time < datetime.now(timezone):
return "Please enter valid date and time."
else:
if day_list[start_date_time.date().weekday()] == "Saturday":
if start_date_time.time() >= limit1 and start_date_time.time() <= limit3:
end_date_time = start_date_time + timedelta(hours=2)
events_result = service.events().list(calendarId='primary', timeMin=start_date_time.isoformat(), timeMax=end_date_time.isoformat()).execute()
if events_result['items']:
return "Sorry slot is not available."
else:
return "Slot is available for appointment. Would you like to proceed?"
else:
return "Please try to check an appointment within working hours, which is 10 AM to 2 PM at saturday."
else:
if start_date_time.time() >= limit1 and start_date_time.time() <= limit2:
end_date_time = start_date_time + timedelta(hours=2)
events_result = service.events().list(calendarId='primary', timeMin=start_date_time.isoformat(), timeMax=end_date_time.isoformat()).execute()
if events_result['items']:
return "Sorry slot is not available."
else:
return "Slot is available for appointment. Would you like to proceed?"
else:
return "Please try to check an appointment within working hours, which is 10 AM to 7 PM."
except:
return "We are unable to process, please try again."
Step 11:
Following that, we will define a function specification that defines all our functionalities, including appointment insertion, updation, deletion, and checking. This function specification will describe the purpose of the function and its parameters, and the value of each parameter. When the user provides input, the ChatGPT model will determine which function should be invoked and extract the relevant parameters to be passed as arguments.
To describe the function specification, you need to add below lines of code:
functions = [
{
"name": "appointment_booking",
"description": "When user want to book appointment, then this function should be called.",
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date",
"example":"2023-07-23",
"description": "Date, when the user wants to book an appointment. The date must be in the format of YYYY-MM-DD.",
},
"time": {
"type": "string",
"example": "20:12:45",
"description": "time, on which user wants to book an appointment on a specified date. Time must be in %H:%M:%S format.",
},
"email_address": {
"type": "string",
"description": "email_address of the user gives for identification.",
}
},
"required": ["date","time","email_address"],
},
},
{
"name": "appointment_reschedule",
"description": "When user want to reschedule appointment, then this function should be called.",
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date",
"example":"2023-07-23",
"description": "It is the date on which the user wants to reschedule the appointment. The date must be in the format of YYYY-MM-DD.",
},
"time": {
"type": "string",
"description": "It is the time on which user wants to reschedule the appointment. Time must be in %H:%M:%S format.",
},
"email_address": {
"type": "string",
"description": "email_address of the user gives for identification.",
}
},
"required": ["date","time","email_address"],
},
},
{
"name": "appointment_delete",
"description": "When user want to delete appointment, then this function should be called.",
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date",
"example":"2023-07-23",
"description": "Date, on which user has appointment and wants to delete it. The date must be in the format of YYYY-MM-DD.",
},
"time": {
"type": "string",
"description": "time, on which user has an appointment and wants to delete it. Time must be in %H:%M:%S format.",
},
"email_address": {
"type": "string",
"description": "email_address of the user gives for identification.",
}
},
"required": ["date","time","email_address"],
},
},
{
"name": "appointment_checking",
"description": "When user wants to check if appointment is available or not, then this function should be called.",
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date",
"example":"2023-07-23",
"description": "Date, when the user wants to book an appointment. The date must be in the format of YYYY-MM-DD.",
},
"time": {
"type": "string",
"example": "20:12:45",
"description": "time, on which user wants to book an appointment on a specified date. Time must be in %H:%M:%S format.",
}
},
"required": ["date","time"],
},
}]
Step 12:
In the final step, we will initiate the testing phase for our appointment booking chatbot. On providing the specific requirements as input, the ChatGPT model will automatically interpret the appropriate function to be invoked, along with the corresponding parameters. It will then return the function name and its arguments, allowing us to execute the function and fulfill our requirements.
To conduct the testing, please include the following lines of code:
day_list = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
messages = [{"role": "system", "content": f"""You are an expert in booking appointments. You need to ask the user for the appointment date, appointment time, and email ID. The user can book the appointment from 10 AM to 7 PM from Monday to Friday, and from 10 AM to 2 PM on Saturdays. You need to remember that today's date is {date.today()} and day is {day_list[date.today().weekday()]}. Check if the time provided by the user is within the working hours then only you will proceed.
Instructions:
- Don't make assumptions about what values to plug into functions, if the user does not provide any of the required parameters then you must need to ask for clarification.
- Make sure the email Id is valid and not empty.
- If a user request is ambiguous, then also you need to ask for clarification.
- When a user asks for a rescheduling date or time of the current appointment, then you must ask for the new appointment details only.
- If a user didn't specify "ante meridiem (AM)" or "post meridiem (PM)" while providing the time, then you must have to ask for clarification. If the user didn't provide day, month, and year while giving the time then you must have to ask for clarification.
Make sure to follow the instructions carefully while processing the request.
"""}]
user_input = input("Please enter your question here: (if you want to exit then write 'exit' or 'bye'.) ")
while user_input.strip().lower() != "exit" and user_input.strip().lower() != "bye":
messages.append({"role": "user", "content": user_input})
# calling chat_completion_request to call ChatGPT completion endpoint
chat_response = chat_completion_request(
messages, functions=functions
)
# fetch response of ChatGPT and call the function
assistant_message = chat_response.json()["choices"][0]["message"]
if assistant_message['content']:
print("Response is: ", assistant_message['content'])
messages.append({"role": "assistant", "content": assistant_message['content']})
else:
fn_name = assistant_message["function_call"]["name"]
arguments = assistant_message["function_call"]["arguments"]
function = locals()[fn_name]
result = function(arguments)
print("Response is: ", result)
user_input = input("Please enter your question here: ")
Testing Results:
Appointment checking:
Appointment booking:
Appointment rescheduling:
Appointment deletion:
Seeking expert assistance to integrate OpenAI’s function calling feature into your chatbot or NLP application? Connect with our Generative AI Developers at letstalk@pragnakalp.com or share your requirements here. Let’s bring your vision to life together!