Spaces:
Sleeping
Sleeping
| """ | |
| Calendar Tool - Google Calendar Integration | |
| Provides Google Calendar operations with governance integration. | |
| Supports: | |
| - Checking schedule conflicts | |
| - Getting upcoming events | |
| - Creating and updating calendar events | |
| - Deleting events | |
| Governance: | |
| - Read actions (get_events, check_conflicts): INTERN+ maturity | |
| - Write actions (create_event, update_event, delete_event): SUPERVISED+ maturity | |
| - STUDENT agents blocked from all calendar operations | |
| """ | |
| import asyncio | |
| from typing import Any, Dict, List, Optional, Tuple | |
| from datetime import datetime, timedelta | |
| from core.database import get_db_session | |
| from core.governance_cache import GovernanceCache | |
| from core.models import AgentRegistry | |
| from integrations.google_calendar_service import google_calendar_service | |
| from core.structured_logger import get_logger | |
| logger = get_logger(__name__) | |
| # Initialize governance cache | |
| _governance_cache = GovernanceCache() | |
| class CalendarTool: | |
| """ | |
| Calendar tool for AI agents. | |
| Provides access to Google Calendar for scheduling and event management: | |
| - Get upcoming events | |
| - Check for scheduling conflicts | |
| - Create new events and meetings | |
| - Update or reschedule existing events | |
| - Cancel/delete events | |
| **Governance**: | |
| - Read operations (get_events, check_conflicts): INTERN+ maturity | |
| - Write operations (create_event, update_event, delete_event): SUPERVISED+ maturity | |
| - STUDENT agents blocked from all operations | |
| Examples: | |
| - "What is my schedule for today?" | |
| - "Check if I am free tomorrow at 2 PM" | |
| - "Schedule a meeting with the design team on Friday at 10 AM" | |
| - "Reschedule my 1 PM meeting to 3 PM" | |
| """ | |
| def __init__(self): | |
| """Initialize Calendar tool.""" | |
| # Governance cache for permission checks | |
| self.governance_cache = GovernanceCache() | |
| # Verify the service is authenticating | |
| try: | |
| # We don't block initialization if auth fails, as token might be added later | |
| authenticated = google_calendar_service.authenticate() | |
| logger.info("CalendarTool initialized", tool_available=True, authenticated=authenticated) | |
| except Exception as e: | |
| logger.warning(f"CalendarTool initialized but auth failed: {e}") | |
| async def run( | |
| self, | |
| action: str, | |
| agent_id: Optional[str] = None, | |
| maturity_level: Optional[str] = None, | |
| user_id: Optional[str] = None, | |
| **kwargs | |
| ) -> Dict[str, Any]: | |
| """ | |
| Execute Calendar operation with governance enforcement. | |
| Args: | |
| action: Operation to perform (get_events, create_event, etc.) | |
| agent_id: Agent identifier for governance check | |
| user_id: User ID for token lookup | |
| **kwargs: Action-specific parameters | |
| Returns: | |
| Dict with operation results | |
| Raises: | |
| PermissionError: If maturity level too low | |
| """ | |
| # Governance check - maturity requirements by action type | |
| read_actions = { | |
| "get_events", | |
| "check_conflicts" | |
| } | |
| write_actions = { | |
| "create_event", | |
| "update_event", | |
| "delete_event" | |
| } | |
| # Determine required maturity level | |
| if action in read_actions: | |
| required_maturity = "INTERN" | |
| elif action in write_actions: | |
| required_maturity = "SUPERVISED" | |
| else: | |
| return { | |
| "success": False, | |
| "error": f"Unknown action: {action}", | |
| "available_actions": sorted(read_actions | write_actions) | |
| } | |
| # Check governance permission | |
| allowed, reason = await self._check_calendar_permission( | |
| agent_id=agent_id, | |
| user_id=user_id or "default", | |
| action=action, | |
| required_maturity=required_maturity | |
| ) | |
| if not allowed: | |
| logger.warning( | |
| "Calendar permission denied", | |
| action=action, | |
| agent_id=agent_id, | |
| reason=reason | |
| ) | |
| raise PermissionError(reason) | |
| # Execute action | |
| try: | |
| result = await self._execute_action( | |
| action=action, | |
| user_id=user_id or "default", | |
| **kwargs | |
| ) | |
| logger.info( | |
| "Calendar action completed", | |
| action=action, | |
| agent_id=agent_id, | |
| success=result.get("success", False) | |
| ) | |
| return result | |
| except PermissionError: | |
| raise | |
| except Exception as e: | |
| logger.error( | |
| "Calendar action failed", | |
| action=action, | |
| error=str(e), | |
| kwargs=kwargs | |
| ) | |
| return { | |
| "success": False, | |
| "error": str(e), | |
| "action": action | |
| } | |
| async def _check_calendar_permission( | |
| self, | |
| agent_id: Optional[str], | |
| user_id: str, | |
| action: str, | |
| required_maturity: str | |
| ) -> Tuple[bool, Optional[str]]: | |
| """ | |
| Check if agent has permission for Calendar operation. | |
| """ | |
| # If no agent_id, it's a human-triggered action (allow) | |
| if not agent_id: | |
| return True, None | |
| # Check governance cache | |
| cache_key = f"calendar_{action}" | |
| cached = _governance_cache.get(agent_id, cache_key) | |
| if cached: | |
| return cached.get("allowed", False), cached.get("reason") | |
| # Check agent maturity level from database | |
| try: | |
| with get_db_session() as db: | |
| agent = db.query(AgentRegistry).filter( | |
| AgentRegistry.id == agent_id | |
| ).first() | |
| if not agent: | |
| return False, f"Agent '{agent_id}' not found" | |
| # Check maturity level | |
| maturity = agent.maturity_level | |
| maturity_order = ["STUDENT", "INTERN", "SUPERVISED", "AUTONOMOUS"] | |
| try: | |
| current_level = maturity_order.index(maturity) | |
| required_level = maturity_order.index(required_maturity) | |
| except ValueError: | |
| return False, f"Invalid maturity level: {maturity}" | |
| allowed = current_level >= required_level | |
| reason = None | |
| if not allowed: | |
| reason = ( | |
| f"Calendar {action} requires {required_maturity}+ maturity " | |
| f"(agent is {maturity})" | |
| ) | |
| # Cache decision | |
| _governance_cache.set(agent_id, cache_key, { | |
| "allowed": allowed, | |
| "reason": reason, | |
| "maturity": maturity | |
| }) | |
| return allowed, reason | |
| except Exception as e: | |
| logger.error("Permission check failed", error=str(e)) | |
| return False, f"Permission check failed: {str(e)}" | |
| async def _execute_action( | |
| self, | |
| action: str, | |
| user_id: str, | |
| **kwargs | |
| ) -> Dict[str, Any]: | |
| """ | |
| Execute specific Calendar action. | |
| """ | |
| # Ensure authenticated | |
| if not google_calendar_service.authenticate(): | |
| return { | |
| "success": False, | |
| "error": "Google Calendar is not authenticated. Please connect your Google account in Settings." | |
| } | |
| # Execute action | |
| if action == "get_events": | |
| time_min_str = kwargs.get("time_min") | |
| time_max_str = kwargs.get("time_max") | |
| max_results = kwargs.get("max_results", 100) | |
| time_min = datetime.fromisoformat(time_min_str) if time_min_str else datetime.utcnow() | |
| time_max = datetime.fromisoformat(time_max_str) if time_max_str else (time_min + timedelta(days=7)) | |
| events = await google_calendar_service.get_events( | |
| time_min=time_min, | |
| time_max=time_max, | |
| max_results=max_results | |
| ) | |
| return { | |
| "success": True, | |
| "action": "get_events", | |
| "count": len(events), | |
| "events": events | |
| } | |
| elif action == "check_conflicts": | |
| start_time_str = kwargs.get("start_time") | |
| end_time_str = kwargs.get("end_time") | |
| if not start_time_str or not end_time_str: | |
| return {"success": False, "error": "start_time and end_time parameters required"} | |
| start_time = datetime.fromisoformat(start_time_str) | |
| end_time = datetime.fromisoformat(end_time_str) | |
| result = await google_calendar_service.check_conflicts( | |
| start_time=start_time, | |
| end_time=end_time | |
| ) | |
| result["action"] = "check_conflicts" | |
| return result | |
| elif action == "create_event": | |
| title = kwargs.get("title") | |
| start_time = kwargs.get("start_time") | |
| end_time = kwargs.get("end_time") | |
| if not title or not start_time or not end_time: | |
| return {"success": False, "error": "title, start_time, and end_time parameters required"} | |
| event_data = { | |
| "title": title, | |
| "description": kwargs.get("description", ""), | |
| "start_time": start_time, | |
| "end_time": end_time, | |
| "location": kwargs.get("location", ""), | |
| "attendees": kwargs.get("attendees", []) | |
| } | |
| event = await google_calendar_service.create_event(event_data) | |
| if event: | |
| return { | |
| "success": True, | |
| "action": "create_event", | |
| "event": event | |
| } | |
| else: | |
| return { | |
| "success": False, | |
| "error": "Failed to create event" | |
| } | |
| elif action == "update_event": | |
| event_id = kwargs.get("event_id") | |
| updates = kwargs.get("updates") | |
| if not event_id or not updates: | |
| return {"success": False, "error": "event_id and updates object required"} | |
| event = await google_calendar_service.update_event( | |
| event_id=event_id, | |
| updates=updates | |
| ) | |
| if event: | |
| return { | |
| "success": True, | |
| "action": "update_event", | |
| "event": event | |
| } | |
| else: | |
| return { | |
| "success": False, | |
| "error": f"Failed to update event {event_id}" | |
| } | |
| elif action == "delete_event": | |
| event_id = kwargs.get("event_id") | |
| if not event_id: | |
| return {"success": False, "error": "event_id required"} | |
| success = await google_calendar_service.delete_event(event_id=event_id) | |
| return { | |
| "success": success, | |
| "action": "delete_event", | |
| "event_id": event_id | |
| } | |
| else: | |
| return { | |
| "success": False, | |
| "error": f"Unknown action: {action}", | |
| "available_actions": ["get_events", "check_conflicts", "create_event", "update_event", "delete_event"] | |
| } | |
| # Tool registration function | |
| def register_calendar_tool(tool_registry=None): | |
| """ | |
| Register CalendarTool with tool registry. | |
| """ | |
| from tools.registry import ToolRegistry, get_tool_registry | |
| if tool_registry is None: | |
| tool_registry = get_tool_registry() | |
| calendar_tool = CalendarTool() | |
| tool_registry.register( | |
| name="calendar_tool", | |
| function=calendar_tool.run, | |
| version="1.0.0", | |
| description="Google Calendar tool for AI agents. Provides event creation, conflict checking, and schedule retrieval.", | |
| category="productivity", | |
| complexity=3, | |
| maturity_required="INTERN", | |
| dependencies=["google-api-python-client", "google-auth-oauthlib"], | |
| tags=["calendar", "schedule", "meeting", "events", "time", "google"] | |
| ) | |
| logger.info("CalendarTool registered with ToolRegistry") | |
| return calendar_tool | |