simran0608 commited on
Commit
49e4e3b
Β·
verified Β·
1 Parent(s): a4c814a

Upload 6 files

Browse files
Files changed (6) hide show
  1. config.py +28 -0
  2. credentials.json +1 -0
  3. model.py +39 -0
  4. schema.py +24 -0
  5. token.json +1 -0
  6. utils.py +577 -0
config.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MODEL_NAME="llama3-8b-8192"
2
+ MODEL_NAME="llama-3.3-70b-versatile"
3
+
4
+
5
+ PROMPT = """
6
+ You are an Appointment Booking Assistant AI.
7
+ - Help users schedule, reschedule, or delete appointments.
8
+ - Appointments must follow these rules:
9
+ 1. Scheduled on weekdays (Monday to Friday) only.
10
+ 2. Between 10 AM and 7 PM.
11
+ 3. Within the next 7 days.
12
+ - Respond appropriately to greetings like "Hi" or "Hello."
13
+ - Provide available slots when requested.
14
+
15
+ If the input includes a specific time or date:
16
+ - Parse the date and time from the input.
17
+ - Use the appropriate tool to check availability or book an appointment.
18
+ - If parsing fails, ask the user for clarification.
19
+
20
+ Available tools:
21
+ - `book-slot-tool` for booking appointments.
22
+ - `check-event` for checking available slots.
23
+ - `delete-slot-tool` for deleting events.
24
+ - `reschedule-event-tool` for rescheduling events.
25
+
26
+ If you cannot fulfill the request, explain why.
27
+ """
28
+
credentials.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"installed":{"client_id":"123736158724-c9fm6hg9svnhaoeqh1kqutts1q01oru1.apps.googleusercontent.com","project_id":"ethereal-app-434922-g3","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
model.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ from config import MODEL_NAME
4
+ from dotenv import load_dotenv
5
+ from langchain_groq import ChatGroq
6
+ from langchain.agents import AgentExecutor
7
+ from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
8
+ from langchain.agents import AgentExecutor
9
+ from langchain.agents import create_tool_calling_agent
10
+ from langchain_core.utils.function_calling import convert_to_openai_function
11
+ from utils import book_slot, check_slots, reschedule_event, delete_event
12
+
13
+
14
+ load_dotenv()
15
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
16
+
17
+ API_KEY = os.environ["API_KEY"]
18
+ def create_agent(PROMPT):
19
+ prompt_template = ChatPromptTemplate.from_messages([
20
+ ("system", PROMPT),
21
+ ("human", "{input}"),
22
+ MessagesPlaceholder(variable_name="agent_scratchpad"),
23
+ ])
24
+
25
+ tools = [book_slot, delete_event, check_slots, reschedule_event]
26
+ functions = [convert_to_openai_function(f) for f in tools]
27
+
28
+ llm = ChatGroq(
29
+ model=MODEL_NAME,
30
+ temperature=0.7,
31
+ max_tokens=None,
32
+ timeout=None,
33
+ max_retries=2,
34
+ api_key=API_KEY
35
+ ).bind_functions(functions=functions)
36
+
37
+ agent = create_tool_calling_agent(llm, tools, prompt_template)
38
+ agent_executor = AgentExecutor(agent=agent, tools=tools)
39
+ return agent_executor
schema.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.pydantic_v1 import BaseModel, Field
2
+
3
+ class symptoms(BaseModel):
4
+ symptoms: str = Field(description="query of user")
5
+
6
+ class bookSlot(BaseModel):
7
+ start_time: str = Field(description="starting time of the slot with date use this format '2024-09-20T15:30:00+05:30'.")
8
+ end_time: str = Field(description="ending time of the slot with date use this format '2024-09-20T15:30:00+05:30'.")
9
+ summary: str = Field(description="summary of the event with title.")
10
+
11
+ class deleteSlot(BaseModel):
12
+ start_time: str = Field(description="starting time of the slot with date use this format '2024-09-20T15:30:00+05:30'.")
13
+
14
+ class reschedule_event(BaseModel):
15
+ start_time: str = Field(description="starting time of the slot with date that need to be reschedule use this format '2024-09-20T15:30:00+05:30'.")
16
+ new_start_time: str = Field(description="new starting time of the slot with date that need to be reschedule use this format '2024-09-20T15:30:00+05:30'.")
17
+ new_end_time: str = Field(description="ending time of the slot with date that need to be reschedule use this format '2024-09-20T15:30:00+05:30'.")
18
+
19
+ class listevent(BaseModel):
20
+ target_date: str = Field(description="Target date for which we want to list the events.")
21
+
22
+ class checkevent(BaseModel):
23
+ date: str = Field(description="Target date for which we want to check the events.")
24
+
token.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"token": "ya29.a0ARW5m74jdMZiSQpKWTQEkw_sxifCi6ZbXAPMY_GiMz98PIfckijA9JecLAXK6S1eTShihyZzfhy-asNki5K_kwUAnOqtrJZljhccpvlH_psJkbLvG3TVJEN3jeObf2mXkthkhQoSkNLIEgWBFGZHIPEY_O5J67aL73KDlzUxGgaCgYKAUkSARMSFQHGX2MiOrQc5XyI3oYteL5Q880jyw0177", "refresh_token": "1//0gZZwK4VIR2K9CgYIARAAGBASNwF-L9Ir8G81_L9U7qclvFtmBADREyA5DXHphCSfC8Pd-Ut9EMxk171HSfJwtTTBVeLdgfZUw3A", "token_uri": "https://oauth2.googleapis.com/token", "client_id": "123736158724-c9fm6hg9svnhaoeqh1kqutts1q01oru1.apps.googleusercontent.com", "client_secret": "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", "scopes": ["https://www.googleapis.com/auth/calendar"], "universe_domain": "googleapis.com", "account": "", "expiry": "2025-01-10T10:48:26.574972Z"}
utils.py ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import timedelta
2
+ from datetime import datetime
3
+ import os.path
4
+ import logging
5
+ from google.auth.transport.requests import Request
6
+ from google.oauth2.credentials import Credentials
7
+ from google_auth_oauthlib.flow import InstalledAppFlow
8
+ from googleapiclient.discovery import build
9
+ from googleapiclient.errors import HttpError
10
+ import smtplib
11
+ from email.mime.multipart import MIMEMultipart
12
+ from email.mime.text import MIMEText
13
+ import pandas as pd
14
+ from langchain.prompts import PromptTemplate
15
+ from langchain.chains import LLMChain
16
+ from langchain_groq import ChatGroq
17
+ from langchain.agents import tool
18
+ from dotenv import load_dotenv
19
+ from schema import bookSlot,deleteSlot,reschedule_event,listevent,checkevent
20
+ load_dotenv()
21
+ API_KEY= os.environ["API_KEY"]
22
+ # Configure logging
23
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
24
+
25
+ llm = ChatGroq(
26
+ model="llama3-8b-8192",
27
+ temperature=0,
28
+ max_tokens=None,
29
+ timeout=None,
30
+ max_retries=2,
31
+ api_key=API_KEY
32
+ )
33
+ SCOPES = ["https://www.googleapis.com/auth/calendar"]
34
+
35
+ EMAIL_SENDER = "[email protected]"
36
+ EMAIL_PASSWORD = "wlxf poqr wgsh qvqs"
37
+
38
+ def get_service():
39
+ """Create and return the Google Calendar API service."""
40
+ print('this function is called.')
41
+ logging.debug("Initializing Google Calendar API service")
42
+ creds = None
43
+ if os.path.exists("token.json"):
44
+ creds = Credentials.from_authorized_user_file("token.json", SCOPES)
45
+ if not creds or not creds.valid:
46
+ if creds and creds.expired and creds.refresh_token:
47
+ creds.refresh(Request())
48
+ else:
49
+ flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES)
50
+ creds = flow.run_local_server(port=0)
51
+ with open("token.json", "w") as token:
52
+ token.write(creds.to_json())
53
+
54
+ return build("calendar", "v3", credentials=creds)
55
+
56
+ def send_email(to_email, subject, body):
57
+ """Send an email notification to the participants."""
58
+ try:
59
+ msg = MIMEMultipart()
60
+ msg['From'] = EMAIL_SENDER
61
+ msg['To'] = to_email
62
+ msg['Subject'] = subject
63
+ msg.attach(MIMEText(body, 'plain'))
64
+
65
+ server = smtplib.SMTP('smtp.gmail.com', 587)
66
+ server.starttls()
67
+ server.login(EMAIL_SENDER, EMAIL_PASSWORD)
68
+ text = msg.as_string()
69
+ server.sendmail(EMAIL_SENDER, to_email, text)
70
+ server.quit()
71
+
72
+ print(f"Email sent to {to_email}")
73
+ except Exception as e:
74
+ print(f"Failed to send email: {e}")
75
+
76
+
77
+ # def is_valid_booking_time(start_time):
78
+ # # Convert string to datetime object
79
+ # start_time_dt = datetime.strptime(start_time, "%Y-%m-%dT%H:%M:%S%z")
80
+
81
+ # # Ensure the booking is within the next 7 days
82
+ # today = datetime.now(start_time_dt.tzinfo)
83
+ # if start_time_dt < today or start_time_dt > (today + timedelta(days=7)):
84
+ # return False, "You can only book appointments within the next 7 days."
85
+
86
+ # # Ensure the booking is on a weekday and within 10 AM to 7 PM
87
+ # if start_time_dt.weekday() >= 5: # 0 = Monday, 6 = Sunday
88
+ # return False, "Appointments can only be booked Monday to Friday."
89
+
90
+ # if not (10 <= start_time_dt.hour < 19): # Ensure the time is between 10 AM to 7 PM
91
+ # return False, "Appointments can only be scheduled between 10 AM and 7 PM."
92
+
93
+ # return True, None
94
+
95
+ def is_valid_booking_time(start_time):
96
+ # Convert string to datetime object
97
+ logging.debug(start_time)
98
+ start_time_dt = datetime.strptime(start_time, "%Y-%m-%dT%H:%M:%S%z")
99
+ logging.debug(start_time_dt)
100
+ # Get today's date at midnight for date comparison
101
+ today = datetime.now(start_time_dt.tzinfo).replace(
102
+ hour=0, minute=0, second=0, microsecond=0
103
+ )
104
+
105
+ # Get the end of the 7th day
106
+ seven_days_later = (today + timedelta(days=7)).replace(
107
+ hour=23, minute=59, second=59
108
+ )
109
+
110
+ # Check if the date falls within the 7-day window
111
+ if start_time_dt.date() < today.date() or start_time_dt.date() > seven_days_later.date():
112
+ return False, "You can only book appointments within the next 7 days."
113
+
114
+ # Ensure the booking is on a weekday and within 10 AM to 7 PM
115
+ if start_time_dt.weekday() >= 5: # 0 = Monday, 6 = Sunday
116
+ return False, "Appointments can only be booked Monday to Friday."
117
+
118
+ if not (10 <= start_time_dt.hour < 19): # Ensure the time is between 10 AM to 7 PM
119
+ return False, "Appointments can only be scheduled between 10 AM and 7 PM."
120
+
121
+ return True, None
122
+ import pytz
123
+ from datetime import datetime, timedelta
124
+ @tool("check-event", args_schema=checkevent, return_direct=True)
125
+ def check_slots(date):
126
+ """
127
+ This function is to check available slot for a given date, excluding times when events are booked.
128
+ It only returns valid slots that fall on weekdays (Mon-Fri) between 10 AM to 7 PM.
129
+
130
+ Args:
131
+ date (str): The date for which to check availability. Can be 'today', 'tomorrow', or a date string.
132
+
133
+ Returns:
134
+ str: Formatted string of available 1-hour slots or a message if no slots are available.
135
+ """
136
+ logging.debug("Entered into check-slots tool")
137
+
138
+ # Handle relative dates with proper timezone
139
+ ist_tz = pytz.timezone('Asia/Kolkata')
140
+ today = datetime.now(ist_tz)
141
+
142
+ if date.lower() == 'today':
143
+ date = today.strftime('%Y-%m-%d')
144
+ elif date.lower() == 'tomorrow':
145
+ tomorrow = today + timedelta(days=1)
146
+ date = tomorrow.strftime('%Y-%m-%d')
147
+ else:
148
+ try:
149
+ # Try to parse the provided date string
150
+ parsed_date = datetime.strptime(date, '%Y-%m-%d')
151
+ date = parsed_date.strftime('%Y-%m-%d')
152
+ except ValueError:
153
+ return "❌ Invalid date format. Please use 'today', 'tomorrow', or format 'YYYY-MM-DD'"
154
+
155
+ # Define the start and end time for the day
156
+ start_time = f"{date}T10:00:00+05:30" # Start at 10 AM
157
+ end_time = f"{date}T19:00:00+05:30" # End at 7 PM
158
+ service = get_service()
159
+
160
+ # Check if the date is valid (weekday, and within 10 AM to 7 PM)
161
+ valid, message = is_valid_booking_time(start_time)
162
+ if not valid:
163
+ formatted_output = "❌ **Invalid Date Selection**\n\n"
164
+ formatted_output += f"{message}\n"
165
+ formatted_output += "\nAppointments can only be scheduled:\n"
166
+ formatted_output += "πŸ“… Monday to Friday\n"
167
+ formatted_output += "πŸ•’ Between 10 AM and 7 PM\n"
168
+ formatted_output += "πŸ“† Within the next 7 days"
169
+ return formatted_output
170
+
171
+ # For today's date, filter out past hours
172
+ current_time = None
173
+ if date == today.strftime('%Y-%m-%d'):
174
+ current_time = today
175
+ if current_time.hour >= 19: # If it's past 7 PM
176
+ return "❌ No more slots available for today. Please check tomorrow's availability."
177
+
178
+ # Query for events between start and end time
179
+ events_result = service.events().list(
180
+ calendarId='primary',
181
+ timeMin=start_time,
182
+ timeMax=end_time,
183
+ singleEvents=True,
184
+ orderBy='startTime'
185
+ ).execute()
186
+
187
+ events = events_result.get('items', [])
188
+
189
+ # Define the working hours with timezone
190
+ work_start = datetime.fromisoformat(f"{date}T10:00:00+05:30").replace(tzinfo=ist_tz)
191
+ if current_time and current_time.hour >= 10:
192
+ # If it's today and after 10 AM, start from the next hour
193
+ work_start = current_time.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
194
+ work_end = datetime.fromisoformat(f"{date}T19:00:00+05:30").replace(tzinfo=ist_tz)
195
+
196
+ available_slots = []
197
+ current_slot = work_start
198
+
199
+ # Add all available slots
200
+ while current_slot + timedelta(hours=1) <= work_end:
201
+ slot_end = current_slot + timedelta(hours=1)
202
+ is_available = True
203
+
204
+ # Check if slot conflicts with any existing event
205
+ for event in events:
206
+ event_start = datetime.fromisoformat(event['start']['dateTime'])
207
+ event_end = datetime.fromisoformat(event['end']['dateTime'])
208
+
209
+ if (current_slot < event_end and slot_end > event_start):
210
+ is_available = False
211
+ break
212
+
213
+ if is_available:
214
+ available_slots.append((current_slot, slot_end))
215
+ current_slot += timedelta(hours=1)
216
+
217
+ # Return available slots in a properly formatted string
218
+ if available_slots:
219
+ formatted_output = f"πŸ“… **Available Slots for {date}:**\n\n"
220
+ for slot in available_slots:
221
+ start_time = slot[0].strftime('%I:%M %p')
222
+ end_time = slot[1].strftime('%I:%M %p')
223
+ formatted_output += f"πŸ•’ {start_time} - {end_time}\n"
224
+
225
+ formatted_output += "\n✨ Each slot is for a 1-hour appointment."
226
+ formatted_output += "\n\nπŸ’‘ To book an appointment, please specify your preferred time slot."
227
+
228
+ return formatted_output
229
+ else:
230
+ return "πŸ˜” I'm sorry, but there are no available slots for the requested date. Would you like to check another date?"# def check_slots(date):
231
+ # """
232
+ # This function is to check available slot for a given date, excluding times when events are booked.
233
+ # It only returns valid slots that fall on weekdays (Mon-Fri) between 10 AM to 7 PM.
234
+
235
+ # Args:
236
+ # date (str): The date for which to check availability (e.g., '2024-09-17').
237
+
238
+ # Returns:
239
+ # str: Formatted string of available 1-hour slots or a message if no slots are available.
240
+ # """
241
+ # logging.debug("Entered into check-slots tool")
242
+ # # Define the start and end time for the day
243
+ # start_time = f"{date}T10:00:00+05:30" # Start at 10 AM
244
+ # end_time = f"{date}T19:00:00+05:30" # End at 7 PM
245
+ # service = get_service()
246
+
247
+ # # Check if the date is valid (weekday, and within 10 AM to 7 PM)
248
+ # valid, message = is_valid_booking_time(start_time)
249
+ # if not valid:
250
+ # return message
251
+
252
+ # # Query for events between start and end time
253
+ # events_result = service.events().list(
254
+ # calendarId='primary',
255
+ # timeMin=start_time,
256
+ # timeMax=end_time,
257
+ # singleEvents=True,
258
+ # orderBy='startTime'
259
+ # ).execute()
260
+
261
+ # events = events_result.get('items', [])
262
+
263
+ # # Define the working hours (10 AM to 7 PM)
264
+ # work_start = datetime.fromisoformat(f"{date}T10:00:00+05:30")
265
+ # work_end = datetime.fromisoformat(f"{date}T19:00:00+05:30")
266
+ # available_slots = []
267
+
268
+ # # Add all the slots starting from work_start until work_end
269
+ # current_time = work_start
270
+
271
+ # for event in events:
272
+ # event_start = datetime.fromisoformat(event['start']['dateTime'])
273
+ # event_end = datetime.fromisoformat(event['end']['dateTime'])
274
+
275
+ # # Find all available 1-hour slots between current_time and the event start
276
+ # while current_time + timedelta(hours=1) <= event_start:
277
+ # slot_start = current_time
278
+ # slot_end = current_time + timedelta(hours=1)
279
+
280
+ # # Ensure the slot is a valid booking time
281
+ # valid, message = is_valid_booking_time(slot_start.isoformat())
282
+ # if valid:
283
+ # available_slots.append((slot_start, slot_end))
284
+ # current_time += timedelta(hours=1)
285
+
286
+ # # Move current_time to the end of the event
287
+ # current_time = max(current_time, event_end)
288
+
289
+ # # Add slots from the last event to the end of the working day
290
+ # while current_time + timedelta(hours=1) <= work_end:
291
+ # slot_start = current_time
292
+ # slot_end = current_time + timedelta(hours=1)
293
+
294
+ # # Ensure the slot is a valid booking time
295
+ # valid, message = is_valid_booking_time(slot_start.isoformat())
296
+ # if valid:
297
+ # available_slots.append((slot_start, slot_end))
298
+ # current_time += timedelta(hours=1)
299
+
300
+ # # Return available slots in a properly formatted string
301
+ # if available_slots:
302
+ # formatted_output = f"πŸ“… **Available Slots for {date}:\n**\n\n"
303
+ # for slot in available_slots:
304
+ # start_time = slot[0].strftime('%I:%M %p')
305
+ # end_time = slot[1].strftime('%I:%M %p')
306
+ # formatted_output += f"\nπŸ•’ {start_time} - {end_time}\n"
307
+
308
+ # formatted_output += "\n✨ Each slot is for a 1-hour appointment."
309
+ # formatted_output += "\n\nπŸ’‘ To book an appointment, please specify your preferred time slot."
310
+
311
+ # return formatted_output
312
+ # else:
313
+ # return "πŸ˜” I'm sorry, but there are no available slots for the requested date. Would you like to check another date?"
314
+
315
+
316
+ def check_slot_availability(start_time, end_time):
317
+ # Define the Asia/Kolkata timezone
318
+ kolkata_tz = pytz.timezone('Asia/Kolkata')
319
+
320
+ # Parse and localize the start_time and end_time into Asia/Kolkata timezone
321
+ start_time_dt = datetime.strptime(start_time, "%Y-%m-%dT%H:%M:%S%z")
322
+ end_time_dt = datetime.strptime(end_time, "%Y-%m-%dT%H:%M:%S%z")
323
+
324
+ # Ensure the times are correctly converted to ISO format without adding "Z"
325
+ time_min = start_time_dt.isoformat() # Already includes the timezone info
326
+ time_max = end_time_dt.isoformat() # Already includes the timezone info
327
+ service=get_service()
328
+ # Fetch events within the given time range in Asia/Kolkata timezone
329
+ events_result = service.events().list(
330
+ calendarId="primary",
331
+ timeMin=time_min,
332
+ timeMax=time_max,
333
+ singleEvents=True,
334
+ orderBy="startTime"
335
+ ).execute()
336
+
337
+ events = events_result.get("items", [])
338
+ return len(events) == 0 # Returns True if the slot is free
339
+
340
+ def find_event_by_time(start_time):
341
+ """
342
+ Finds an event by its start time in the user's Google Calendar.
343
+
344
+ Args:
345
+ start_time (str): The start time of the event in ISO format (e.g., '2024-09-17T14:30:00+05:30').
346
+
347
+ Returns:
348
+ dict or None: The event details if found, otherwise None.
349
+ """
350
+ try:
351
+ print(f"Searching for event starting at {start_time}")
352
+ service=get_service()
353
+ # Calculate the end time (assuming 1-hour event window)
354
+ start_time_dt = datetime.fromisoformat(start_time)
355
+ end_time_dt = start_time_dt + timedelta(hours=1)
356
+ end_time = end_time_dt.isoformat()
357
+
358
+ # Query Google Calendar API for events in this time window
359
+ events_result = service.events().list(
360
+ calendarId="primary",
361
+ timeMin=start_time,
362
+ timeMax=end_time,
363
+ singleEvents=True,
364
+ orderBy="startTime",
365
+ ).execute()
366
+
367
+ events = events_result.get("items", [])
368
+ print(f"Events found: {events}")
369
+
370
+ # Return the first event that matches the time exactly
371
+ for event in events:
372
+ event_start = event['start'].get('dateTime')
373
+ if event_start == start_time:
374
+ print(f"Matching event found: {event['summary']} at {event_start}")
375
+ return event
376
+
377
+ print(f"No event found starting at {start_time}")
378
+ return None
379
+
380
+ except HttpError as error:
381
+ print(f"An error occurred: {error}")
382
+ return None
383
+ except Exception as e:
384
+ print(f"Unexpected error: {e}")
385
+ return None
386
+
387
+
388
+
389
+
390
+ @tool("list_event", args_schema=listevent, return_direct=True)
391
+ def list_upcoming_events(target_date):
392
+ """
393
+ Lists the upcoming events on the user's calendar for a specific date.
394
+
395
+ Args:
396
+ target_date (str): The date to filter events on, in 'YYYY-MM-DD' format.
397
+
398
+ Returns:
399
+ string: Summary of the events
400
+ """
401
+ service=get_service()
402
+ logging.debug("Entered into list events tools")
403
+ # Parse the target date to create timeMin and timeMax bounds
404
+ # Convert the target date string to a datetime object
405
+ target_date_obj = datetime.strptime(target_date, "%Y-%m-%d")
406
+
407
+ # Set timeMin to the beginning of the day and timeMax to the end of the day
408
+ time_min = target_date_obj.isoformat() + "Z" # Beginning of the day in UTC
409
+ time_max = (target_date_obj + timedelta(days=1)).isoformat() + "Z" # End of the day in UTC
410
+
411
+ print(f"Getting events for {target_date}")
412
+
413
+ try:
414
+ events_result = (
415
+ service.events()
416
+ .list(
417
+ calendarId="primary",
418
+ timeMin=time_min,
419
+ timeMax=time_max,
420
+ singleEvents=True,
421
+ orderBy="startTime",
422
+ )
423
+ .execute()
424
+ )
425
+ events = events_result.get("items", [])
426
+
427
+ if not events:
428
+ print(f"No events found for {target_date}.")
429
+ else:
430
+ for event in events:
431
+ start = event["start"].get("dateTime", event["start"].get("date"))
432
+ result=start, event["summary"]
433
+ return result
434
+ except HttpError as error:
435
+ print(f"An error occurred: {error}")
436
+
437
+ @tool("book-slot-tool", args_schema=bookSlot, return_direct=True)
438
+ def book_slot(start_time, end_time, summary):
439
+ """
440
+ This functions is to boos a appointment/slot for user by creating an event on the calendar.
441
+
442
+ Args:
443
+ start_time (str): The start time of the slot (e.g., '2024-09-16T14:00:00+05:30').
444
+ end_time (str): The end time of the slot (e.g., '2024-09-16T15:00:00+05:30').
445
+ summary (str): Summary or title of the event.
446
+
447
+ Returns:
448
+ str: Confirmation message if the event is created or an error message if booking fails.
449
+ """
450
+ service = get_service()
451
+ logging.debug("Entered into book slot tools")
452
+ is_valid, error_message = is_valid_booking_time(start_time)
453
+
454
+ if not is_valid:
455
+ # Return the error message with proper formatting
456
+ formatted_output = "❌ **Invalid Booking Time**\n\n"
457
+ formatted_output += f"{error_message}\n"
458
+ formatted_output += "\nAppointments can only be scheduled:\n"
459
+ formatted_output += "πŸ“… Monday to Friday\n"
460
+ formatted_output += "πŸ•’ Between 10 AM and 7 PM\n"
461
+ formatted_output += "πŸ“† Within the next 7 days\n"
462
+ return formatted_output
463
+ if not check_slot_availability(start_time, end_time):
464
+ formatted_output = "❌ **Slot Unavailable**\n\n"
465
+ formatted_output += "\nThe requested slot is not available.\n"
466
+ formatted_output += "\nPlease choose another time."
467
+ return formatted_output
468
+
469
+ # Create the event object
470
+ event = {
471
+ 'summary': summary,
472
+ 'start': {
473
+ 'dateTime': start_time, # ISO 8601 format
474
+ 'timeZone': 'Asia/Kolkata', # Use appropriate timezone
475
+ },
476
+ 'end': {
477
+ 'dateTime': end_time,
478
+ 'timeZone': 'Asia/Kolkata',
479
+ },
480
+ }
481
+
482
+ try:
483
+ # Insert the event into the primary calendar
484
+ event_result = service.events().insert(calendarId='primary', body=event).execute()
485
+ formatted_output = "βœ… **Event Created Successfully!**\n\n"
486
+ formatted_output += f"\nπŸ“Œ **Summary:** {summary}\n"
487
+ formatted_output += f"\nπŸ•’ **Start Time:** {start_time}\n"
488
+ formatted_output += f"\nπŸ•’ **End Time:** {end_time}\n\n"
489
+ formatted_output += "\nπŸ“… The event has been added to your primary calendar."
490
+ return formatted_output
491
+ except HttpError as error:
492
+ return f"❌ **An error occurred:** {error}\n\nPlease try again or contact support if the issue persists."
493
+
494
+ @tool("delete-slot-tool", args_schema=deleteSlot, return_direct=True)
495
+ def delete_event(start_time):
496
+ """
497
+ Deletes an event by start time on the user's calendar.
498
+
499
+ Args:
500
+ start_time (str): The start time of the event (e.g., '2024-09-20T15:30:00+05:30').
501
+
502
+ Returns:
503
+ str: Confirmation message if the event is deleted.
504
+ """
505
+ service = get_service()
506
+ logging.debug("Entered into delete events tools")
507
+ event = find_event_by_time(start_time)
508
+
509
+ if event:
510
+ try:
511
+ service.events().delete(calendarId='primary', eventId=event['id']).execute()
512
+ formatted_output = "\nπŸ—‘οΈ **Event Deleted Successfully!**\n\n"
513
+ formatted_output += f"\nπŸ“Œ **Summary:** {event['summary']}\n"
514
+ formatted_output += f"\nπŸ•’ **Start Time:** {event['start']['dateTime']}\n\n"
515
+ formatted_output += "\nThe event has been removed from your primary calendar."
516
+ return formatted_output
517
+ except HttpError as error:
518
+ return f"❌ **An error occurred:** {error}\n\nPlease try again or contact support if the issue persists."
519
+ else:
520
+ formatted_output = "❓ **No Event Found**\n\n"
521
+ formatted_output += f"πŸ•’ **\nRequested Start Time:** {start_time}\n\n"
522
+ formatted_output += "\nNo event was found for the specified time. Please check the time and try again."
523
+ return formatted_output
524
+
525
+
526
+ @tool("reschedule-event-tool", args_schema=reschedule_event, return_direct=True)
527
+ def reschedule_event(start_time, new_start_time, new_end_time):
528
+ """
529
+ Reschedules an existing event by providing new start and end times.
530
+
531
+ Args:
532
+ start_time (str): The start time of the existing event (e.g., '2024-09-18T14:00:00+05:30').
533
+ new_start_time (str): The new start time of the event (e.g., '2024-09-18T12:00:00+05:30').
534
+ new_end_time (str): The new end time of the event (e.g., '2024-09-18T14:00:00+05:30').
535
+
536
+ Returns:
537
+ str: Confirmation message if the event is rescheduled or an error message if rescheduling fails.
538
+ """
539
+ service = get_service()
540
+ logging.debug("Entered into reshedule events tools")
541
+ if not is_valid_booking_time(start_time):
542
+ formatted_output = "❌ **Invalid Booking Time**\n\n"
543
+ formatted_output += "\nAppointments can only be scheduled:\n"
544
+ formatted_output += "\nπŸ“… Monday to Friday\n"
545
+ formatted_output += "\nπŸ•’ Between 10 AM and 7 PM\n"
546
+ formatted_output += "\nπŸ“† Within the next 7 days\n"
547
+ return formatted_output
548
+
549
+ if not check_slot_availability(new_start_time, new_end_time):
550
+ formatted_output = "❌ **Slot Unavailable**\n\n"
551
+ formatted_output += "\nThe requested slot is not available.\n"
552
+ formatted_output += "\nPlease choose another time."
553
+ return formatted_output
554
+
555
+ try:
556
+ event = find_event_by_time(start_time)
557
+
558
+ if not event:
559
+ formatted_output = "❓ **No Event Found**\n\n"
560
+ formatted_output += f"\nπŸ•’ **Requested Start Time:** {start_time}\n\n"
561
+ formatted_output += "\nNo event was found for the specified time. Please check the time and try again."
562
+ return formatted_output
563
+
564
+ event['start']['dateTime'] = new_start_time
565
+ event['end']['dateTime'] = new_end_time
566
+
567
+ updated_event = service.events().update(calendarId='primary', eventId=event['id'], body=event).execute()
568
+
569
+ formatted_output = "βœ… **Event Rescheduled Successfully!**\n\n"
570
+ formatted_output += f"\nπŸ”„ **Original Start Time:** {start_time}\n"
571
+ formatted_output += f"\nπŸ†• **New Start Time:** {new_start_time}\n"
572
+ formatted_output += f"\nπŸ†• **New End Time:** {new_end_time}\n\n"
573
+ formatted_output += "\nYour appointment has been updated in the calendar."
574
+ return formatted_output
575
+
576
+ except HttpError as error:
577
+ return f"❌ **An error occurred:** {error}\n\nPlease try again or contact support if the issue persists."