annator-command-center / tools /calendar_tool.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 10)
a37e6db verified
Raw
History Blame Contribute Delete
12.6 kB
"""
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