Skip to content

Commit 9ccd21b

Browse files
committed
feat (intent): added gcal intent
1 parent fe6d059 commit 9ccd21b

5 files changed

Lines changed: 213 additions & 2 deletions

File tree

src/model/app/app.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
from model.context.gmail import GmailContextEngine
5959
from model.context.internet import InternetSearchContextEngine
60+
from model.context.gcalendar import GCalendarContextEngine
6061

6162
# Load environment variables from .env file
6263
load_dotenv("model/.env")
@@ -519,13 +520,15 @@ async def startup_event():
519520
asyncio.create_task(cleanup_tasks_periodically())
520521

521522
user_id = "user1" # Replace with dynamic user ID retrieval if needed
522-
enabled_data_sources = ["gmail", "internet_search"] # Add internet_search here
523+
enabled_data_sources = ["gmail", "internet_search", "gcalendar"] # Add gcalendar here
523524

524525
for source in enabled_data_sources:
525526
if source == "gmail":
526527
engine = GmailContextEngine(user_id, task_queue, memory_backend, manager, db_lock)
527528
elif source == "internet_search":
528529
engine = InternetSearchContextEngine(user_id, task_queue, memory_backend, manager, db_lock)
530+
elif source == "gcalendar":
531+
engine = GCalendarContextEngine(user_id, task_queue, memory_backend, manager, db_lock)
529532
else:
530533
continue # Skip unrecognized sources
531534
asyncio.create_task(engine.start())

src/model/context/formats.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,51 @@
6060
},
6161
"additionalProperties": False,
6262
"description": "JSON format for only messages"
63+
}
64+
65+
gcalendar_context_engine_required_format = {
66+
"type": "object",
67+
"properties": {
68+
"tasks": {
69+
"type": "array",
70+
"items": {
71+
"type": "object",
72+
"properties": {
73+
"description": {
74+
"type": "string",
75+
"description": "Description of the task"
76+
},
77+
"priority": {
78+
"type": "integer",
79+
"description": "Priority of the task",
80+
"enum": [0, 1, 2]
81+
}
82+
},
83+
"required": ["description", "priority"],
84+
"additionalProperties": False
85+
},
86+
"description": "List of tasks"
87+
},
88+
"memory_operations": {
89+
"type": "array",
90+
"items": {
91+
"type": "object",
92+
"properties": {
93+
"text": {
94+
"type": "string",
95+
"description": "Text for memory operation"
96+
}
97+
},
98+
"required": ["text"],
99+
"additionalProperties": False
100+
},
101+
"description": "List of memory operations"
102+
},
103+
"message": {
104+
"type": "string",
105+
"description": "Message to the user"
106+
}
107+
},
108+
"additionalProperties": False,
109+
"description": "JSON format for tasks, memory operations, and message"
63110
}

src/model/context/gcalendar.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from model.context.base import BaseContextEngine
2+
from model.agents.functions import authenticate_gcalendar
3+
from model.context.runnables import get_gcalendar_context_runnable
4+
from datetime import datetime, timedelta
5+
from dateutil import parser
6+
import asyncio
7+
8+
class GCalendarContextEngine(BaseContextEngine):
9+
"""Context Engine for processing Google Calendar data."""
10+
11+
def __init__(self, *args, **kwargs):
12+
print("GCalendarContextEngine.__init__ started")
13+
super().__init__(*args, **kwargs)
14+
print("GCalendarContextEngine.__init__ - calling authenticate_gcalendar()")
15+
self.gcalendar_service = authenticate_gcalendar()
16+
print("GCalendarContextEngine.__init__ - gcalendar_service authenticated")
17+
self.category = "gcalendar"
18+
print(f"GCalendarContextEngine.__init__ - category set to: {self.category}")
19+
print("GCalendarContextEngine.__init__ finished")
20+
21+
async def start(self):
22+
"""Start the engine, running periodically every hour."""
23+
print("GCalendarContextEngine.start started")
24+
while True:
25+
print("GCalendarContextEngine.start - running engine iteration")
26+
await self.run_engine()
27+
print("GCalendarContextEngine.start - engine iteration finished, sleeping for 3600 seconds")
28+
await asyncio.sleep(3600) # Check every hour
29+
30+
async def fetch_new_data(self):
31+
"""Fetch upcoming events from Google Calendar for the next 24 hours."""
32+
print("GCalendarContextEngine.fetch_new_data started")
33+
now = datetime.utcnow().isoformat() + "Z"
34+
time_max = (datetime.utcnow() + timedelta(days=1)).isoformat() + "Z"
35+
print(f"GCalendarContextEngine.fetch_new_data - fetching events from {now} to {time_max}")
36+
events_result = self.gcalendar_service.events().list(
37+
calendarId="primary",
38+
timeMin=now,
39+
timeMax=time_max,
40+
maxResults=10,
41+
singleEvents=True,
42+
orderBy="startTime"
43+
).execute()
44+
events = events_result.get("items", [])
45+
print(f"GCalendarContextEngine.fetch_new_data - fetched {len(events)} events")
46+
47+
# Update context with the last fetch time
48+
self.context["gcalendar"] = self.context.get("gcalendar", {})
49+
self.context["gcalendar"]["last_fetched"] = now
50+
print("GCalendarContextEngine.fetch_new_data - saving context")
51+
await self.save_context()
52+
print(f"GCalendarContextEngine.fetch_new_data - returning {len(events)} events")
53+
print("GCalendarContextEngine.fetch_new_data finished")
54+
return events
55+
56+
async def process_new_data(self, new_events):
57+
"""Process new calendar events into summaries."""
58+
print("GCalendarContextEngine.process_new_data started")
59+
print(f"GCalendarContextEngine.process_new_data - processing {len(new_events)} new events")
60+
summaries = []
61+
for event in new_events:
62+
event_summary = event.get("summary", "No title")
63+
start_time = parser.parse(event["start"].get("dateTime", event["start"].get("date")))
64+
start_time_str = start_time.strftime("%Y-%m-%d %H:%M")
65+
summary = f"You have an event '{event_summary}' at {start_time_str}"
66+
summaries.append(summary)
67+
print(f"GCalendarContextEngine.process_new_data - generated summary: {summary}")
68+
joined_summaries = "\n".join(summaries)
69+
print(f"GCalendarContextEngine.process_new_data - joined summaries: {joined_summaries}")
70+
print("GCalendarContextEngine.process_new_data finished")
71+
return joined_summaries
72+
73+
async def get_runnable(self):
74+
"""Return the GCalendar-specific runnable."""
75+
print("GCalendarContextEngine.get_runnable started")
76+
runnable = get_gcalendar_context_runnable()
77+
print(f"GCalendarContextEngine.get_runnable - returning runnable: {runnable}")
78+
print("GCalendarContextEngine.get_runnable finished")
79+
return runnable
80+
81+
async def get_category(self):
82+
"""Return the memory category for GCalendar."""
83+
print("GCalendarContextEngine.get_category started")
84+
print(f"GCalendarContextEngine.get_category - returning category: {self.category}")
85+
print("GCalendarContextEngine.get_category finished")
86+
return self.category

