"""Calendar agent — events, conflict detection, and scheduling.""" from __future__ import annotations import json from typing import Any from langchain_core.messages import HumanMessage, SystemMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI from app.config.settings import settings from app.core.agent_registry import ChatAgent, registry _SYSTEM_PROMPT = ( "You are a calendar management assistant. Help the user manage events, " "detect scheduling conflicts, and suggest reschedules.\n" "Rules:\n" " - Work exclusively with event metadata provided in context\n" " - Never store or reference raw calendar data\n" " - date_range format: ISO 8601 interval, e.g. '2024-01-01/2024-01-07'\n" " - Always confirm the date/time scope of any operation" ) @tool async def list_events(date_range: str) -> str: """List calendar events in a date range (ISO 8601 interval, e.g. '2024-01-01/2024-01-07').""" return json.dumps({ "action": "list", "table": "events", "filters": {"date_range": date_range}, }) @tool async def detect_conflicts(events: str) -> str: """Detect scheduling conflicts in a JSON array of event metadata objects.""" return json.dumps({ "action": "analyse", "table": "events", "input": events, "result": "conflicts_detected", }) @tool async def suggest_reschedule(conflict: str) -> str: """Suggest a reschedule for a conflicting event. Pass the conflict as a JSON string.""" return json.dumps({ "action": "suggest_reschedule", "table": "events", "input": conflict, }) @registry.register class CalendarAgent(ChatAgent): def get_name(self) -> str: return "calendar_agent" def get_description(self) -> str: return "Calendar management: events, conflicts, scheduling" def get_tools(self) -> list[Any]: return [list_events, detect_conflicts, suggest_reschedule] async def handle(self, query: str, context: dict[str, Any]) -> str: llm = ChatOpenAI(model="gpt-4o", temperature=0, api_key=settings.OPENAI_API_KEY) messages = [ SystemMessage(content=_SYSTEM_PROMPT), HumanMessage( content=f"User query: {query}\nContext: {json.dumps(context)[:1000]}" ), ] return await self._tool_loop(llm, messages, self.get_tools())