import datetime from typing import List, Dict, Tuple import pytz from langchain.tools import tool # or whatever decorator you use # ──────────────────────────────────────────────────────────────────────────── # 1️⃣ Keep your existing helper (unchanged) # ──────────────────────────────────────────────────────────────────────────── @tool def get_current_time_in_timezone(timezone: str) -> str: """Return the current local time in a given timezone (YYYY-MM-DD HH:MM:SS).""" try: tz = pytz.timezone(timezone) local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") return local_time except Exception as e: return f"Error: {e}" # ──────────────────────────────────────────────────────────────────────────── # 2️⃣ New tool: find the first 30-minute slot that fits everyone’s workday # ──────────────────────────────────────────────────────────────────────────── @tool def find_overlap_slot( timezones: List[str], workday_start: int = 9, workday_end: int = 18, slot_minutes: int = 30 ) -> str: """ Find the next common slot for a stand-up. Args: timezones: List of IANA tz strings (e.g. ["Europe/Berlin", "Asia/Bishkek"]) workday_start: Local workday start hour (24h clock, default 09) workday_end: Local workday end hour (default 18) slot_minutes: Length of slot to find (default 30) Returns: Human-readable description of the first viable slot, or error msg. """ # 1. Build a list of "free intervals" for each participant now_utc = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) candidates: List[Tuple[datetime.datetime, datetime.datetime]] = [] for tz_name in timezones: try: tz = pytz.timezone(tz_name) except pytz.UnknownTimeZoneError: return f"Unknown timezone: {tz_name}" local_now = now_utc.astimezone(tz) # Next work-day window start_local = local_now.replace(hour=workday_start, minute=0, second=0, microsecond=0) end_local = local_now.replace(hour=workday_end, minute=0, second=0, microsecond=0) if local_now > end_local: # Move to tomorrow start_local += datetime.timedelta(days=1) end_local += datetime.timedelta(days=1) elif local_now > start_local: # Move start forward so we don't schedule in the past start_local = local_now # Convert to UTC for overlap maths candidates.append(( start_local.astimezone(pytz.utc), end_local.astimezone(pytz.utc) )) # 2. Intersect all availability windows slot_start = max(interval[0] for interval in candidates) slot_end = min(interval[1] for interval in candidates) if slot_end - slot_start < datetime.timedelta(minutes=slot_minutes): return "No overlapping work-hour slot found in the next day." # 3. Return the first slot of the requested length chosen_start = slot_start chosen_end = slot_start + datetime.timedelta(minutes=slot_minutes) # 4. Build a friendly summary summary_lines = [ f"✅ Proposed stand-up slot ({slot_minutes} min)", f" • UTC: {chosen_start.strftime('%Y-%m-%d %H:%M')} – {chosen_end.strftime('%H:%M')}" ] for tz_name in timezones: tz = pytz.timezone(tz_name) loc_start = chosen_start.astimezone(tz).strftime('%Y-%m-%d %H:%M') loc_end = chosen_end.astimezone(tz).strftime('%H:%M') summary_lines.append(f" • {tz_name}: {loc_start} – {loc_end}") return "\n".join(summary_lines) # ──────────────────────────────────────────────────────────────────────────── # 3️⃣ Example of an empty placeholder tool you can extend later # ──────────────────────────────────────────────────────────────────────────── @tool def my_custom_tool(arg1: str, arg2: int) -> str: """A template tool you can repurpose for anything.""" return f"Received arg1={arg1}, arg2={arg2}. What magic will you build?"