Spaces:
Runtime error
Runtime error
File size: 9,507 Bytes
465fb49 |
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 |
from agentpro.tools import Tool
from typing import Any
import datetime
import dateutil.parser
import re
class DailyEventSummaryTool(Tool):
# ββ 1) Class attributes (Pydantic fields) βββββββββββββββββββββββββββ
name: str = "Daily Event Summary"
description: str = (
"Provide a narrative summary of Google Calendar events for any requested day. "
"You can ask for today, tomorrow, day after tomorrow, last day of this year, "
"a weekday of the current week (e.g., 'Wednesday'), or an explicit date like 'June 10, 2025'."
)
action_type: str = "daily_event_summary"
input_format: str = (
"Natural language calendar query. Examples:\n"
" β’ \"Whatβs on my calendar today?\"\n"
" β’ \"Show me my schedule tomorrow.\"\n"
" β’ \"Plan for day after tomorrow.\"\n"
" β’ \"Events on the last day of this year.\"\n"
" β’ \"What do I have on Wednesday?\"\n"
" β’ \"What do I have on June 10, 2025?\"\n"
)
# ββ 2) We expect a Google Calendar βserviceβ to be passed in at instantiation ββ
service: Any
def run(self, input_text: Any) -> str:
"""
Determine which calendar day the user wants and return a single-sentence
narrative describing each event's start time, end time, and duration.
Supported queries:
- βtodayβ
- βtomorrowβ
- βday after tomorrowβ
- βlast day of this yearβ (Dec 31, current year)
- any weekday of the current week (e.g., βMondayβ, βFridayβ)
- explicit dates (e.g., βJune 10, 2025β or β2025-06-10β)
"""
text = str(input_text).lower()
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# A) Handle relative-day keywords and weekdays of this week
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
today_utc = datetime.datetime.utcnow().date()
target_date = None
# 1) βtodayβ
if "today" in text:
target_date = today_utc
# 2) βtomorrowβ (but not βday after tomorrowβ)
elif "tomorrow" in text and "day after" not in text:
target_date = today_utc + datetime.timedelta(days=1)
# 3) βday after tomorrowβ
elif "day after tomorrow" in text:
target_date = today_utc + datetime.timedelta(days=2)
# 4) βlast day of this yearβ or βlast day of the yearβ
elif "last day of this year" in text or "last day of the year" in text:
year = today_utc.year
target_date = datetime.date(year, 12, 31)
else:
# 5) Try to match a weekday name in the current week
weekdays = {
"monday": 1,
"tuesday": 2,
"wednesday": 3,
"thursday": 4,
"friday": 5,
"saturday": 6,
"sunday": 7
}
for name, iso_num in weekdays.items():
if name in text:
# Compute offset from today's ISO weekday to the requested one
today_iso = today_utc.isoweekday() # Monday=1 ... Sunday=7
delta_days = iso_num - today_iso
target_date = today_utc + datetime.timedelta(days=delta_days)
break
# 6) If still None, try to parse an explicit date
if target_date is None and re.search(r"\d", text):
try:
parsed_dt = dateutil.parser.parse(text, fuzzy=True)
target_date = parsed_dt.date()
except (ValueError, OverflowError):
target_date = None
# If we still don't have a date, return fallback instructions
if target_date is None:
return (
"Sorry, I couldnβt figure out which day you mean.\n"
"Please ask for:\n"
" β’ βtodayβ\n"
" β’ βtomorrowβ\n"
" β’ βday after tomorrowβ\n"
" β’ βlast day of this yearβ\n"
" β’ a weekday this week (e.g., βWednesdayβ)\n"
" β’ or specify an explicit date (e.g., βJune 10, 2025β)."
)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# B) Build UTCβbased timestamps for that entire day: [00:00 β next 00:00)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
start_of_day = datetime.datetime.combine(target_date, datetime.time.min).isoformat() + "Z"
end_of_day = (
datetime.datetime.combine(target_date, datetime.time.min)
+ datetime.timedelta(days=1)
).isoformat() + "Z"
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# C) Query Google Calendar API for events in that window
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
events_res = (
self.service.events()
.list(
calendarId="primary",
timeMin=start_of_day,
timeMax=end_of_day,
singleEvents=True,
orderBy="startTime"
)
.execute()
)
items = events_res.get("items", [])
if not items:
return f"You have no events scheduled for {target_date.strftime('%B %d, %Y')}."
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# D) Build narrative: βOn {date}, first meeting is βXβ, which will start at {h:mm AM/PM}
# and end at {h:mm AM/PM} (Duration: {N hours M minutes}), and second meeting is β¦.β
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ordinals = [
"first", "second", "third", "fourth", "fifth",
"sixth", "seventh", "eighth", "ninth", "tenth"
]
narrative_parts = []
for idx, ev in enumerate(items):
start_raw = ev["start"].get("dateTime")
end_raw = ev["end"].get("dateTime")
summary = ev.get("summary", "(no title)")
if start_raw and end_raw:
# Timed event
start_dt = datetime.datetime.fromisoformat(start_raw.replace("Z", "+00:00"))
end_dt = datetime.datetime.fromisoformat(end_raw.replace("Z", "+00:00"))
# Format βH:MM AM/PMβ
start_str = start_dt.strftime("%I:%M %p").lstrip("0")
end_str = end_dt.strftime("%I:%M %p").lstrip("0")
# Compute duration
duration_ts = end_dt - start_dt
total_seconds = int(duration_ts.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
if hours and minutes:
duration_str = f"{hours} hours {minutes} minutes"
elif hours:
duration_str = f"{hours} hours"
else:
duration_str = f"{minutes} minutes"
ordinal = ordinals[idx] if idx < len(ordinals) else f"{idx+1}th"
part = (
f"{ordinal} meeting is β{summary},β which will start at {start_str} "
f"and end at {end_str} (Duration: {duration_str})"
)
else:
# All-day event
start_date = ev["start"].get("date")
ordinal = ordinals[idx] if idx < len(ordinals) else f"{idx+1}th"
part = f"{ordinal} event is β{summary},β which is an all-day event on {start_date}"
narrative_parts.append(part)
# Join all parts with β, and β¦β
joined = ", and ".join(narrative_parts)
date_str = target_date.strftime("%B %d, %Y")
return f"On {date_str}, {joined}." |