src/model/context/prompts.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,53 @@
8484
Chat History:
8585
{chat_history}
8686
87-
Generate the appropriate messages based on the above information."""
87+
Generate the appropriate messages based on the above information."""
88+
89+
gcalendar_context_engine_system_prompt_template = """You are the Context Engine for Google Calendar, responsible for processing upcoming event information and generating appropriate tasks, memory operations, and messages for the user.
90+
91+
You will receive the following inputs:
92+
93+
- New information: Summaries of upcoming events from Google Calendar.
94+
- Related memories: Existing memories related to Google Calendar.
95+
- Ongoing tasks: Current tasks in the task queue.
96+
- Chat history: Recent chat messages with the user.
97+
98+
Based on this, generate:
99+
100+
- Tasks to do: New tasks the user might need to perform based on the upcoming events (e.g., prepare for a meeting, buy a gift for a birthday).
101+
- Memory operations: Add, update, or delete memories based on the events (e.g., remember a meeting time, update event details).
102+
- Message: A message to send to the user to inform them about the upcoming events or remind them of tasks.
103+
104+
Consider the user's current context, ongoing tasks, and chat history to make relevant suggestions.
105+
106+
Output a JSON object with the following structure:
107+
{
108+
"tasks": [
109+
{
110+
"description": "Task description",
111+
"priority": 1
112+
}
113+
],
114+
"memory_operations": [
115+
{
116+
"text": "Memory text"
117+
}
118+
],
119+
"message": "Message to the user"
120+
}
121+
122+
Only include sections that have items. If there are no tasks, memory operations, or message, omit those fields."""
123+
124+
gcalendar_context_engine_user_prompt_template = """Upcoming Event Summaries:
125+
{new_information}
126+
127+
Related Memories:
128+
{related_memories}
129+
130+
Ongoing Tasks:
131+
{ongoing_tasks}
132+
133+
Chat History:
134+
{chat_history}
135+
136+
Generate the appropriate tasks, memory operations, and message based on the above information."""

src/model/context/runnables.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,30 @@ def get_internet_search_context_runnable():
5555
required_format=internet_search_context_engine_required_format,
5656
response_type="json",
5757
stateful=False,
58+
)
59+
60+
def get_gcalendar_context_runnable():
61+
"""Configure and return a Runnable for the GCalendar Context Engine."""
62+
model_mapping = {
63+
"openai": (os.getenv("OPENAI_API_URL"), OpenAIRunnable),
64+
"claude": (os.getenv("CLAUDE_API_URL"), ClaudeRunnable),
65+
"gemini": (os.getenv("GEMINI_API_URL"), GeminiRunnable),
66+
}
67+
model_name, provider = get_selected_model() # Assume this function exists
68+
69+
if provider and provider in model_mapping:
70+
model_url, runnable_class = model_mapping[provider]
71+
else:
72+
model_url = os.getenv("BASE_MODEL_URL")
73+
runnable_class = OllamaRunnable # Default fallback, adjust as needed
74+
75+
return runnable_class(
76+
model_url=model_url,
77+
model_name=model_name,
78+
system_prompt_template=gcalendar_context_engine_system_prompt_template,
79+
user_prompt_template=gcalendar_context_engine_user_prompt_template,
80+
input_variables=["new_information", "related_memories", "ongoing_tasks", "chat_history"],
81+
required_format=gcalendar_context_engine_required_format,
82+
response_type="json",
83+
stateful=False,
5884
)

0 commit comments

Comments
 (0)